mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Pin the FE-facing aggregate_portfolio_recommendations (previously untested): it sums a Scenario's default Recommendations onto the Scenario row, joining Recommendation → Plan on recommendation.plan_id. Locks the m2m→plan_id read cut for the FE-critical path, now testable thanks to the full-parity ScenarioModel. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
96 lines
3.3 KiB
Python
96 lines
3.3 KiB
Python
"""Characterisation of the FE-facing portfolio aggregation
|
|
(`aggregate_portfolio_recommendations`): it sums a Scenario's **default**
|
|
Recommendations and writes the totals onto the Scenario row.
|
|
|
|
This pins the `recommendation.plan_id` linkage the m2m retirement introduced
|
|
(ADR-0017 amendment): the aggregation joins Recommendation → Plan on
|
|
`recommendation.plan_id`, so only measures carrying the right `plan_id` (and
|
|
`default = True`) are summed.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from sqlmodel import Session
|
|
|
|
from backend.app.db.functions.portfolio_functions import (
|
|
aggregate_portfolio_recommendations,
|
|
)
|
|
from backend.app.db.models.recommendations import (
|
|
PlanModel,
|
|
Recommendation,
|
|
ScenarioModel,
|
|
)
|
|
from domain.modelling.portfolio_goal import PortfolioGoal
|
|
|
|
|
|
def _rec(
|
|
*, plan_id: int, default: bool, cost: float, kwh: float, gbp: float, co2: float
|
|
) -> Recommendation:
|
|
return Recommendation(
|
|
property_id=10,
|
|
plan_id=plan_id,
|
|
type="cavity_wall_insulation",
|
|
measure_type="cavity_wall_insulation",
|
|
description="Cavity wall insulation",
|
|
estimated_cost=cost,
|
|
kwh_savings=kwh,
|
|
energy_cost_savings=gbp,
|
|
co2_equivalent_savings=co2,
|
|
total_work_hours=4.0,
|
|
default=default,
|
|
already_installed=False,
|
|
)
|
|
|
|
|
|
def test_aggregation_sums_default_measures_linked_by_plan_id(
|
|
db_session: Session,
|
|
) -> None:
|
|
# Arrange — one Scenario + Plan, two default measures (summed) plus a
|
|
# non-default one (excluded), all linked by recommendation.plan_id.
|
|
db_session.add(
|
|
ScenarioModel(
|
|
id=7,
|
|
portfolio_id=1,
|
|
goal=PortfolioGoal.INCREASING_EPC,
|
|
goal_value="C",
|
|
is_default=True,
|
|
)
|
|
)
|
|
db_session.add(
|
|
PlanModel(id=100, portfolio_id=1, property_id=10, scenario_id=7, is_default=True)
|
|
)
|
|
db_session.add_all(
|
|
[
|
|
_rec(plan_id=100, default=True, cost=1000.0, kwh=500.0, gbp=120.0, co2=0.5),
|
|
_rec(plan_id=100, default=True, cost=500.0, kwh=300.0, gbp=80.0, co2=0.2),
|
|
# excluded: not default
|
|
_rec(plan_id=100, default=False, cost=9.0, kwh=9.0, gbp=9.0, co2=9.0),
|
|
]
|
|
)
|
|
db_session.commit()
|
|
|
|
# Act
|
|
aggregate_portfolio_recommendations(
|
|
db_session,
|
|
portfolio_id=1,
|
|
scenario_id=7,
|
|
total_valuation_increase=2500.0,
|
|
labour_days=3.0,
|
|
aggregated_data={},
|
|
)
|
|
db_session.commit()
|
|
|
|
# Assert — the default measures' sums land on the Scenario row
|
|
scenario = db_session.query(ScenarioModel).filter_by(id=7).one()
|
|
assert scenario.cost is not None
|
|
assert abs(scenario.cost - 1500.0) <= 1e-9 # 1000 + 500
|
|
assert scenario.energy_savings is not None
|
|
assert abs(scenario.energy_savings - 800.0) <= 1e-9 # Σ kwh_savings
|
|
assert scenario.energy_cost_savings is not None
|
|
assert abs(scenario.energy_cost_savings - 200.0) <= 1e-9 # 120 + 80
|
|
assert scenario.co2_equivalent_savings is not None
|
|
assert abs(scenario.co2_equivalent_savings - 0.7) <= 1e-9 # 0.5 + 0.2
|
|
assert scenario.total_work_hours is not None
|
|
assert abs(scenario.total_work_hours - 8.0) <= 1e-9 # 4 + 4
|
|
assert scenario.property_valuation_increase == 2500.0
|
|
assert scenario.labour_days == 3.0
|