Bootcamp / utils /comparator.py
Vadym Myroshnyk
save
9f1fdcf
Raw
History Blame Contribute Delete
2.44 kB
import difflib
import re
from typing import List, Tuple
def preprocess(text: str) -> List[str]:
return re.sub(r"[^\w\s]", "", text.lower()).split()
def tokenize_raw_text(text: str) -> List[str]:
return re.findall(r"\b\w+(?:'\w+)?\b|[^\w\s]", text)
def extract_segment_with_punct(raw_words: List[str], start_index: int, word_count: int) -> Tuple[
List[Tuple[str, bool]], int]:
segment = []
word_seen = 0
i = start_index
while word_seen < word_count and i < len(raw_words):
word = raw_words[i]
if re.match(r"\w+", word): # це слово
punct = ""
i += 1
while i < len(raw_words) and not re.match(r"\w+", raw_words[i]):
punct += raw_words[i]
i += 1
segment.append((word + punct, True))
word_seen += 1
else:
segment.append((word, False))
i += 1
return segment, i
def style_word(word: str, correct: bool) -> str:
color = "#e6ffe6" if correct else "#ffe6e6"
text_color = "#006600" if correct else "#990000"
content = word if correct else "..."
return (
f"<span style='background-color:{color}; color:{text_color}; padding:4px 10px; "
f"margin:4px; border-radius:999px; font-weight:500; display:inline-block;'>{content}</span>"
)
def highlight_fuzzy_diff(user_text: str, original_text: str) -> Tuple[str, int]:
original_words_raw = tokenize_raw_text(original_text)
original_words_clean = preprocess(original_text)
user_words_clean = preprocess(user_text)
matcher = difflib.SequenceMatcher(None, user_words_clean, original_words_clean)
highlighted = []
correct_count = 0
total_count = 0
original_pointer = 0
for op, i1, i2, j1, j2 in matcher.get_opcodes():
word_count = j2 - j1
segment_raw, original_pointer = extract_segment_with_punct(original_words_raw, original_pointer, word_count)
for token, is_word in segment_raw:
if is_word:
total_count += 1
is_correct = op == "equal"
if is_correct:
correct_count += 1
highlighted.append(style_word(token, is_correct))
else:
highlighted.append(token)
score_percent = round((correct_count / total_count) * 100) if total_count > 0 else 0
return " ".join(highlighted), score_percent