mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Move the scenario and installed_measure tables into
infrastructure/postgres/modelling/ as full-parity SQLModel definitions
(ScenarioModel, InstalledMeasureModel + MeasureType), completing the cluster
consolidation. backend/app/db/models/recommendations.py is now a pure
re-export shim.
ScenarioModel.goal is the PortfolioGoal enum (legacy planning branches on it),
sourced from domain/modelling/portfolio_goal.py; the repo's to_domain maps it to
its value string, so domain Scenario.goal is now the value ("Increasing EPC")
consistent with the orchestrator's check — fixing the latent name-vs-value
inconsistency the old str column masked (the scenario repo test stored the enum
*name*). Parity columns are nullable (mirror convention; live NOT-NULLs owned by
Drizzle).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
73 lines
2.9 KiB
Python
73 lines
2.9 KiB
Python
from __future__ import annotations
|
|
|
|
import enum
|
|
from datetime import datetime
|
|
from typing import ClassVar, Optional
|
|
|
|
from sqlalchemy import Column, TIMESTAMP
|
|
from sqlalchemy import Enum as SAEnum
|
|
from sqlmodel import Field, SQLModel
|
|
|
|
|
|
class MeasureType(enum.Enum):
|
|
air_source_heat_pump = "air_source_heat_pump"
|
|
boiler_upgrade = "boiler_upgrade"
|
|
high_heat_retention_storage_heaters = "high_heat_retention_storage_heaters"
|
|
secondary_heating = "secondary_heating"
|
|
|
|
roomstat_programmer_trvs = "roomstat_programmer_trvs"
|
|
time_temperature_zone_control = "time_temperature_zone_control"
|
|
cylinder_thermostat = "cylinder_thermostat"
|
|
|
|
cavity_wall_insulation = "cavity_wall_insulation"
|
|
extension_cavity_wall_insulation = "extension_cavity_wall_insulation"
|
|
external_wall_insulation = "external_wall_insulation"
|
|
internal_wall_insulation = "internal_wall_insulation"
|
|
loft_insulation = "loft_insulation"
|
|
flat_roof_insulation = "flat_roof_insulation"
|
|
room_roof_insulation = "room_roof_insulation"
|
|
solid_floor_insulation = "solid_floor_insulation"
|
|
suspended_floor_insulation = "suspended_floor_insulation"
|
|
|
|
double_glazing = "double_glazing"
|
|
secondary_glazing = "secondary_glazing"
|
|
draught_proofing = "draught_proofing"
|
|
|
|
mechanical_ventilation = "mechanical_ventilation"
|
|
low_energy_lighting = "low_energy_lighting"
|
|
solar_pv = "solar_pv"
|
|
hot_water_tank_insulation = "hot_water_tank_insulation"
|
|
sealing_open_fireplace = "sealing_open_fireplace"
|
|
|
|
|
|
class InstalledMeasureModel(SQLModel, table=True):
|
|
"""The single SQLModel definition of the live ``installed_measure`` table
|
|
(ADR-0017 amendment). ``measure_type`` is the ``MeasureType`` Postgres enum;
|
|
the remaining NOT-NULLs are relaxed to nullable (mirror convention — the
|
|
live constraints are owned by the Drizzle schema)."""
|
|
|
|
__tablename__: ClassVar[str] = "installed_measure" # pyright: ignore[reportIncompatibleVariableOverride]
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
uprn: Optional[int] = Field(default=None, index=True)
|
|
measure_type: MeasureType = Field(
|
|
sa_column=Column(
|
|
SAEnum(
|
|
MeasureType,
|
|
name="measure_type",
|
|
values_callable=lambda cls: [m.value for m in cls], # pyright: ignore[reportUnknownLambdaType, reportUnknownMemberType, reportUnknownVariableType]
|
|
create_type=False,
|
|
),
|
|
nullable=False,
|
|
)
|
|
)
|
|
installed_at: Optional[datetime] = Field(
|
|
default=None, sa_column=Column(TIMESTAMP, nullable=True)
|
|
)
|
|
sap_points: Optional[float] = Field(default=None)
|
|
carbon_savings: Optional[float] = Field(default=None)
|
|
kwh_savings: Optional[float] = Field(default=None)
|
|
bill_savings: Optional[float] = Field(default=None)
|
|
heat_demand_savings: Optional[float] = Field(default=None)
|
|
source: Optional[str] = Field(default=None)
|
|
is_active: bool = True
|