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