Commit graph

145 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
59ef8a12ab Treat a selection with no documents as a recorded failure 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:33:25 +00:00
Khalim Conn-Kowlessar
e7181b7d1f Build, upload and email a Download Package for a property set 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:31:49 +00:00
Khalim Conn-Kowlessar
115f506353 Build, upload and email a Download Package for a property set 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:30:38 +00:00
Daniel Roth
1085384f21 Merge branch 'main' into feature/abri-api-integration 2026-07-07 14:08:52 +00:00
KhalimCK
9ff4de0563
Merge pull request #1481 from Hestia-Homes/feature/modelling-trigger-run
Modelling Run Distributor: POST /v1/modelling/trigger-run
2026-07-07 14:36:45 +01:00
Daniel Roth
7ad0dc0595 Merge branch 'main' into feature/abri-api-integration 2026-07-07 12:46:17 +00:00
Khalim Conn-Kowlessar
9cbb8a95b5 Completing a re-run failed batch un-fails the task 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 11:11:52 +00:00
Daniel Roth
8d70679ab0 Amending an appointment needs only the deal's changed date and time 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:07:23 +00:00
Daniel Roth
9bae8df6ea A changed booking amends the deal's OpenHousing appointment 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:01:01 +00:00
Daniel Roth
a93346ec7f A changed booking amends the deal's OpenHousing appointment 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 09:56:54 +00:00
Jun-te Kim
bc1cca77db
Merge pull request #1356 from Hestia-Homes/feature/historic-epc-repository
Historic EPC repository: DDD port + resolver for address→UPRN
2026-07-07 10:56:33 +01:00
Daniel Roth
a5868cc2e0 One AbriOrchestrator runs every Abri flow for a deal 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 16:26:48 +00:00
Daniel Roth
049475ec96 A failed HubSpot write raises a scrubbed error carrying the orphaned job_no 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:58:36 +00:00
Daniel Roth
96f86aacac A logged job's job_no is written to HubSpot and then the deal database 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:54:05 +00:00
Daniel Roth
ce7b302c17 Log an OpenHousing job from a confirmed survey booking's deal fields 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:51:21 +00:00
Daniel Roth
98674ae895 Log an OpenHousing job from a confirmed survey booking's deal fields 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:50:07 +00:00
Daniel Roth
76896f9a8e rename tenant data sync to abri tenant data sync 2026-07-06 14:05:42 +00:00
Daniel Roth
8fd808ffd9 HubSpot deal-contacts transport lives under infrastructure like the other API clients 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:01:47 +00:00
Daniel Roth
81d25caead A failed HubSpot write stops the sync and reports the orphaned contacts 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:35:47 +00:00
Daniel Roth
065217f8af A failed HubSpot write stops the sync and reports the orphaned contacts 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:35:20 +00:00
Daniel Roth
64d397e6b4 Tenant vulnerabilities are recorded on the tenant's own contact 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:31:00 +00:00
Daniel Roth
52f2286ee9 Runner-up number fills the contact's secondary phone number 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:29:38 +00:00
Daniel Roth
76d0b4bac9 Abri tenancy signatory becomes an associated HubSpot deal contact 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:24:47 +00:00
Daniel Roth
78fcba45e5 Abri tenancy signatory becomes an associated HubSpot deal contact 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:22:52 +00:00
Khalim Conn-Kowlessar
9204228366 An expired historic cert conditions ingestion's prediction and labels it 🟩
Ingestion's lookup chain becomes: new EPC API -> historic backup by exact
UPRN -> plain prediction. A found record's stable attributes fill the
gaps Landlord Overrides left (overrides always win) and enrich the target
with age band / fuel / TFA band; the persisted predicted-slot row carries
source="expired". Reader unwired or shard miss -> behaviour unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:38:07 +00:00
Khalim Conn-Kowlessar
9e1c1b71a4 An expired historic cert conditions ingestion's prediction and labels it 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:34:54 +00:00
Daniel Roth
41d46e3910
Merge pull request #1454 from Hestia-Homes/feature/extend-sharepoint-renamer
Extend sharepoint renamer to work for batch subfolders
2026-07-03 14:48:07 +01:00
Daniel Roth
e533316771 UPRN-prefixed property folders rename to a single-UPRN filename 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 12:09:32 +00:00
Daniel Roth
46ed8bfe86 Renamer warns once per property only after all roots miss 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 11:19:34 +00:00
Daniel Roth
213cd92998 Renamer warns once per property only after all roots miss 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 11:18:42 +00:00
Daniel Roth
1196a8f75d Renamer finds properties filed under _Sero Batch roots 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 11:16:46 +00:00
Daniel Roth
a442dcfbaa Renamer discovers _Sero Batch roots under Property Folders 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 11:13:39 +00:00
Daniel Roth
dcd2a7442f Renamer discovers _Sero Batch roots under Property Folders 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 11:12:59 +00:00
Khalim Conn-Kowlessar
bf240e3315 ASHP sizing targets the Appendix-N efficiency peak (realistic installed pump) 🟩
Sizing selects the aroTHERM plus rung nearest PSR ~0.8 (0.8 x design heat
loss) — the Appendix-N efficiency peak, reproducing the pump a real
installer fits — rather than the MCS PSR>=1.0 capacity target, which
oversizes. Validated against the relodged Elmhurst ASHP cert (delta 0
held at the 5 kW Vaillant); the two self-snapshot pins re-pin to their
correctly-sized larger pumps, and the orchestrator/harness thread the
calculator's design heat loss so production sizes to the dwelling. Manual
SapResult test stubs carry the new design_heat_loss_kw field.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 11:02:13 +00:00
Khalim Conn-Kowlessar
cb91954bb4 The orchestrator threads the dwelling design heat loss into ASHP sizing 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 10:31:57 +00:00
Khalim Conn-Kowlessar
da805bce21 Persist no Lodged Performance for a predicted Property 🟩
The PropertyBaselineOrchestrator now reads no Lodged Performance off a predicted
Property's neighbour-synthesised EPC: lodged is None when source_path is
'predicted', so no phantom lodged figure is manufactured. The Effective half is
unchanged. Rebaseliner port + PropertyBaselinePerformance.lodged widen to
Optional; the pristine-cert paths assert non-None (lodged is None only for a
predicted Property, which is always physical_state_changed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 22:10:57 +00:00
Khalim Conn-Kowlessar
4d5504fa10 feat: thread physical-state-change signal into rebaselining 🟩
Add Property.physical_state_changed (true on Site Notes / Landlord Overrides
/ Prediction — trigger (b)/(c)) and pass it from the
PropertyBaselineOrchestrator into the Rebaseliner. So an overridden or
predicted SAP>=10.2 property now stores calc(effective) as its Effective
baseline instead of echoing the lodged headline — closing the "81 in the DB"
divergence between the displayed baseline and the modelled plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 19:15:11 +00:00
Jun-te Kim
b1ff711260 perf(modelling_e2e): batch SubTask bookkeeping to stop per-property writes
Even after batching the data writes, the handler still wrote to the DB per
property through the orchestrator's SubTask bookkeeping: create + start +
complete each self-committed, and _cascade re-listed every sibling and re-saved
the parent on every transition — ~5 writes per property plus an O(N^2) cascade.

- TaskOrchestrator.run_subtasks: create all children in one INSERT, run each
  (failures isolated per child), then persist all terminal states in one bulk
  save and cascade the parent once. Children go WAITING -> terminal; the
  transient IN_PROGRESS row is never written.
- SubTaskRepository.create_many / save_many (bulk INSERT / bulk fetch + update).
- _cascade short-circuits when the Task is already FAILED (terminal) — skips the
  sibling roll-up entirely.
- modelling_e2e handler fans out via run_subtasks instead of per-property
  create_child_subtask + run_subtask.

Per N-property batch the SubTask bookkeeping drops from ~5N writes + an O(N^2)
cascade to ~2 writes + 1 cascade.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 19:26:42 +00:00
Jun-te Kim
6679d8c621 Fall back to computed kWh for predicted-EPC baselines (no lodged RHI)
16 modelling_e2e properties failed with "Effective EPC is missing
renewable_heat_incentive; cannot read baseline space-heating / hot-water kWh".

Baseline runs for predicted properties too (ADR-0031), reading space/water-
heating kWh off the EPC's lodged RHI block. Predicted EPCs deep-copy a neighbour
template that may carry no RHI, so `_require_rhi` hard-failed the whole subtask.

Fix: when the EPC has no RHI, fall back to the property's OWN computed figures
from the scored SapResult (space_heating_kwh_per_yr / hot_water_kwh_per_yr) —
more representative than a neighbour's lodged numbers. Only when there is also no
SapResult (the rebaseliner scored nothing) is there genuinely no demand to
record, and we still fail noisily. Lodged certs are unchanged (RHI still wins).

Regression tests: fallback-to-computed, and the no-RHI/no-result raise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 08:58:24 +00:00
Daniel Roth
25c695186b measured excluded plus get geospatial nearby stuff working 2026-06-22 16:12:16 +00:00
Jun-te Kim
3a0a122b7f Group landlord property-override enums under domain/epc/property_overrides 🟪
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 14:49:58 +00:00
Khalim Conn-Kowlessar
694cdd9c23 feat(modelling): mark a Property as run via has_recommendations + updated_at
The new pipeline left no per-Property record of a run (the old engine set
property.has_recommendations and populated property_details_epc). Restore the
marker: PropertyRepository.mark_modelled sets has_recommendations (true when the
Plan carries measures, mirroring the old engine) and bumps updated_at, so a
first-run under the new process is identifiable as updated_at >= 2026-06-01.

ModellingOrchestrator marks each Property after its Scenarios (true if any
Scenario yielded a measure); run_modelling_e2e's --persist path marks it too
(its compute runs on in-memory fakes, so the DB UoW sets it directly). Adds the
has_recommendations/updated_at columns to the PropertyRow mirror.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 23:34:33 +00:00
Khalim Conn-Kowlessar
3580d059ec feat(modelling): drive measure scoping from the Scenario's exclusions
The measures a run considers should come from the Scenario, not a CLI flag.
The live scenario table persists exclusions only (no inclusions column), as a
Postgres text-array of exact MeasureType values.

- Scenario gains `exclusions: frozenset[MeasureType]` + `considered_measures()`
  (all measures minus the excluded ones, or None when none are excluded).
- ScenarioModel.to_domain parses the `{a,b,c}` exclusions array into
  MeasureTypes, raising on a token that is not an exact MeasureType value
  (no high-level category expansion), per the strict-enum convention.
- ModellingOrchestrator._plan_for derives the allowlist from the Scenario's
  exclusions, combined (intersection) with any explicit considered_measures
  override via the new `combine_considered_measures`.
- run_modelling_e2e sources the allowlist from the Scenario; --measures /
  --exclude-measures become optional overlays (e.g. the technical
  secondary_heating_removal exclusion the catalogue cannot yet stock).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 15:26:25 +00:00
Khalim Conn-Kowlessar
5c19737fc5 feat(modelling): gate generation by the considered-measures allowlist
`restrict_to_considered_measures` filtered candidates only *after* every
generator had run, so an excluded measure still queried the catalogue.
That crashed properties with a lodged secondary heater: the live
`material.type` enum has no `secondary_heating_removal` value, so the
query raised a psycopg2 `InvalidTextRepresentation` before the allowlist
could drop it.

`_candidate_recommendations` now pairs each generator with the measure
types it can emit and runs it only when the allowlist admits one of them
(None = all), so an excluded measure never reaches the catalogue.
`restrict_to_considered_measures` still trims disallowed Options off the
multi-Option survivors. Add `--exclude-measures` to run_modelling_e2e
(allowlist minus the excluded set) for excluding one measure without
enumerating the rest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 14:56:09 +00:00
KhalimCK
90bed458f4
Merge pull request #1238 from Hestia-Homes/feature/epc-prediction
Feature/epc prediction
2026-06-16 21:58:40 +08:00
Khalim Conn-Kowlessar
7ca1f815f6 refactor(epc-prediction): PR review — rename ComparableProperty, relocate PredictionTarget
Two review points from @dancafc:

1) Rename the `Comparable` dataclass → `ComparableProperty` (it models one
   comparable *property*; the collection stays `ComparableProperties`). Applied
   across domain, repositories, orchestration, harness, scripts, and tests with a
   word-boundary rename so `ComparableProperties` is untouched.

2) Move `PredictionTarget` out of comparable_properties.py into prediction_target.py
   (where `PredictionTargetAttributes` + `build_prediction_target` already live).
   comparable_properties.py now imports it; no import cycle (prediction_target no
   longer depends on comparable_properties). Importers updated.

92 tests pass across the touched suites; pyright strict clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 13:34:44 +00:00
Daniel Roth
6063fe051e ignore jpg and heic files. resolve filepath relatively 2026-06-16 10:06:39 +00:00
Khalim Conn-Kowlessar
5727ac53c1 feat(epc-prediction): slice-5e ingestion wiring (gate → predict → persist)
Wire EPC Prediction gap-fill into IngestionOrchestrator (ADR-0031). When the
predictor collaborators are injected (ComparablesRepo + PredictionAttributesReader
+ EpcPrediction), an EPC-less Property is predicted from its postcode cohort and
persisted to the predicted slot; the eligibility gate (unknown property_type) and
"a lodged EPC is never predicted over" both hold. The two-phase contract is kept:
prediction attributes (Landlord Overrides) resolve in the unit prep phase, the
cohort fetch + select + predict run in the no-unit IO phase, persistence in the
write phase. All three collaborators are OPTIONAL — unwired, ingestion behaves
exactly as before (existing tests unchanged).

3 tests (predict+persist, gate, lodged-wins); 228 pass across orchestration +
epc_prediction + repositories; pyright strict clean. Production composition-root
wiring (real ComparableProperties + override-attributes adapters) is part of the
Jun-te handover.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 04:03:02 +00:00
Daniel Roth
963b7d70fe fix terraform error and pass handler bool for dry runs 2026-06-15 14:06:54 +00:00
Daniel Roth
beb4e5d0d9 Move SharePoint renamer logic from scripts/ into orchestrator and app-root handler 2026-06-15 11:01:51 +00:00