File size: 2,213 Bytes
526950e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
49
50
51
52
53
54
55
56
57
58
59
"""Term detection over text using term_registry.words[] aliases.

Builds one compiled regex from all aliases (case-insensitive, word-bounded).
Maps matches back to canonical slugs. Supports 1-hop expansion via depends_on.
"""
from __future__ import annotations
import re


def build_regex(registry: dict) -> re.Pattern:
    """Compile a single regex matching any alias in registry.terms[*].words[].

    Aliases are sorted longest-first so 'elasticity of demand' matches
    before 'elasticity'. Word boundaries enforced. Case-insensitive.
    """
    aliases: list[tuple[str, str]] = []  # (alias, canonical_slug)
    for slug, term in registry.get("terms", {}).items():
        for word in term.get("words", []) or []:
            aliases.append((word, slug))
    # Longest first to prevent shorter aliases shadowing longer ones
    aliases.sort(key=lambda p: len(p[0]), reverse=True)
    if not aliases:
        return re.compile(r"$^")  # never matches
    pattern = r"\b(" + "|".join(re.escape(a[0]) for a in aliases) + r")\b"
    return re.compile(pattern, re.IGNORECASE)


def find_terms(text: str, regex: re.Pattern, registry: dict) -> list[str]:
    """Return list of canonical slugs whose aliases appear in text.

    Deduped, ordered by first appearance.
    """
    # Build alias -> slug lookup (case-folded)
    alias_to_slug: dict[str, str] = {}
    for slug, term in registry.get("terms", {}).items():
        for word in term.get("words", []) or []:
            alias_to_slug[word.lower()] = slug
    seen: list[str] = []
    for m in regex.finditer(text):
        slug = alias_to_slug.get(m.group(0).lower())
        if slug and slug not in seen:
            seen.append(slug)
    return seen


def expand_one_hop(seed_slugs: list[str], registry: dict) -> list[str]:
    """Return 1-hop neighbors (via depends_on) of seed_slugs, excluding seeds.

    Output is deterministic: deduped, sorted alphabetically.
    """
    seeds = set(seed_slugs)
    neighbors: set[str] = set()
    terms = registry.get("terms", {})
    for seed in seeds:
        for n in terms.get(seed, {}).get("depends_on", []) or []:
            if n not in seeds:
                neighbors.add(n)
    return sorted(neighbors)