"""Read port for the per-Property ``property_overrides`` fact layer (ADR-0006). The write side (``PropertyOverrideRepository.upsert_all``) materialises the fact layer at Finalise; this is the read side. It is deliberately *faithful* — it returns the resolved enum-value snapshots exactly as stored ("House", "Detached", "Solid brick, …"), every ``(override_component, building_part)`` for the Property, making no judgement about what is resolvable. Consumers translate: EPC Prediction maps property_type/built_form into the gov-EPC code space and gates on it (see ``domain/epc/override_code_mapping.py``); the later ``epc_with_overlay`` slice will read wall/roof here too. """ from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Optional @dataclass(frozen=True) class ResolvedPropertyOverride: """One ``property_overrides`` row, in enum-value space (as stored).""" override_component: str building_part: int override_value: str @dataclass(frozen=True) class ResolvedPropertyOverrides: """Every resolved override for one Property — a faithful value-space snapshot.""" rows: tuple[ResolvedPropertyOverride, ...] def value(self, override_component: str, building_part: int) -> Optional[str]: """The resolved value for one ``(component, building_part)``, or None when the Property has no such override row.""" for row in self.rows: if ( row.override_component == override_component and row.building_part == building_part ): return row.override_value return None class PropertyOverridesReader(ABC): @abstractmethod def overrides_for(self, property_id: int) -> ResolvedPropertyOverrides: """Every resolved Landlord Override for the Property, as stored. Empty when the Property has no overrides.""" ...