feat(modelling): cut plan→recommendation readers onto plan_id

Rewrite the three structurally-identical m2m-join readers
(portfolio_functions.aggregate_portfolio_recommendations,
Outputs.get_recommendations_from_db, export get_recommendations) to join
PlanModel directly via recommendation.plan_id, dropping the plan_recommendations
join and its now-unused import. The writers set plan_id (prior slice), so the
rows resolve. test_export pins the export reader through the cut (its fixtures
now set recommendation.plan_id). A portfolio_functions DB characterization test
lands with the scenario consolidation (which provides the full-parity scenario
table the aggregation writes to).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-03 21:09:43 +00:00
parent 27fcc5b184
commit af5dbe325d
4 changed files with 14 additions and 24 deletions

View file

@ -11,7 +11,6 @@ from backend.app.db.models.portfolio import PropertyModel, PropertyDetailsEpcMod
from backend.app.db.models.recommendations import (
Recommendation,
PlanModel,
PlanRecommendations,
)
@ -124,20 +123,15 @@ class Outputs:
return plans_data
def get_recommendations_from_db(self, plan_ids):
# Get recommendations through PlanRecommendations for those plans and that are default
# Get default recommendations for those plans, linked by recommendation.plan_id
recommendations_query = (
self.session.query(Recommendation, PlanModel.scenario_id)
.join(
PlanRecommendations,
Recommendation.id == PlanRecommendations.recommendation_id,
)
.join(
PlanModel,
PlanModel.id
== PlanRecommendations.plan_id, # Join with Plan to access scenario_id
PlanModel.id == Recommendation.plan_id, # access scenario_id
)
.filter(
PlanRecommendations.plan_id.in_(plan_ids),
Recommendation.plan_id.in_(plan_ids),
Recommendation.default == True, # Filtering for default recommendations
)
.all()

View file

@ -1,7 +1,6 @@
from sqlalchemy import func
from backend.app.db.models.recommendations import (
PlanModel,
PlanRecommendations,
Recommendation,
ScenarioModel,
)
@ -26,11 +25,7 @@ def aggregate_portfolio_recommendations(
),
func.sum(Recommendation.energy_cost_savings).label("energy_cost_savings"),
)
.join(
PlanRecommendations,
PlanRecommendations.recommendation_id == Recommendation.id,
)
.join(PlanModel, PlanModel.id == PlanRecommendations.plan_id)
.join(PlanModel, PlanModel.id == Recommendation.plan_id)
.filter(
PlanModel.portfolio_id == portfolio_id,
PlanModel.scenario_id == scenario_id,

View file

@ -8,7 +8,6 @@ from collections import defaultdict
from backend.app.db.models.recommendations import (
Recommendation,
PlanModel,
PlanRecommendations,
RecommendationMaterials,
)
from backend.app.db.models.portfolio import (
@ -157,13 +156,9 @@ class DbMethods:
stmt = (
select(Recommendation, PlanModel.scenario_id, PlanModel.name)
.join(
PlanRecommendations,
Recommendation.id == PlanRecommendations.recommendation_id,
)
.join(PlanModel, PlanModel.id == PlanRecommendations.plan_id)
.join(PlanModel, PlanModel.id == Recommendation.plan_id)
.where(
PlanRecommendations.plan_id.in_(plan_ids),
Recommendation.plan_id.in_(plan_ids),
Recommendation.default.is_(True),
Recommendation.already_installed.is_(False),
)

View file

@ -171,13 +171,17 @@ def test_default_export_integration(db_session):
# 5) Insert recommendation
# ----------------------------------------
rec_to_plan = dict(
zip(plan_recs_df["recommendation_id"], plan_recs_df["plan_id"])
)
recs = [
Recommendation(
plan_id=rec_to_plan.get(row["id"]),
**{
col: row[col]
for col in Recommendation.__table__.columns.keys()
if col in row
}
if col in row and col != "plan_id"
},
)
for _, row in recommendations_df.iterrows()
]
@ -607,9 +611,11 @@ def test_solar_with_battery_example(db_session):
# -------------------------------------------------
recommendations_df.loc[0, "measure_type"] = "solar_pv"
rec_to_plan = dict(zip(plan_recs_df.recommendation_id, plan_recs_df.plan_id))
for row in recommendations_df.itertuples(index=False):
rec = Recommendation(
id=row.id,
plan_id=rec_to_plan.get(row.id),
property_id=row.property_id,
measure_type=row.measure_type,
estimated_cost=row.estimated_cost,