from sqlalchemy import insert, delete from sqlalchemy.orm import Session from backend.app.db.models.recommendations import Plan, Recommendation, RecommendationMaterials, PlanRecommendations from backend.app.db.models.portfolio import PropertyModel, PropertyTargetsModel, PropertyDetailsMeter, \ PropertyDetailsEpcModel def create_plan(session: Session, plan): """ This function will create a record for the plan in the database if it does not exist. :param session: The database session :param plan: dictionary of data representing a plan to be created """ new_plan = Plan(**plan) session.add(new_plan) session.flush() return new_plan.id def create_recommendation(session: Session, recommendation): """ This function will create a record for the recommendation in the database if it does not exist. :param session: The database session :param recommendation: dictionary of data representing a recommendation to be created """ new_recommendation = Recommendation(**recommendation) session.add(new_recommendation) session.flush() return new_recommendation.id def create_recommendation_material(session: Session, recommendation_id, material_id, depth): """ This function will create a record for the recommendation_material in the database if it does not exist. :param session: The databse session :param recommendation_id: ID of the recommendation :param material_id: ID of the material :param depth: depth of the material, may be null if a material where depth is not applicable """ new_recommendation_material = RecommendationMaterials( recommendation_id=recommendation_id, material_id=material_id, depth=depth ) session.add(new_recommendation_material) session.flush() return new_recommendation_material.id def create_plan_recommendations(session: Session, plan_id, recommendation_ids): """ This function will create records for the plan_recommendation in the database. :param session: The database session :param plan_id: ID of the plan :param recommendation_ids: list of recommendation IDs """ # Prepare a list of dictionaries for bulk insert data = [{"plan_id": plan_id, "recommendation_id": rid} for rid in recommendation_ids] # Bulk insert using SQLAlchemy's core API session.execute(insert(PlanRecommendations).values(data)) def upload_recommendations(session: Session, recommendations_to_upload, property_id): # Prepare data for bulk insert for Recommendation recommendations_data = [ { "property_id": property_id, "type": rec["type"], "description": rec["description"], "estimated_cost": rec["total"], "default": rec["default"], "starting_u_value": rec.get("starting_u_value"), "new_u_value": rec.get("new_u_value"), "sap_points": rec["sap_points"], "heat_demand": rec["heat_demand"], "co2_equivalent_savings": rec["co2_equivalent_savings"], "total_work_hours": rec["labour_hours"], "energy_cost_savings": rec["energy_cost_savings"], "labour_days": rec["labour_days"] } for rec in recommendations_to_upload ] session.bulk_insert_mappings(Recommendation, recommendations_data) # To get the IDs of the newly inserted recommendations, we need to flush the session session.flush() # Map the uploaded_recommendation_ids with the original data for reference uploaded_recommendation_ids = [rec.id for rec in session.query(Recommendation).filter( Recommendation.property_id == property_id, Recommendation.description.in_([rec["description"] for rec in recommendations_to_upload]) )] # Prepare data for bulk insert for RecommendationMaterials # We can have multiple materials per recommendation. The aggregation of the materials will total the # recommendation figures recommendation_materials_data = [ { "recommendation_id": recommendation_id, "material_id": part["id"], "depth": int(part["depth"]) if part["depth"] else None, "quantity": part["quantity"], "quantity_unit": part["quantity_unit"], "estimated_cost": part["total"], } for rec, recommendation_id in zip(recommendations_to_upload, uploaded_recommendation_ids) for part in rec["parts"] ] session.bulk_insert_mappings(RecommendationMaterials, recommendation_materials_data) # flush the changes to get the newly created IDs session.flush() return uploaded_recommendation_ids def clear_portfolio(session: Session, portfolio_id: int): # Fetch all property IDs associated with the given portfolio property_ids = session.query(PropertyModel.id).filter(PropertyModel.portfolio_id == portfolio_id).all() property_ids = [p.id for p in property_ids] # Fetch all recommendation IDs associated with the properties recommendation_ids = session.query(Recommendation.id).filter(Recommendation.property_id.in_(property_ids)).all() recommendation_ids = [r.id for r in recommendation_ids] # Delete all entries from RecommendationMaterials for these recommendations session.execute( delete(RecommendationMaterials).where(RecommendationMaterials.recommendation_id.in_(recommendation_ids)) ) # Delete all entries from PlanRecommendations that reference plans in the portfolio session.execute(delete(PlanRecommendations).where(PlanRecommendations.plan_id.in_( session.query(Plan.id).filter(Plan.portfolio_id == portfolio_id).subquery().as_scalar() ))) # Delete all Plans associated with the portfolio session.execute(delete(Plan).where(Plan.portfolio_id == portfolio_id)) # Delete all Recommendations associated with the properties session.execute(delete(Recommendation).where(Recommendation.property_id.in_(property_ids))) # Now, delete the PropertyModels and related details # Delete PropertyTargetsModel, PropertyDetailsMeter, PropertyDetailsEpcModel, and PropertyModel session.execute(delete(PropertyTargetsModel).where(PropertyTargetsModel.portfolio_id == portfolio_id)) # session.execute(delete(PropertyDetailsMeter).where(PropertyDetailsMeter.uprn.in_(property_ids))) session.execute(delete(PropertyDetailsEpcModel).where(PropertyDetailsEpcModel.portfolio_id == portfolio_id)) session.execute(delete(PropertyModel).where(PropertyModel.portfolio_id == portfolio_id)) # Commit the changes session.commit()