mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Merge branch 'feat/address2uprn-certificate-number' into feature/uprn-confirmation-before-finalise
# Conflicts: # backend/address2UPRN/main.py
This commit is contained in:
commit
4e0134dd27
3 changed files with 37 additions and 18 deletions
|
|
@ -38,22 +38,29 @@ def get_epc_data_with_postcode(postcode: str) -> pd.DataFrame:
|
|||
service = EpcClientService(auth_token=token)
|
||||
results = service.search_by_postcode(postcode)
|
||||
return pd.DataFrame(
|
||||
[{"address": r.address_line_1, "uprn": r.uprn} for r in results]
|
||||
[
|
||||
{
|
||||
"address": r.address_line_1,
|
||||
"uprn": r.uprn,
|
||||
"certificate_number": r.certificate_number,
|
||||
}
|
||||
for r in results
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def get_uprn_from_historic_epc(
|
||||
user_inputed_address: str,
|
||||
postcode: str,
|
||||
) -> Optional[tuple[str, str, float]]:
|
||||
) -> Optional[tuple[str, str, float, Optional[str]]]:
|
||||
"""Resolve a UPRN via historic EPC S3 data.
|
||||
|
||||
Returns (uprn, address, lexiscore) when the historic dataset agrees on a
|
||||
single rank-1 UPRN, None otherwise (no stored data, zero score, or
|
||||
ambiguous top rank). The score gate is `unambiguous_uprn`'s own (score > 0);
|
||||
the 0.7 heuristic used for the new-EPC source isn't applied here because
|
||||
historic addresses use a more verbose format that systematically depresses
|
||||
lexiscores.
|
||||
Returns (uprn, address, lexiscore, certificate_number) when the historic
|
||||
dataset agrees on a single rank-1 UPRN, None otherwise (no stored data,
|
||||
zero score, or ambiguous top rank). The score gate is `unambiguous_uprn`'s
|
||||
own (score > 0); the 0.7 heuristic used for the new-EPC source isn't
|
||||
applied here because historic addresses use a more verbose format that
|
||||
systematically depresses lexiscores.
|
||||
"""
|
||||
repo = HistoricEpcS3Repository.with_default_s3_client()
|
||||
return HistoricEpcResolver(repo).resolve_uprn(user_inputed_address, postcode)
|
||||
|
|
@ -63,7 +70,7 @@ def get_uprn_with_epc_df(
|
|||
user_inputed_address: str,
|
||||
epc_df: pd.DataFrame,
|
||||
verbose: bool = False,
|
||||
) -> Optional[str | tuple[str, str, float]]:
|
||||
) -> Optional[str | tuple[str, str, float, Optional[str]]]:
|
||||
"""
|
||||
Return uprn (str) using a pre-fetched EPC dataframe.
|
||||
This avoids calling the API multiple times for the same postcode.
|
||||
|
|
@ -92,6 +99,7 @@ def get_uprn_with_epc_df(
|
|||
|
||||
address = top_rank_df["address"].values[0]
|
||||
score = float(top_rank_df["lexiscore"].values[0])
|
||||
certificate_number = top_rank_df["certificate_number"].values[0]
|
||||
|
||||
logger.info(f"Address found to be: {address}, with lexiscore {score}")
|
||||
# Safe to return the agreed UPRN
|
||||
|
|
@ -102,7 +110,7 @@ def get_uprn_with_epc_df(
|
|||
return None
|
||||
|
||||
if verbose:
|
||||
return (found_uprn, address, score)
|
||||
return (found_uprn, address, score, certificate_number)
|
||||
else:
|
||||
return found_uprn
|
||||
|
||||
|
|
@ -124,7 +132,7 @@ def get_uprn(
|
|||
"""
|
||||
df = get_epc_data_with_postcode(postcode=postcode)
|
||||
|
||||
result: Optional[tuple[str, str, float]] = get_uprn_with_epc_df(
|
||||
result: Optional[tuple[str, str, float, Optional[str]]] = get_uprn_with_epc_df(
|
||||
user_inputed_address=user_inputed_address,
|
||||
epc_df=df,
|
||||
verbose=True,
|
||||
|
|
@ -397,6 +405,7 @@ def handler(event, context, local=False):
|
|||
"address2uprn_uprn": "invalid postcode",
|
||||
"address2uprn_address": "invalid postcode",
|
||||
"address2uprn_lexiscore": "invalid postcode",
|
||||
"address2uprn_certificate_number": "invalid postcode",
|
||||
"address2uprn_status": "invalid_postcode",
|
||||
}
|
||||
)
|
||||
|
|
@ -439,7 +448,9 @@ def handler(event, context, local=False):
|
|||
continue
|
||||
|
||||
# Get UPRN using the pre-fetched EPC data with all return options
|
||||
result: Optional[tuple[str, str, float]] = get_uprn_with_epc_df(
|
||||
result: Optional[
|
||||
tuple[str, str, float, Optional[str]]
|
||||
] = get_uprn_with_epc_df(
|
||||
user_inputed_address=address2uprn_user_input,
|
||||
epc_df=epc_df,
|
||||
verbose=True,
|
||||
|
|
@ -468,7 +479,7 @@ def handler(event, context, local=False):
|
|||
|
||||
# Parse result tuple if successful
|
||||
if result:
|
||||
uprn, found_address, score = result
|
||||
uprn, found_address, score, certificate_number = result
|
||||
logger.info(
|
||||
f"Found UPRN for {address2uprn_user_input} in {postcode}: {uprn} (score: {score})"
|
||||
)
|
||||
|
|
@ -478,6 +489,7 @@ def handler(event, context, local=False):
|
|||
"uprn": uprn,
|
||||
"address": found_address,
|
||||
"lexiscore": score,
|
||||
"certificate_number": certificate_number,
|
||||
"norm_address": norm_address,
|
||||
"error": None,
|
||||
}
|
||||
|
|
@ -492,6 +504,7 @@ def handler(event, context, local=False):
|
|||
"uprn": None,
|
||||
"address": None,
|
||||
"lexiscore": None,
|
||||
"certificate_number": None,
|
||||
"norm_address": norm_address,
|
||||
"error": None,
|
||||
}
|
||||
|
|
@ -508,6 +521,7 @@ def handler(event, context, local=False):
|
|||
"uprn": None,
|
||||
"address": None,
|
||||
"lexiscore": None,
|
||||
"certificate_number": None,
|
||||
"norm_address": "",
|
||||
"error": str(e),
|
||||
}
|
||||
|
|
@ -526,6 +540,9 @@ def handler(event, context, local=False):
|
|||
"address2uprn_uprn": final_uprn,
|
||||
"address2uprn_address": rec["address"] if final_uprn else None,
|
||||
"address2uprn_lexiscore": rec["lexiscore"],
|
||||
"address2uprn_certificate_number": (
|
||||
rec["certificate_number"] if final_uprn else None
|
||||
),
|
||||
"address2uprn_status": (
|
||||
"error" if rec["error"] is not None else status
|
||||
),
|
||||
|
|
|
|||
|
|
@ -49,9 +49,10 @@ class HistoricEpcResolver:
|
|||
|
||||
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)."""
|
||||
) -> Optional[tuple[str, str, float, Optional[str]]]:
|
||||
"""``(uprn, matched_address, lexiscore, certificate_number)`` for an
|
||||
unambiguous rank-1 match, else None (no data / ambiguous tie / zero
|
||||
score). ``certificate_number`` is the historic dataset's ``lmk_key``."""
|
||||
matches: HistoricEpcMatches = self.match(user_address, postcode)
|
||||
uprn: Optional[str] = matches.unambiguous_uprn()
|
||||
if not uprn or uprn == "nan":
|
||||
|
|
@ -59,4 +60,4 @@ class HistoricEpcResolver:
|
|||
top: Optional[ScoredHistoricEpc] = matches.top()
|
||||
if top is None:
|
||||
return None
|
||||
return uprn, top.record.address, top.lexiscore
|
||||
return uprn, top.record.address, top.lexiscore, top.record.lmk_key
|
||||
|
|
|
|||
|
|
@ -73,10 +73,11 @@ def test_resolve_uprn_returns_unambiguous_match():
|
|||
|
||||
# Assert
|
||||
assert result is not None
|
||||
uprn, address, score = result
|
||||
uprn, address, score, certificate_number = result
|
||||
assert uprn == "100"
|
||||
assert address == "47 GORDON ROAD"
|
||||
assert score > 0
|
||||
assert certificate_number == ""
|
||||
|
||||
|
||||
def test_resolve_uprn_is_none_when_postcode_has_no_data():
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue