Model/domain/modelling/scenario.py
Khalim Conn-Kowlessar 62a968119c feat(modelling): domain Scenario + ScenarioPostgresRepository (#1157)
Slice 1 of the #1157 build. The FE creates a Scenario and passes only
its id to the pipeline; the Modelling stage reads it back here.

- domain/modelling/scenario.py: thin `Scenario(id, goal, goal_value,
  budget, is_default)` — the slice the stage uses today (goal/budget for
  the Optimiser later; is_default drives plan.is_default). No phases
  (ADR-0005); legacy file-path/aggregate columns not modelled.
- infrastructure/postgres/scenario_table.py: `ScenarioRow` SQLModel
  mirror of the live `scenario` table (ADR-0017), declaring only the
  read columns; goal mapped as its string value.
- ScenarioPostgresRepository.get_many(scenario_ids) -> list[Scenario]:
  bulk read, input-order-preserving, raises on a missing id.

The method shape lives on the concrete repo for now; it is promoted to
an @abstractmethod on the port when the real orchestrator is wired and
the bare-stub instantiations retire (keeps the stubbed Modelling wiring
composing meanwhile). 2 tests, pyright strict clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 11:19:52 +00:00

27 lines
1,001 B
Python

"""Scenario — the named retrofit brief the Modelling stage scores against.
Built by a user in the scenario-builder UI and persisted before any modelling
fires; the pipeline is handed only its id and reads it back via a
`ScenarioRepository`. This is the thin slice the Modelling stage uses today:
the goal + budget that the Optimiser will consume (#1160) and `is_default`
(which drives `plan.is_default`). The legacy file-path / portfolio-aggregate
columns are not modelled. Carries no phases — multi-phase is deferred
(ADR-0005). See CONTEXT.md.
"""
from dataclasses import dataclass
from typing import Optional
@dataclass(frozen=True)
class Scenario:
"""A retrofit brief: its goal, optional budget, and whether it is the
Property's default Scenario. `goal` / `goal_value` are the lodged target
(e.g. "INCREASING_EPC" → band "C"); carried for the Optimiser, not yet
enforced."""
id: int
goal: str
goal_value: str
budget: Optional[float]
is_default: bool