feat(fase0): synthetic reference domain (D4) + backend profile skeleton (D2)
Completes Fase 0 (skeleton & decision-lock): - reference_domain.py + data/reference_projects.json: a synthetic "anleggskostnad" portfolio (3 fictional construction-cost projects with cost line items) as the framework's bundled reference input. Plain typed loader (frozen dataclasses); the JSON-Schema data-source *contract* (B5) is deliberately deferred to Fase 2. - backends.py: Profile (azure|local) + ChatBackend Protocol seam + AzureFoundryBackend/LocalBackend stubs + get_backend() selector (fail-fast on unknown profile). Empty skeleton per D2 — create_chat_client raises NotImplementedError until live wiring in Fase 1. Return type is the MAF BaseChatClient (the common base of FoundryChatClient/OpenAIChatClient). Quality gate green: ruff format + check, mypy (src) clean, 12 pytest passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H9FyyENxebxVThjrn9et8C
This commit is contained in:
parent
491a746bba
commit
b57aa83a30
5 changed files with 282 additions and 0 deletions
75
src/portfolio_optimiser/reference_domain.py
Normal file
75
src/portfolio_optimiser/reference_domain.py
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
"""Synthetic reference domain (D4): a small, fictional set of "anleggskostnad"
|
||||
(construction-cost) projects with dummy data.
|
||||
|
||||
This is the framework's bundled example input — a portfolio of *independent*
|
||||
projects the optimiser runs against. The framework finds cost-savings INSIDE
|
||||
each project (Enhet B), so every project carries cost line items where a savings
|
||||
measure could later be proposed and then deterministically validated.
|
||||
|
||||
It is a synthetic FIXTURE — not real data, and not the validated IR. The
|
||||
deliberate data-source *contract* (JSON-Schema-validated config, B5) is a Fase 2
|
||||
concern; here we keep a plain, typed loader over a bundled JSON file.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from importlib.resources import files
|
||||
|
||||
_DATA_RESOURCE = "data/reference_projects.json"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CostItem:
|
||||
"""One cost line in a project's estimate."""
|
||||
|
||||
code: str
|
||||
description: str
|
||||
quantity: float
|
||||
unit: str
|
||||
unit_cost: float # NOK per unit
|
||||
|
||||
@property
|
||||
def total_cost(self) -> float:
|
||||
return self.quantity * self.unit_cost
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Project:
|
||||
"""One independent construction-cost project (Enhet B operates inside this)."""
|
||||
|
||||
id: str
|
||||
name: str
|
||||
description: str
|
||||
currency: str
|
||||
cost_items: tuple[CostItem, ...]
|
||||
|
||||
@property
|
||||
def total_cost(self) -> float:
|
||||
return sum((item.total_cost for item in self.cost_items), 0.0)
|
||||
|
||||
|
||||
def load_reference_projects() -> tuple[Project, ...]:
|
||||
"""Load the bundled synthetic reference projects (D4)."""
|
||||
resource = files("portfolio_optimiser").joinpath(_DATA_RESOURCE)
|
||||
raw = json.loads(resource.read_text(encoding="utf-8"))
|
||||
return tuple(
|
||||
Project(
|
||||
id=p["id"],
|
||||
name=p["name"],
|
||||
description=p["description"],
|
||||
currency=p["currency"],
|
||||
cost_items=tuple(
|
||||
CostItem(
|
||||
code=c["code"],
|
||||
description=c["description"],
|
||||
quantity=c["quantity"],
|
||||
unit=c["unit"],
|
||||
unit_cost=c["unit_cost"],
|
||||
)
|
||||
for c in p["cost_items"]
|
||||
),
|
||||
)
|
||||
for p in raw["projects"]
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue