revert epc timeout to 10s

This commit is contained in:
Khalim Conn-Kowlessar 2026-06-23 11:19:23 +00:00
parent 52e5a4d402
commit c3422704f5
3 changed files with 20 additions and 27 deletions

View file

@ -12,9 +12,10 @@ When no lodged EPC is found, EPC Prediction (Path 3, ADR-0031) synthesises one
from the postcode cohort. ``_cohort_cache`` is module-level so warm Lambda
containers re-processing the same postcode avoid redundant fetches.
``secondary_heating_removal`` is excluded unconditionally: the live ``material``
catalogue does not yet carry this measure type, causing a crash during catalogue
reads for properties with a lodged secondary heater.
All Measure Types are considered: pricing goes through
``catalogue_with_off_catalogue_overrides`` so the measures the live ``material``
catalogue cannot supply (``secondary_heating_removal``, the glazing and heating
gaps) are priced from the committed off-catalogue overlay instead of crashing.
DB engine is module-scoped so the connection pool is reused across warm
invocations (ADR-0012).
@ -45,7 +46,6 @@ from domain.epc_prediction.prediction_target import build_prediction_target
from domain.geospatial.coordinates import Coordinates
from domain.geospatial.planning_restrictions import PlanningRestrictions
from domain.geospatial.spatial_reference import SpatialReference
from domain.modelling.measure_type import MeasureType
from domain.property.property import Property, PropertyIdentity
from domain.tasks.tasks import Source
from harness.console import run_modelling
@ -67,7 +67,9 @@ from repositories.geospatial.geospatial_s3_repository import (
ParquetReader,
)
from repositories.postgres_unit_of_work import PostgresUnitOfWork
from repositories.product.product_postgres_repository import ProductPostgresRepository
from repositories.product.composite_product_repository import (
catalogue_with_off_catalogue_overrides,
)
from repositories.property.landlord_override_overlays import overlays_from
from repositories.property.override_backed_prediction_attributes_reader import (
OverrideBackedPredictionAttributesReader,
@ -212,7 +214,7 @@ def handler(body: dict[str, Any], context: Any) -> None:
read_session = Session(engine)
try:
scenario = ScenarioPostgresRepository(read_session).get_many([scenario_id])[0]
products = ProductPostgresRepository(read_session)
products = catalogue_with_off_catalogue_overrides(read_session)
errors: list[int] = []
@ -282,21 +284,14 @@ def handler(body: dict[str, Any], context: Any) -> None:
None if no_solar else _solar_insights_for(solar_client, spatial)
)
# secondary_heating_removal is absent from the live material.type
# enum; exclude unconditionally until the catalogue gap is resolved.
# system_tune_up and system_tune_up_zoned have no active product.
considered: Optional[frozenset[MeasureType]] = (
frozenset(MeasureType)
- {MeasureType.SECONDARY_HEATING_REMOVAL}
- {MeasureType.SYSTEM_TUNE_UP_ZONED}
- {MeasureType.SYSTEM_TUNE_UP}
)
# All Measure Types are considered: the off-catalogue overlay
# (catalogue_with_off_catalogue_overrides) prices the measures the
# live material catalogue cannot supply, so none need excluding.
plan = run_modelling(
effective_epc,
planning_restrictions=restrictions,
solar_insights=solar_insights,
considered_measures=considered,
considered_measures=None,
products=products,
scenario=scenario,
print_table=False,

View file

@ -19,10 +19,8 @@ from datatypes.epc.search import EpcSearchResult
class EpcClientService:
BASE_URL = "https://api.get-energy-performance-data.communities.gov.uk"
# The gov API's per-UPRN search latency is variable: usually ~0.2s but with
# intermittent slow spells. 10s was low enough that a slow spell timed out and
# call_with_retry then re-issued it (compounding the cost); 30s rides out the
# spell instead. Rate-limit (429) handling stays with call_with_retry.
REQUEST_TIMEOUT = 30.0
# intermittent slow spells.
REQUEST_TIMEOUT = 10.0
def __init__(self, auth_token: str) -> None:
self._headers = {

View file

@ -167,7 +167,7 @@ def test_lodged_epc_path_saves_epc_plan_and_marks_modelled() -> None:
patch("applications.modelling_e2e.handler.ScenarioPostgresRepository")
).return_value.get_many.return_value = [MagicMock()]
stack.enter_context(
patch("applications.modelling_e2e.handler.ProductPostgresRepository")
patch("applications.modelling_e2e.handler.catalogue_with_off_catalogue_overrides")
)
stack.enter_context(
patch("applications.modelling_e2e.handler.Session")
@ -285,7 +285,7 @@ def test_prediction_path_saves_plan_without_epc_save() -> None:
patch("applications.modelling_e2e.handler.ScenarioPostgresRepository")
).return_value.get_many.return_value = [MagicMock()]
stack.enter_context(
patch("applications.modelling_e2e.handler.ProductPostgresRepository")
patch("applications.modelling_e2e.handler.catalogue_with_off_catalogue_overrides")
)
stack.enter_context(
patch("applications.modelling_e2e.handler.Session")
@ -381,7 +381,7 @@ def test_empty_cohort_gates_property_out_and_raises() -> None:
patch("applications.modelling_e2e.handler.ScenarioPostgresRepository")
).return_value.get_many.return_value = [MagicMock()]
stack.enter_context(
patch("applications.modelling_e2e.handler.ProductPostgresRepository")
patch("applications.modelling_e2e.handler.catalogue_with_off_catalogue_overrides")
)
stack.enter_context(patch("applications.modelling_e2e.handler.Session"))
MockUoW = stack.enter_context(
@ -462,7 +462,7 @@ def test_partial_batch_failure_raises_runtime_error_listing_failed_ids() -> None
patch("applications.modelling_e2e.handler.ScenarioPostgresRepository")
).return_value.get_many.return_value = [MagicMock()]
stack.enter_context(
patch("applications.modelling_e2e.handler.ProductPostgresRepository")
patch("applications.modelling_e2e.handler.catalogue_with_off_catalogue_overrides")
)
stack.enter_context(patch("applications.modelling_e2e.handler.Session"))
stack.enter_context(
@ -576,7 +576,7 @@ def test_cohort_cache_prevents_duplicate_candidates_for_calls() -> None:
patch("applications.modelling_e2e.handler.ScenarioPostgresRepository")
).return_value.get_many.return_value = [MagicMock()]
stack.enter_context(
patch("applications.modelling_e2e.handler.ProductPostgresRepository")
patch("applications.modelling_e2e.handler.catalogue_with_off_catalogue_overrides")
)
stack.enter_context(patch("applications.modelling_e2e.handler.Session"))
stack.enter_context(
@ -659,7 +659,7 @@ def test_dry_run_skips_all_db_writes() -> None:
patch("applications.modelling_e2e.handler.ScenarioPostgresRepository")
).return_value.get_many.return_value = [MagicMock()]
stack.enter_context(
patch("applications.modelling_e2e.handler.ProductPostgresRepository")
patch("applications.modelling_e2e.handler.catalogue_with_off_catalogue_overrides")
)
stack.enter_context(patch("applications.modelling_e2e.handler.Session"))
stack.enter_context(