Model/infrastructure/postgres/product_table.py
Khalim Conn-Kowlessar b2c8980dd2 feat(modelling): ProductRepository + Postgres materials-table source
Product(measure_type, unit_cost_per_m2, contingency_rate). ProductRepository
is the DDD port abstracting the catalogue source; ProductPostgresRepository
reads the externally-owned material table (defensive SQLModel view
MaterialRow) and maps an active row to a Product — total_cost becomes the
fully-loaded unit_cost_per_m2 — joining the per-measure-type contingency
(contingencies.py, mirrors Costs.CONTINGENCIES; cavity 0.10). Strict-raise
on missing/inactive row. A JSON-backed impl will follow behind the same
port for ETL-gap costs.

Two DB tests against an ephemeral Postgres (map active row; raise on
inactive-only). Toward #1155 cost (4b). Also generalises the CONTEXT
Simulation Overlay wording: windows are targeted by index, building-part
association carried via window_location (_window_bp_index). pyright clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 08:32:38 +00:00

24 lines
895 B
Python

from __future__ import annotations
from typing import ClassVar, Optional
from sqlmodel import Field, SQLModel
class MaterialRow(SQLModel, table=True):
"""Defensive view of the externally-owned ``material`` catalogue table.
Declares only the columns the modelling backend reads to price a Measure
Option; other columns (r-values, labour breakdowns, etc.) are left off so
schema churn elsewhere doesn't ripple in. `total_cost` is the fully-loaded
cost per the row's `cost_unit` (GBP/m^2 for fabric measures).
"""
__tablename__: ClassVar[str] = "material" # pyright: ignore[reportIncompatibleVariableOverride]
id: int = Field(primary_key=True)
type: str
total_cost: Optional[float] = Field(default=None)
cost_unit: Optional[str] = Field(default=None)
description: Optional[str] = Field(default=None)
is_active: bool = Field(default=True)