Commit graph

6511 commits

Author SHA1 Message Date
Jun-te Kim
3b7d26fe34 added test for a 1000 examples 2026-06-09 16:02:21 +00:00
Daniel Roth
f6a4b0f722
Merge pull request #1206 from Hestia-Homes/bug/audit-generator-deploy-fix
Add missing variable to lambda variables
2026-06-09 16:39:34 +01:00
Daniel Roth
c1dda41857 add missing variable to lambda variables 2026-06-09 15:33:35 +00:00
Daniel Roth
63b1fa08a8
Merge pull request #1204 from Hestia-Homes/feature/generate-ventilation-audit-from-magicplan
New application to generate ventilation audit file for a given hubspot deal ID
2026-06-09 16:25:12 +01:00
Daniel Roth
dcd5204b54 put db engine construction inside handler to avoid import errors in test 2026-06-09 15:18:42 +00:00
Daniel Roth
236f33c25f move spreadsheet population logic to domain 2026-06-09 14:43:24 +00:00
Daniel Roth
48a413a5e2 correct imports in postgres repo 2026-06-09 14:25:29 +00:00
Daniel Roth
94f8ef5458 Merge branch 'main' into feature/generate-ventilation-audit-from-magicplan 2026-06-09 14:22:57 +00:00
Daniel Roth
163703285f deployment 2026-06-09 14:15:52 +00:00
Daniel Roth
b9eb23f6df allow write to real s3 when running locally 2026-06-09 13:52:39 +00:00
Daniel Roth
608006d977 reapply conditional formatting to populated file 2026-06-09 13:29:11 +00:00
Daniel Roth
f8c955b2d3 local runner and correct template path 2026-06-09 13:03:05 +00:00
Daniel Roth
e010fa1b40 remove redundant files 2026-06-09 12:52:04 +00:00
Daniel Roth
e11ab85d9d
Merge pull request #1202 from Hestia-Homes/bug/magicplan-task-handler
Use correct task handler decorator for magicplan
2026-06-09 13:36:12 +01:00
Daniel Roth
ea899fe0ab actually revert changes 2026-06-09 12:27:57 +00:00
Daniel Roth
ade6891ff0 revert accidental changes 2026-06-09 12:24:12 +00:00
Daniel Roth
31c2e07a02 revert accidental changes to magicplan repo 2026-06-09 12:19:39 +00:00
Daniel Roth
dcf0b2ccb9 use task_handler rather subtask_handler for magicplan 2026-06-09 12:17:57 +00:00
Daniel Roth
82156fac8f Audit generator populates XLSX, uploads to S3, and records UploadedFile row 🟪
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 12:05:55 +00:00
Daniel Roth
612d522b35 Audit generator populates XLSX, uploads to S3, and records UploadedFile row 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 12:01:08 +00:00
Daniel Roth
a1d09aa880 Audit generator populates XLSX, uploads to S3, and records UploadedFile row 🟥
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 11:59:09 +00:00
Daniel Roth
f08a75e103 UploadedFilePostgresRepository returns latest uploaded file by deal ID and type 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 11:54:14 +00:00
Daniel Roth
53f0da8666 UploadedFilePostgresRepository returns latest uploaded file by deal ID and type 🟥
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 11:53:25 +00:00
Daniel Roth
5178cd02c5 UploadedFile, FileTypeEnum, FileSourceEnum importable from infrastructure.postgres.uploaded_file_table 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 11:50:51 +00:00
Daniel Roth
41b282042f UploadedFile, FileTypeEnum, FileSourceEnum importable from infrastructure.postgres.uploaded_file_table 🟥
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 11:42:53 +00:00
Jun-te Kim
06cb4f7b6e Merge branch 'feature/bill-derivation' into feature/junte+khalim 2026-06-09 10:06:40 +00:00
Khalim Conn-Kowlessar
4006753620 fix(scripts): authenticate the EPC client with OPEN_EPC_API_TOKEN
The new gov EPC API (api.get-energy-performance-data..., Bearer auth) returns
403 "Bad authentication header" with EPC_AUTH_TOKEN but 200 with
OPEN_EPC_API_TOKEN — the token name is misleading (it is the Bearer token for
the new API, not the open-data API). Verified live against
/api/domestic/search. Unblocks the live EPC fetch in run_modelling_e2e.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 20:58:22 +00:00
Khalim Conn-Kowlessar
0f6077a830 feat(scripts): DB-catalogue local run + optional --persist for run_modelling_e2e
Slice 5 (local run sources the DB, read-only) + slice 6 (optional persist),
landing together as one script rewrite (the persist path is interleaved with
the compute path).

The same local computation now runs whether or not the result is stored:
- Both modes price against the live `material` catalogue (read-only
  ProductPostgresRepository over one shared Session) and model against a real
  Scenario read from the DB (--scenario-id; its goal_value drives the band,
  rejected if null) — so the inspected recommendations are exactly what gets
  stored. The JSON sample catalogue is no longer used by this script.
- --measures restricts the run to a comma-separated considered_measures
  allowlist (e.g. high_heat_retention_storage_heaters,solar_pv).
- --persist writes the inputs (EPC + spatial + solar) and the *same* computed
  Plan via the production repos in one PostgresUnitOfWork, then commits
  (idempotent: PlanPostgresRepository replaces by (property_id, scenario_id)).
  Gated: --persist requires --scenario-id and --portfolio-id. Default is
  inspect-only — no DB writes.

harness.console.run_modelling gains `products` and `scenario` overrides (the
seam the script drives); defaults unchanged, so existing callers are
unaffected. Suite 257 pass + 3 xfail; pyright clean; --help/guard/measure
parsing verified. Not yet executed against the DB (awaiting property_ids +
write-confirm).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 20:45:50 +00:00
Khalim Conn-Kowlessar
62e1d4b813 fix(product): deterministic catalogue pick by ordering get() by id
ProductPostgresRepository.get took .first() with no ORDER BY, so when a
measure type has several active material rows (the live catalogue holds 74
solar_pv, 5 high_heat_retention_storage_heaters) the chosen row — hence the
cost and material_id — depended on the database's physical row order. Order by
id so a re-seed prices the same product every time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 20:35:45 +00:00
Khalim Conn-Kowlessar
7942a8101a feat(modelling): considered_measures allowlist on the orchestrator
Add domain/modelling/considered_measures.py::restrict_to_considered_measures —
the pure allowlist that limits a run to a chosen set of MeasureType (mirroring
the legacy engine's `inclusions`). It filters at the Option level, so a
multi-option Recommendation (e.g. Heating & Hot Water competing HHRSH against
an ASHP bundle) is kept with only its allowed Options; a Recommendation left
with none is dropped. None = consider everything (unrestricted default).

Thread `considered_measures: frozenset[MeasureType] | None` through
ModellingOrchestrator.run -> _plan_for -> _scored_candidate_groups /
_candidate_recommendations (applies the filter) and _measure_dependencies
(suppresses a forced dependency whose required measure is outside the
allowlist, so a restricted run forces nothing it is not considering). The
local-run seam (harness.console.run_modelling) gains the same param.

The Optimiser still freely chooses among survivors — including none. Tests:
the pure filter (3 cases) + an orchestrator-seam test proving a
{solar_pv}-restricted run yields only solar_pv options. 257 pass + 3 xfail;
pyright clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 20:32:11 +00:00
Khalim Conn-Kowlessar
9ef97be958 refactor(modelling): type measure_type fields as MeasureType
Tighten the recommendation/plan vocabulary off generic str:
MeasureOption.measure_type and PlanMeasure.measure_type are now MeasureType
(also _GlazingTarget.measure_type, MeasureDependency.triggers ->
frozenset[MeasureType], and the optimiser's chosen/required-type locals).
Because MeasureType is a StrEnum the change is transparent to persistence
(the `recommendation` varchar column), the optimiser group-by key, and every
`== "solar_pv"`-style comparison — so pyright now enforces the enum at every
construction site with no runtime behaviour change.

The catalogue boundary stays str: ProductRepository.get(measure_type: str)
and Product.measure_type are unchanged (they map arbitrary DB/JSON rows), so
the fake product repos in tests need no edit. Test construction helpers coerce
their str arg via MeasureType(...); direct constructions use members.

Suite green: tests/domain/modelling + orchestration + harness 253 pass + 3
xfail; pyright clean on production + tests (pre-existing moto + property-
override-rowcount baselines untouched).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 20:13:31 +00:00
Khalim Conn-Kowlessar
d58ac60d29 feat(modelling): MeasureType StrEnum as the canonical measure vocabulary
Introduce domain/modelling/measure_type.py — a StrEnum with one member per
modelled measure (the 15 the generators emit). A StrEnum so each member *is*
its string value: it persists straight into the `recommendation` varchar
column, is the optimiser's group-by key, and compares equal to the catalogue /
EPC strings — so it replaces the per-generator string constants with no
persistence or optimiser change.

Repoint every generator's measure-type constant/literal to a MeasureType
member (wall, solid_wall, roof, floor, glazing, lighting, ventilation,
heating, solar). Field annotations stay `str` for now; tightening them to
MeasureType is the next slice.

This is the enum the historical engine deferred (engine.py:970
"TODO - formalise property measure types into an enum") and the vocabulary the
forthcoming `considered_measures` allowlist will speak (mirroring the legacy
`inclusions`).

Suite green: tests/domain/modelling + orchestration + harness 253 pass + 3
xfail; pyright clean on the enum + generators.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 19:54:04 +00:00
Jun-te Kim
b48700e964 Merge branch 'main' into feature/junte+khalim 2026-06-08 16:56:15 +00:00
Daniel Roth
e5e67c203b temp commit to backup prd in case laptop dies 2026-06-08 16:48:37 +00:00
Daniel Roth
209a0c401c audit generator application framework 2026-06-08 16:20:05 +00:00
Daniel Roth
7497865fb2 Retrieve plan by uploaded_file_id 🟪 2026-06-08 15:44:19 +00:00
Daniel Roth
f8fcf38886 get_plan_by_uploaded_file_id 🟩 2026-06-08 15:37:52 +00:00
Jun-te Kim
0ccb0e0bf9
Merge pull request #1196 from Hestia-Homes/feautre/additional_properties_for_tracking
Feautre/additional properties for tracking
2026-06-08 16:19:34 +01:00
Khalim Conn-Kowlessar
1b4806f8e4 feat(scripts): wire S3 geospatial + Google Solar into run_modelling_e2e
Per Property the inspection script now resolves the UPRN's spatial
reference from the Ordnance Survey Open-UPRN parquet in S3
(GeospatialS3Repository over a boto3 ParquetReader) and threads both
levers into run_modelling:

- planning_restrictions: the conservation/listed/heritage flags that gate
  the wall + solar measures (ADR-0019/0020).
- solar_insights: a live Google Solar buildingInsights fetch keyed on the
  reference coordinates, so the Solar PV Options can fire (ADR-0026).

Mirrors IngestionOrchestrator._fetch's coords->solar flow. Degrades
gracefully per Property: a UPRN S3 doesn't cover -> unrestricted/no-solar;
a point Google has no coverage for (BuildingInsightsNotFoundError) ->
no-solar; both still modelled. --no-solar skips the Google leg. A context
note (restrictions; solar) is printed and written to the md/csv summary.

Verified live: spatial_for + solar fetch round-trip on real UPRNs (S3 via
ambient ~/.aws creds, pyarrow reads parquet bytes). pyright clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 14:55:33 +00:00
Daniel Roth
26ba09a1c7
Merge pull request #1194 from Hestia-Homes/correct-path-to-magicplan-dockerfile
Correct magicplan dockerfile path in terraform
2026-06-08 15:54:47 +01:00
Daniel Roth
e1515b0c00 correct path in terraform 2026-06-08 14:49:41 +00:00
Daniel Roth
c40a7c0af9
Merge pull request #1192 from Hestia-Homes/bug/fix-magicplan-orchestrator-tests
Fix magicplan tests to reflect changes to the orchestrator
2026-06-08 15:44:01 +01:00
Jun-te Kim
a1b4bf4e98 added 4 deal proeprties 2026-06-08 14:42:06 +00:00
Daniel Roth
648e726ca2 fix orchestration tests 2026-06-08 14:40:14 +00:00
Khalim Conn-Kowlessar
0918dd37ec feat(scripts): run_modelling_e2e — inspect recommendations per property_id
Revives the local recommendation-inspection flow for specific Properties.
`scripts/run_modelling_e2e.py` reads each Property's UPRN from the DB
(read-only), fetches the latest EPC live from the gov EPC API by UPRN, runs the
Modelling stage in memory (all Generators → Optimiser → costed, attributed
Plan), and prints a per-Property plan table + writes a Markdown/CSV summary.
Persists nothing — purely for inspection.

The local DB's Properties have no linked ingested EPC (epc_property.property_id
is NULL for all rows; Ingestion's source clients are stubbed, #1136), so the
EPC must be fetched inline rather than read back. Builds the connection from the
`DB_*` env vars in backend/.env and the EPC token from `EPC_AUTH_TOKEN`.

Threads optional solar insights through harness `run_modelling` (so Solar PV
Options can fire once coordinates are wired) and adds the `solar_pv` catalogue
row. Solar + planning restrictions + DB persistence are noted follow-ups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 14:25:33 +00:00
Daniel Roth
1b673622e2
Merge pull request #1191 from Hestia-Homes/bump-rds-version
Bump RDS to 14.22
2026-06-08 15:19:22 +01:00
Daniel Roth
84d4fa096d remove tests/ from pytest.ini 2026-06-08 14:15:50 +00:00
Daniel Roth
d23946a0a3 bump to 14.22 2026-06-08 14:14:15 +00:00
Daniel Roth
19c0a583cb
Merge pull request #1183 from Hestia-Homes/feature/handle-new-magicplan-response-structure
Get ventilation fields from new magicplan API response structure
2026-06-08 15:07:35 +01:00
Daniel Roth
3136d5a0f8 use correct db session to write to db 2026-06-08 14:07:03 +00:00