cipher-attacks's picture
inline fuzzy + courses
d896946
Raw History Blame Contribute Delete
1.35 kB
"""Typo-tolerant matching for course search (pure, tested). 'phyy' still finds 'Physics'."""
import difflib
import re
from typing import Optional, Sequence
_WORD = re.compile(r"[a-z0-9\u1200-\u137f]+")
def words(text: str) -> list[str]:
return _WORD.findall(text.lower())
def _common_prefix(a: str, b: str) -> int:
n = 0
for x, y in zip(a, b):
if x != y:
break
n += 1
return n
def _token_score(token: str, candidate_words: Sequence[str]) -> int:
best = 0
for w in candidate_words:
if token == w:
return 4
if w.startswith(token):
best = max(best, 3)
elif token in w:
best = max(best, 2)
else:
p = _common_prefix(token, w)
if (p >= 3 and p / len(token) >= 0.6) or (len(token) >= 4 and difflib.SequenceMatcher(None, token, w).ratio() >= 0.75):
best = max(best, 1)
return best
def match_score(tokens: Sequence[str], texts: Sequence[str]) -> Optional[int]:
"""None if ANY token matches nothing; otherwise a higher score = better match."""
candidate_words = [w for t in texts for w in words(t)]
total = 0
for token in tokens:
score = _token_score(token, candidate_words)
if score == 0:
return None
total += score
return total