|
| 1 | +from bson import ObjectId |
| 2 | + |
| 3 | +from app.root_logger import get_root_logger |
| 4 | +from app.localisation import * |
| 5 | +from ..criterion_base import BaseCriterion |
| 6 | +from ..criterion_result import CriterionResult |
| 7 | +from app.audio import Audio |
| 8 | +from app.presentation import Presentation |
| 9 | +from app.utils import normalize_text, delete_punctuation |
| 10 | +from ..text_comparison import SlidesSimilarityEvaluator |
| 11 | + |
| 12 | +logger = get_root_logger('web') |
| 13 | + |
| 14 | + |
| 15 | +# Критерий, оценивающий, насколько текст слайда перекликается с речью студента на этом слайде |
| 16 | +class ComparisonSpeechSlidesCriterion(BaseCriterion): |
| 17 | + PARAMETERS = dict( |
| 18 | + skip_slides=list.__name__, |
| 19 | + ) |
| 20 | + |
| 21 | + def __init__(self, parameters, dependent_criteria, name=''): |
| 22 | + super().__init__( |
| 23 | + name=name, |
| 24 | + parameters=parameters, |
| 25 | + dependent_criteria=dependent_criteria, |
| 26 | + ) |
| 27 | + self.evaluator = SlidesSimilarityEvaluator() |
| 28 | + if 'slide_speech_threshold' not in self.parameters: |
| 29 | + self.parameters['slide_speech_threshold'] = 0.125 |
| 30 | + |
| 31 | + @property |
| 32 | + def description(self): |
| 33 | + return { |
| 34 | + "Критерий": t(self.name), |
| 35 | + "Описание": t( |
| 36 | + "Проверяет, что текст слайда соответствует словам, которые произносит студент во время демонстрации " |
| 37 | + "этого слайда"), |
| 38 | + "Оценка": t("1, если среднее значение соответствия речи содержимому слайдов равно или превосходит заданного порога (от 0 до 1), " |
| 39 | + "иначе r / значение порога, где r - среднее значение соответствия речи демонстрируемым слайдам") |
| 40 | + } |
| 41 | + |
| 42 | + def skip_slide(self, current_slide_text: str) -> bool: |
| 43 | + for skip_slide in self.parameters['skip_slides']: |
| 44 | + if skip_slide.lower() in delete_punctuation(current_slide_text).lower(): |
| 45 | + return True |
| 46 | + return False |
| 47 | + |
| 48 | + def apply(self, audio: Audio, presentation: Presentation, training_id: ObjectId, |
| 49 | + criteria_results: dict) -> CriterionResult: |
| 50 | + # Результаты сравнения текстов |
| 51 | + results = {} |
| 52 | + |
| 53 | + slides_to_process = [] |
| 54 | + |
| 55 | + for current_slide_index in range(len(audio.audio_slides)): |
| 56 | + # Список слов, сказанных студентом на данном слайде -- список из RecognizedWord |
| 57 | + current_slide_speech = audio.audio_slides[current_slide_index].recognized_words |
| 58 | + # Удаление time_stamp-ов и probability, ибо работа будет вестись только со словами |
| 59 | + current_slide_speech = list(map(lambda x: x.word.value, current_slide_speech)) |
| 60 | + # Нормализация текста выступления |
| 61 | + current_slide_speech = " ".join(normalize_text(current_slide_speech)) |
| 62 | + |
| 63 | + # Если на данном слайде ничего не сказано, то не обрабатываем данный слайд |
| 64 | + if len(current_slide_speech.split()) == 0: |
| 65 | + results[current_slide_index + 1] = 0.000 |
| 66 | + continue |
| 67 | + |
| 68 | + # Список слов со слайда презентации |
| 69 | + current_slide_text = presentation.slides[current_slide_index].words |
| 70 | + # Проверяем, входит ли рассматриваемый слайд в список нерасмматриваемых |
| 71 | + if self.skip_slide(current_slide_text): |
| 72 | + logger.info(f"Слайд №{current_slide_index + 1} пропущен") |
| 73 | + continue |
| 74 | + |
| 75 | + # Нормализация текста слайда |
| 76 | + current_slide_text = " ".join(normalize_text(current_slide_text.split())) |
| 77 | + slides_to_process.append((current_slide_speech, current_slide_text, current_slide_index + 1)) |
| 78 | + |
| 79 | + self.evaluator.train_model([" ".join(list(map(lambda x: x[0], slides_to_process))), " ".join(list(map(lambda x: x[1], slides_to_process)))]) |
| 80 | + |
| 81 | + for speech, slide_text, slide_number in slides_to_process: |
| 82 | + results[slide_number] = self.evaluator.evaluate_semantic_similarity(speech, slide_text) |
| 83 | + |
| 84 | + results = dict(sorted(results.items())) |
| 85 | + |
| 86 | + score = (sum(list(results.values())) / len(list(results.values()))) / self.parameters['slide_speech_threshold'] |
| 87 | + |
| 88 | + return CriterionResult(1 if score >= 1 else score, "Отлично" if score >= 1 else "Следует уделить внимание " |
| 89 | + "соотвествию речи на слайдах " |
| 90 | + "{}".format(",\n".join([f"№{n} - {results[n]}" for n in dict(filter(lambda item: item[1] < self.parameters['slide_speech_threshold'], results.items()))]))) |
0 commit comments