diff --git a/backend/app/db/functions/recommendations_functions.py b/backend/app/db/functions/recommendations_functions.py index 11001a7f..b544b43b 100644 --- a/backend/app/db/functions/recommendations_functions.py +++ b/backend/app/db/functions/recommendations_functions.py @@ -1,6 +1,6 @@ from sqlalchemy.orm import sessionmaker from backend.app.db.connection import db_engine -from backend.app.db.models.recommendations import Plan +from backend.app.db.models.recommendations import Plan, Recommendation, RecommendationMaterials def create_plan(plan): @@ -16,3 +16,56 @@ def create_plan(plan): session.commit() return new_plan.id + + +def create_recommendation(recommendation): + """ + This function will create a record for the recommendation in the database if it does not exist. + :param recommendation: dictionary of data representing a recommendation to be created + """ + + Session = sessionmaker(bind=db_engine) + with Session() as session: + new_recommendation = Recommendation(**recommendation) + session.add(new_recommendation) + session.commit() + + return new_recommendation.id + + +def create_recommendation_material(recommendation_id, material_id, depth): + """ + This function will create a record for the recommendation_material in the database if it does not exist. + :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 + """ + + Session = sessionmaker(bind=db_engine) + with Session() as session: + new_recommendation_material = RecommendationMaterials( + recommendation_id=recommendation_id, + material_id=material_id, + depth=depth + ) + session.add(new_recommendation_material) + session.commit() + + return new_recommendation_material.id + + +def create_plan_recommendations(plan_id, recommendation_ids): + """ + This function will create a record for the plan_recommendation in the database if it does not exist. + :param plan_id: ID of the plan + :param recommendation_ids: list of recommendation IDs + """ + + Session = sessionmaker(bind=db_engine) + with Session() as session: + for recommendation_id in recommendation_ids: + session.execute( + 'INSERT INTO plan_recommendations (plan_id, recommendation_id) VALUES (:plan_id, :recommendation_id)', + {'plan_id': plan_id, 'recommendation_id': recommendation_id} + ) + session.commit() diff --git a/backend/app/db/models/recommendations.py b/backend/app/db/models/recommendations.py index 435c07dd..66d1a063 100644 --- a/backend/app/db/models/recommendations.py +++ b/backend/app/db/models/recommendations.py @@ -1,7 +1,8 @@ from sqlalchemy import Column, BigInteger, String, Float, Boolean, TIMESTAMP, ForeignKey from sqlalchemy.orm import declarative_base, relationship from sqlalchemy.sql import func -from backend.app.db.models.portfolio import Portfolio +from backend.app.db.models.portfolio import Portfolio, PropertyModel +from backend.app.db.models.materials import Material Base = declarative_base() @@ -10,7 +11,7 @@ class Recommendation(Base): __tablename__ = 'recommendation' id = Column(BigInteger, primary_key=True, autoincrement=True) - property_id = Column(BigInteger, ForeignKey('property.id'), nullable=False) + property_id = Column(BigInteger, ForeignKey(PropertyModel.id), nullable=False) created_at = Column(TIMESTAMP, nullable=False, server_default=func.now()) type = Column(String, nullable=False) description = Column(String, nullable=False) @@ -33,12 +34,9 @@ class RecommendationMaterials(Base): id = Column(BigInteger, primary_key=True, autoincrement=True) recommendation_id = Column(BigInteger, ForeignKey('recommendation.id'), nullable=False) - material_id = Column(BigInteger, ForeignKey('material.id'), nullable=False) + material_id = Column(BigInteger, ForeignKey(Material.id), nullable=False) created_at = Column(TIMESTAMP, nullable=False, server_default=func.now()) - # recommendation = relationship('Recommendation') - # material = relationship('Material') - class Plan(Base): __tablename__ = 'plan' @@ -55,6 +53,3 @@ class PlanRecommendations(Base): id = Column(BigInteger, primary_key=True, autoincrement=True) plan_id = Column(BigInteger, ForeignKey('plan.id'), nullable=False) recommendation_id = Column(BigInteger, ForeignKey('recommendation.id'), nullable=False) - - plan = relationship('Plan') - recommendation = relationship('Recommendation') diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index e1ee5dea..cb21009e 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -19,7 +19,9 @@ from backend.app.db.functions.property_functions import ( create_property, create_property_targets, update_property_data, create_property_details_epc ) from backend.app.db.functions.materials_functions import get_materials -from backend.app.db.functions.recommendations_functions import create_plan +from backend.app.db.functions.recommendations_functions import ( + create_plan, create_recommendation, create_recommendation_material, create_plan_recommendations +) # TODO: This is placeholder until data is stored in DB from backend.app.plan.uvalue_estimates_walls import uvalue_estimates_walls @@ -250,6 +252,9 @@ async def trigger_plan(body: PlanTriggerRequest): update_property_data(property_id=p.id, portfolio_id=body.portfolio_id, property_data=property_data) # Upload recommendations + + # TODO: We start off by optimising the recommendations + recommendations_to_upload = recommendations[p.id] if not recommendations: continue @@ -262,7 +267,37 @@ async def trigger_plan(body: PlanTriggerRequest): ) # upload recommendations + uploaded_recommendation_ids = [] + for rec in recommendations_to_upload: + # TODO: implement costs (at least a placeholder) + estimated_cost = sum([x["cost"] if x["cost"] else 0 for x in rec["parts"]]) - # create the bridging between the plan and the recommendation + recommendation_id = create_recommendation( + { + "type": rec["type"], # TODO: Add this to output + "description": rec["description"], # TODO: Add this to output + "estimated_cost": estimated_cost, + "default": True, + "starting_u_value": rec.get("starting_u_value"), # TODO: Add this to output + "new_u_value": rec.get("new_u_value"), + "sap_points": rec["sap_points"] # TODO: Add this to output + # Remaining outputs yet to be handled + } + ) + uploaded_recommendation_ids.append(recommendation_id) + + # create the bridging between the recommendation and the materials + for part in rec["parts"]: + create_recommendation_material( + recommendation_id=recommendation_id, + material_id=part["id"], + depth=part["depths"][0] if part["depths"] else None, + ) + + # Finally, match the recommendation to the plan + create_plan_recommendations( + plan_id=new_plan_id, + recommendation_ids=uploaded_recommendation_ids + ) return Response(status_code=200)