Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
39ef5f3
paragraphs_count
Dariiiii Jul 3, 2024
c95caf5
pdf
Dariiiii Jul 3, 2024
702377a
docx
Dariiiii Jul 3, 2024
43343fa
docx2
Dariiiii Jul 3, 2024
c2b1d54
docx 3
Dariiiii Jul 3, 2024
e19f0ee
paragraphs_count 1.1
Dariiiii Jul 3, 2024
0a097d4
delete unnecessary
Dariiiii Jul 3, 2024
5e402e7
docx4
Dariiiii Jul 3, 2024
360a794
added link for bug reports
MarinaProsche Aug 7, 2024
3a11a6c
try ti fix actions
MarinaProsche Aug 7, 2024
380f811
change spell docker-compose
MarinaProsche Aug 7, 2024
e541efe
change spelling in all docs
MarinaProsche Aug 7, 2024
1e31ebf
Merge branch 'master' into 532_form_for_bugs
MarinaProsche Sep 3, 2024
7207a94
links and support added
MarinaProsche Sep 4, 2024
e2a1e58
send constants to html directly
MarinaProsche Sep 20, 2024
7bc1a21
update pres_image_capture
HadronCollider Apr 30, 2025
4dadd51
Update SlidePPTX.__init__
HadronCollider Jun 17, 2025
e2567cc
Merge branch 'master' into dev
HadronCollider Sep 21, 2025
2a1645c
update SWTask1 headers (2 goals)
HadronCollider Sep 21, 2025
22bcfec
rm reports.md from main branches
HadronCollider Sep 21, 2025
5c60f5f
update feedback for ReportSectionComponent
HadronCollider Oct 9, 2025
e56bb81
update SWSectionSizeCheck constants (2 goals)
HadronCollider Oct 9, 2025
e4d718a
update regexp in ReferencesToLiteratureCheck
HadronCollider Oct 9, 2025
e4800c9
update SWSectionSizeCheck feedback
HadronCollider Oct 9, 2025
faf23aa
fix /login/ flask redirect
HadronCollider Oct 9, 2025
6000fde
parse <h1> as style in MD
HadronCollider Oct 9, 2025
9e8b6a6
Merge remote-tracking branch 'origin/549_paragraphs_count_check' into…
HadronCollider Oct 10, 2025
bcdf126
Merge remote-tracking branch 'origin/534_main_character_check' into dev
HadronCollider Oct 10, 2025
55a2069
Merge remote-tracking branch 'origin/532_form_for_bugs' into dev
HadronCollider Oct 11, 2025
2f08052
mv header.html to root.html (rm from every template)
HadronCollider Oct 11, 2025
50d9e06
fix ReportParagraphsCountCheck
HadronCollider Oct 12, 2025
fe1c3dd
update NIR configs
HadronCollider Oct 26, 2025
5de49fc
update paragraphs_count_check
HadronCollider Oct 31, 2025
2332586
update sw_sections_size_check feedback
HadronCollider Nov 7, 2025
ddbbfc4
update main_character_check
HadronCollider Nov 7, 2025
3c00936
some update for main_character_check
HadronCollider Nov 10, 2025
d418b04
add username for selenium test (instead of .env)
HadronCollider Nov 10, 2025
e2e7cde
rename "слайд" to "страница" on results page
HadronCollider Nov 10, 2025
4baf0f5
rm debug logs in main_character_check
HadronCollider Nov 10, 2025
7919f40
add sleep to selenium test
HadronCollider Nov 10, 2025
c8abefa
rm debug logs in main_character_check
HadronCollider Nov 10, 2025
edba4e6
update FileLoadTestSelenium
HadronCollider Nov 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile_selenium
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ COPY tests/requirements.txt tests/requirements.txt
RUN pip install -r tests/requirements.txt
COPY tests ./tests

ENTRYPOINT python3 tests/main.py --login ${LOGIN} --password ${PASSWORD}
ENTRYPOINT sleep 30 && python3 tests/main.py --login ${LOGIN} --password ${PASSWORD}
10 changes: 6 additions & 4 deletions app/main/checks/presentation_checks/name_of_image_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ def __init__(self, file_info):
def check(self):
slides_without_capture = set()
slide_with_image_only = set()
result_str = 'Не пройдена! '
result_str = ''
result = True
all_captions = []
for num, slide in enumerate(self.file.slides, 1):
captions = slide.get_captions()
Expand All @@ -27,14 +28,15 @@ def check(self):
if caption[0] != slide.get_title():
slide_with_image_only.add(num)
if slides_without_capture:
result = False
result_str += (
'Подписи к рисункам на следующих слайдах отсутствуют или не содержат слова "Рисунок": {}'.format(
', '.join(self.format_page_link(sorted(slides_without_capture)))) + '<br>')
if slide_with_image_only:
result_str += (
'Подписи к рисункам на следующих слайдах без текста необязательны: {}'.format(
', '.join(self.format_page_link(sorted(slide_with_image_only)))) + '<br>')
if result_str:
return answer(False, name_of_image_check_results(result_str, all_captions))
if result:
return answer(True, f'Пройдена! {result_str}')
else:
return answer(True, 'Пройдена!')
return answer(False, name_of_image_check_results(f"Не пройдена! {result_str}", all_captions))
2 changes: 2 additions & 0 deletions app/main/checks/report_checks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@
from .sw_section_size import SWSectionSizeCheck
from .sw_keywords_check import SWKeywordsCheck
from .task_tracker import ReportTaskTracker
from .paragraphs_count_check import ReportParagraphsCountCheck
from .template_name import ReportTemplateNameCheck
15 changes: 8 additions & 7 deletions app/main/checks/report_checks/literature_references.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ def check(self):
return answer(False,
f'В Списке использованных источников не найдено ни одного источника.<br><br>Проверьте корректность использования нумированного списка.')
references, ref_sequence = self.search_references(start_literature_par)
all_numbers = set()
for i in range(1, number_of_sources + 1):
all_numbers.add(i)
all_numbers = set(range(1, number_of_sources + 1))
if len(references.symmetric_difference(all_numbers)) == 0:
if not self.min_ref <= number_of_sources <= self.max_ref:
return answer(False, f'Список источников оформлен верно, однако их количество ({number_of_sources}) не удовлетворяет необходимому критерию. <br> Количество источников должно быть не менее {self.min_ref}.')
Expand All @@ -81,7 +79,7 @@ def check(self):
result_str += '''
Если возникли проблемы, попробуйте сделать следующее:
<ul>
<li>Убедитесь, что для ссылки на источник используются квадратные скобки;</li>
<li>Убедитесь, что для ссылки на источник используются квадратные скобки (т.е. [1], [2-4]);</li>
<li>Убедитесь, что для оформления списка литературы был использован нумированный список;</li>
<li>Убедитесь, что после и перед нумированным списком отсутствуют непустые абзацы.</li>
<li>Убедитесь, что один источник не разбит на две строки клавишей "Enter".</li>
Expand All @@ -93,13 +91,16 @@ def search_references(self, start_par):
prev_ref = 0
ref_sequence = []
array_of_references = set()
reg_exp = r'\[[\^]{0,1}[\d \-,]+\]' # md can use [^5] format for hyperlink
for i in range(0, start_par):
if isinstance(self.file.paragraphs[i], str):
detected_references = re.findall(r'\[[\d \-,]+\]', self.file.paragraphs[i])
detected_references = re.findall(reg_exp, self.file.paragraphs[i])
else:
detected_references = re.findall(r'\[[\d \-,]+\]', self.file.paragraphs[i].paragraph_text)
detected_references = re.findall(reg_exp, self.file.paragraphs[i].paragraph_text)

if detected_references:
for reference in detected_references:
for reference_raw in detected_references:
reference = reference_raw.replace('^', '') # TODO: kostyl'...
for one_part in re.split(r'[\[\],]', reference):
if re.match(r'\d+[ \-]+\d+', one_part):
start, end = re.split(r'[ -]+', one_part)
Expand Down
126 changes: 96 additions & 30 deletions app/main/checks/report_checks/main_character_check.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,114 @@
from ..base_check import BaseReportCriterion, answer
from .main_page_settings import ReportMainPageSetting
import re
import copy


class ReportMainCharacterCheck(BaseReportCriterion):
label = "Проверка фамилии и должности заведующего кафедрой"
description = 'Зав. кафедрой: А.А. Лисс'
id = 'main_character_check'
label = "Проверка составляющих титульного листа, задания и календарного плана"
description = ""
id = "main_character_check"
priority = True

def __init__(self, file_info, main_character_name_right="А.А. Лисс", main_character_name_wrong="К.В. Кринкин",
main_character_job_right="Зав. кафедрой", main_character_job_wrong="И.о. зав. кафедрой"):
def __init__(self, file_info, tables_count_to_verify=8):
super().__init__(file_info)
self.headers = []
self.main_character_name_right = main_character_name_right
self.main_character_name_wrong = main_character_name_wrong
self.main_character_job_right = main_character_job_right
self.main_character_job_wrong = main_character_job_wrong
self.first_check_list = None
self.second_check_list = None
self.tables_count_to_verify = tables_count_to_verify

def late_init(self):
self.headers = self.file.make_headers(self.file_type['report_type'])
self.headers = self.file.make_headers(self.file_type["report_type"])

def check(self):
self.first_check_list = copy.deepcopy(ReportMainPageSetting.FIRST_TABLE)
self.second_check_list = copy.deepcopy(ReportMainPageSetting.SECOND_TABLE)
if self.file.page_counter() < 4:
return answer(False, "В отчете недостаточно страниц. Нечего проверять.")
if self.tables_count_to_verify > len(self.file.tables):
return answer(
False,
f"Количество таблиц на страницах титульного листа, задания и календарного плана должно быть не меньше {self.tables_count_to_verify}",
)
self.late_init()
result_str = ''
result_str = ""
pages = []
for header in self.headers:
if header["marker"] and header["main_character"]:
page = header["page"]
text_on_page = self.file.pdf_file.text_on_page[page]
if text_on_page.find(self.main_character_name_wrong) >= 0 and not text_on_page.find(
self.main_character_name_right) >= 0:
result_str += f"На странице {self.format_page_link([page])} указана неверная фамилия заведующего " \
f"кафедрой. Убедитесь, что {self.main_character_job_right} {self.main_character_name_right}.<br>"
elif not text_on_page.find(self.main_character_name_right) >= 0:
result_str += f"На странице {self.format_page_link([page])} не указано ФИО заведующего кафедрой, в " \
f"графе {self.main_character_job_right} должно быть указано {self.main_character_name_right}.<br>"
if text_on_page.find(self.main_character_job_wrong) >= 0 and not text_on_page.find(
self.main_character_job_right) >= 0:
result_str += f'На странице {self.format_page_link([page])} указана неверная должность ' \
f'заведующего кафедрой, должно быть "{self.main_character_job_right}".<br>'
if not result_str:
return answer(True, 'ФИО и должность исполняющего обязанности зав. кафедрой указаны верно на всех листах.')
pages.append(header["page"])
for i in range(self.tables_count_to_verify):
table = self.file.tables[i]
extract_table = self.extract_table_contents(table)
self.check_table(self.first_check_list, extract_table, i+1)
self.check_table(self.second_check_list, extract_table, i+1)
links = self.format_page_link(pages)
result_score = 1.0
check_result = self.first_check_list + self.second_check_list
penalty_score = 1 / len(check_result)
for res in check_result:
if res["found_key"] > 1 and res["key"] == "Консультант":
links = self.format_page_link(pages[1:])
result_str += f"Предупреждение: помните, что на страницах {links} указывается только консультант, являющийся фактическим руководителем (консультант от кафедры / по допразделу не указываются).<br>"
elif res["found_key"] < res["find"] and res["key"] != "Консультант":
result_score -= penalty_score * (res["find"] - res['found_value'])
result_str += f"Поле '{res['key']}' не найдено на страницах {links}. Его удалось обнаружить {res['found_key']} из {res['find']} раз. Проверьте корректность всех вхождений.<br>"
elif res["found_value"] < res["find"]:
result_score -= penalty_score * (res["find"] - res['found_value'])
links = self.format_page_link(pages)
result_str += f"Содержимое поля '{res['key']}' указано корректно {res['found_value']} из {res['find']} раз. Проверьте корректность всех вхождений на страницах {links}.<br>"
result_str += f"""<br>Логи проверки:<br><code>{"<br>".join(res['logs'] for res in check_result)}</code><br>"""
if result_score < 0:
result_score = 0 # for case with big penalty

if result_score == 1.0:
return answer(result_score, f"Пройдена!<br>{result_str}")
else:
result_str += f'<br>Убедитесь, что вы использовали правильные формы бланков титульного листа, задания и календарного плана.' \
f'<br>Для бакалавров: <a href="https://drive.google.com/drive/folders/1pvv9HJIUB0VZUXteGqtLcVq6zIgZ6rbZ">Формы бланков для бакалавров</a>.' \
f'<br>Для магистров: <a href="https://drive.google.com/drive/folders/1KOoXzKv4Wf-XyGzOf1X8gN256sgame1D">Формы бланков для магистров</a>.'
return answer(False, result_str)
result_str += (
f"<br>Убедитесь, что вы использовали правильные формы бланков титульного листа, задания и календарного плана."
f'<br>Для бакалавров: <a href="https://drive.google.com/drive/folders/1pvv9HJIUB0VZUXteGqtLcVq6zIgZ6rbZ">Формы бланков для бакалавров</a>.'
f'<br>Для магистров: <a href="https://drive.google.com/drive/folders/1KOoXzKv4Wf-XyGzOf1X8gN256sgame1D">Формы бланков для магистров</a>.'
)
return answer(result_score, result_str)

def extract_table_contents(self, table):
contents = []
processed_cells = set()
for row in table.rows:
row_text = []
for cell in row.cells:
if cell not in processed_cells:
row_text.append(cell.text.strip())
processed_cells.add(cell)
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):
if item["key"] in line:
item["found_key"] += 1
item["logs"] += f"'{item['key']}': ключ компоненты найден в строке '{line}' в таблице №{table_num}<br>"

for value in item["value"]:
if re.search(value, line):
item["found_value"] += 1
item["logs"] += f"\t'{item['key']}': значение компоненты '{value}' найдено в строке '{line}' в таблице №{table_num}<br>"
break

if item["key"] != "Зав. кафедрой" and item["key"] != "Консультант":
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):
item["found_value"] += 1
item["logs"] += f"'{item['key']}': значение компоненты '{value}' найдено в строке '{line}' в таблице №{table_num}<br>"
69 changes: 69 additions & 0 deletions app/main/checks/report_checks/main_page_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
class ReportMainPageSetting:
FIRST_TABLE = [
{
"key": "Направление",
"found_value": 0,
"found_key": 0,
"find": 1,
"value": [
"01.03.02 Прикладная математика и информатика",
"09.03.04 Программная инженерия",
],
"logs": ""
},
{
"key": "Профиль",
"found_value": 0,
"found_key": 0,
"find": 1,
"value": [
"Математическое обеспечение программно-информационных систем",
"Разработка программно-информационных систем"
],
"logs": ""
},
{
"key": "Факультет",
"found_value": 0,
"found_key": 0,
"find": 1,
"value": ["КТИ"],
"logs": ""
},
{
"key": "Кафедра",
"found_value": 0,
"found_key": 0,
"find": 1,
"value": ["МО ЭВМ"],
"logs": ""
},
{
"key": "Зав. кафедрой",
"found_value": 0,
"found_key": 0,
"find": 3,
"value": ["А.А. Лисс"],
"logs": ""
}
]

SECOND_TABLE = [
{
"key": "Руководитель",
"found_value": 0,
"found_key": 0,
"find": 3,
"value": [r"(Руководитель).*([кд]\..+\.н\., (доцент|профессор))[|]*([А-Я]\.[А-Я]\. [А-Я][а-я]+)"], #
"logs": ""
},
{
"key": "Консультант",
"found_value": 0,
"found_key": 0,
"find": 1,
"value": [r"[кд]\..+\.н\., (доцент|профессор)"], # (Консультант).*(([кд]\..+\.н\., (доцент|профессор))|).*([А-Я]\.[А-Я]\. [А-Я][а-я]+)
# (([кд]\..+\.н\., (доцент|профессор))|)[|]*([А-Я]\.[А-Я]\. [А-Я][а-я]+)
"logs": ""
}
]
Loading