mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Merge branch 'main' into feature/epc-dataclasses
This commit is contained in:
commit
6f8a304568
10 changed files with 94 additions and 39 deletions
|
|
@ -652,7 +652,8 @@ async def model_engine(body: PlanTriggerRequest):
|
|||
epc_records["original_epc"]["estimated"] = False
|
||||
|
||||
prepared_epc = EPCRecord(
|
||||
epc_records=epc_records, run_mode="newdata", cleaning_data=cleaning_data, address_metadata=addr
|
||||
epc_records=epc_records, run_mode="newdata", cleaning_data=cleaning_data,
|
||||
# address_metadata=addr Switched off to remove injecting landlord inputs
|
||||
)
|
||||
|
||||
input_properties.append(
|
||||
|
|
@ -724,6 +725,10 @@ async def model_engine(body: PlanTriggerRequest):
|
|||
# 1) EPC expired 2) Missing EPC 3) Different information from landlord vs EPC
|
||||
needs_rebaselining = p.epc_is_expired | p.epc_is_estimated | (len(p.epc_record.landlord_differences) > 0)
|
||||
|
||||
# Hack - skip
|
||||
if "SAP05" in p.epc_record.walls_description:
|
||||
continue
|
||||
|
||||
if needs_rebaselining:
|
||||
p.create_base_difference_epc_record(cleaned_lookup=cleaned)
|
||||
scoring_data = p.base_difference_record.df.copy()
|
||||
|
|
|
|||
|
|
@ -113,12 +113,17 @@ def task_handler():
|
|||
|
||||
@wraps(func)
|
||||
def wrapper(event: dict[str, Any], context: Any, *args, **kwargs):
|
||||
|
||||
logger = setup_logger()
|
||||
|
||||
# Parse body: Records-style SQS or plain dict event
|
||||
if "Records" in event:
|
||||
raw_body = event["Records"][0].get("body", {})
|
||||
records = event.get("Records", [event]) # fallback for non-SQS
|
||||
|
||||
results = []
|
||||
failures = []
|
||||
|
||||
for record in records:
|
||||
# Parse body
|
||||
raw_body = record.get("body", record)
|
||||
|
||||
if isinstance(raw_body, str):
|
||||
try:
|
||||
body = json.loads(raw_body)
|
||||
|
|
@ -126,43 +131,55 @@ def task_handler():
|
|||
body = {}
|
||||
else:
|
||||
body = raw_body or {}
|
||||
else:
|
||||
body = event
|
||||
|
||||
# Create fresh task + subtask
|
||||
logger.info("Creating task for source: %s", task_source)
|
||||
task_id, subtask_id = TasksInterface.create_task(
|
||||
task_source=task_source,
|
||||
inputs=body,
|
||||
)
|
||||
logger.info("Created task_id=%s subtask_id=%s", task_id, subtask_id)
|
||||
# Create task per message
|
||||
logger.info("Creating task for source: %s", task_source)
|
||||
task_id, subtask_id = TasksInterface.create_task(
|
||||
task_source=task_source,
|
||||
inputs=body,
|
||||
)
|
||||
|
||||
interface = SubTaskInterface()
|
||||
logger.info("Created task_id=%s subtask_id=%s", task_id, subtask_id)
|
||||
|
||||
interface.update_subtask_status(
|
||||
subtask_id=subtask_id,
|
||||
status="in progress",
|
||||
)
|
||||
|
||||
try:
|
||||
result = func(body, context, *args, **kwargs)
|
||||
interface = SubTaskInterface()
|
||||
|
||||
interface.update_subtask_status(
|
||||
subtask_id=subtask_id,
|
||||
status="complete",
|
||||
outputs={"result": result} if result else None,
|
||||
status="in progress",
|
||||
)
|
||||
logger.info("Task %s completed successfully", task_id)
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("Task %s failed: %s", task_id, e)
|
||||
interface.update_subtask_status(
|
||||
subtask_id=subtask_id,
|
||||
status="failed",
|
||||
outputs={"error": str(e)},
|
||||
)
|
||||
raise
|
||||
try:
|
||||
result = func(body, context, *args, **kwargs)
|
||||
|
||||
interface.update_subtask_status(
|
||||
subtask_id=subtask_id,
|
||||
status="complete",
|
||||
outputs={"result": result} if result else None,
|
||||
)
|
||||
|
||||
logger.info("Task %s completed successfully", task_id)
|
||||
results.append(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("Task %s failed: %s", task_id, e)
|
||||
|
||||
interface.update_subtask_status(
|
||||
subtask_id=subtask_id,
|
||||
status="failed",
|
||||
outputs={"error": str(e)},
|
||||
)
|
||||
|
||||
if "Records" in event:
|
||||
failures.append({"itemIdentifier": record["messageId"]})
|
||||
else:
|
||||
# Handle non-SQS events
|
||||
raise
|
||||
|
||||
if "Records" in event:
|
||||
return {"batchItemFailures": failures}
|
||||
|
||||
# Handle non-SQS events
|
||||
return results
|
||||
|
||||
return wrapper
|
||||
|
||||
|
|
|
|||
|
|
@ -580,7 +580,22 @@ class EPCRecord:
|
|||
if existing is not None and v is not None and abs(existing - v) > 1: # 1m tolerance
|
||||
self.landlord_differences[k] = v
|
||||
else:
|
||||
if v != self._prepared_epc.get(k) and (not pd.isnull(v)) and (not pd.isnull(self._prepared_epc.get(k))):
|
||||
|
||||
# Check if something has been cleaned. We want to avoid triggering re-baselining if we cleaned
|
||||
# a value. In the address meta, it will possibly contain the original value, so we'd pick up a
|
||||
# diference if the original value was something to be cleaned, we clean that value and then end up
|
||||
# comparing the original value to the new clean one
|
||||
cleaned_value = self._prepared_epc.get(k)
|
||||
original_value = self.original_epc.get(k.replace("_", "-"))
|
||||
|
||||
# We check if the value has been cleaned
|
||||
if cleaned_value != original_value:
|
||||
# The thing we want to compare against, is the original value
|
||||
compare_to = original_value
|
||||
else:
|
||||
compare_to = cleaned_value
|
||||
|
||||
if v != compare_to and (not pd.isnull(v)) and (not pd.isnull(self._prepared_epc.get(k))):
|
||||
self.landlord_differences[k] = v
|
||||
|
||||
self._prepared_epc.update(self.landlord_differences)
|
||||
|
|
|
|||
|
|
@ -778,6 +778,12 @@ class RetrieveFindMyEpc:
|
|||
'Air or ground source heat pump': ["air_source_heat_pump"],
|
||||
"Add PV Battery": ["solar_pv_battery"],
|
||||
"Add PV diverter": ["solar_pv_diverter"], # Don't have a recommendation yet
|
||||
"Draughtproof single-glazed windows": ["double_glazing"],
|
||||
"Upgrade heating controls": ["roomstat_programmer_trvs", "time_temperature_zone_control"],
|
||||
"Low energy lighting recommendation": ["low_energy_lighting"],
|
||||
"Install cavity wall insulation": ["cavity_wall_insulation"],
|
||||
"Install solar water heating": ["solar_water_heating"],
|
||||
'Install photovoltaics, 25% of roof area': ["solar_pv"],
|
||||
}
|
||||
|
||||
survey = True
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ data "terraform_remote_state" "pashub_to_ara" {
|
|||
backend = "s3"
|
||||
config = {
|
||||
bucket = "pashub-to-ara-terraform-state"
|
||||
key = "ev:/${var.stage}/terraform.tfstate"
|
||||
key = "env:/${var.stage}/terraform.tfstate"
|
||||
region = "eu-west-2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,6 +140,9 @@ class HeatingRecommender:
|
|||
# All heat systems are in here so we identify whether two of these are true
|
||||
# MainHeatAttributes.HEAT_SYSTEMS
|
||||
|
||||
if "sap05" in self.property.main_heating["clean_description"].lower():
|
||||
return False
|
||||
|
||||
n_trues = 0
|
||||
for heat_system in MainHeatAttributes.HEAT_SYSTEMS:
|
||||
if self.property.main_heating[f"has_{heat_system.replace(' ', '_')}"]:
|
||||
|
|
@ -318,6 +321,9 @@ class HeatingRecommender:
|
|||
:param measures: A list of measures for the recommendations
|
||||
"""
|
||||
|
||||
if "sap05" in self.property.main_heating["clean_description"].lower():
|
||||
return
|
||||
|
||||
measures = MEASURE_MAP["heating"] if measures is None else measures
|
||||
|
||||
# if we have a non-invasive ashp recommendation, we get the configuration directly from the property instance
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@ class LightingRecommendations:
|
|||
:return:
|
||||
"""
|
||||
|
||||
if "sap05" in self.property.lighting["clean_description"].lower():
|
||||
return
|
||||
|
||||
if self.property.lighting["low_energy_proportion"] >= 1:
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ class SecondaryHeating:
|
|||
def recommend(self, phase: int):
|
||||
# Reset
|
||||
self.recommendation = []
|
||||
if self.property.epc_record.secondheat_description in ["None", None]:
|
||||
if self.property.epc_record.secondheat_description in ["None", None] or (
|
||||
"sap05" in self.property.epc_record.secondheat_description.lower()
|
||||
):
|
||||
# No secondary heating system, so no recommendation to remove it
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ class WallRecommendations(Definitions):
|
|||
if (
|
||||
(insulation_thickness in ["average", "above average"])
|
||||
or self.property.walls["is_filled_cavity"]
|
||||
or self.property.walls["clean_description"] is None
|
||||
or self.property.walls["clean_description"] in [None, "Sap05:walls"]
|
||||
) and ("cavity_extract_and_refill" not in measures
|
||||
):
|
||||
return
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ class WindowsRecommendations:
|
|||
measures = MEASURE_MAP["windows"] if measures is None else measures
|
||||
|
||||
# If we have no windows recs, leave
|
||||
if not any(x in measures for x in MEASURE_MAP["windows"]):
|
||||
if not any(x in measures for x in MEASURE_MAP["windows"]) or "sap05" in self.property.windows[
|
||||
"clean_description"].lower():
|
||||
return
|
||||
|
||||
if self.property.windows["glazing_type"] in ["triple", "high performance"]:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue