mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Compose repository and matcher into scored historic EPC matches 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d084a0a785
commit
d7fd093c4f
2 changed files with 86 additions and 0 deletions
33
repositories/historic_epc/historic_epc_resolver.py
Normal file
33
repositories/historic_epc/historic_epc_resolver.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from datatypes.epc.domain.historic_epc_matching import (
|
||||
HistoricEpcMatches,
|
||||
rank_historic_epc,
|
||||
)
|
||||
from repositories.historic_epc.historic_epc_repository import (
|
||||
HistoricEpcRepository,
|
||||
PostcodeNotFound,
|
||||
)
|
||||
|
||||
|
||||
class HistoricEpcResolver:
|
||||
"""Resolves an address to a historic-EPC match by composing the repository
|
||||
(fetch a postcode's records) with the matcher (score them against the
|
||||
address). This is where ``address2uprn`` plugs onto the old-EPC backup.
|
||||
"""
|
||||
|
||||
def __init__(self, repo: HistoricEpcRepository) -> None:
|
||||
self._repo = repo
|
||||
|
||||
def match(self, user_address: str, postcode: str) -> HistoricEpcMatches:
|
||||
"""All of the postcode's historic EPCs scored against ``user_address``."""
|
||||
raise NotImplementedError
|
||||
|
||||
def resolve_uprn(
|
||||
self, user_address: str, postcode: str
|
||||
) -> Optional[tuple[str, str, float]]:
|
||||
"""``(uprn, matched_address, lexiscore)`` for an unambiguous rank-1
|
||||
match, else None (no data / ambiguous tie / zero score)."""
|
||||
raise NotImplementedError
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
"""HistoricEpcResolver composes the repository with the address matcher.
|
||||
|
||||
Exercised against a fake in-memory repository (a dict of postcode -> records),
|
||||
so the resolver's composition is tested with no S3 and no network — the matcher
|
||||
and the repo each have their own tests.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
|
||||
from datatypes.epc.domain.historic_epc import HistoricEpc
|
||||
from datatypes.epc.domain.historic_epc_matching import HistoricEpcMatches
|
||||
from repositories.historic_epc.historic_epc_repository import HistoricEpcRepository
|
||||
from repositories.historic_epc.historic_epc_resolver import HistoricEpcResolver
|
||||
|
||||
|
||||
def _hist(address: str, uprn: str) -> HistoricEpc:
|
||||
fields = {f.name: "" for f in dataclasses.fields(HistoricEpc)}
|
||||
fields["address"] = address
|
||||
fields["uprn"] = uprn
|
||||
return HistoricEpc(**fields)
|
||||
|
||||
|
||||
class _FakeRepo(HistoricEpcRepository):
|
||||
def __init__(self, by_postcode: dict[str, list[HistoricEpc]]) -> None:
|
||||
self._by_postcode = by_postcode
|
||||
|
||||
def get_for_postcode(self, postcode: str) -> list[HistoricEpc]:
|
||||
key = postcode.upper().replace(" ", "")
|
||||
return self._by_postcode.get(key, [])
|
||||
|
||||
|
||||
def test_match_composes_repo_and_matcher_into_scored_matches():
|
||||
# Arrange
|
||||
repo = _FakeRepo(
|
||||
{
|
||||
"AB338AL": [
|
||||
_hist("48 GORDON ROAD", "200"),
|
||||
_hist("47 GORDON ROAD", "100"),
|
||||
]
|
||||
}
|
||||
)
|
||||
resolver = HistoricEpcResolver(repo)
|
||||
|
||||
# Act
|
||||
result = resolver.match("47 Gordon Road", "AB33 8AL")
|
||||
|
||||
# Assert
|
||||
assert isinstance(result, HistoricEpcMatches)
|
||||
assert result.postcode == "AB338AL"
|
||||
assert len(result.matches) == 2
|
||||
assert result.top().record.address == "47 GORDON ROAD"
|
||||
Loading…
Add table
Reference in a new issue