Coverage for backend/django/Economics/formulas/builders/metric_formulas.py: 100%
33 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-06-23 21:51 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-06-23 21:51 +0000
1from __future__ import annotations
3from dataclasses import dataclass
4from decimal import Decimal
5from typing import Any
7from Economics.formulas.models import EconomicsMetricFormula
9from Economics.studies.models import EconomicsStudy
10from Economics.formulas.engine.core import FormulaError
11from Economics.formulas.builders.metrics import BoundMetricFormula
12from Economics.formulas.builders.metric_property_formulas import metric_property_formula_bindings
15@dataclass(frozen=True)
16class PersistedMetricFormula:
17 """Small immutable handle returned to financial metric contracts."""
19 id: int
20 property_formula: str
21 formula_audit: dict[str, Any]
22 blocked_reason: str = ""
25class MetricFormulaStore:
26 """Persist formula rows for study-backed financial metric calculations."""
28 def __init__(self, study: EconomicsStudy):
29 self.study = study
30 self.persisted_metric_keys: set[str] = set()
32 def persist_metric_formula(
33 self,
34 *,
35 metric_key: str,
36 formula: BoundMetricFormula,
37 value: Decimal | None,
38 status: str,
39 assumptions: Any,
40 formula_audit: dict[str, Any],
41 ) -> PersistedMetricFormula:
42 """Upsert the formula row used for a single financial metric."""
44 self.persisted_metric_keys.add(metric_key)
45 property_formula = ""
46 blocked_reason = ""
47 try:
48 property_formula = formula.render_property_formula(
49 metric_property_formula_bindings(
50 self.study,
51 metric_key=metric_key,
52 formula=formula,
53 assumptions=assumptions,
54 )
55 )
56 except FormulaError as exc:
57 blocked_reason = exc.message
58 record, _created = EconomicsMetricFormula.objects.update_or_create(
59 flowsheet=self.study.flowsheet,
60 study=self.study,
61 metric_key=metric_key,
62 defaults={
63 "formula_key": formula.formula.key,
64 "formula": formula_audit.get("formula") or "",
65 "property_formula": property_formula,
66 "unit": formula.formula.unit,
67 "value": str(value) if value is not None else None,
68 "status": status,
69 "formula_audit": formula_audit,
70 "blocked_reason": blocked_reason,
71 },
72 )
73 return PersistedMetricFormula(
74 id=record.pk,
75 property_formula=record.property_formula,
76 formula_audit=record.formula_audit,
77 blocked_reason=record.blocked_reason,
78 )
80 def delete_stale_metric_formulas(self) -> None:
81 """Remove persisted formulas that were not emitted by the latest calculation."""
83 self.study.metric_formulas.exclude(metric_key__in=self.persisted_metric_keys).delete()
85 def delete_all_metric_formulas(self) -> None:
86 """Remove all persisted formulas for a study after a failed calculation."""
88 self.study.metric_formulas.all().delete()