|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import re |
| 4 | + |
3 | 5 | from assayer.models import ModelResult |
4 | 6 |
|
5 | 7 | _model = None |
6 | 8 |
|
| 9 | +_NON_BOUNDARY_ABBREVIATIONS = {"dr", "mr", "mrs", "ms", "prof", "sr", "jr", "st"} |
| 10 | + |
7 | 11 |
|
8 | 12 | def _get_model(): |
9 | 13 | global _model |
@@ -34,22 +38,57 @@ def compute_similarity(results: list[ModelResult]) -> dict[tuple[str, str], floa |
34 | 38 | for i in range(len(valid)): |
35 | 39 | for j in range(i + 1, len(valid)): |
36 | 40 | score = float(np.dot(normalized[i], normalized[j])) |
| 41 | + score = max(-1.0, min(1.0, score)) |
37 | 42 | similarity[(valid[i].model, valid[j].model)] = score |
38 | 43 |
|
39 | 44 | return similarity |
40 | 45 |
|
41 | 46 |
|
42 | 47 | def readability_stats(text: str) -> dict[str, float]: |
43 | | - sentences = [ |
44 | | - s |
45 | | - for s in text.replace("!", ".").replace("?", ".").split(".") |
46 | | - if s.strip() |
47 | | - ] |
48 | 48 | words = text.split() |
49 | 49 | word_count = len(words) |
50 | | - sentence_count = len(sentences) or 1 |
| 50 | + sentence_count = _count_sentences(text) |
51 | 51 | return { |
52 | 52 | "word_count": float(word_count), |
53 | 53 | "sentence_count": float(sentence_count), |
54 | 54 | "avg_sentence_length": word_count / sentence_count, |
55 | 55 | } |
| 56 | + |
| 57 | + |
| 58 | +def _count_sentences(text: str) -> int: |
| 59 | + count = 0 |
| 60 | + start = 0 |
| 61 | + |
| 62 | + for match in re.finditer(r"[.!?]+", text): |
| 63 | + punct_start, punct_end = match.span() |
| 64 | + if punct_end < len(text) and not text[punct_end].isspace(): |
| 65 | + continue |
| 66 | + if _is_non_boundary_period(text, punct_start, punct_end): |
| 67 | + continue |
| 68 | + |
| 69 | + if text[start:punct_end].strip(): |
| 70 | + count += 1 |
| 71 | + start = punct_end |
| 72 | + |
| 73 | + if text[start:].strip(): |
| 74 | + count += 1 |
| 75 | + |
| 76 | + return count or 1 |
| 77 | + |
| 78 | + |
| 79 | +def _is_non_boundary_period(text: str, punct_start: int, punct_end: int) -> bool: |
| 80 | + if text[punct_start] != ".": |
| 81 | + return False |
| 82 | + if ( |
| 83 | + punct_start > 0 |
| 84 | + and punct_start + 1 < len(text) |
| 85 | + and text[punct_start - 1].isdigit() |
| 86 | + and text[punct_start + 1].isdigit() |
| 87 | + ): |
| 88 | + return True |
| 89 | + |
| 90 | + token_match = re.search(r"([A-Za-z]+)\.$", text[:punct_end]) |
| 91 | + if not token_match: |
| 92 | + return False |
| 93 | + |
| 94 | + return token_match.group(1).lower() in _NON_BOUNDARY_ABBREVIATIONS |
0 commit comments