mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
refetch_epc=False skips API entirely; EPC-less properties go straight to prediction path
When refetch_epc=False and no stored lodged EPC exists, the handler no longer falls back to a live EPC API call — it treats the property as EPC-less and hands it to the prediction path. This keeps REFETCH_EPC (lodged path) and REPREDICT_EPC (prediction path) cleanly independent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b1fd9d9368
commit
17a9f0aafc
3 changed files with 46 additions and 18 deletions
|
|
@ -544,11 +544,13 @@ def handler(body: dict[str, Any], context: Any, orchestrator: TaskOrchestrator,
|
|||
)
|
||||
|
||||
stored_lodged = stored_lodged_epcs.get(pid)
|
||||
if not refetch_epc and stored_lodged is not None:
|
||||
if refetch_epc:
|
||||
epc: Optional[EpcPropertyData] = epc_client.get_by_uprn(uprn)
|
||||
elif stored_lodged is not None:
|
||||
logger.info(f"property={pid} using stored lodged EPC (refetch_epc=False)")
|
||||
epc: Optional[EpcPropertyData] = stored_lodged
|
||||
epc = stored_lodged
|
||||
else:
|
||||
epc = epc_client.get_by_uprn(uprn)
|
||||
epc = None # no stored lodged EPC; prediction path handles this property
|
||||
overrides = overlays_from(overrides_reader.overrides_for(pid))
|
||||
predicted_epc: Optional[EpcPropertyData] = None
|
||||
|
||||
|
|
|
|||
|
|
@ -45,14 +45,12 @@ DRY_RUN: bool = False
|
|||
# False → Lambda skips the Google Solar fetch (re-uses stored Solar data).
|
||||
REFETCH_SOLAR: bool = True
|
||||
|
||||
# False → skip the EPC API call for properties that already have a stored lodged
|
||||
# EPC; the API is still called for any property that has no stored lodged EPC.
|
||||
# False → use stored lodged EPC for properties that have one; properties with no
|
||||
# stored lodged EPC are treated as EPC-less and routed to prediction (no API call).
|
||||
REFETCH_EPC: bool = True
|
||||
|
||||
# False → skip live EPC prediction for properties that already have a stored
|
||||
# predicted EPC; live prediction still runs for any property that reaches the
|
||||
# prediction branch with no stored predicted EPC. Only relevant for properties
|
||||
# without a lodged EPC (either stored or freshly fetched).
|
||||
# False → use stored predicted EPC for EPC-less properties that have one; live
|
||||
# prediction still runs when no stored predicted EPC exists for the property.
|
||||
REPREDICT_EPC: bool = True
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -1176,15 +1176,24 @@ def test_refetch_epc_false_with_stored_epc_skips_api_call() -> None:
|
|||
)
|
||||
|
||||
|
||||
def test_refetch_epc_false_without_stored_epc_falls_back_to_api() -> None:
|
||||
"""refetch_epc=False + no stored lodged EPC: handler falls back to the live
|
||||
EPC API call rather than silently skipping the property."""
|
||||
def test_refetch_epc_false_without_stored_epc_skips_api_and_goes_to_prediction() -> None:
|
||||
"""refetch_epc=False + no stored lodged EPC: the EPC API is not called;
|
||||
the property is treated as EPC-less and prediction runs instead."""
|
||||
# Arrange
|
||||
mock_engine = _engine_mock([PROPERTY_ID], [UPRN], [POSTCODE])
|
||||
live_epc = MagicMock()
|
||||
mock_plan = _plan_mock()
|
||||
mock_uow = MagicMock()
|
||||
|
||||
mock_predicted_epc = MagicMock()
|
||||
from datatypes.epc.domain.epc_property_data import BuildingPartIdentifier
|
||||
|
||||
mock_part = MagicMock()
|
||||
mock_part.identifier = BuildingPartIdentifier.MAIN
|
||||
mock_predicted_epc.sap_building_parts = [mock_part]
|
||||
|
||||
mock_comparables = MagicMock()
|
||||
mock_comparables.members = [MagicMock()]
|
||||
|
||||
with ExitStack() as stack:
|
||||
stack.enter_context(patch("applications.modelling_e2e.handler.os.environ", _ENV))
|
||||
stack.enter_context(
|
||||
|
|
@ -1193,7 +1202,7 @@ def test_refetch_epc_false_without_stored_epc_falls_back_to_api() -> None:
|
|||
mock_epc_client = stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.EpcClientService")
|
||||
).return_value
|
||||
mock_epc_client.get_by_uprn.return_value = live_epc
|
||||
mock_epc_client.get_by_uprn.return_value = MagicMock() # would be called if flag ignored
|
||||
stack.enter_context(patch("applications.modelling_e2e.handler.GeospatialS3Repository"))
|
||||
stack.enter_context(patch("applications.modelling_e2e.handler.GoogleSolarApiClient"))
|
||||
stack.enter_context(
|
||||
|
|
@ -1208,6 +1217,22 @@ def test_refetch_epc_false_without_stored_epc_falls_back_to_api() -> None:
|
|||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.PropertyOverridesPostgresReader")
|
||||
).return_value.overrides_for_many.return_value = {}
|
||||
from domain.epc_prediction.prediction_target import PredictionTargetAttributes
|
||||
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.OverrideBackedPredictionAttributesReader")
|
||||
).return_value.attributes_for.return_value = PredictionTargetAttributes(
|
||||
property_type="2"
|
||||
)
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.select_comparables")
|
||||
).return_value = mock_comparables
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.EpcPrediction")
|
||||
).return_value.predict.return_value = mock_predicted_epc
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.EpcComparablePropertiesRepository")
|
||||
).return_value.candidates_for.return_value = [MagicMock()]
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.ScenarioPostgresRepository")
|
||||
).return_value.get_many.return_value = [MagicMock()]
|
||||
|
|
@ -1218,7 +1243,7 @@ def test_refetch_epc_false_without_stored_epc_falls_back_to_api() -> None:
|
|||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.run_modelling", return_value=mock_plan)
|
||||
)
|
||||
# No stored lodged EPC for this property
|
||||
# No stored lodged EPC
|
||||
stack.enter_context(
|
||||
patch("applications.modelling_e2e.handler.EpcPostgresRepository")
|
||||
).return_value.get_for_properties.return_value = {}
|
||||
|
|
@ -1231,10 +1256,13 @@ def test_refetch_epc_false_without_stored_epc_falls_back_to_api() -> None:
|
|||
# Act
|
||||
_call_handler({**_BODY, "refetch_epc": False})
|
||||
|
||||
# Assert — API was called as fallback; live EPC is persisted
|
||||
mock_epc_client.get_by_uprn.assert_called_once_with(UPRN)
|
||||
# Assert — API was NOT called; prediction ran and its output was persisted
|
||||
mock_epc_client.get_by_uprn.assert_not_called()
|
||||
mock_uow.epc.save.assert_called_once_with(
|
||||
live_epc, property_id=PROPERTY_ID, portfolio_id=PORTFOLIO_ID
|
||||
mock_predicted_epc,
|
||||
property_id=PROPERTY_ID,
|
||||
portfolio_id=PORTFOLIO_ID,
|
||||
source="predicted",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue