from typing import Iterable from backend.app.db.models.energy_assessments import ( EnergyAssessment, EnergyAssessmentScenarios, EnergyAssessmentDocuments, DocumentTypeEnum ) from sqlalchemy.orm import Session from sqlalchemy.exc import IntegrityError from typing import Optional, List, Dict from sqlalchemy import desc from utils.logger import setup_logger logger = setup_logger() def bulk_insert_energy_assessments(session: Session, data_list: List[dict]) -> Dict[int, int]: """ This function inserts or updates multiple energy assessment records into the database and returns a mapping of uprn to energy_assessment_id. :param session: The SQLAlchemy session. :param data_list: A list of dictionaries containing energy assessment data. :return: A dictionary mapping each uprn to its corresponding energy_assessment_id. """ uprn_to_assessment_id = {} try: for data in data_list: uprn = data.get('uprn') inspection_date = data.get('inspection_date') # Check if a record with the same uprn and inspection_date exists existing_record = session.query(EnergyAssessment).filter_by( uprn=uprn, inspection_date=inspection_date ).first() if existing_record: # Update the existing record with new data for key, value in data.items(): setattr(existing_record, key, value) session.add(existing_record) # Map the uprn to the existing record's ID uprn_to_assessment_id[uprn] = existing_record.id else: # Insert a new record new_assessment = EnergyAssessment(**data) session.add(new_assessment) # Flush the session to get the newly created ID before commit session.flush() # Map the uprn to the new record's ID uprn_to_assessment_id[uprn] = new_assessment.id # Commit the transaction session.commit() logger.info("All records inserted or updated successfully.") except IntegrityError as e: # Rollback the session in case of error session.rollback() logger.info(f"Error occurred: {e}") return uprn_to_assessment_id def get_latest_assessments_for_uprns( session: Session, uprns: Iterable[int], ) -> dict[int, dict]: """ Fetch the latest energy assessment per UPRN in a single query. Returns a dict: uprn -> assessment_dict | empty_response """ uprns = [u for u in uprns if u] if not uprns: return {} # DISTINCT ON requires matching ORDER BY records = ( session.query(EnergyAssessment) .filter(EnergyAssessment.uprn.in_(uprns)) .order_by( EnergyAssessment.uprn, desc(EnergyAssessment.inspection_date), ) .distinct(EnergyAssessment.uprn) .all() ) result: dict[int, dict] = {} for record in records: result[record.uprn] = record.to_dict() # Fill missing uprns with empty response uprn_set = set(uprns) found_set = set(result.keys()) missing_uprns = uprn_set - found_set for uprn in missing_uprns: result[uprn] = EnergyAssessment.empty_response() return result def create_scenarios_for_documents(session: Session, document_list: List[dict], uprn_to_assessment_id: dict): """ Creates scenarios for documents by UPRN and links them to the energy assessments. :param session: The SQLAlchemy session. :param document_list: A list of dictionaries containing document data. :param uprn_to_assessment_id: A dictionary mapping UPRN to energy_assessment_id. """ try: for document in document_list: uprn = document.get('uprn') scenario_name = document.get('scenario_id') if scenario_name: # Get the associated energy_assessment_id for the UPRN energy_assessment_id = uprn_to_assessment_id.get(uprn) # Check if the scenario already exists existing_scenario = session.query(EnergyAssessmentScenarios).filter_by( scenario_name=scenario_name, energy_assessment_id=energy_assessment_id ).first() if not existing_scenario: # Create the scenario new_scenario = EnergyAssessmentScenarios( scenario_name=scenario_name, energy_assessment_id=energy_assessment_id ) session.add(new_scenario) session.flush() # Get the new scenario ID # Update document with new scenario ID document['scenario_id'] = new_scenario.id else: # If the scenario already exists, just use its ID document['scenario_id'] = existing_scenario.id # Commit the scenarios session.commit() logger.info("Scenarios created successfully.") except IntegrityError as e: session.rollback() logger.info(f"Error occurred: {e}") def create_documents(session: Session, document_list: List[dict]): """ Inserts documents into the energy_assessment_documents table, linking them to scenarios and assessments. :param session: The SQLAlchemy session. :param document_list: A list of dictionaries containing document data. """ try: for document in document_list: # Ensure the document_type is cast to Enum new_document = EnergyAssessmentDocuments( uprn=document['uprn'], document_type=DocumentTypeEnum(document['document_type']).value, document_location=document['document_location'], energy_assessment_id=document['energy_assessment_id'], scenario_id=document.get('scenario_id') # Might be None if no scenario ) session.add(new_document) # Commit all document insertions session.commit() logger.info("Documents created successfully.") except IntegrityError as e: session.rollback() logger.info(f"Error occurred: {e}")