"""Render a Plan as a plain-text sense-check table. The DB-less inspection harness prints this so the modelled package — its SAP band transition, cost, and each Plan Measure's attributed SAP / bill impact — can be eyeballed and debugged by hand. Pure presentation: it reads a `Plan` domain object and returns a string, computing nothing. """ from __future__ import annotations from typing import Optional from datatypes.epc.domain.epc import Epc from domain.modelling.plan import Plan _KG_PER_TONNE = 1000.0 def _band(sap_continuous: float) -> str: return Epc.from_sap_score(round(sap_continuous)).value def _signed_gbp(value: Optional[float]) -> str: return "n/a" if value is None else f"{value:+,.0f}" def _money(value: Optional[float]) -> str: if value is None: return "n/a" sign = "-" if value < 0 else "" return f"{sign}£{abs(value):,.0f}" def _signed_kwh(value: Optional[float]) -> str: return "n/a" if value is None else f"{value:+,.0f}" def format_plan_table(plan: Plan) -> str: """A multi-line table: one package summary line, then one line per Plan Measure (signed so positive is an improvement / a saving).""" co2_tonnes_saved: float = plan.co2_savings_kg_per_yr / _KG_PER_TONNE header = ( f"Plan SAP {plan.baseline.sap_continuous:.1f} ({_band(plan.baseline.sap_continuous)})" f" -> {plan.post_sap_continuous:.1f} ({plan.post_epc_rating.value})" f" CO2 saved {co2_tonnes_saved:.2f} t/yr" f" cost £{plan.cost_of_works:,.0f} (+£{plan.contingency_cost:,.0f} cont.)" f" bill saved {_money(plan.energy_bill_savings)}/yr" ) valuation = plan.valuation valuation_line = f" valuation uplift {valuation.average_pct:+.1%}" if valuation.average_value is not None and valuation.post_retrofit_value is not None: valuation_line += ( f" ({_money(valuation.average_value)}" f" -> {_money(valuation.post_retrofit_value)})" ) columns = ( f" {'measure':<30}{'SAP':>7}{'cost':>10}" f"{'kWh/yr':>10}{'£/yr':>9}" ) rows = [ f" {measure.measure_type:<30}" f"{measure.impact.sap_points:>+7.1f}" f"{('£' + format(measure.cost.total, ',.0f')):>10}" f"{_signed_kwh(measure.kwh_savings):>10}" f"{_signed_gbp(measure.energy_cost_savings):>9}" for measure in plan.measures ] return "\n".join([header, valuation_line, columns, *rows])