mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Reclassify Gas CPSU dumping-ground overrides via the generalized main-heating reclassify 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f6f4c3717d
commit
de7bfc5dfe
3 changed files with 126 additions and 91 deletions
|
|
@ -1,9 +1,11 @@
|
|||
"""One-time re-classification of HHRSH overrides funnelled into old storage.
|
||||
"""One-time re-classification of main-heating overrides funnelled into a garbage
|
||||
drawer for want of a correct archetype (#1376).
|
||||
|
||||
#1376 / ADR-0044: with no High Heat Retention archetype, the LLM classified
|
||||
"Electric Storage Systems: High heat retention storage heaters" to
|
||||
``"Electric storage heaters, old"`` (SAP 401) — scoring the *best* modern storage
|
||||
as the *worst* old large-volume type. An audit found 111 such rows.
|
||||
Covers both dumping grounds the ``main_heating_guard`` now rescues:
|
||||
- **HHRSH** (ADR-0044): "High heat retention storage heaters" → "Electric storage
|
||||
heaters, old" (SAP 401), the worst old type — 111 rows.
|
||||
- **Gas CPSU** (ADR-0045): solid-fuel room heaters (open fire / + back boiler /
|
||||
closed + boiler) and electric "NA"-type boilers → "Gas CPSU" (120, mains gas).
|
||||
|
||||
The live classifier now applies ``main_heating_guard`` deterministically (so new
|
||||
intakes are correct); this fixes the rows written before it. The **same guard**
|
||||
|
|
@ -11,9 +13,9 @@ decides the correction here, so the backfill and the live path cannot drift.
|
|||
|
||||
Updates the TEXT ``property_overrides.override_value`` (what the modelling reads —
|
||||
the immediate fix) unconditionally. The ``landlord_main_heating_system_overrides``
|
||||
``value`` classifier cache is a ``main_heating_system`` **pgEnum**; the new HHRSH
|
||||
archetype is an FE-owned value, so its cache write is **deferred** until the
|
||||
Drizzle migration adds it (the Class-A/B pattern).
|
||||
``value`` classifier cache is a ``main_heating_system`` **pgEnum**; the new
|
||||
archetypes are FE-owned values, so their cache writes are **deferred** until the
|
||||
Drizzle migration adds them (the Class-A/B pattern).
|
||||
|
||||
DRY-RUN BY DEFAULT: prints the counts it would change and writes nothing. Pass
|
||||
``--apply`` to execute inside a transaction. Idempotent — only rows whose stored
|
||||
|
|
@ -31,13 +33,14 @@ from domain.epc.property_overrides.main_heating_guard import main_heating_guard
|
|||
from scripts.e2e_common import build_engine, load_env
|
||||
|
||||
|
||||
def hhrsh_storage_corrections(
|
||||
def main_heating_corrections(
|
||||
stored: Iterable[tuple[str, str]],
|
||||
) -> dict[str, str]:
|
||||
"""``(description, stored override_value)`` → the HHRSH archetype value, for the
|
||||
descriptions the main-heating guard resolves to HHRSH whose stored value is not
|
||||
already it. Descriptions the guard defers and rows already on the target are
|
||||
omitted, so re-running against corrected data is a no-op."""
|
||||
"""``(description, stored override_value)`` → the faithful archetype value, for
|
||||
the descriptions the main-heating guard resolves (HHRSH, solid-fuel room
|
||||
heaters, electric boiler) whose stored value differs. Descriptions the guard
|
||||
defers and rows already on the target are omitted, so re-running against
|
||||
corrected data is a no-op."""
|
||||
corrections: dict[str, str] = {}
|
||||
for description, value in stored:
|
||||
member = main_heating_guard(description)
|
||||
|
|
@ -86,15 +89,15 @@ _ENUM_VALUES = text(
|
|||
|
||||
|
||||
def reclassify(conn: Connection, *, apply: bool) -> tuple[int, set[str]]:
|
||||
"""Re-map old-storage HHRSH overrides onto the high-heat-retention archetype.
|
||||
Returns the number of ``property_overrides`` rows found and the set of target
|
||||
values the live ``main_heating_system`` pgEnum does not yet carry (cache-
|
||||
deferred until the FE migration)."""
|
||||
"""Re-map main-heating overrides off their garbage-drawer archetypes onto the
|
||||
faithful ones the guard resolves. Returns the number of ``property_overrides``
|
||||
rows found and the set of target values the live ``main_heating_system`` pgEnum
|
||||
does not yet carry (cache-deferred until the FE migration)."""
|
||||
stored = [(r.description, r.value) for r in conn.execute(_DISTINCT)]
|
||||
enum_values = {r[0] for r in conn.execute(_ENUM_VALUES)}
|
||||
total = 0
|
||||
deferred: set[str] = set()
|
||||
for description, new_value in hhrsh_storage_corrections(stored).items():
|
||||
for description, new_value in main_heating_corrections(stored).items():
|
||||
params = {"description": description, "new_value": new_value}
|
||||
total += conn.execute(_OVERRIDES_COUNT, params).scalar() or 0
|
||||
in_enum = new_value in enum_values
|
||||
|
|
@ -124,9 +127,10 @@ def main() -> None:
|
|||
|
||||
verb = "re-classified" if args.apply else "would re-classify"
|
||||
print(
|
||||
f"{verb} {total} high-heat-retention storage override row(s) off "
|
||||
"'Electric storage heaters, old' onto the HHRSH archetype (SAP 409) — "
|
||||
"property_overrides / TEXT, what the modelling reads."
|
||||
f"{verb} {total} main-heating override row(s) off their garbage-drawer "
|
||||
"archetypes (old storage / Gas CPSU) onto faithful HHRSH / solid-fuel / "
|
||||
"electric-boiler archetypes — property_overrides / TEXT, what the "
|
||||
"modelling reads."
|
||||
)
|
||||
if deferred:
|
||||
print(
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
"""The HHRSH reclassify maps the "old storage" dumping ground onto the high-heat-
|
||||
retention archetype, leaves other storage subtypes alone, and is idempotent
|
||||
(#1376 / ADR-0044)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from domain.epc.property_overlays.main_heating_system_overlay import (
|
||||
main_heating_overlay_for,
|
||||
)
|
||||
from domain.epc.property_overrides.main_heating_guard import main_heating_guard
|
||||
from domain.epc.property_overrides.main_heating_system_type import (
|
||||
MainHeatingSystemType,
|
||||
)
|
||||
from scripts.reclassify_hhrsh_storage import hhrsh_storage_corrections
|
||||
|
||||
|
||||
def test_hhrsh_rows_are_remapped_off_old_storage() -> None:
|
||||
# Arrange — HHRSH funnelled into old storage (the bug), plus a genuinely-old
|
||||
# storage row and a gas row that must be left untouched.
|
||||
stored = [
|
||||
(
|
||||
"electric storage systems: high heat retention storage heaters",
|
||||
"Electric storage heaters, old",
|
||||
),
|
||||
(
|
||||
"electric storage systems: old (large volume) storage heaters",
|
||||
"Electric storage heaters, old",
|
||||
),
|
||||
("gas boiler", "Gas boiler, regular"),
|
||||
]
|
||||
|
||||
# Act
|
||||
corrections = hhrsh_storage_corrections(stored)
|
||||
|
||||
# Assert — only the HHRSH row moves, to its own archetype.
|
||||
assert corrections == {
|
||||
"electric storage systems: high heat retention storage heaters": (
|
||||
"Electric storage heaters, high heat retention"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def test_every_guard_target_round_trips_to_a_resolvable_archetype() -> None:
|
||||
# The HHRSH archetype the guard emits must decode to a real SAP heating code —
|
||||
# a guard/overlay drift can't silently write an unmodellable value (ADR-0044).
|
||||
member = main_heating_guard(
|
||||
"Electric Storage Systems: High heat retention storage heaters"
|
||||
)
|
||||
|
||||
assert member is not None
|
||||
assert member in MainHeatingSystemType
|
||||
simulation = main_heating_overlay_for(member.value, 0)
|
||||
assert simulation is not None
|
||||
assert simulation.heating is not None
|
||||
assert simulation.heating.sap_main_heating_code is not None
|
||||
|
||||
|
||||
def test_already_corrected_rows_need_no_change() -> None:
|
||||
# Act / Assert — idempotent.
|
||||
assert (
|
||||
hhrsh_storage_corrections(
|
||||
[
|
||||
(
|
||||
"electric storage systems: high heat retention storage heaters",
|
||||
"Electric storage heaters, high heat retention",
|
||||
)
|
||||
]
|
||||
)
|
||||
== {}
|
||||
)
|
||||
101
tests/scripts/test_reclassify_main_heating.py
Normal file
101
tests/scripts/test_reclassify_main_heating.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
"""The main-heating reclassify maps the garbage-drawer archetypes (old storage,
|
||||
Gas CPSU) onto the faithful ones the guard resolves, leaves correctly-classified
|
||||
rows alone, and is idempotent (#1376 / ADR-0044, ADR-0045)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from domain.epc.property_overlays.main_heating_system_overlay import (
|
||||
main_heating_overlay_for,
|
||||
)
|
||||
from domain.epc.property_overrides.main_heating_guard import main_heating_guard
|
||||
from domain.epc.property_overrides.main_heating_system_type import (
|
||||
MainHeatingSystemType,
|
||||
)
|
||||
from scripts.reclassify_main_heating import main_heating_corrections
|
||||
|
||||
|
||||
def test_garbage_drawer_rows_are_remapped_to_faithful_archetypes() -> None:
|
||||
# Arrange — HHRSH funnelled into old storage, and the Gas CPSU dumping ground
|
||||
# (solid-fuel room heaters + electric "NA" boiler); plus correctly-classified
|
||||
# rows (genuine old storage, a gas combi) that must be left untouched.
|
||||
stored = [
|
||||
(
|
||||
"electric storage systems: high heat retention storage heaters",
|
||||
"Electric storage heaters, old",
|
||||
),
|
||||
("solid fuel room heaters: open fire in grate", "Gas CPSU"),
|
||||
(
|
||||
"solid fuel room heaters: open fire with back boiler (no radiators)",
|
||||
"Gas CPSU",
|
||||
),
|
||||
(
|
||||
"solid fuel room heaters: closed room heater with boiler (no radiators)",
|
||||
"Gas CPSU",
|
||||
),
|
||||
("boiler: a rated na", "Gas CPSU"),
|
||||
# Correctly classified — the guard does not claim these.
|
||||
(
|
||||
"electric storage systems: old (large volume) storage heaters",
|
||||
"Electric storage heaters, old",
|
||||
),
|
||||
("boiler: a rated combi", "Gas boiler, combi"),
|
||||
]
|
||||
|
||||
# Act
|
||||
corrections = main_heating_corrections(stored)
|
||||
|
||||
# Assert — every mis-classified row moves to its faithful archetype.
|
||||
assert corrections == {
|
||||
"electric storage systems: high heat retention storage heaters": (
|
||||
"Electric storage heaters, high heat retention"
|
||||
),
|
||||
"solid fuel room heaters: open fire in grate": (
|
||||
"Solid fuel room heater, open fire"
|
||||
),
|
||||
"solid fuel room heaters: open fire with back boiler (no radiators)": (
|
||||
"Solid fuel room heater, open fire with back boiler"
|
||||
),
|
||||
"solid fuel room heaters: closed room heater with boiler (no radiators)": (
|
||||
"Solid fuel room heater, closed with boiler"
|
||||
),
|
||||
"boiler: a rated na": "Electric boiler",
|
||||
}
|
||||
|
||||
|
||||
def test_every_guard_target_round_trips_to_a_resolvable_archetype() -> None:
|
||||
# Every archetype the guard emits for the rescued descriptions must decode to a
|
||||
# real SAP heating code — a guard/overlay drift can't silently write an
|
||||
# unmodellable value (ADR-0044, ADR-0045).
|
||||
descriptions = [
|
||||
"Electric Storage Systems: High heat retention storage heaters",
|
||||
"Solid fuel room heaters: Open fire in grate",
|
||||
"Solid fuel room heaters: Open fire with back boiler (no radiators)",
|
||||
"Solid fuel room heaters: Closed room heater with boiler (no radiators)",
|
||||
"Boiler: A rated NA",
|
||||
]
|
||||
|
||||
for description in descriptions:
|
||||
member = main_heating_guard(description)
|
||||
|
||||
assert member is not None, description
|
||||
assert member in MainHeatingSystemType, description
|
||||
simulation = main_heating_overlay_for(member.value, 0)
|
||||
assert simulation is not None, description
|
||||
assert simulation.heating is not None, description
|
||||
assert simulation.heating.sap_main_heating_code is not None, description
|
||||
|
||||
|
||||
def test_already_corrected_rows_need_no_change() -> None:
|
||||
# Act / Assert — idempotent.
|
||||
assert (
|
||||
main_heating_corrections(
|
||||
[
|
||||
(
|
||||
"electric storage systems: high heat retention storage heaters",
|
||||
"Electric storage heaters, high heat retention",
|
||||
),
|
||||
("boiler: a rated na", "Electric boiler"),
|
||||
]
|
||||
)
|
||||
== {}
|
||||
)
|
||||
Loading…
Add table
Reference in a new issue