mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Add a `source` discriminator (lodged | predicted) to the EPC store so a Property holds a lodged EPC and a predicted one (EPC Prediction gap-fill) at once (ADR-0031). EpcRepository.save gains source="lodged"; idempotent delete is now per-source (a predicted save no longer wipes lodged, and vice versa); get_for_property/get_for_properties filter lodged; new get_predicted_for_property / get_predicted_for_properties read predicted. PropertyPostgresRepository.get + get_many hydrate Property.predicted_epc, so the predicted picture reaches the modelling read (both load via get_many). FakeEpcRepo mirrors the dual slot. EpcPropertyModel gains `source` (default "lodged"); the test DB builds from the SQLModel mirror so this is exercised without the prod migration. The matching Drizzle change (column + per-(property_id,source) uniqueness) is the team's to action before merge — docs/MIGRATION_NOTE_predicted_epc_source.md. 3 store tests (coexist, idempotent predicted re-save leaves lodged, lodged-only has no predicted) + property-repo wiring; 85 pass across affected suites; new code pyright-clean (2 pre-existing wwhrs errors in epc_property_table untouched). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
2.4 KiB
Markdown
51 lines
2.4 KiB
Markdown
# Migration note — `epc_property.source` (predicted-EPC slot)
|
|
|
|
**For the team to action before merging the EPC Prediction production-wiring
|
|
branch.** The model-side code is done and tested against the SQLModel-built test
|
|
DB; the **production Drizzle schema needs a matching column** that this repo does
|
|
not own.
|
|
|
|
## What changed in code (this branch)
|
|
|
|
Per **ADR-0031**, a Property can now hold a **lodged** EPC and a **predicted** EPC
|
|
(EPC Prediction gap-fill) at the same time. The two are distinguished by a new
|
|
`source` discriminator on the `epc_property` row:
|
|
|
|
- `infrastructure/postgres/epc_property_table.py` — `EpcPropertyModel` gains
|
|
`source: str = Field(default="lodged")`.
|
|
- `repositories/epc/epc_postgres_repository.py` — `save(..., source="lodged")`
|
|
writes it; `_delete_for_property` is now per-source (idempotency no longer wipes
|
|
the other slot); `get_for_property` / `get_for_properties` filter `source =
|
|
'lodged'`; new `get_predicted_for_property` / `get_predicted_for_properties`
|
|
read `source = 'predicted'`.
|
|
|
|
The test database is built from the SQLModel mirrors via `create_all`, so tests
|
|
already exercise the column. **Production is not** — hence this note.
|
|
|
|
## Required Drizzle migration
|
|
|
|
On the `epc_property` table:
|
|
|
|
1. **Add column** `source` — `text` (or your enum), **NOT NULL**, **default
|
|
`'lodged'`**. The default backfills every existing row as a real EPC, which is
|
|
correct (all current rows are lodged).
|
|
2. **Relax any single-row-per-property uniqueness.** If a unique constraint /
|
|
index exists on `epc_property(property_id)`, it must become
|
|
**`(property_id, source)`** — a property may now have one `lodged` row and one
|
|
`predicted` row. (Verify whether such a constraint exists; the SQLModel mirror
|
|
has none, but the production schema may.)
|
|
3. **Recommended index** `(property_id, source)` — every predicted/lodged read
|
|
filters on both columns.
|
|
|
|
## Allowed values
|
|
|
|
`source ∈ {'lodged', 'predicted'}` (see `EpcSource` in
|
|
`repositories/epc/epc_repository.py`). No other values are written.
|
|
|
|
## Why
|
|
|
|
ADR-0031: predicted EPCs are stored in their own slot rather than overwriting the
|
|
lodged `epc`, so (a) provenance is structural — the Validation Cohort excludes
|
|
predicted-sourced Properties and the UI flags them — and (b) lodged + predicted
|
|
coexist, which the planned **EPC Anomaly Flags** feature needs (compare a
|
|
Property's lodged EPC against its predicted one).
|