"""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