From 4e291e4184d37104195dac68c209511cf7d13c0f Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 13 Jul 2026 15:03:55 +0000 Subject: [PATCH] =?UTF-8?q?Resolve=20landlord=20references=20to=20UPRNs,?= =?UTF-8?q?=20excluding=20unresolved=20rows=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../condition/property_reference_reader.py | 19 +++++++++++ .../condition/property_uprn_lookup.py | 18 +++++++++++ .../lookups/test_property_uprn_lookup.py | 32 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 repositories/condition/property_reference_reader.py create mode 100644 repositories/condition/property_uprn_lookup.py create mode 100644 tests/condition/lookups/test_property_uprn_lookup.py diff --git a/repositories/condition/property_reference_reader.py b/repositories/condition/property_reference_reader.py new file mode 100644 index 000000000..1eba7524e --- /dev/null +++ b/repositories/condition/property_reference_reader.py @@ -0,0 +1,19 @@ +from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import List, Optional + + +@dataclass(frozen=True) +class PropertyReference: + """A property's landlord reference and resolved UPRN, as held in the + ``property`` table. ``uprn`` is nullable — a property whose UPRN has not been + resolved yet cannot receive condition rows.""" + + landlord_property_id: Optional[str] + uprn: Optional[int] + + +class PropertyReferenceReader(ABC): + @abstractmethod + def references_for_portfolio(self, portfolio_id: int) -> List[PropertyReference]: + ... diff --git a/repositories/condition/property_uprn_lookup.py b/repositories/condition/property_uprn_lookup.py new file mode 100644 index 000000000..b9c8e4e2a --- /dev/null +++ b/repositories/condition/property_uprn_lookup.py @@ -0,0 +1,18 @@ +from typing import Dict + +from repositories.condition.property_reference_reader import PropertyReferenceReader +from repositories.condition.uprn_lookup import UprnLookup + + +class PropertyUprnLookup(UprnLookup): + """Resolves a landlord property reference to a UPRN from the ``property`` + table, scoped to one portfolio (ADR-0064).""" + + def __init__( + self, reader: PropertyReferenceReader, portfolio_id: int + ) -> None: + self._reader = reader + self._portfolio_id = portfolio_id + + def get_property_ref_to_uprn_lookup(self) -> Dict[str, int]: + raise NotImplementedError diff --git a/tests/condition/lookups/test_property_uprn_lookup.py b/tests/condition/lookups/test_property_uprn_lookup.py new file mode 100644 index 000000000..f303b55f1 --- /dev/null +++ b/tests/condition/lookups/test_property_uprn_lookup.py @@ -0,0 +1,32 @@ +from typing import List + +from repositories.condition.property_reference_reader import ( + PropertyReference, + PropertyReferenceReader, +) +from repositories.condition.property_uprn_lookup import PropertyUprnLookup + + +class FakePropertyReferenceReader(PropertyReferenceReader): + def __init__(self, references: List[PropertyReference]) -> None: + self._references = references + + def references_for_portfolio(self, portfolio_id: int) -> List[PropertyReference]: + return self._references + + +def test_property_uprn_lookup_excludes_rows_with_no_uprn(): + # Arrange + reader = FakePropertyReferenceReader( + [ + PropertyReference(landlord_property_id="443", uprn=100093305101), + PropertyReference(landlord_property_id="999", uprn=None), + ] + ) + lookup = PropertyUprnLookup(reader, portfolio_id=824) + + # Act + mapping = lookup.get_property_ref_to_uprn_lookup() + + # Assert + assert mapping == {"443": 100093305101}