diff --git a/app/main/checks/presentation_checks/__init__.py b/app/main/checks/presentation_checks/__init__.py index 52bd5f73..2d2b9459 100644 --- a/app/main/checks/presentation_checks/__init__.py +++ b/app/main/checks/presentation_checks/__init__.py @@ -17,3 +17,4 @@ from .name_of_image_check import PresImageCaptureCheck from .task_tracker import TaskTracker from .overview_in_tasks import OverviewInTasks +from .was_were_check import PresWasWereCheck diff --git a/app/main/checks/presentation_checks/was_were_check.py b/app/main/checks/presentation_checks/was_were_check.py new file mode 100644 index 00000000..58caa450 --- /dev/null +++ b/app/main/checks/presentation_checks/was_were_check.py @@ -0,0 +1,21 @@ +from ..base_check import BasePresCriterion, answer +from app.nlp.is_passive_was_were_sentence import CritreriaType, generate_output_text, get_was_were_sentences + +class PresWasWereCheck(BasePresCriterion): + label = 'Проверка на пассивные конструкции, начинающиеся с Был/Была/Было/Были, которые можно убрать без потери смысла' + description = '' + id = 'pres_was_were_check' + + def __init__(self, file_info, threshold=3): + super().__init__(file_info) + self.threshold = threshold + + def check(self): + detected_sentences, total_sentences = get_was_were_sentences(self.file, CritreriaType.PRESENTATION) + if total_sentences > self.threshold: + result_str = generate_output_text(detected_sentences, CritreriaType.PRESENTATION, self.format_page_link) + result_score = 0 + else: + result_str = 'Пройдена!' + result_score = 1 + return answer(result_score, result_str) \ No newline at end of file diff --git a/app/main/checks/report_checks/__init__.py b/app/main/checks/report_checks/__init__.py index 30f22617..e22e7bfd 100644 --- a/app/main/checks/report_checks/__init__.py +++ b/app/main/checks/report_checks/__init__.py @@ -33,4 +33,4 @@ from .sw_keywords_check import SWKeywordsCheck from .task_tracker import ReportTaskTracker from .paragraphs_count_check import ReportParagraphsCountCheck -from .template_name import ReportTemplateNameCheck +from .was_were_check import ReportWasWereCheck diff --git a/app/main/checks/report_checks/was_were_check.py b/app/main/checks/report_checks/was_were_check.py new file mode 100644 index 00000000..fbb1a9f8 --- /dev/null +++ b/app/main/checks/report_checks/was_were_check.py @@ -0,0 +1,23 @@ +from ..base_check import BaseReportCriterion, answer +from app.nlp.is_passive_was_were_sentence import CritreriaType, generate_output_text, get_was_were_sentences + +class ReportWasWereCheck(BaseReportCriterion): + label = 'Проверка на пассивные конструкции, начинающиеся с Был/Была/Было/Были, которые можно убрать без потери смысла' + description = '' + id = 'report_was_were_check' + + def __init__(self, file_info, threshold=3): + super().__init__(file_info) + self.threshold = threshold + + def check(self): + if self.file.page_counter() < 4: + return answer(False, 'В отчёте недостаточно страниц. Нечего проверять.') + detected, total_sentences = get_was_were_sentences(self.file, CritreriaType.REPORT) + if total_sentences > self.threshold: + result_str = generate_output_text(detected, CritreriaType.REPORT, self.format_page_link) + result_score = 0 + else: + result_str = 'Пройдена!' + result_score = 1 + return answer(result_score, result_str) \ No newline at end of file diff --git a/app/nlp/is_passive_was_were_sentence.py b/app/nlp/is_passive_was_were_sentence.py new file mode 100644 index 00000000..4a64fb09 --- /dev/null +++ b/app/nlp/is_passive_was_were_sentence.py @@ -0,0 +1,113 @@ +import re +import pymorphy2 +import string +from enum import Enum + +morph = pymorphy2.MorphAnalyzer() + + +class CritreriaType(Enum): + REPORT = 'report' + PRESENTATION = 'pres' + + +def criteria_type_to_str(type: CritreriaType): + if type == CritreriaType.REPORT: + return "Страница" + elif type == CritreriaType.PRESENTATION: + return "Слайд" + else: + return "Элемент" + +def get_content_by_file(file, type: CritreriaType): + if type == CritreriaType.REPORT: + return file.pdf_file.get_text_on_page().items() + elif type == CritreriaType.PRESENTATION: + return enumerate(file.get_text_from_slides()) + +def clean_word(word): + punct = string.punctuation.replace('-', '') + return word.translate(str.maketrans('', '', punct)) + + +def is_passive_was_were_sentece(sentence): + """ + Примеры плохих предложений (пассивные конструкции с "Был*" - можно убрать): + - Был проведен анализ данных + - Была выполнена работа по исследованию + - Было принято решение о внедрении + - Были получены следующие результаты + - Была создана база данных + + Примеры хороших предложений ("Был*" нельзя убрать): + - Было бы здорово получить новые данные + - Был сильный скачок напряжения + - Были времена, когда это казалось невозможным + - Был студентом университета три года назад + - Была программистом до выхода на пенсию + """ + first_words = re.split(r'\s+', sentence.strip(), maxsplit=2) + if len(first_words) < 2: + return False + + first_word = clean_word(first_words[0]) + second_word = clean_word(first_words[1]) + + parsed = morph.parse(first_word)[0] + if (parsed.normal_form == 'быть' and + 'past' in parsed.tag and + parsed.tag.POS == 'VERB'): + second_word_parsed = morph.parse(second_word)[0] + return ('PRTS' in second_word_parsed.tag and + 'pssv' in second_word_parsed.tag) + return False + + +def generate_output_text(detected_senteces, type: CritreriaType, format_page_link_fn=None): + output = 'Обнаружены конструкции (Был/Была/Было/Были), которые можно удалить без потери смысла:

' + if type == CritreriaType.REPORT: + offset_index = 0 + elif type == CritreriaType.PRESENTATION: + offset_index = 1 + for index, messages in detected_senteces.items(): + display_index = index + offset_index + output_type = criteria_type_to_str(type) + if format_page_link_fn: + output += f'{output_type} {format_page_link_fn([display_index])}:
' + '
'.join(messages) + '

' + else: + output += f'{output_type} №{display_index}:
' + '
'.join(messages) + '

' + return output + + +def get_was_were_sentences(file, type: CritreriaType): + detected = {} + total_sentences = 0 + for page_index, page_text in get_content_by_file(file, type): + lines = re.split(r'\n', page_text) + non_empty_line_counter = 0 + for line_index, line in enumerate(lines): + print(line_index, line) + line = line.strip() + if not line: + continue + + non_empty_line_counter += 1 + sentences = re.split(r'[.!?…]+\s*', line) + + for sentence in sentences: + sentence = sentence.strip() + if not sentence: + continue + + if is_passive_was_were_sentece(sentence): + total_sentences += 1 + if page_index not in detected: + detected[page_index] = [] + truncated_sentence = sentence[:50] + '...' if len(sentence) > 50 else sentence + if type == CritreriaType.PRESENTATION: + err_str = f'Строка {non_empty_line_counter}: {truncated_sentence}' + elif type == CritreriaType.REPORT: + err_str = f'Строка {line_index+1}: {truncated_sentence}' + detected[page_index].append(err_str) + + return detected, total_sentences \ No newline at end of file