File size: 2,955 Bytes
c2b7a7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
60
61
62
"""Helper functions for faculty record lookups used by multiple blueprints."""

from __future__ import annotations

import difflib
from typing import Any, Dict, List, Optional, Tuple

from ..data.utils import canonicalize_name, generate_name_variants
from .base import AnalysisContext


def _lookup_record(context: AnalysisContext, target_name: str) -> Tuple[Any, List[str], Optional[Dict[str, Any]]]:
    """Return the faculty record matching ``target_name`` and any lookup notes."""

    notes: List[str] = []
    dataset = context.catalog.get("faculty")
    record = dataset.get_by_key(target_name)
    if record is None:
        target_canonical = canonicalize_name(target_name)
        for row in dataset.records:
            if canonicalize_name(row.get("Name", "")) == target_canonical:
                record = row
                break
    if record is None:
        candidates = [row.get("Name", "") for row in dataset.records if row.get("Name")]
        closest = difflib.get_close_matches(target_name, candidates, n=1, cutoff=0.6)
        if closest:
            record = dataset.get_by_key(closest[0])
            notes.append(f"Showing results for '{closest[0]}' (closest match).")
    # Fallback: some names appear only in office-assignment CSVs (e.g., "Bain,Connor").
    # Try to match those assignee names back into the faculty roster using
    # common name permutations.
    office_row: Optional[Dict[str, Any]] = None
    if record is None:
        offices = context.catalog.try_get("faculty_offices")
        if offices:
            for row in offices.records:
                assignee = row.get("Assignee Name") or row.get("Assignee") or row.get("Name")
                if not assignee:
                    continue
                # If the assignee directly matches the target name (or its variants),
                # prefer returning the matched faculty roster row when available; if
                # no roster row matches, keep the office row as a fallback to emit
                # office/location facts.
                matched = False
                for variant in generate_name_variants(assignee):
                    # If the variant matches the requested target, consider it a hit.
                    if canonicalize_name(variant) == canonicalize_name(target_name):
                        candidate = dataset.get_by_key(variant)
                        if candidate is not None:
                            record = candidate
                            notes.append(f"Matched '{assignee}' from faculty offices to roster entry '{candidate.get('Name')}'.")
                            matched = True
                            break
                        # remember office row as fallback if no roster entry exists
                        office_row = row
                        matched = True
                        break
                if matched:
                    break
    return record, notes, office_row