From 91c88743f74aa95b0688978d4c5d88f7e32aec71 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 13 Jul 2026 23:13:22 +0000 Subject: [PATCH] =?UTF-8?q?Pivot=20solar=20PV=20with=20a=20battery=20to=20?= =?UTF-8?q?its=20own=20column=20=F0=9F=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- domain/scenario_export/scenario_sheet.py | 25 ++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/domain/scenario_export/scenario_sheet.py b/domain/scenario_export/scenario_sheet.py index e38116679..64cb5a6d9 100644 --- a/domain/scenario_export/scenario_sheet.py +++ b/domain/scenario_export/scenario_sheet.py @@ -41,6 +41,14 @@ MEASURE_COLUMNS: tuple[str, ...] = ( "system_tune_up_zoned", ) +# A solar_pv Option that installs a battery pivots to its own column, kept in the +# frozen contract so a sheet with battery-solar shares columns with one without. +_BATTERY_MEASURE_COLUMNS: tuple[str, ...] = ("solar_pv_with_battery",) + +# Every measure cost column present on a sheet: the frozen base contract plus the +# battery variant. +_ALL_MEASURE_COLUMNS: tuple[str, ...] = MEASURE_COLUMNS + _BATTERY_MEASURE_COLUMNS + # Property identity columns, leading every sheet before the measure columns. _ID_COLUMNS: tuple[str, ...] = ("property_id", "landlord_property_id") @@ -62,6 +70,14 @@ def _sum(values: Sequence[Optional[float]]) -> float: return sum(value or 0.0 for value in values) +def _measure_column(measure: ExportMeasure) -> str: + """The pivot column for a measure: a solar_pv Option carrying a battery lands + in its own ``solar_pv_with_battery`` column (ADR-0065).""" + if measure.measure_type == "solar_pv" and measure.includes_battery: + return "solar_pv_with_battery" + return measure.measure_type + + @dataclass(frozen=True) class ExportMeasure: """One selected measure on a Property's default Plan for the Scenario: the @@ -129,7 +145,7 @@ def shape_scenario_sheet( ) -> ExportSheet: """Shape one Scenario's Properties into a wide export sheet (ADR-0065).""" columns: tuple[str, ...] = ( - _ID_COLUMNS + MEASURE_COLUMNS + _SAVINGS_COLUMNS + _TOTAL_COLUMNS + _ID_COLUMNS + _ALL_MEASURE_COLUMNS + _SAVINGS_COLUMNS + _TOTAL_COLUMNS ) rows: list[dict[str, Any]] = [] for prop in properties: @@ -139,11 +155,12 @@ def shape_scenario_sheet( } # Every measure column is present, blank unless this Property has it, so # all scenario sheets share one contract (ADR-0065). - for column in MEASURE_COLUMNS: + for column in _ALL_MEASURE_COLUMNS: row[column] = "" for measure in prop.measures: - if measure.measure_type in MEASURE_COLUMNS: - row[measure.measure_type] = measure.estimated_cost + column = _measure_column(measure) + if column in _ALL_MEASURE_COLUMNS: + row[column] = measure.estimated_cost # Roll the selected measures' attributed impact up to the Property. row["sap_points"] = _sum([m.sap_points for m in prop.measures]) row["co2_equivalent_savings"] = _sum(