mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
Classify the landlord Hot Water and Heating columns into categories 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ad3b1f15a8
commit
cc228b3da5
6 changed files with 233 additions and 0 deletions
|
|
@ -11,8 +11,10 @@ from domain.epc.built_form_type import BuiltFormType
|
|||
from domain.epc.construction_age_band import ConstructionAgeBand
|
||||
from domain.epc.glazing_type import GlazingType
|
||||
from domain.epc.main_fuel_type import MainFuelType
|
||||
from domain.epc.main_heating_system_type import MainHeatingSystemType
|
||||
from domain.epc.property_type import PropertyType
|
||||
from domain.epc.roof_type import RoofType
|
||||
from domain.epc.water_heating_type import WaterHeatingType
|
||||
from domain.epc.wall_type import WallType
|
||||
from domain.epc.wall_type_construction_dates import (
|
||||
wall_type_construction_date_prompt_hint,
|
||||
|
|
@ -36,6 +38,12 @@ from infrastructure.postgres.landlord_glazing_override_table import (
|
|||
from infrastructure.postgres.landlord_main_fuel_override_table import (
|
||||
LandlordMainFuelOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_main_heating_system_override_table import (
|
||||
LandlordMainHeatingSystemOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_water_heating_override_table import (
|
||||
LandlordWaterHeatingOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_property_type_override_table import (
|
||||
LandlordPropertyTypeOverrideRow,
|
||||
)
|
||||
|
|
@ -144,6 +152,26 @@ def _build_columns(
|
|||
session, LandlordConstructionAgeBandOverrideRow
|
||||
),
|
||||
),
|
||||
"water_heating": lambda src: ClassifiableColumn(
|
||||
name="water_heating",
|
||||
source_column=src,
|
||||
classifier=ChatGptColumnClassifier(
|
||||
chat_gpt, WaterHeatingType, WaterHeatingType.UNKNOWN
|
||||
),
|
||||
repo=LandlordOverridesRepository[WaterHeatingType](
|
||||
session, LandlordWaterHeatingOverrideRow
|
||||
),
|
||||
),
|
||||
"main_heating_system": lambda src: ClassifiableColumn(
|
||||
name="main_heating_system",
|
||||
source_column=src,
|
||||
classifier=ChatGptColumnClassifier(
|
||||
chat_gpt, MainHeatingSystemType, MainHeatingSystemType.UNKNOWN
|
||||
),
|
||||
repo=LandlordOverridesRepository[MainHeatingSystemType](
|
||||
session, LandlordMainHeatingSystemOverrideRow
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
columns: list[ClassifiableColumn[Any]] = []
|
||||
|
|
|
|||
24
domain/epc/main_heating_system_type.py
Normal file
24
domain/epc/main_heating_system_type.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class MainHeatingSystemType(Enum):
|
||||
"""A landlord-supplied main-heating-system description, as resolved by the
|
||||
landlord-description-overrides context.
|
||||
|
||||
Each member's value is the canonical system archetype that the main-heating
|
||||
Simulation Overlay
|
||||
(``domain/epc/property_overlays/main_heating_system_overlay.py``) maps to a
|
||||
representative SAP ``sap_main_heating_code`` — so the member values MUST stay
|
||||
in lock-step with that overlay's ``_MAIN_HEATING_CODES`` keys. The SEDBUK A-G
|
||||
efficiency band the Hyde "Heating" column carries is NOT modelled yet
|
||||
(deferred), so archetypes map to their modern/condensing code. ``UNKNOWN``
|
||||
covers values the classifier cannot resolve and the not-yet-modelled systems
|
||||
(heat pumps, community heating).
|
||||
"""
|
||||
|
||||
GAS_COMBI = "Gas boiler, combi"
|
||||
GAS_REGULAR = "Gas boiler, regular"
|
||||
GAS_CPSU = "Gas CPSU"
|
||||
ELECTRIC_STORAGE_FAN = "Electric storage heaters, fan"
|
||||
DIRECT_ELECTRIC = "Direct-acting electric"
|
||||
UNKNOWN = "Unknown"
|
||||
21
domain/epc/water_heating_type.py
Normal file
21
domain/epc/water_heating_type.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class WaterHeatingType(Enum):
|
||||
"""A landlord-supplied water-heating description, as resolved by the
|
||||
landlord-description-overrides context.
|
||||
|
||||
Each member's value is the canonical "<system>, <fuel>" description that the
|
||||
water-heating Simulation Overlay
|
||||
(``domain/epc/property_overlays/water_heating_overlay.py``) decomposes into
|
||||
the SAP ``water_heating_code`` + ``water_heating_fuel`` int codes the
|
||||
calculator reads — so the member values MUST stay in lock-step with that
|
||||
overlay's ``_WATER_HEATING_CODES`` keys. ``UNKNOWN`` covers values the
|
||||
classifier cannot resolve, and any combination not yet given verified codes
|
||||
(it leaves the lodged cert's hot-water arrangement untouched).
|
||||
"""
|
||||
|
||||
FROM_MAIN_MAINS_GAS = "From main system, mains gas"
|
||||
FROM_MAIN_ELECTRICITY = "From main system, electricity"
|
||||
ELECTRIC_IMMERSION = "Electric immersion, electricity"
|
||||
UNKNOWN = "Unknown"
|
||||
|
|
@ -25,9 +25,24 @@ from infrastructure.postgres.landlord_property_type_override_table import (
|
|||
from infrastructure.postgres.landlord_roof_type_override_table import (
|
||||
LandlordRoofTypeOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_construction_age_band_override_table import (
|
||||
LandlordConstructionAgeBandOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_glazing_override_table import (
|
||||
LandlordGlazingOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_main_fuel_override_table import (
|
||||
LandlordMainFuelOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_main_heating_system_override_table import (
|
||||
LandlordMainHeatingSystemOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_wall_type_override_table import (
|
||||
LandlordWallTypeOverrideRow,
|
||||
)
|
||||
from infrastructure.postgres.landlord_water_heating_override_table import (
|
||||
LandlordWaterHeatingOverrideRow,
|
||||
)
|
||||
from repositories.landlord_overrides.landlord_override_reader import (
|
||||
LandlordOverrideReader,
|
||||
)
|
||||
|
|
@ -38,6 +53,11 @@ _ROW_TYPES: dict[str, type] = {
|
|||
"built_form_type": LandlordBuiltFormTypeOverrideRow,
|
||||
"wall_type": LandlordWallTypeOverrideRow,
|
||||
"roof_type": LandlordRoofTypeOverrideRow,
|
||||
"main_fuel": LandlordMainFuelOverrideRow,
|
||||
"glazing": LandlordGlazingOverrideRow,
|
||||
"construction_age_band": LandlordConstructionAgeBandOverrideRow,
|
||||
"water_heating": LandlordWaterHeatingOverrideRow,
|
||||
"main_heating_system": LandlordMainHeatingSystemOverrideRow,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
"""SQLModel mirror of the ``landlord_main_heating_system_overrides`` Drizzle table.
|
||||
|
||||
The schema source of truth lives in the ``assessment-model`` TS repo
|
||||
(`src/app/db/schema/landlord_overrides.ts`). The migrations are owned there;
|
||||
this row class only mirrors the columns so the Python lambda can read/write.
|
||||
See ADR-0003. Shape mirrors ``LandlordWallTypeOverrideRow`` -- the only
|
||||
differences are the table name, the ``main_heating_system`` pgEnum on ``value``,
|
||||
and the unique-constraint name.
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import ClassVar
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlalchemy import BigInteger, Column, UniqueConstraint
|
||||
from sqlalchemy import Enum as SAEnum
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from domain.epc.main_heating_system_type import MainHeatingSystemType
|
||||
from infrastructure.postgres.landlord_override_enums import override_source_sa_enum
|
||||
|
||||
|
||||
class LandlordMainHeatingSystemOverrideRow(SQLModel, table=True):
|
||||
__tablename__: ClassVar[str] = "landlord_main_heating_system_overrides" # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
__table_args__: ClassVar[tuple[UniqueConstraint, ...]] = ( # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
# Shortened (drop the redundant ``_overrides``) to stay within
|
||||
# PostgreSQL's 63-char identifier limit; mirrors the Drizzle name.
|
||||
UniqueConstraint(
|
||||
"portfolio_id",
|
||||
"description",
|
||||
name="landlord_main_heating_system_portfolio_description_unique",
|
||||
),
|
||||
)
|
||||
|
||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||
|
||||
# bigint to match the Drizzle ``portfolio_id`` FK; SQLModel's default int
|
||||
# mapping is 32-bit Integer and would overflow once portfolio IDs exceed
|
||||
# 2^31. The FK to ``portfolio.id`` is enforced by the Drizzle migration,
|
||||
# not declared here -- the ``portfolio`` table is not modelled in Python.
|
||||
portfolio_id: int = Field(
|
||||
sa_column=Column(BigInteger, nullable=False, index=True),
|
||||
)
|
||||
|
||||
description: str = Field(nullable=False)
|
||||
|
||||
value: MainHeatingSystemType = Field(
|
||||
sa_column=Column(
|
||||
SAEnum(
|
||||
MainHeatingSystemType,
|
||||
name="main_heating_system",
|
||||
values_callable=lambda cls: [m.value for m in cls], # pyright: ignore[reportUnknownLambdaType, reportUnknownMemberType, reportUnknownVariableType]
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
|
||||
# Shared SAEnum -- see ``landlord_override_enums`` for why this single
|
||||
# instance is reused by every ``landlord_*_overrides`` row class.
|
||||
source: str = Field(
|
||||
sa_column=Column(override_source_sa_enum, nullable=False),
|
||||
)
|
||||
|
||||
created_at: datetime = Field(
|
||||
default_factory=lambda: datetime.now(timezone.utc),
|
||||
nullable=False,
|
||||
)
|
||||
updated_at: datetime = Field(
|
||||
default_factory=lambda: datetime.now(timezone.utc),
|
||||
nullable=False,
|
||||
)
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
"""SQLModel mirror of the ``landlord_water_heating_overrides`` Drizzle table.
|
||||
|
||||
The schema source of truth lives in the ``assessment-model`` TS repo
|
||||
(`src/app/db/schema/landlord_overrides.ts`). The migrations are owned there;
|
||||
this row class only mirrors the columns so the Python lambda can read/write.
|
||||
See ADR-0003. Shape mirrors ``LandlordWallTypeOverrideRow`` -- the only
|
||||
differences are the table name, the ``water_heating`` pgEnum on ``value``, and
|
||||
the unique-constraint name.
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import ClassVar
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlalchemy import BigInteger, Column, UniqueConstraint
|
||||
from sqlalchemy import Enum as SAEnum
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from domain.epc.water_heating_type import WaterHeatingType
|
||||
from infrastructure.postgres.landlord_override_enums import override_source_sa_enum
|
||||
|
||||
|
||||
class LandlordWaterHeatingOverrideRow(SQLModel, table=True):
|
||||
__tablename__: ClassVar[str] = "landlord_water_heating_overrides" # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
__table_args__: ClassVar[tuple[UniqueConstraint, ...]] = ( # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
UniqueConstraint(
|
||||
"portfolio_id",
|
||||
"description",
|
||||
name="landlord_water_heating_overrides_portfolio_description_unique",
|
||||
),
|
||||
)
|
||||
|
||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||
|
||||
# bigint to match the Drizzle ``portfolio_id`` FK; SQLModel's default int
|
||||
# mapping is 32-bit Integer and would overflow once portfolio IDs exceed
|
||||
# 2^31. The FK to ``portfolio.id`` is enforced by the Drizzle migration,
|
||||
# not declared here -- the ``portfolio`` table is not modelled in Python.
|
||||
portfolio_id: int = Field(
|
||||
sa_column=Column(BigInteger, nullable=False, index=True),
|
||||
)
|
||||
|
||||
description: str = Field(nullable=False)
|
||||
|
||||
value: WaterHeatingType = Field(
|
||||
sa_column=Column(
|
||||
SAEnum(
|
||||
WaterHeatingType,
|
||||
name="water_heating",
|
||||
values_callable=lambda cls: [m.value for m in cls], # pyright: ignore[reportUnknownLambdaType, reportUnknownMemberType, reportUnknownVariableType]
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
|
||||
# Shared SAEnum -- see ``landlord_override_enums`` for why this single
|
||||
# instance is reused by every ``landlord_*_overrides`` row class.
|
||||
source: str = Field(
|
||||
sa_column=Column(override_source_sa_enum, nullable=False),
|
||||
)
|
||||
|
||||
created_at: datetime = Field(
|
||||
default_factory=lambda: datetime.now(timezone.utc),
|
||||
nullable=False,
|
||||
)
|
||||
updated_at: datetime = Field(
|
||||
default_factory=lambda: datetime.now(timezone.utc),
|
||||
nullable=False,
|
||||
)
|
||||
Loading…
Add table
Reference in a new issue