diff --git a/app/main/check_packs/pack_config.py b/app/main/check_packs/pack_config.py
index 91e08134..471949ae 100644
--- a/app/main/check_packs/pack_config.py
+++ b/app/main/check_packs/pack_config.py
@@ -50,6 +50,7 @@
["empty_task_page_check"],
["water_in_the_text_check"],
["report_task_tracker"],
+ ["references_in_chapter_check"],
]
DEFAULT_TYPE = 'pres'
diff --git a/app/main/checks/report_checks/__init__.py b/app/main/checks/report_checks/__init__.py
index 30f22617..faa3275e 100644
--- a/app/main/checks/report_checks/__init__.py
+++ b/app/main/checks/report_checks/__init__.py
@@ -34,3 +34,4 @@
from .task_tracker import ReportTaskTracker
from .paragraphs_count_check import ReportParagraphsCountCheck
from .template_name import ReportTemplateNameCheck
+from .lit_ref_in_spec_chapter import LitRefInChapter
diff --git a/app/main/checks/report_checks/banned_words_check.py b/app/main/checks/report_checks/banned_words_check.py
index 351b403e..58c67cd3 100644
--- a/app/main/checks/report_checks/banned_words_check.py
+++ b/app/main/checks/report_checks/banned_words_check.py
@@ -5,12 +5,13 @@
class ReportBannedWordsCheck(BaseReportCriterion):
label = "Проверка наличия запретных слов в тексте отчёта"
- description = 'Запрещено упоминание слова "мы"'
+ description = 'Запрещено упоминание определенных "опасных" слов'
id = 'banned_words_check'
def __init__(self, file_info, headers_map=None):
super().__init__(file_info)
self.words = []
+ self.warned_words = []
self.min_count = 0
self.max_count = 0
if headers_map:
@@ -21,12 +22,14 @@ def __init__(self, file_info, headers_map=None):
def late_init(self):
self.headers_main = self.file.get_main_headers(self.file_type['report_type'])
if self.headers_main in StyleCheckSettings.CONFIGS.get(self.config):
- self.words = [morph.normal_forms(word)[0] for word in StyleCheckSettings.CONFIGS.get(self.config)[self.headers_main]['banned_words']]
+ self.words = {morph.normal_forms(word)[0] for word in StyleCheckSettings.CONFIGS.get(self.config)[self.headers_main]['banned_words']}
+ self.warned_words = {morph.normal_forms(word)[0] for word in StyleCheckSettings.CONFIGS.get(self.config)[self.headers_main]['warned_words']}
self.min_count = StyleCheckSettings.CONFIGS.get(self.config)[self.headers_main]['min_count_for_banned_words_check']
self.max_count = StyleCheckSettings.CONFIGS.get(self.config)[self.headers_main]['max_count_for_banned_words_check']
else:
if 'any_header' in StyleCheckSettings.CONFIGS.get(self.config):
- self.words = [morph.normal_forms(word)[0] for word in StyleCheckSettings.CONFIGS.get(self.config)['any_header']['banned_words']]
+ self.words = {morph.normal_forms(word)[0] for word in StyleCheckSettings.CONFIGS.get(self.config)['any_header']['banned_words']}
+ self.warned_words = {morph.normal_forms(word)[0] for word in StyleCheckSettings.CONFIGS.get(self.config)['any_header']['warned_words']}
self.min_count = StyleCheckSettings.CONFIGS.get(self.config)['any_header']['min_count_for_banned_words_check']
self.max_count = StyleCheckSettings.CONFIGS.get(self.config)['any_header']['max_count_for_banned_words_check']
@@ -34,29 +37,35 @@ def check(self):
if self.file.page_counter() < 4:
return answer(False, "В отчете недостаточно страниц. Нечего проверять.")
self.late_init()
- detected_lines = {}
result_str = f'Запрещенные слова: {"; ".join(self.words)}
'
- count = 0
+ banned_counter = {'words': self.words, 'detected_lines': {}, 'count': 0}
+ warned_counter = {'words': self.warned_words,'detected_lines': {}, 'count': 0}
for k, v in self.file.pdf_file.get_text_on_page().items():
lines_on_page = re.split(r'\n', v)
for index, line in enumerate(lines_on_page):
- words_on_line = re.split(r'[^\w-]+', line)
- words_on_line = [morph.normal_forms(word)[0] for word in words_on_line]
- count_banned_words = set(words_on_line).intersection(self.words)
- if count_banned_words:
- count += len(count_banned_words)
- if k not in detected_lines.keys():
- detected_lines[k] = []
- detected_lines[k].append(f'Строка {index + 1}: {line} [{"; ".join(count_banned_words)}]')
- if len(detected_lines):
+ words_on_line = {morph.normal_forms(word)[0] for word in re.split(r'[^\w-]+', line)}
+ for counter in (banned_counter, warned_counter):
+ count_banned_words = words_on_line.intersection(counter['words'])
+ if count_banned_words:
+ counter['count'] += len(count_banned_words)
+ if k not in counter['detected_lines'].keys():
+ counter['detected_lines'][k] = []
+ counter['detected_lines'][k].append(f'Строка {index + 1}: {line} [{"; ".join(count_banned_words)}]')
+ if len(banned_counter['detected_lines']):
result_str += 'Обнаружены запретные слова!
'
- for k, v in detected_lines.items():
- result_str += f'Страница №{k}:
{"
".join(detected_lines[k])}
'
+ for k, v in banned_counter['detected_lines'].items():
+ result_str += f'Страница №{k}:
{"
".join(banned_counter['detected_lines'][k])}
'
else:
result_str = 'Пройдена!'
+
+ if len(warned_counter['detected_lines']):
+ result_str += f'
Обнаружены потенциально опасные слова (не влияют на результат проверки)!
Обратите внимание, что их использование возможно только в подтвержденных случаях: {"; ".join(self.warned_words)}
'
+ for k, v in warned_counter['detected_lines'].items():
+ result_str += f'Страница №{k}:
{"
".join(warned_counter['detected_lines'][k])}
'
+
result_score = 1
- if count > self.min_count:
- if count <= self.max_count:
+ if banned_counter['count'] > self.min_count:
+ if banned_counter['count'] <= self.max_count:
result_score = 0.5
else:
result_score = 0
diff --git a/app/main/checks/report_checks/lit_ref_in_spec_chapter.py b/app/main/checks/report_checks/lit_ref_in_spec_chapter.py
new file mode 100644
index 00000000..d31aa4e6
--- /dev/null
+++ b/app/main/checks/report_checks/lit_ref_in_spec_chapter.py
@@ -0,0 +1,96 @@
+import re
+from .style_check_settings import StyleCheckSettings
+from ..base_check import BaseReportCriterion, answer
+
+
+class LitRefInChapter(BaseReportCriterion):
+ label = "Проверка количества ссылок на источники в определенном разделе"
+ description = ''
+ id = 'references_in_chapter_check'
+
+ def __init__(self, file_info, min_ref_value=0.5, max_ref_value=1, headers_map=None):
+ super().__init__(file_info)
+ self.chapters_for_lit_ref = {}
+ self.lit_ref_count = {}
+ self.min_ref_value = min_ref_value
+ self.max_ref_value = max_ref_value
+ if headers_map:
+ self.config = headers_map
+ else:
+ self.config = 'VKR_HEADERS' if (self.file_type['report_type'] == 'VKR') else 'LR_HEADERS'
+
+ def late_init(self):
+ self.chapters = self.file.make_chapters(self.file_type['report_type'])
+ self.headers_main = self.file.get_main_headers(self.file_type['report_type'])
+ if self.headers_main in StyleCheckSettings.CONFIGS.get(self.config):
+ self.chapters_for_lit_ref = StyleCheckSettings.CONFIGS.get(self.config)[self.headers_main][
+ 'chapters_for_lit_ref']
+ else:
+ if 'any_header' in StyleCheckSettings.CONFIGS.get(self.config):
+ self.chapters_for_lit_ref = StyleCheckSettings.CONFIGS.get(self.config)['any_header'][
+ 'chapters_for_lit_ref']
+
+ def check(self):
+ if self.file.page_counter() < 4:
+ return answer(False, "В отчете недостаточно страниц. Нечего проверять.")
+ self.late_init()
+ if not self.chapters_for_lit_ref:
+ return answer(True, 'Для загруженной работы данная проверка не предусмотрена.')
+ result = []
+ result_str = f'Пройдена!'
+ currant_head = ''
+ chapter_for_check = 0
+ ref_in_annotation = False
+ for chapter in self.chapters:
+ header = chapter["text"].lower()
+ if currant_head:
+ self.lit_ref_count[currant_head].append(chapter['number'])
+ if currant_head in self.chapters_for_lit_ref:
+ chapter_for_check += 1
+ ref_count = len(self.search_references(self.lit_ref_count[currant_head][0],
+ self.lit_ref_count[currant_head][1]))
+ if ref_count > self.chapters_for_lit_ref[currant_head][1] or ref_count < \
+ self.chapters_for_lit_ref[currant_head][0]:
+ result.append(f'«{currant_head[0].upper() + currant_head[1:]}» : {ref_count}')
+ if currant_head == 'аннотация' or currant_head == 'annotation':
+ ref_in_annotation = True
+ self.lit_ref_count[header] = [chapter['number'], ]
+ currant_head = header
+ if result:
+ if chapter_for_check > 0:
+ ref_value = round((chapter_for_check - len(result)) / chapter_for_check, 2)
+ else:
+ ref_value = 1.0
+ result_str = (f'Доля соответствия количества ссылок необходимому в требуемых разделах равна {ref_value}'
+ f'
Количество ссылок на источники не удовлетворяет допустимому в следующих разделах:
{"
".join(res for res in result)}'
+ f'
Допустимые пороги количества ссылок:
'
+ f'{"
".join(f"«{chapter.capitalize()}»: от {limit[0]} до {limit[1]}" for chapter, limit in self.chapters_for_lit_ref.items())}')
+ result_str += 'В аннотации не должно быть ссылок на литературу.' if ref_in_annotation else ''
+ if ref_value >= self.max_ref_value and not ref_in_annotation:
+ return answer(1, f'Пройдена!')
+ elif ref_value >= self.min_ref_value and not ref_in_annotation:
+ return answer(ref_value, f'Частично пройдена! {result_str}')
+ else:
+ return answer(0, f'Не пройдена! {result_str}')
+ elif ref_in_annotation:
+ return answer(0, 'В аннотации не должно быть ссылок на литературу.')
+ else:
+ return answer(1, result_str)
+
+ def search_references(self, start_par, end_par):
+ array_of_references = []
+ for i in range(start_par, end_par):
+ if isinstance(self.file.paragraphs[i], str):
+ detected_references = re.findall(r'\[[\d \-,]+\]', self.file.paragraphs[i])
+ else:
+ detected_references = re.findall(r'\[[\d \-,]+\]', self.file.paragraphs[i].paragraph_text)
+ if detected_references:
+ for reference in detected_references:
+ for one_part in re.split(r'[\[\],]', reference):
+ if re.match(r'\d+[ \-]+\d+', one_part):
+ start, end = re.split(r'[ -]+', one_part)
+ for k in range(int(start), int(end) + 1):
+ array_of_references.append((k))
+ elif one_part != '':
+ array_of_references.append(int(one_part))
+ return array_of_references
diff --git a/app/main/checks/report_checks/main_character_check.py b/app/main/checks/report_checks/main_character_check.py
index c596e999..47914a6f 100644
--- a/app/main/checks/report_checks/main_character_check.py
+++ b/app/main/checks/report_checks/main_character_check.py
@@ -82,12 +82,6 @@ def extract_table_contents(self, table):
contents.append("|".join(row_text))
return contents
- def calculate_find_value(self, table, index):
- count = int((len(table) - index - 2) / 2)
- if count >= 0:
- return count
- return 0
-
def check_table(self, check_list, table, table_num):
for item in check_list:
for i, line in enumerate(table):
@@ -105,10 +99,7 @@ def check_table(self, check_list, table, table_num):
continue
elif item["key"] in ["Зав. кафедрой", "Консультант"] and item["found_key"] > 0:
- if item["key"] == "Консультант":
- if item["found_key"] == 1:
- item["find"] += self.calculate_find_value(table, i)
for value in item["value"]:
- if re.search(value, line):
+ if "Руководитель" not in line and re.search(value, line): # исключаем из поиска строки с рукодителем
item["found_value"] += 1
item["logs"] += f"'{item['key']}': значение компоненты '{value}' найдено в строке '{line}' в таблице №{table_num}
"
diff --git a/app/main/checks/report_checks/style_check_settings.py b/app/main/checks/report_checks/style_check_settings.py
index 7c3a61b5..791e8c2e 100644
--- a/app/main/checks/report_checks/style_check_settings.py
+++ b/app/main/checks/report_checks/style_check_settings.py
@@ -10,14 +10,15 @@ class StyleCheckSettings:
HEADER_REGEX = "^\\D+.+$"
HEADER_1_REGEX = "^()([\\w\\s]+)$"
HEADER_2_REGEX = "^()([\\w\\s]+)\\.$"
- STD_BANNED_WORDS = ['мы', 'моя', 'мои', 'моё', 'наш', 'наши',
+ STD_BANNED_WORDS = ('мы', 'моя', 'мои', 'моё', 'наш', 'наши',
'аттач', 'билдить', 'бинарник', 'валидный', 'дебаг', 'деплоить', 'десктопное', 'железо',
- 'исходники', 'картинка', 'консольное', 'конфиг', 'кусок', 'либа', 'лог', 'мануал', 'машина',
+ 'исходники', 'картинка', 'консольное', 'конфиг', 'кусок', 'либа', 'лог', 'мануал',
'отнаследованный', 'парсинг', 'пост', 'распаковать', 'сбоит', 'скачать', 'склонировать', 'скрипт',
'тестить', 'тул', 'тула', 'тулза', 'фиксить', 'флажок', 'флаг', 'юзкейс', 'продакт', 'продакшн',
- 'прод', 'фидбек', 'дедлайн', 'дэдлайн', 'оптимально', 'оптимальный', 'надежный', 'интуитивный',
+ 'прод', 'фидбек', 'дедлайн', 'дэдлайн', 'оптимально', 'надежный', 'интуитивный',
'хороший', 'плохой', 'идеальный', 'быстро', 'медленно', 'какой-нибудь', 'некоторый', 'почти'
- ] # TODO: list of "warning" words
+ )
+ STD_WARNED_WORDS = ('машина', 'оптимальный') # TODO: list of "warning" words
STD_MIN_LIT_REF = 1
STD_MAX_LIT_REF = 1000 #just in case for future edit
HEADER_1_STYLE = {
@@ -98,9 +99,11 @@ class StyleCheckSettings:
"style": HEADER_1_STYLE,
"docx_style": ["heading 1"],
"headers": ["Исходный код программы"],
+ "chapters_for_lit_ref": {},
"unify_regex": APPENDIX_UNIFY_REGEX,
"regex": APPENDIX_REGEX,
"banned_words": STD_BANNED_WORDS,
+ "warned_words": STD_WARNED_WORDS,
'min_count_for_banned_words_check': 3,
'max_count_for_banned_words_check': 6,
'min_ref_for_literature_references_check': STD_MIN_LIT_REF,
@@ -111,6 +114,7 @@ class StyleCheckSettings:
"style": HEADER_2_STYLE,
"docx_style": ["heading 2"],
"headers": ["Цель работы", "Выполнение работы", "Выводы"],
+ "chapters_for_lit_ref": {},
"unify_regex": None,
"regex": HEADER_1_REGEX,
}
@@ -122,9 +126,12 @@ class StyleCheckSettings:
"style": HEADER_1_STYLE,
"docx_style": ["heading 2"],
"headers": ["ВВЕДЕНИЕ", "ЗАКЛЮЧЕНИЕ", "СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ"],
+ "chapters_for_lit_ref":{},
+ # { 'введение': [0, 1], 'определения, обозначения и сокращения': [2, 5], 'заключение': [1, 5] },
"unify_regex": None,
"regex": HEADER_REGEX,
"banned_words": STD_BANNED_WORDS,
+ "warned_words": STD_WARNED_WORDS,
'min_count_for_banned_words_check': 3,
'max_count_for_banned_words_check': 6,
'min_ref_for_literature_references_check': STD_MIN_LIT_REF,
@@ -149,7 +156,8 @@ class StyleCheckSettings:
"ПЛАН РАБОТЫ НА ВЕСЕННИЙ СЕМЕСТР", "ОТЗЫВ РУКОВОДИТЕЛЯ", "СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ"],
"unify_regex": None,
"regex": HEADER_REGEX,
- "banned_words": STD_BANNED_WORDS + ['доработать', 'доработка', 'переписать', 'рефакторинг', 'исправление']
+ "banned_words": STD_BANNED_WORDS + ('доработать', 'доработка', 'переписать', 'рефакторинг', 'исправление'),
+ "warned_words": STD_WARNED_WORDS
},
}
@@ -162,10 +170,11 @@ class StyleCheckSettings:
"ПЛАН РАБОТЫ НА ОСЕННИЙ СЕМЕСТР", "СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ"],
"unify_regex": None,
"regex": HEADER_REGEX,
- "banned_words": STD_BANNED_WORDS + ['доработать', 'доработка', 'переписать', 'рефакторинг', 'исправление']
+ "banned_words": STD_BANNED_WORDS + ('доработать', 'доработка', 'переписать', 'рефакторинг', 'исправление'),
+ "warned_words": STD_WARNED_WORDS
},
}
-
+
NIR3_CONFIG = {
'any_header':
{
@@ -175,7 +184,8 @@ class StyleCheckSettings:
"ПЛАН РАБОТЫ НА ВЕСЕННИЙ СЕМЕСТР", "ОТЗЫВ РУКОВОДИТЕЛЯ", "СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ"],
"unify_regex": None,
"regex": HEADER_REGEX,
- "banned_words": STD_BANNED_WORDS + ['доработать', 'доработка', 'переписать', 'рефакторинг', 'исправление']
+ "banned_words": STD_BANNED_WORDS + ('доработать', 'доработка', 'переписать', 'рефакторинг', 'исправление'),
+ "warned_words": STD_WARNED_WORDS
},
}
@@ -189,10 +199,13 @@ class StyleCheckSettings:
"Методы обоснования",
"Статья",
],
+ "chapters_for_lit_ref": {},
"header_for_report_section_component": "Поставленная цель и задачи",
+ "headers_for_lit_count": [],
"unify_regex": None,
"regex": HEADER_REGEX,
"banned_words": STD_BANNED_WORDS,
+ "warned_words": STD_WARNED_WORDS,
'min_count_for_banned_words_check': 3,
'max_count_for_banned_words_check': 6,
},
@@ -204,10 +217,13 @@ class StyleCheckSettings:
"Характеристика выводов",
"Статья",
],
+ "chapters_for_lit_ref": {},
"header_for_report_section_component": "",
+ "headers_for_lit_count": [],
"unify_regex": None,
"regex": HEADER_REGEX,
"banned_words": STD_BANNED_WORDS,
+ "warned_words": STD_WARNED_WORDS,
'min_count_for_banned_words_check': 3,
'max_count_for_banned_words_check': 6,
}
@@ -230,6 +246,7 @@ class StyleCheckSettings:
"unify_regex": None,
"regex": HEADER_REGEX,
"banned_words": STD_BANNED_WORDS,
+ "warned_words": STD_WARNED_WORDS,
'min_ref_for_literature_references_check': 1,
'mах_ref_for_literature_references_check': 1000, #just for future possible edit
'min_count_for_banned_words_check': 2,
@@ -241,14 +258,15 @@ class StyleCheckSettings:
"docx_style": ["heading 2"],
"headers": ["Принцип отбора аналогов",
"Критерии сравнения аналогов",
- "Таблица сравнения аналогов",
"Выводы по итогам сравнения",
"Выбор метода решения",
"Список использованных источников"
],
+ "chapters_for_lit_ref": {},
"unify_regex": None,
"regex": HEADER_REGEX,
"banned_words": STD_BANNED_WORDS,
+ "warned_words": STD_WARNED_WORDS,
'min_ref_for_literature_references_check': 3,
'mах_ref_for_literature_references_check': 1000, #just for future possible edit
'min_count_for_banned_words_check': 2,
@@ -265,9 +283,11 @@ class StyleCheckSettings:
"Заключение",
"Список использованных источников"
],
+ "chapters_for_lit_ref": {},
"unify_regex": None,
"regex": HEADER_REGEX,
"banned_words": STD_BANNED_WORDS,
+ "warned_words": STD_WARNED_WORDS,
'min_ref_for_literature_references_check': 5,
'mах_ref_for_literature_references_check': 1000, #just for future possible edit
'min_count_for_banned_words_check': 2,
diff --git a/app/routes/results.py b/app/routes/results.py
index 91cc5b7f..95f4f508 100644
--- a/app/routes/results.py
+++ b/app/routes/results.py
@@ -3,6 +3,7 @@
from time import time
from flask import Blueprint, Response, render_template
+from flask_login import current_user, login_required
from wsgiref.handlers import format_date_time as format_date
from app.db import db_methods
@@ -16,6 +17,7 @@
@results_bp.route("/", methods=["GET"])
+@login_required
def results_main(_id):
try:
oid = ObjectId(_id)
@@ -24,11 +26,15 @@ def results_main(_id):
return render_template("./404.html")
check = db_methods.get_check(oid)
if check is not None:
- # show processing time for user
- avg_process_time = None if check.is_ended else db_methods.get_average_processing_time()
- return render_template("./results.html", navi_upload=True, results=check,
- columns=TABLE_COLUMNS, avg_process_time=avg_process_time,
- stats=format_check(check.pack()))
+ # show check only for author or admin
+ if current_user.is_admin or current_user.username == check.user:
+ # show processing time for user
+ avg_process_time = None if check.is_ended else db_methods.get_average_processing_time()
+ return render_template("./results.html", navi_upload=True, results=check,
+ columns=TABLE_COLUMNS, avg_process_time=avg_process_time,
+ stats=format_check(check.pack()))
+ else:
+ return "У вас нет прав на просмотр результатов чужих проверок", 403
else:
logger.info("Запрошенная проверка не найдена: " + _id)
return render_template("./404.html")
diff --git a/app/templates/404.html b/app/templates/404.html
index bf8140dd..ad648890 100644
--- a/app/templates/404.html
+++ b/app/templates/404.html
@@ -4,7 +4,7 @@
{% block main %}
-

+
Запрашиваемый ресурс не найден
diff --git a/assets/styles/404.css b/assets/styles/404.css
index c833cc62..384902ba 100644
--- a/assets/styles/404.css
+++ b/assets/styles/404.css
@@ -1,3 +1,3 @@
#middle-container {
- background-color: black;
+ background-color: rgb(255, 255, 255);
}
diff --git a/tests/test_recheck.py b/tests/test_recheck.py
new file mode 100644
index 00000000..4fc8135c
--- /dev/null
+++ b/tests/test_recheck.py
@@ -0,0 +1,36 @@
+import time
+from basic_selenium_test import BasicSeleniumTest
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support.wait import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class RecheckTestSelenium(BasicSeleniumTest):
+
+ def test_recheck_file(self):
+ check_id = self.open_statistic()
+ if check_id:
+ URL = self.get_url(f"/recheck/{check_id}")
+ self.get_driver().get(URL)
+ obj = WebDriverWait(self.driver, 10).until(
+ EC.presence_of_element_located((By.ID, "results_title"))
+ )
+ if "Производится проверка файла" in obj.text:
+ start_time = time.time()
+ max_time = 240
+ while (time.time() - start_time) < max_time:
+ time.sleep(10)
+ try:
+ obj = WebDriverWait(self.driver, 10).until(
+ EC.presence_of_element_located((By.ID, "results_table"))
+ )
+ if obj is not None:
+ self.assertNotEquals(obj, None)
+ return
+ except:
+ continue
+ self.fail("Result of check is not found")
+ else:
+ self.fail("No checking status after /recheck")
+ else:
+ self.skipTest("No check in system for testing recheck")