File size: 7,473 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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
"""Blueprint that returns location information for faculty, staff, or students."""

from __future__ import annotations

from typing import Any, Dict, List, Optional

from .base import AnalysisContext, Blueprint, BlueprintResult, Fact
from .faculty_profile import _lookup_record
from ..data.utils import canonicalize_name


class LocationBlueprint(Blueprint):
    """Return where a person (faculty, staff, or student) can be found on campus.
    
    Automatically detects whether the name matches a faculty member, staff member,
    or student and returns the appropriate location information.
    """

    name = "location"

    def run(self, context: AnalysisContext, **kwargs: Any) -> BlueprintResult:
        target_name = (kwargs.get("name") or kwargs.get("person") or "").strip()
        if not target_name:
            return BlueprintResult(self.name, kwargs, facts=[], notes=["No name provided."])

        facts: List[Fact] = []
        notes: List[str] = []
        found_faculty = False
        found_staff = False
        found_student = False

        # Try faculty lookup first
        faculty_facts, faculty_notes, found_faculty = self._lookup_faculty_location(context, target_name)
        facts.extend(faculty_facts)
        notes.extend(faculty_notes)

        # Try staff lookup
        staff_facts, staff_notes, found_staff = self._lookup_staff_location(context, target_name)
        facts.extend(staff_facts)
        notes.extend(staff_notes)

        # Try student lookup
        student_facts, student_notes, found_student = self._lookup_student_seating(context, target_name)
        facts.extend(student_facts)
        notes.extend(student_notes)

        # If none found, report not found
        if not found_faculty and not found_staff and not found_student:
            return BlueprintResult(
                self.name,
                kwargs,
                facts=[],
                notes=[f"'{target_name}' not found in faculty, staff, or student records."],
            )

        return BlueprintResult(self.name, kwargs, facts=facts, notes=notes)

    def _lookup_faculty_location(
        self, context: AnalysisContext, target_name: str
    ) -> tuple[List[Fact], List[str], bool]:
        """Look up faculty office location. Returns (facts, notes, found)."""
        
        record, lookup_notes, office_row = _lookup_record(context, target_name)
        if record is None and office_row is None:
            return [], [], False

        facts: List[Fact] = []
        notes = list(lookup_notes)

        # Try to resolve office through relationship
        office_matches = []
        if record is not None:
            office_matches = context.catalog.resolve_relationship("faculty_to_office", record)

        if office_matches:
            office_row = office_matches[0]
            office_entity = context.catalog.try_get("faculty_offices")
            office_source = office_entity.origin if office_entity else None
            building = (office_row.get("Building") or "").strip()
            room = (office_row.get("Room") or office_row.get("Room Location") or "").strip()
            location = " ".join(bit for bit in (building, room) if bit).strip()
            facts.append(
                Fact(
                    subject=record["Name"],
                    predicate="office",
                    value=location or "Office location unavailable",
                    source=office_source,
                )
            )
        elif office_row is not None:
            # Matched office assignment but no roster record
            office_entity = context.catalog.try_get("faculty_offices")
            office_source = office_entity.origin if office_entity else None
            building = (office_row.get("Building") or office_row.get("Location") or "").strip()
            room = (office_row.get("Room") or office_row.get("Room Location") or "").strip()
            location = " ".join(bit for bit in (building, room) if bit).strip()
            facts.append(
                Fact(
                    subject=office_row.get("Assignee Name") or target_name,
                    predicate="office",
                    value=location or "Office location unavailable",
                    source=office_source,
                )
            )
        elif record is not None:
            notes.append(f"No office assignment found for {record['Name']}.")

        return facts, notes, (record is not None or office_row is not None)

    def _lookup_staff_location(
        self, context: AnalysisContext, target_name: str
    ) -> tuple[List[Fact], List[str], bool]:
        """Look up staff office location. Returns (facts, notes, found)."""
        
        staff = context.catalog.try_get("staff")
        if not staff:
            return [], [], False

        record = None
        canonical_target = canonicalize_name(target_name)
        for row in staff.records:
            if canonicalize_name(row.get("Name", "")) == canonical_target:
                record = row
                break

        if record is None:
            return [], [], False

        facts: List[Fact] = []
        notes: List[str] = []
        
        location = (record.get("Room Location") or "").strip()
        if location and location.lower() not in {"not listed", "n/a", "none", ""}:
            facts.append(
                Fact(
                    subject=record.get("Name", target_name),
                    predicate="office",
                    value=location,
                    source=staff.origin,
                    confidence=0.9,
                )
            )
        else:
            notes.append(f"No office location listed for {record.get('Name', target_name)}.")

        return facts, notes, True

    def _lookup_student_seating(
        self, context: AnalysisContext, target_name: str
    ) -> tuple[List[Fact], List[str], bool]:
        """Look up student seating location. Returns (facts, notes, found)."""
        
        students = context.catalog.try_get("students")
        if not students:
            return [], [], False

        record = None
        canonical_target = canonicalize_name(target_name)
        for row in students.records:
            if canonicalize_name(row.get("Name")) == canonical_target:
                record = row
                break

        if record is None:
            return [], [], False

        seating = context.catalog.resolve_relationship("student_to_mudd_seat", record)
        seating_entity = context.catalog.try_get("mudd_seating")
        seating_origin = seating_entity.origin if seating_entity else None
        
        facts: List[Fact] = []
        for seat in seating:
            facts.append(
                Fact(
                    subject=record.get("Name", target_name),
                    predicate="seating",
                    value={
                        "room": seat.get("Room Number"),
                        "desk": seat.get("Desk Number"),
                        "track": seat.get("Track"),
                        "advisor": seat.get("Advocate/Advisor"),
                        "email": seat.get("Email address"),
                    },
                    source=seating_origin,
                    confidence=0.8,
                )
            )

        notes: List[str] = []
        if not facts:
            notes.append(f"No seating assignment listed for {record['Name']}.")

        return facts, notes, True