Coverage for backend/django/Economics/results/services/comparison/contracts.py: 100%
99 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-07-22 05:22 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-07-22 05:22 +0000
1"""Typed service contracts for Economics study comparisons."""
3from __future__ import annotations
5from datetime import datetime
6from decimal import Decimal
7from enum import StrEnum
9from pydantic import BaseModel, ConfigDict, Field, JsonValue
11from Economics.results.models import EconomicsResultRun
12from Economics.results.services.financial_metrics.contracts import DiscountedCashFlowRow
13from Economics.results.services.financial_metrics.metric_catalog import MetricComparisonDirection
14from Economics.shared.choices import EconomicsBaselineMode
15from Economics.studies.models import EconomicsStudy
18class EconomicsComparisonContract(BaseModel):
19 """Base model for typed study comparison service contracts."""
21 model_config = ConfigDict(arbitrary_types_allowed=True)
24class ComparisonTarget(EconomicsComparisonContract):
25 """Resolved or saved comparison study metadata used during evaluation."""
27 study_id: int | None
28 flowsheet_id: int | None
29 name: str
30 flowsheet_name: str
31 availability_status: str
32 study: EconomicsStudy | None = None
35class ComparisonAvailabilityStatus(StrEnum):
36 AVAILABLE = "available"
37 UNAVAILABLE = "unavailable"
40class ComparisonStatus(StrEnum):
41 BASELINE = "baseline"
42 BASELINE_NOT_APPLICABLE = "baseline_not_applicable"
43 COMPARABLE = "comparable"
44 MISSING_BASELINE = "missing_baseline"
45 MISSING_METRIC = "missing_metric"
46 MISSING_RESULT = "missing_result"
47 RECALCULATION_UNAVAILABLE = "recalculation_unavailable"
48 UNAVAILABLE = "unavailable"
49 UNIT_MISMATCH = "unit_mismatch"
50 ZERO_BASELINE = "zero_baseline"
53class ComparisonValidationError(ValueError):
54 """Configuration validation error that maps cleanly to serializer errors."""
57class EconomicsStudyComparisonRequest(EconomicsComparisonContract):
58 """Full saved comparison configuration replacement."""
60 comparison_study_ids: list[int] = Field(min_length=1, max_length=10)
61 baseline_mode: EconomicsBaselineMode = EconomicsBaselineMode.STUDY
62 baseline_study_id: int | None = None
65class WarningSummaryPayload(EconomicsComparisonContract):
66 code: str
67 severity: str
68 message: str
69 context: dict[str, JsonValue] | None = None
72class ComparisonConfigPayload(EconomicsComparisonContract):
73 id: int | None
74 active_study: int
75 baseline_mode: EconomicsBaselineMode
76 baseline_study_id: int
77 comparison_study_ids: list[int]
78 updated_at: datetime | None
81class ComparisonStudyPayload(EconomicsComparisonContract):
82 id: int | None
83 name: str
84 flowsheet_id: int | None
85 flowsheet_name: str
86 result_run_id: int | None
87 result_status: str
88 result_currency: str
89 availability_status: str
92class ComparisonChartDatasetPayload(EconomicsComparisonContract):
93 """Chart dataset assembled for study-comparison visualizations."""
95 id: int
96 chart_key: str
97 title: str
98 chart_type: str
99 source_row_keys: list[str]
100 chart_data: dict[str, JsonValue]
101 rendering_metadata: dict[str, JsonValue]
104class ComparisonValuePayload(EconomicsComparisonContract):
105 study_id: int | None
106 amount: str | None
107 unit: str
108 percentage_difference: str | None
109 comparison_status: ComparisonStatus
110 is_best: bool = False
113class ComparisonRowPayload(EconomicsComparisonContract):
114 row_key: str
115 label: str
116 comparison_direction: MetricComparisonDirection
117 maximum_fraction_digits: int
118 values: list[ComparisonValuePayload]
121class ComparisonMetricValue(EconomicsComparisonContract):
122 amount: Decimal | None
123 unit: str
124 comparison_status: ComparisonStatus | None = None
127class ComparisonPayload(EconomicsComparisonContract):
128 active_study_id: int
129 baseline_mode: EconomicsBaselineMode
130 baseline_study_id: int
131 config: ComparisonConfigPayload
132 studies: list[ComparisonStudyPayload]
133 rows: list[ComparisonRowPayload]
134 resource_rows: list[ComparisonRowPayload]
135 chart_datasets: list[ComparisonChartDatasetPayload]
136 warnings: list[WarningSummaryPayload]
139class ResultContext(EconomicsComparisonContract):
140 run: EconomicsResultRun
141 classification: str
142 lines: dict[str, ComparisonMetricValue]
143 discounted_cash_flow: tuple[DiscountedCashFlowRow, ...] = ()