mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Slice 5a: the promotion. Replaces StubRebaseliner in production and collapses the
shadow runner into the rebaseliner (ADR-0013 amendment).
- CalculatorRebaseliner runs Sap10Calculator on every Property:
* sap_version < 10.2 -> Effective Performance IS the calculator output
(band via Epc.from_sap_score, CO2 kg->t, PEUI rounded), reason "pre_sap10".
* sap_version >= 10.2 -> Effective = lodged (API figures on-target), and the
calculator only logs divergence (SAP>0.5, PEUI/CO2 1%) as a validation signal.
* a calculator raise propagates -> batch aborts (ADR-0012); fix the cert at once.
- Rebaseliner.rebaseline gains property_id (for the divergence log).
- LoggingCalculatorShadow / the calculator_shadow seam removed from the
orchestrator; its divergence-comparison logic now lives in the rebaseliner.
- StubRebaseliner kept (signature updated) for orchestrator/repo unit tests.
The SapResult->EnergyBreakdown adapter + BillDerivation wiring (to populate the
bill block) follow once the appliances/cooking SapResult fields land.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 lines
2.5 KiB
Python
62 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
from typing import Literal
|
|
|
|
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
|
from domain.property_baseline.performance import Performance
|
|
|
|
RebaselineReason = Literal["none", "pre_sap10", "physical_state_changed", "both"]
|
|
|
|
# The SAP spec version below which a cert's recorded scores reflect a superseded
|
|
# methodology and must be ML-rebaselined (CONTEXT.md: Rebaselining).
|
|
_SAP10_FLOOR = 10.0
|
|
|
|
|
|
class RebaselineNotImplemented(Exception):
|
|
"""A Property needs Rebaselining, but the ML adapter is not wired yet.
|
|
|
|
Raised rather than silently recording ``reason="none"`` for a property that
|
|
genuinely needs rebaselining — a plausible-but-wrong baseline is expensive to
|
|
discover downstream. Surfaces how much of a First Run cohort the pipeline can
|
|
handle today (#1135).
|
|
"""
|
|
|
|
|
|
class Rebaseliner(ABC):
|
|
"""Produces a Property's Effective Performance from its Effective EPC.
|
|
|
|
Rebaselining (CONTEXT.md) re-predicts the rated quantities via ML when the
|
|
EPC was lodged pre-SAP10 or its physical state diverged from the lodged EPC;
|
|
otherwise Effective Performance equals Lodged. Injected into the
|
|
PropertyBaselineOrchestrator (ADR-0011) so the ML adapter can swap in without
|
|
touching the orchestrator, and so the single-property re-score-on-override
|
|
flow reuses the same port.
|
|
"""
|
|
|
|
@abstractmethod
|
|
def rebaseline(
|
|
self, property_id: int, effective_epc: EpcPropertyData, lodged: Performance
|
|
) -> tuple[Performance, RebaselineReason]: ...
|
|
|
|
|
|
class StubRebaseliner(Rebaseliner):
|
|
"""A no-calculator stub for tests that don't want the real calculator.
|
|
|
|
SAP10 certs pass through untouched — Effective Performance equals Lodged,
|
|
reason ``"none"``. A pre-SAP10 cert genuinely needs rebaselining, which this
|
|
stub does not do, so it raises rather than fabricating a "none". Production
|
|
uses ``CalculatorRebaseliner`` (the calculator is load-bearing — ADR-0013
|
|
amendment); this stub stays for orchestrator/repo unit tests.
|
|
"""
|
|
|
|
def rebaseline(
|
|
self, property_id: int, effective_epc: EpcPropertyData, lodged: Performance
|
|
) -> tuple[Performance, RebaselineReason]:
|
|
sap_version = effective_epc.sap_version
|
|
if sap_version is not None and sap_version < _SAP10_FLOOR:
|
|
raise RebaselineNotImplemented(
|
|
f"Property needs rebaselining (pre-SAP10 cert, sap_version="
|
|
f"{sap_version}); ML rebaselining is not implemented yet"
|
|
)
|
|
return lodged, "none"
|