File size: 1,349 Bytes
d896946
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
"""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