diff --git a/.github/workflows/_deploy_lambda.yml b/.github/workflows/_deploy_lambda.yml index 707c9e00..3a407c5a 100644 --- a/.github/workflows/_deploy_lambda.yml +++ b/.github/workflows/_deploy_lambda.yml @@ -80,6 +80,8 @@ on: required: false TF_VAR_pashub_password: required: false + TF_VAR_hubspot_api_key: + required: false jobs: deploy: runs-on: ubuntu-latest @@ -146,6 +148,7 @@ jobs: TF_VAR_social_housing_wave_3_sharepoint_id: ${{ secrets.TF_VAR_social_housing_wave_3_sharepoint_id }} TF_VAR_pashub_email: ${{ secrets.TF_VAR_pashub_email }} TF_VAR_pashub_password: ${{ secrets.TF_VAR_pashub_password }} + TF_VAR_hubspot_api_key: ${{ secrets.TF_VAR_hubspot_api_key }} run: | ECR_REPO_URL_VAR="" if [[ -n "${{ inputs.ecr_repo }}" ]]; then @@ -191,6 +194,7 @@ jobs: TF_VAR_social_housing_wave_3_sharepoint_id: ${{ secrets.TF_VAR_social_housing_wave_3_sharepoint_id }} TF_VAR_pashub_email: ${{ secrets.TF_VAR_pashub_email }} TF_VAR_pashub_password: ${{ secrets.TF_VAR_pashub_password }} + TF_VAR_hubspot_api_key: ${{ secrets.TF_VAR_hubspot_api_key }} run: | EXTRA_VARS="" if [[ -n "${{ inputs.ecr_repo }}" ]]; then diff --git a/.github/workflows/deploy_terraform.yml b/.github/workflows/deploy_terraform.yml index cbcd88c4..22f16fee 100644 --- a/.github/workflows/deploy_terraform.yml +++ b/.github/workflows/deploy_terraform.yml @@ -505,7 +505,7 @@ jobs: # Deploy Hubspot ETL Lambda # ============================================================ hubspot_etl_lambda: - needs: [hubspot_etl_image, determine_stage] + needs: [hubspot_etl_image, determine_stage, pashub_to_ara_lambda] uses: ./.github/workflows/_deploy_lambda.yml with: lambda_name: hubspot-etl-to-ara @@ -518,6 +518,7 @@ jobs: TF_VAR_db_host: ${{ secrets.DEV_DB_HOST }} TF_VAR_db_name: ${{ secrets.DEV_DB_NAME }} TF_VAR_db_port: ${{ secrets.DEV_DB_PORT }} + TF_VAR_hubspot_api_key: ${{ secrets.HUBSPOT_API_KEY }} AWS_ACCESS_KEY_ID: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.DEV_AWS_REGION }} diff --git a/MEMORY.md b/MEMORY.md new file mode 100644 index 00000000..5229429e --- /dev/null +++ b/MEMORY.md @@ -0,0 +1,22 @@ +# Project Memory + +## HubSpot New Field Addition Process + +When adding a new field from HubSpot to the deal table, touch these 4 locations in order: + +1. **DB model** — `backend/app/db/models/organisation.py` + Add `field_name: Optional[str] = Field(default=None)` to `HubspotDealData`, near related fields. + +2. **HubSpot API fetch** — `etl/hubspot/hubspotClient.py`, `from_deal_id_get_info()` + Add the HubSpot internal property name string to the `properties=[...]` list. + +3. **Soft check** — `etl/hubspot/hubspotDataTodB.py`, `update_deal_with_checks()` + Add a `soft_assert(deal_in_db.field_name == hs_deal.get("hs_property_name"), "field_name mismatch")` entry to the `checks` list. + +4. **Upsert** — `etl/hubspot/hubspotDataTodB.py`, `upsert_deal()` + - **Update branch**: add `"field_name": deal_data.get("hs_property_name")` to the attr dict + - **Insert branch**: add `field_name=deal_data.get("hs_property_name")` to the `HubspotDealData(...)` constructor + +**Notes:** +- Date fields: wrap with `self._parse_hs_date(deal_data.get(...))` in steps 3 & 4. +- If HubSpot property name differs from DB column name (e.g. `coordination_status__stage_1_` → `coordination_status`), use the HS name in `.get()` and the DB name as the key/attr. diff --git a/asset_list/app.py b/asset_list/app.py index 5794eaf3..b0030667 100644 --- a/asset_list/app.py +++ b/asset_list/app.py @@ -74,23 +74,23 @@ def app(): """ data_folder = "/workspaces/model/asset_list" - data_filename = "Calico ARA Upload Review.xlsx" - sheet_name = "Sheet1" - postcode_column = "Postcode" - address1_column = "Units" + data_filename = "Waverley UPRN Match.xlsx" + sheet_name = "in" + postcode_column = "postcode_clean" + address1_column = "domna_found_address" address1_method = None - fulladdress_column = "Units" - address_cols_to_concat = ["Units"] + fulladdress_column = "domna_found_address" + address_cols_to_concat = [] missing_postcodes_method = None landlord_year_built = None - landlord_os_uprn = None - landlord_property_type = None # Good to include if landlord gave + landlord_os_uprn = "domna_found_uprn" + landlord_property_type = "Property Type 1" # Good to include if landlord gave landlord_built_form = None # Good to include if landlord gave landlord_wall_construction = None landlord_roof_construction = None landlord_heating_system = None landlord_existing_pv = None - landlord_property_id = "llid" + landlord_property_id = "WBC Ref" landlord_sap = None outcomes_filename = None outcomes_sheetname = None diff --git a/backend/Property.py b/backend/Property.py index 5e994cae..1d79b16d 100644 --- a/backend/Property.py +++ b/backend/Property.py @@ -890,6 +890,7 @@ class Property: "lodged_co2_emissions": float(self.epc_record.original_epc["co2-emissions-current"]), "lodged_heat_demand": float(self.epc_record.original_epc["energy-consumption-current"]), "has_been_remodelled": self.epc_record.has_been_remodelled, + "environment_impact_current": self.epc_record.environment_impact_current } return property_details_epc diff --git a/backend/SearchEpc.py b/backend/SearchEpc.py index a633176e..7d3a8b3d 100644 --- a/backend/SearchEpc.py +++ b/backend/SearchEpc.py @@ -2,6 +2,7 @@ import os import time import re +from typing import Optional from urllib.parse import urlencode import usaddress import pandas as pd @@ -162,7 +163,8 @@ class SearchEpc: property_type=None, fast=False, heating_system: [str, None] = None, - associated_uprns: [List[int] | None] = None + associated_uprns: [List[int] | None] = None, + lmk_key: Optional[str] = None, ): """ Address lines 1 and postcode are mandatory fields. The other address lines are optional @@ -225,6 +227,7 @@ class SearchEpc: # Be default, this is set to false. This flag indicates whether we should take the existing EPC, but use # the estimated EPC to clean missings self.clean_missing_on_expired = False + self.lmk_key = lmk_key def set_strict_property_type_search(self): """ @@ -289,18 +292,22 @@ class SearchEpc: else: return None - def _get_epc(self, params, size): + def _get_epc(self, params, size, lmk_key=None): """ To be called by get_epc() - not for external usage """ - url = os.path.join(self.client.domestic.host, "search") - if size: - url += "?" + urlencode({k: v for k, v in {"size": size}.items() if v}) + if lmk_key is not None: + url = os.path.join(self.client.domestic.host, f"certificate/{lmk_key}") + else: + url = os.path.join(self.client.domestic.host, "search") + if size: + url += "?" + urlencode({k: v for k, v in {"size": size}.items() if v}) for retry in range(self.max_retries): try: response = self.client.domestic.call(method="get", url=url, params=params) + if response: self.data = response return { @@ -341,6 +348,15 @@ class SearchEpc: self.data = output["response"] return output["msg"] + if self.lmk_key: + data = {"rows": []} + api_response = self._get_epc(params={}, size=size, lmk_key=self.lmk_key) + data["rows"].extend(api_response["response"]["rows"]) + api_response["msg"] = self.SUCCESS + self.data = data + self.data["rows"][0]["uprn"] = str(self.uprn) + return api_response["msg"] + if not self.uprn and not self.address1 and not self.postcode: raise ValueError("No search parameters provided") diff --git a/backend/address2UPRN/README.md b/backend/address2UPRN/README.md index 646fec01..6b0fc753 100644 --- a/backend/address2UPRN/README.md +++ b/backend/address2UPRN/README.md @@ -32,9 +32,9 @@ Step 3) Alright, now lets make the input for postcode-splitter sqs to get the ba postcode-splitter-sqs => https://eu-west-2.console.aws.amazon.com/sqs/v3/home?region=eu-west-2#/queues/https%3A%2F%2Fsqs.eu-west-2.amazonaws.com%2F337213553626%2Fpostcode-splitter-queue-dev { - "task_id": "ea615ac3-ac28-46c4-8bff-2431c5b9c13d", - "sub_task_id": "85a23b67-8f18-4299-9bf0-69bfb87adbc7", - "s3_uri": "s3://retrofit-data-dev/ara_raw_inputs/eon/eon(Sheet1).csv" + "sub_task_id": "c5afbd49-f0cd-4930-82bf-bafc5243a34a", + "task_id": "67a4b3f0-cc7a-4e8a-b314-deb783e0eedb", + "s3_uri": "s3://retrofit-data-dev/ara_raw_inputs/eon/pickering ferens/Pickering Ferens - SHDF W3 Post Bid Stage MDS - Template - Vr4(in).csv" } Each batch of csv should be saved in retrofit-data-dev/ara_postcode_splitter_batches///.csv @@ -52,9 +52,9 @@ I uploaded the missing uprn here: s3://retrofit-data-dev/ara_raw_inputs/calico/m ordnance_survey sqs is => https://eu-west-2.console.aws.amazon.com/sqs/v3/home?region=eu-west-2#/queues/https%3A%2F%2Fsqs.eu-west-2.amazonaws.com%2F337213553626%2FordnanceSurvey-queue-dev { - "s3_uri": "s3://retrofit-data-dev/ara_raw_inputs/calico/missinguprn.csv", - "task_id": "a7b70a02-4df4-45b5-a50b-196e095910bb", - "sub_task_id": "567cf73b-1210-4909-9ecc-36ae7e23420e" + "s3_uri": "s3://retrofit-data-dev/ara_raw_inputs/eon/beyond_housing/Book(Sheet1).csv", + "task_id": "ccdec0d1-ebf3-484f-b2ae-397200dd25da", + "sub_task_id": "569d41f6-45cd-4e64-a586-eb8c2097375d" } diff --git a/backend/address2UPRN/main.py b/backend/address2UPRN/main.py index d0ba36e6..79c0de69 100644 --- a/backend/address2UPRN/main.py +++ b/backend/address2UPRN/main.py @@ -351,9 +351,9 @@ def handler(event, context, local=False): { "body": json.dumps( { - "task_id": "e31f2f21-175b-4a91-a3ec-a6baa325e917", - "sub_task_id": "6a427b6e-1ece-4983-b1e5-9bffccc53d1d", - "s3_uri": "s3://retrofit-data-dev/ara_postcode_splitter_batches/e31f2f21-175b-4a91-a3ec-a6baa325e917/8673913b-1a88-42d7-8578-0449123d94b0/2026-02-18T11:47:00.822579_f95467f5.csv", + "sub_task_id": "d7363c83-2ef7-4474-b30f-980fd587350c", + "task_id": "a042af13-8b57-4709-ad22-ecac1ccca4bd", + "s3_uri": "s3://retrofit-data-dev/ara_raw_inputs/essex/Copy of EPC register Essex(August 2025)(in) (2).csv", } ) } @@ -462,6 +462,15 @@ def handler(event, context, local=False): # Validate postcode before processing if not AddressMatch.is_valid_postcode(postcode): logger.warning(f"Postcode {postcode} is invalid, skipping") + for row in postcode_rows: + results_data.append( + { + **row, + "address2uprn_uprn": "invalid postcode", + "address2uprn_address": "invalid postcode", + "address2uprn_lexiscore": "invalid postcode", + } + ) continue # Fetch EPC data once per postcode diff --git a/backend/addresses/Address.py b/backend/addresses/Address.py index f348b141..0d957679 100644 --- a/backend/addresses/Address.py +++ b/backend/addresses/Address.py @@ -48,6 +48,10 @@ class Address: # domna_full_address: Optional[str] # domna_address_1: Optional[str] + # Identifiers to handle the case where we have erroneous matches based on UPRN due to probelmatic EPC data + lmk_key: Optional[str] + epc_certificate_number: Optional[str] + @property def request_data(self) -> dict[str, Optional[str]]: """ diff --git a/backend/addresses/Addresses.py b/backend/addresses/Addresses.py index c1624522..4c046554 100644 --- a/backend/addresses/Addresses.py +++ b/backend/addresses/Addresses.py @@ -134,6 +134,19 @@ class Addresses: postcode = str(row.get("postcode", "")).strip().upper() + lmk_key = row.get("lmk_key", None) + # Handle NAN + if pd.isnull(lmk_key): + lmk_key = None + + epc_certificate_number = row.get("certificate_number", None) + if pd.isnull(epc_certificate_number): + epc_certificate_number = None + + landlord_heating_system = row.get("epc_heating_type", None) + if pd.isnull(landlord_heating_system): + landlord_heating_system = None + return Address( uprn=uprn, landlord_property_id=str(row["landlord_property_id"]) if row.get("landlord_property_id") else None, @@ -153,7 +166,7 @@ class Addresses: landlord_roof_construction=None, landlord_floor_construction=None, landlord_windows_type=None, - landlord_heating_system=row.get("epc_heating_type"), + landlord_heating_system=landlord_heating_system, landlord_fuel_type=None, landlord_heating_controls=None, landlord_hot_water_system=None, @@ -168,4 +181,8 @@ class Addresses: landlord_has_sloping_ceiling=None, landlord_multi_glaze_proportion=None, landlord_construction_age_band=None, + + # EPC identifiers which are helpful if UPRN is problematic + lmk_key=lmk_key, + epc_certificate_number=epc_certificate_number ) diff --git a/backend/app/config.py b/backend/app/config.py index 80a2d46a..9532ddd6 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -38,6 +38,7 @@ class Settings(BaseSettings): PLAN_TRIGGER_BUCKET: str = "changeme" ENGINE_SQS_URL: str = "changeme" CATEGORISATION_SQS_URL: str = "changeme" + PASHUB_TO_ARA_SQS_URL: str = "changeme" # Third parties EPC_AUTH_TOKEN: str = "changeme" diff --git a/backend/app/db/functions/epc_functions.py b/backend/app/db/functions/epc_functions.py index 1dcb92fe..f43c270b 100644 --- a/backend/app/db/functions/epc_functions.py +++ b/backend/app/db/functions/epc_functions.py @@ -11,7 +11,7 @@ class EpcStoreService: Service layer for EPC data lookup and persistence. """ - FRESHNESS_DAYS = 180 # Upgraded to 180 days + FRESHNESS_DAYS = 14 # Upgraded to 14 days # status labels FRESH = "fresh" @@ -22,7 +22,7 @@ class EpcStoreService: def get_epc_for_uprn(cls, session: Session, uprn: int): """ Query EPC data for a given UPRN and return a dict describing: - - epc_api: only if within last 30 days + - epc_api: only if within last 21 days - epc_page: only if epc_api exists - status: 'fresh', 'expired', or 'missing' """ diff --git a/backend/app/db/functions/uploaded_files_functions.py b/backend/app/db/functions/uploaded_files_functions.py new file mode 100644 index 00000000..3708813a --- /dev/null +++ b/backend/app/db/functions/uploaded_files_functions.py @@ -0,0 +1,25 @@ +from typing import Optional + +from sqlalchemy import select + +from backend.app.db.connection import db_read_session +from backend.app.db.models.uploaded_file import ( + FileSourceEnum, + FileTypeEnum, + UploadedFile, +) + + +def get_uploaded_file_by_listing_type_and_source( + hubspot_listing_id: int, + file_type: FileTypeEnum, + file_source: FileSourceEnum, +) -> Optional[UploadedFile]: + with db_read_session() as session: + statement = select(UploadedFile).where( + UploadedFile.hubspot_listing_id == hubspot_listing_id, + UploadedFile.file_type == file_type, + UploadedFile.file_source == file_source, + ) + + return session.exec(statement).one_or_none() diff --git a/backend/app/db/models/hubspot_deal_data.py b/backend/app/db/models/hubspot_deal_data.py new file mode 100644 index 00000000..758f688d --- /dev/null +++ b/backend/app/db/models/hubspot_deal_data.py @@ -0,0 +1,79 @@ +import uuid +from sqlmodel import SQLModel, Field, Column, text +from datetime import datetime +from typing import Optional +from sqlalchemy import DateTime +from sqlalchemy.sql import func + + +class HubspotDealData(SQLModel, table=True): + __tablename__ = "hubspot_deal_data" + + id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) + + # HubSpot Deal identifiers + deal_id: str = Field(index=True, nullable=False) + dealname: Optional[str] = Field(default=None) + dealstage: Optional[str] = Field(default=None) + company_id: Optional[str] = Field(default=None) + project_code: Optional[str] = Field(default=None) + + # HubSpot custom properties + landlord_property_id: Optional[str] = Field(default=None) + uprn: Optional[str] = Field(default=None) + outcome: Optional[str] = Field(default=None) + outcome_notes: Optional[str] = Field(default=None) + + major_condition_issue_description: Optional[str] = Field(default=None) + major_condition_issue_photos: Optional[str] = Field(default=None) + major_condition_issue_evidence_s3_url: Optional[str] = Field(default=None) + + coordination_status: Optional[str] = Field(default=None) + coordination_comments: Optional[str] = Field(default=None) + design_status: Optional[str] = Field(default=None) + + listing_id: Optional[str] = Field(default=None) + pashub_link: Optional[str] = Field(default=None) + sharepoint_link: Optional[str] = Field(default=None) + dampmould_growth: Optional[str] = Field(default=None) + damp_mould_and_repairs_comments: Optional[str] = Field(default=None) + pre_sap: Optional[str] = Field(default=None) + coordinator: Optional[str] = Field(default=None) + mtp_completion_date: Optional[datetime] = Field(default=None) + mtp_re_model_completion_date: Optional[datetime] = Field(default=None) + ioe_v3_completion_date: Optional[datetime] = Field(default=None) + proposed_measures: Optional[str] = Field(default=None) + approved_package: Optional[str] = Field(default=None) + designer: Optional[str] = Field(default=None) + design_completion_date: Optional[datetime] = Field(default=None) + actual_measures_installed: Optional[str] = Field(default=None) + installer: Optional[str] = Field(default=None) + installer_handover: Optional[str] = Field(default=None) + lodgement_status: Optional[str] = Field(default=None) + measures_lodgement_date: Optional[datetime] = Field(default=None) + lodgement_date: Optional[datetime] = Field(default=None) + expected_commencement_date: Optional[datetime] = Field(default=None) + surveyor: Optional[str] = Field(default=None) + confirmed_survey_date: Optional[datetime] = Field(default=None) + confirmed_survey_time: Optional[str] = Field(default=None) + surveyed_date: Optional[datetime] = Field(default=None) + design_type: Optional[str] = Field(default=None) + + created_at: Optional[datetime] = Field( + sa_column=Column( + DateTime(timezone=True), + server_default=text("(NOW() AT TIME ZONE 'utc')"), + nullable=False, + ), + default=func.now(), + ) # Nullable in db but optional here as value is set on db save for new record + + updated_at: Optional[datetime] = Field( + sa_column=Column( + DateTime(timezone=True), + server_default=text("(NOW() AT TIME ZONE 'utc')"), + onupdate=func.now(), + nullable=False, + ), + default=func.now(), + ) # Nullable in db but optional here as value is set on db save for new record diff --git a/backend/app/db/models/organisation.py b/backend/app/db/models/organisation.py index e8649cdd..8afc5d63 100644 --- a/backend/app/db/models/organisation.py +++ b/backend/app/db/models/organisation.py @@ -1,9 +1,7 @@ -from sqlmodel import SQLModel, Field, Column, text +import uuid +from sqlmodel import SQLModel, Field from datetime import datetime, timezone from typing import Optional -from sqlalchemy import DateTime -from sqlalchemy.sql import func -import uuid class Organisation(SQLModel, table=True): @@ -13,46 +11,3 @@ class Organisation(SQLModel, table=True): updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) hubspot_company_id: Optional[str] = None name: Optional[str] = None - - -class HubspotDealData(SQLModel, table=True): - __tablename__ = "hubspot_deal_data" - - id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) - - # HubSpot Deal identifiers - deal_id: str = Field(index=True, nullable=False) - dealname: Optional[str] = Field(default=None) - dealstage: Optional[str] = Field(default=None) - company_id: Optional[str] = Field(default=None) - project_code: Optional[str] = Field(default=None) - - # HubSpot custom properties - landlord_property_id: Optional[str] = Field(default=None) - uprn: Optional[str] = Field(default=None) - outcome: Optional[str] = Field(default=None) - outcome_notes: Optional[str] = Field(default=None) - - major_condition_issue_description: Optional[str] = Field(default=None) - major_condition_issue_photos: Optional[str] = Field(default=None) - major_condition_issue_evidence_s3_url: Optional[str] = Field(default=None) - - coordination_status: Optional[str] = Field(default=None) - design_status: Optional[str] = Field(default=None) - - created_at: datetime = Field( - sa_column=Column( - DateTime(timezone=True), - server_default=text("(NOW() AT TIME ZONE 'utc')"), - nullable=False, - ) - ) - - updated_at: datetime = Field( - sa_column=Column( - DateTime(timezone=True), - server_default=text("(NOW() AT TIME ZONE 'utc')"), - onupdate=func.now(), - nullable=False, - ) - ) diff --git a/backend/app/db/models/portfolio.py b/backend/app/db/models/portfolio.py index c511b6c9..a4f9a675 100644 --- a/backend/app/db/models/portfolio.py +++ b/backend/app/db/models/portfolio.py @@ -263,6 +263,8 @@ class PropertyDetailsEpcModel(Base): lodged_heat_demand = Column(Float) has_been_remodelled = Column(Boolean, default=False) + environment_impact_current = Column(Float) + class PropertyDetailsSpatial(Base): __tablename__ = "property_details_spatial" diff --git a/backend/app/db/models/uploaded_file.py b/backend/app/db/models/uploaded_file.py index 726ed0a3..71763790 100644 --- a/backend/app/db/models/uploaded_file.py +++ b/backend/app/db/models/uploaded_file.py @@ -14,12 +14,15 @@ class FileTypeEnum(enum.Enum): PAR_PHOTO_PACK = "par_photo_pack" PAS_2023_PROPERTY = "pas_2023_property" PAS_2023_OCCUPANCY = "pas_2023_occupancy" + ECMK_SITE_NOTE = "ecmk_site_note" + ECMK_RD_SAP_SITE_NOTE = "ecmk_rd_sap_site_note" class FileSourceEnum(enum.Enum): PAS_HUB = "pas hub" SHAREPOINT = "sharepoint" HUBSPOT = "hubspot" + ECMK = "ecmk" class UploadedFile(Base): @@ -36,9 +39,21 @@ class UploadedFile(Base): hubspot_listing_id = Column(BigInteger, nullable=True) file_type = Column( - SqlEnum(FileTypeEnum, name="file_type", create_type=False), nullable=True + SqlEnum( + FileTypeEnum, + name="file_type", + create_type=False, + values_callable=lambda enum_cls: [e.value for e in enum_cls], + ), + nullable=True, ) file_source = Column( - SqlEnum(FileSourceEnum, name="file_source", create_type=False), nullable=True + SqlEnum( + FileSourceEnum, + name="file_source", + create_type=False, + values_callable=lambda enum_cls: [e.value for e in enum_cls], + ), + nullable=True, ) diff --git a/backend/ecmk_fetcher/address_list.py b/backend/ecmk_fetcher/address_list.py new file mode 100644 index 00000000..ba636a70 --- /dev/null +++ b/backend/ecmk_fetcher/address_list.py @@ -0,0 +1,74 @@ +import re +from dataclasses import dataclass +from typing import Any, Dict, Optional +from openpyxl import Workbook, load_workbook +from openpyxl.worksheet.worksheet import Worksheet + + +@dataclass +class PropertyRow: + row_index: int + address: str + listing_id: str + + +def extract_addresses_from_spreadsheet( + filepath: str, +) -> Dict[str, PropertyRow]: + wb: Workbook = load_workbook(filepath, data_only=True) + ws: Worksheet = wb["Southern RA-Lite Programme 3103"] + + header_row: int = 1 + id_col: Optional[int] = None + deal_name_col: Optional[int] = None + listing_id_col: Optional[int] = None + + # find columns + for col in range(1, ws.max_column + 1): + raw_value: Any = ws.cell(row=header_row, column=col).value + value: str = str(raw_value).strip().lower() if raw_value else "" + + if value == "id": + id_col = col + elif value == "deal name": + deal_name_col = col + elif value == "associated listing ids": + listing_id_col = col + + if id_col is None or deal_name_col is None or listing_id_col is None: + raise Exception("Missing required columns") + + properties: Dict[str, PropertyRow] = {} + + for row in range(2, ws.max_row + 1): + id_val: Any = ws.cell(row=row, column=id_col).value + deal_name: Any = ws.cell(row=row, column=deal_name_col).value + listing_id: Any = ws.cell(row=row, column=listing_id_col).value + + if not id_val or not deal_name or not listing_id: + continue + + property_id: str = str(id_val).strip() + + properties[property_id] = PropertyRow( + row_index=row, + address=extract_succinct_address(str(deal_name)), + listing_id=listing_id, + ) + + return properties + + +def extract_succinct_address(deal_name: str) -> str: + left_part = deal_name.split("|")[0].strip() + + postcode_match: Optional[re.Match[str]] = re.search( + r"\b([A-Z]{1,2}\d[A-Z\d]?\s*\d[A-Z]{2})\b", + left_part, + re.IGNORECASE, + ) + + postcode = postcode_match.group(1).upper() if postcode_match else None + first_part = left_part.split(",")[0].strip() + + return f"{first_part} {postcode}" if postcode else first_part diff --git a/backend/ecmk_fetcher/browser.py b/backend/ecmk_fetcher/browser.py new file mode 100644 index 00000000..de349b92 --- /dev/null +++ b/backend/ecmk_fetcher/browser.py @@ -0,0 +1,99 @@ +import os +from typing import Optional +from playwright.sync_api import Page, Locator, Response +from playwright.sync_api import TimeoutError as PlaywrightTimeoutError + +from backend.ecmk_fetcher.reports import build_report_selector +from utils.logger import setup_logger + +# from .reports import build_report_selector + +logger = setup_logger() + + +def attach_debug_listeners(page: Page) -> None: + def handle_response(response: Response) -> None: + if "download" in response.url or "report" in response.url: + logger.info(f"[RESPONSE] {response.status} {response.url}") + + page.on("response", handle_response) + + +def login(page: Page, username: str, password: str) -> None: + page.goto("https://assessorhub.net/", timeout=30000) + + page.locator("#Username").fill(username) + page.locator("#Password").fill(password) + + with page.expect_navigation(): + page.click("button[type='submit']") + + if "login" in page.url.lower(): + raise Exception("Login failed") + + logger.info("Login successful") + + +def go_to_assessments(page: Page) -> None: + page.goto("https://assessorhub.net/Companies/Assessments") + page.wait_for_selector("#assessmentDatatable tbody tr") + + +def go_to_assessment_details(page: Page, row: Locator) -> None: + row.locator("a").click() + page.wait_for_load_state("networkidle") + page.wait_for_selector("a.download-report-btn") + + +def get_first_row_signature(page: Page) -> str: + return page.locator("#assessmentDatatable tbody tr").first.inner_text() + + +def go_to_next_page(page: Page) -> bool: + logger.info("Going to next page") + before = get_first_row_signature(page) + + page.locator("#assessmentDatatable_next a").click() + page.wait_for_timeout(2000) + + after = get_first_row_signature(page) + + return before != after + + +def download_report_by_selector(page: Page, selector: str) -> Optional[str]: + try: + element = page.locator(selector) + element.wait_for(state="visible", timeout=10000) + + if not element.is_enabled(): + return None + + element.scroll_into_view_if_needed() + + with page.expect_download(timeout=15000) as download_info: + element.click() + + download = download_info.value + filename = download.suggested_filename + + save_path = os.path.join(os.getcwd(), filename) + download.save_as(save_path) + + return save_path + + except PlaywrightTimeoutError: + logger.error(f"Download failed for {selector}") + return None + + +def download_with_retry(page: Page, report_type: int) -> Optional[str]: + selector: str = build_report_selector(report_type) + + for _ in range(3): + file_path = download_report_by_selector(page, selector) + if file_path: + return file_path + page.wait_for_timeout(1500) + + return None diff --git a/backend/ecmk_fetcher/handler/Dockerfile b/backend/ecmk_fetcher/handler/Dockerfile new file mode 100644 index 00000000..fa2126fd --- /dev/null +++ b/backend/ecmk_fetcher/handler/Dockerfile @@ -0,0 +1,26 @@ +FROM mcr.microsoft.com/playwright/python:v1.58.0-jammy + +# Install AWS Lambda RIE +ADD https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/local/bin/aws-lambda-rie +RUN chmod +x /usr/local/bin/aws-lambda-rie + +# Set working directory (Lambda task root) +WORKDIR /var/task + +COPY backend/ecmk_fetcher/handler/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY utils/ utils/ +COPY backend/ backend/ +COPY datatypes/ datatypes/ + +# Local lambda entrypoint +# ENTRYPOINT ["/usr/local/bin/aws-lambda-rie", "python", "-m", "awslambdaric"] + +#AWS lambda entrypoint +ENTRYPOINT ["python", "-m", "awslambdaric"] + +# ----------------------------- +# Lambda handler +# ----------------------------- +CMD ["backend.ecmk_fetcher.handler.handler.handler"] \ No newline at end of file diff --git a/backend/ecmk_fetcher/handler/handler.py b/backend/ecmk_fetcher/handler/handler.py new file mode 100644 index 00000000..b777cc9f --- /dev/null +++ b/backend/ecmk_fetcher/handler/handler.py @@ -0,0 +1,16 @@ +from typing import Any, Mapping + +from backend.ecmk_fetcher.processor import run_job +from utils.logger import setup_logger + +logger = setup_logger() + + +def handler(event: Mapping[str, Any], context: Any) -> None: + logger.info("Entered handler") + run_job() + + +if __name__ == "__main__": + event = {"Records": [{"body": "{}"}]} + handler(event, None) diff --git a/backend/ecmk_fetcher/handler/requirements.txt b/backend/ecmk_fetcher/handler/requirements.txt new file mode 100644 index 00000000..2692484e --- /dev/null +++ b/backend/ecmk_fetcher/handler/requirements.txt @@ -0,0 +1,12 @@ +awslambdaric +playwright==1.58.0 +msal +openpyxl +sqlalchemy==2.0.36 +sqlmodel +pytz==2024.2 +psycopg2-binary==2.9.10 +pydantic-settings==2.6.0 +boto3==1.35.44 +pandas==2.2.2 +numpy<2.0 \ No newline at end of file diff --git a/backend/ecmk_fetcher/hubspot-crm-exports-southern-ra-lite-programme-3103-2026-03-31-2.xlsx b/backend/ecmk_fetcher/hubspot-crm-exports-southern-ra-lite-programme-3103-2026-03-31-2.xlsx new file mode 100644 index 00000000..ebb8d569 Binary files /dev/null and b/backend/ecmk_fetcher/hubspot-crm-exports-southern-ra-lite-programme-3103-2026-03-31-2.xlsx differ diff --git a/backend/ecmk_fetcher/local_handler/docker-compose.yml b/backend/ecmk_fetcher/local_handler/docker-compose.yml new file mode 100644 index 00000000..fd642499 --- /dev/null +++ b/backend/ecmk_fetcher/local_handler/docker-compose.yml @@ -0,0 +1,11 @@ +version: "3.9" + +services: + ecmk-fetcher-lambda: + build: + context: ../../../ + dockerfile: backend/ecmk_fetcher/handler/Dockerfile + ports: + - "9000:8080" + env_file: + - ../../../.env \ No newline at end of file diff --git a/backend/ecmk_fetcher/local_handler/invoke_local_lambda.py b/backend/ecmk_fetcher/local_handler/invoke_local_lambda.py new file mode 100644 index 00000000..ba76301e --- /dev/null +++ b/backend/ecmk_fetcher/local_handler/invoke_local_lambda.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +import json +import requests + +HOST = "localhost" +PORT = "9000" + +LAMBDA_URL = f"http://{HOST}:{PORT}/2015-03-31/functions/function/invocations" + +payload = { + "Records": [ + { + "body": json.dumps( + { + "test": 123456, + } + ) + } + ] +} + +response = requests.post(LAMBDA_URL, json=payload) + +print("Status code:", response.status_code) +print("Response:") +print(response.text) diff --git a/backend/ecmk_fetcher/processor.py b/backend/ecmk_fetcher/processor.py new file mode 100644 index 00000000..2f122080 --- /dev/null +++ b/backend/ecmk_fetcher/processor.py @@ -0,0 +1,181 @@ +import os +from typing import Dict +from playwright.sync_api import ( + sync_playwright, + Locator, + Page, + Browser, + BrowserContext, +) + +from backend.app.db.functions.uploaded_files_functions import ( + get_uploaded_file_by_listing_type_and_source, +) +from backend.app.db.models.uploaded_file import FileSourceEnum, FileTypeEnum +from backend.ecmk_fetcher.address_list import ( + PropertyRow, + extract_addresses_from_spreadsheet, +) +from backend.ecmk_fetcher.browser import ( + attach_debug_listeners, + download_with_retry, + go_to_assessment_details, + go_to_assessments, + go_to_next_page, + login, +) +from backend.ecmk_fetcher.reports import ( + REPORT_TYPES, + build_property_id, + map_report_type_to_db_file_type, +) +from backend.ecmk_fetcher.upload import ( + upload_file_to_s3_and_update_db, + upload_file_to_sharepoint, +) +from utils.logger import setup_logger +from utils.sharepoint.domna_sharepoint_client import DomnaSharepointClient +from utils.sharepoint.domna_sites import DomnaSites + +logger = setup_logger() + + +def run_job() -> None: + + username: str = "" # TODO: get from github secrets + password: str = "" + + property_list_file: str = ( + "hubspot-crm-exports-southern-ra-lite-programme-3103-2026-03-31-2.xlsx" + ) + + BASE_DIR: str = os.path.dirname(__file__) + filepath: str = os.path.join(BASE_DIR, property_list_file) + + property_map: Dict[str, PropertyRow] = extract_addresses_from_spreadsheet(filepath) + + sharepoint_client: DomnaSharepointClient = DomnaSharepointClient( + sharepoint_location=DomnaSites.PRIVATE_PAY + ) + + sharepoint_base_path: str = "/Projects/Southern Housing/SH-SURV-26-001/Assessments" + + s3_bucket: str = "retrofit-energy-assessments-dev" + + with sync_playwright() as p: + browser: Browser = p.chromium.launch(headless=True) + context: BrowserContext = browser.new_context() + page: Page = context.new_page() + + attach_debug_listeners(page) + + try: + login(page, username, password) + go_to_assessments(page) + + while True: + rows: Locator = page.locator("#assessmentDatatable tbody tr") + row_count: int = rows.count() + + for i in range(row_count): + row: Locator = rows.nth(i) + + try: + cells: Locator = row.locator("td") + + first_name: str = cells.nth(1).inner_text().strip() + last_name: str = cells.nth(2).inner_text().strip() + address: str = cells.nth(5).inner_text().strip() + postcode: str = cells.nth(7).inner_text().strip() + status: str = cells.nth(9).inner_text().strip() + + if first_name == "Oliver" and last_name == "Stephens": + continue + + if status != "Submitted (not Lodged)": + continue + + property_id: str = build_property_id(address, postcode) + + property_row: PropertyRow | None = property_map.get(property_id) + + if not property_row: + continue + + logger.info(f"Match found for property {address}") + + sharepoint_address: str = property_row.address + + go_to_assessment_details(page, row) + + for report_type in REPORT_TYPES: + hubspot_listing_id: str = property_row.listing_id + try: + db_file_type: FileTypeEnum = ( + map_report_type_to_db_file_type(report_type) + ) + + except ValueError: + logger.error( + f"Unknown report type {report_type}, skipping file" + ) + continue + + if get_uploaded_file_by_listing_type_and_source( + hubspot_listing_id=int(hubspot_listing_id), + file_type=db_file_type, + file_source=FileSourceEnum.ECMK, + ): + logger.debug("File already uploaded to s3, skipping") + continue + + file_path: str | None = download_with_retry( + page, report_type + ) + + if not file_path: + continue + + logger.info( + f"Successfully downloaded file {os.path.basename(file_path)} from ECMK" + ) + + try: + upload_file_to_sharepoint( + client=sharepoint_client, + file_path=file_path, + base_path=sharepoint_base_path, + subpath=sharepoint_address, + ) + logger.info( + f"Successfully loaded {os.path.basename(file_path)} to sharepoint for {address}" + ) + + # Upload to s3 and update db + upload_file_to_s3_and_update_db( + bucket=s3_bucket, + file_path=file_path, + hubspot_listing_id=hubspot_listing_id, + file_type=db_file_type, + ) + + except Exception: + raise + finally: + if os.path.exists(file_path): + os.remove(file_path) + + page.go_back() + page.wait_for_selector( + "#assessmentDatatable tbody tr", timeout=15000 + ) + + except Exception as e: + raise Exception(f"Row processing failed: {str(e)}") from e + + if not go_to_next_page(page): + break + + finally: + context.close() + browser.close() diff --git a/backend/ecmk_fetcher/reports.py b/backend/ecmk_fetcher/reports.py new file mode 100644 index 00000000..d8d11d50 --- /dev/null +++ b/backend/ecmk_fetcher/reports.py @@ -0,0 +1,37 @@ +from enum import Enum + +from backend.app.db.models.uploaded_file import FileTypeEnum + + +class FileDownloadButtonType(Enum): + ASSESSOR_HUB_SITENOTE_REPORT = 11 + CERTIFICATE = 9 + SITENOTE_REPORT = 8 + RAW_XML = 7 + SAP_WORK_SHEET = 15 + + +REPORT_TYPES = [ + FileDownloadButtonType.ASSESSOR_HUB_SITENOTE_REPORT.value, + FileDownloadButtonType.SITENOTE_REPORT.value, +] + + +def map_report_type_to_db_file_type(report_type: int) -> FileTypeEnum: + match report_type: + case FileDownloadButtonType.ASSESSOR_HUB_SITENOTE_REPORT.value: + return FileTypeEnum.ECMK_SITE_NOTE + case FileDownloadButtonType.SITENOTE_REPORT.value: + return FileTypeEnum.ECMK_RD_SAP_SITE_NOTE + case _: + raise ValueError("Unknown report type") + + +def build_report_selector(report_type: int) -> str: + return f"a.download-report-btn[data-report-type='{report_type}']" + + +def build_property_id(address: str, postcode: str) -> str: + number = address.split(" ")[0] + postcode_clean = postcode.replace(" ", "").upper() + return f"{number}{postcode_clean}" diff --git a/backend/ecmk_fetcher/upload.py b/backend/ecmk_fetcher/upload.py new file mode 100644 index 00000000..0a744e53 --- /dev/null +++ b/backend/ecmk_fetcher/upload.py @@ -0,0 +1,51 @@ +from datetime import datetime, timezone +import os + +from backend.app.db.connection import db_session +from backend.app.db.models.uploaded_file import ( + FileSourceEnum, + FileTypeEnum, + UploadedFile, +) +from utils.s3 import upload_file_to_s3 +from utils.sharepoint.domna_sharepoint_client import DomnaSharepointClient + + +def upload_file_to_sharepoint( + client: DomnaSharepointClient, + file_path: str, + base_path: str, + subpath: str, +) -> None: + filename = os.path.basename(file_path) + + full_path = f"{base_path}/{subpath}/1. Retrofit Assessment/A. Assessment" + + client.upload_file( + file_path=file_path, + sharepoint_path=full_path, + file_name=filename, + ) + + +def upload_file_to_s3_and_update_db( + bucket: str, file_path: str, hubspot_listing_id: str, file_type: FileTypeEnum +) -> None: + filename: str = os.path.basename(file_path) + key: str = f"documents/hubspot_listing_id/{hubspot_listing_id}/{filename}" + + upload_file_to_s3(file_path, bucket, key) + + uploaded_file = UploadedFile( + s3_file_bucket=bucket, + s3_file_key=key, + s3_upload_timestamp=datetime.now(timezone.utc), + hubspot_listing_id=hubspot_listing_id, + file_source=FileSourceEnum.ECMK.value, + file_type=file_type.value, + ) + + with db_session() as session: + # TODO: we should do multiple files at once to reduce db trips + session.add(uploaded_file) + session.commit() diff --git a/backend/engine/engine.py b/backend/engine/engine.py index b1b57529..f7a374e0 100644 --- a/backend/engine/engine.py +++ b/backend/engine/engine.py @@ -582,7 +582,8 @@ async def model_engine(body: PlanTriggerRequest): os_api_key="", full_address=addr.full_address, heating_system=addr.landlord_heating_system, - associated_uprns=associated_uprns + associated_uprns=associated_uprns, + lmk_key=addr.lmk_key ) epc_searcher.ordnance_survey_client.built_form = addr.landlord_built_form epc_searcher.ordnance_survey_client.property_type = addr.landlord_property_type @@ -627,7 +628,12 @@ async def model_engine(body: PlanTriggerRequest): # if we have a remote assment data type, we pull the additional data and include it epc_page_source, find_my_epc_components = {}, [] - if (body.event_type == "remote_assessment") and not (epc_searcher.newest_epc.get("estimated")): + if ((body.event_type == "remote_assessment") and not ( + epc_searcher.newest_epc.get("estimated")) + ) or addr.epc_certificate_number: + + if addr.epc_certificate_number: + rrn = addr.epc_certificate_number property_non_invasive_recommendations, patch, epc_page_source, find_my_epc_components = ( RetrieveFindMyEpc.get_from_epc_with_fallback( epc=epc_searcher.newest_epc, @@ -641,8 +647,13 @@ async def model_engine(body: PlanTriggerRequest): epc_records = patch_epc(patch, epc_records) + # Hack - temp while we're planning to rebuild backend + if addr.epc_certificate_number is not None and epc_records["original_epc"].get("estimated"): + epc_records["original_epc"]["estimated"] = False + prepared_epc = EPCRecord( - epc_records=epc_records, run_mode="newdata", cleaning_data=cleaning_data, address_metadata=addr + epc_records=epc_records, run_mode="newdata", cleaning_data=cleaning_data, + # address_metadata=addr Switched off to remove injecting landlord inputs ) input_properties.append( @@ -714,6 +725,10 @@ async def model_engine(body: PlanTriggerRequest): # 1) EPC expired 2) Missing EPC 3) Different information from landlord vs EPC needs_rebaselining = p.epc_is_expired | p.epc_is_estimated | (len(p.epc_record.landlord_differences) > 0) + # Hack - skip + if "SAP05" in p.epc_record.walls_description: + continue + if needs_rebaselining: p.create_base_difference_epc_record(cleaned_lookup=cleaned) scoring_data = p.base_difference_record.df.copy() diff --git a/backend/epc_api/api_spec.md b/backend/epc_api/api_spec.md new file mode 100644 index 00000000..5e1c309d --- /dev/null +++ b/backend/epc_api/api_spec.md @@ -0,0 +1 @@ +Hello World \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-7.0/cepc+rr.json b/backend/epc_api/json_samples/CEPC-7.0/cepc+rr.json new file mode 100644 index 00000000..876603e7 --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-7.0/cepc+rr.json @@ -0,0 +1,278 @@ +{ + "ber": 158.9, + "ser": 59.26, + "ter": 39.95, + "tyr": 106.52, + "uprn": 12457, + "status": "entered", + "postcode": "PT42 7AD", + "post_town": "POSTTOWN", + "issue_date": "2013-08-16", + "methodology": "SBEM", + "report_type": 3, + "schema_type": "CEPC-7.0", + "uprn_source": "Energy Assessor", + "valid_until": "2023-08-15", + "asset_rating": 134, + "language_code": 1, + "output_engine": "EPCgen, v4.1.e.5", + "property_type": "A3/A4/A5 Restaurant and Cafes/Drinking Establishments and Hot Food takeaways", + "address_line_2": "Acme Coffee", + "address_line_3": "13 Old Street", + "assessment_type": "CEPC", + "inspection_date": "2013-08-10", + "inspection_type": "Physical", + "ac_questionnaire": { + "ac_present": "No", + "ac_rated_output": { + "ac_rating_unknown_flag": 1 + }, + "ac_inspection_commissioned": 4 + }, + "calculation_tool": "CLG, iSBEM, v4.1.e, SBEM, v4.1.e.5", + "is_heritage_site": "N", + "transaction_type": 1, + "registration_date": "2013-08-15", + "building_complexity": "Level 3", + "new_build_benchmark": 34, + "location_description": "Located in main shopping area in Posttown town centre", + "technical_information": { + "floor_area": 314, + "building_level": 3, + "main_heating_fuel": "Grid Supplied Electricity", + "building_environment": "Heating and Natural Ventilation" + }, + "summary_of_performance": { + "building_data": [ + { + "area": 313.8, + "weather": "BEL", + "activities": [ + { + "id": 1010, + "area": 142.6 + }, + { + "id": 1030, + "area": 66.9 + }, + { + "id": 1032, + "area": 58.4 + }, + { + "id": 1036, + "area": 4.8 + }, + { + "id": 1031, + "area": 41.1 + } + ], + "building_w_k": 1317.44, + "hvac_systems": [ + { + "area": 142.6, + "type": "No Heating or Cooling", + "fuel_type": "Oil", + "activities": [ + { + "id": 1010, + "area": 142.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 0, + "heating_sseff": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 0, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 0, + "mj_m2_cooling_dem": 47.0757, + "mj_m2_heating_dem": 604.144 + }, + { + "area": 171.2, + "type": "Other local room heater - unfanned", + "fuel_type": "Grid Supplied Electricity", + "activities": [ + { + "id": 1030, + "area": 66.9 + }, + { + "id": 1032, + "area": 58.4 + }, + { + "id": 1036, + "area": 4.8 + }, + { + "id": 1031, + "area": 41.1 + } + ], + "heat_source": "Room heater", + "cooling_sseer": 0, + "heating_sseff": 0.8, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 255.695, + "cooling_gen_seer": 0, + "heating_gen_seff": 1, + "kwh_m2_auxiliary": 1.95411, + "mj_m2_cooling_dem": 683.721, + "mj_m2_heating_dem": 736.402 + } + ], + "analysis_type": "ACTUAL", + "area_exterior": 873.7, + "building_alpha": 3.81869, + "building_w_m2k": 1.50789, + "q50_infiltration": 25, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 27.3554, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 139.499, + "kwh_m2_lighting": 139.423, + "kwh_m2_supplied": 307.344, + "kwh_m2_auxiliary": 1.06611, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 66.704, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 0, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 313.8, + "weather": "BEL", + "activities": [ + { + "id": 1010, + "area": 142.6 + }, + { + "id": 1030, + "area": 66.9 + }, + { + "id": 1032, + "area": 58.4 + }, + { + "id": 1036, + "area": 4.8 + }, + { + "id": 1031, + "area": 41.1 + } + ], + "building_w_k": 301.464, + "hvac_systems": [ + { + "area": 142.6, + "type": "No Heating or Cooling", + "fuel_type": "Oil", + "activities": [ + { + "id": 1010, + "area": 142.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 0, + "heating_sseff": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 0, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 0, + "mj_m2_cooling_dem": 18.7903, + "mj_m2_heating_dem": 179.595 + }, + { + "area": 171.2, + "type": "Other local room heater - unfanned", + "fuel_type": "Oil", + "activities": [ + { + "id": 1030, + "area": 66.9 + }, + { + "id": 1032, + "area": 58.4 + }, + { + "id": 1036, + "area": 4.8 + }, + { + "id": 1031, + "area": 41.1 + } + ], + "heat_source": "Room heater", + "cooling_sseer": 0, + "heating_sseff": 0.792, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 104.881, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 0.521096, + "mj_m2_cooling_dem": 287.737, + "mj_m2_heating_dem": 299.036 + } + ], + "analysis_type": "NOTIONAL", + "area_exterior": 873.7, + "building_alpha": 42.6344, + "building_w_m2k": 0.345043, + "q50_infiltration": 6.0896, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 32.702, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 89.9222, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 57.2199, + "kwh_m2_lighting": 25.9805, + "kwh_m2_supplied": 26.2648, + "kwh_m2_auxiliary": 0.284295, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 66.704, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 0, + "kwh_m2_district_heating": 0 + } + } + ] + }, + "existing_stock_benchmark": 90, + "related_certificate_number": "4444-5555-6666-7777-8889", + "current_energy_efficiency_band": "F" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-7.0/cepc-rr.json b/backend/epc_api/json_samples/CEPC-7.0/cepc-rr.json new file mode 100644 index 00000000..9e594325 --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-7.0/cepc-rr.json @@ -0,0 +1,60 @@ +{ + "uprn": 12457, + "status": "cancelled", + "postcode": "SW1A 2AA", + "post_town": "Fulchester", + "issue_date": "2020-05-14", + "report_type": 4, + "schema_type": "CEPC-7.0", + "uprn_source": "Energy Assessor", + "valid_until": "2021-05-03", + "long_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Consider installing an air source heat pump.", + "recommendation_code": "EPC-R5" + } + ], + "language_code": 1, + "other_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Consider installing PV.", + "recommendation_code": "EPC-R4" + } + ], + "property_type": "B1 Offices and Workshop businesses", + "short_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Consider replacing T8 lamps with retrofit T5 conversion kit.", + "recommendation_code": "ECP-L5" + }, + { + "co2_impact": "LOW", + "recommendation": "Introduce HF (high frequency) ballasts for fluorescent tubes: Reduced number of fittings required.", + "recommendation_code": "EPC-L7" + } + ], + "address_line_1": "Some Unit", + "address_line_2": "2 Lonely Street", + "address_line_3": "Some Area", + "address_line_4": "Some County", + "medium_payback": [ + { + "co2_impact": "MEDIUM", + "recommendation": "Add optimum start/stop to the heating system.", + "recommendation_code": "EPC-H7" + } + ], + "assessment_type": "CEPC-RR", + "inspection_date": "2020-05-04", + "calculation_tool": "CEPC Compute v0.2", + "formatted_report": "ZGVmYXVsdA==", + "registration_date": "2020-05-05", + "technical_information": { + "floor_area": 10, + "building_environment": "Natural Ventilation Only" + }, + "related_certificate_number": "0000-0000-0000-0000-0001" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-7.0/cepc.json b/backend/epc_api/json_samples/CEPC-7.0/cepc.json new file mode 100644 index 00000000..d65c34bf --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-7.0/cepc.json @@ -0,0 +1,285 @@ +{ + "ber": 67.09, + "ser": 42.07, + "ter": 23.2, + "tyr": 67.98, + "uprn": 12457, + "status": "entered", + "postcode": "SW1A 2AA", + "post_town": "Whitbury", + "issue_date": "2020-05-14", + "report_type": 3, + "schema_type": "CEPC-7.0", + "uprn_source": "Energy Assessor", + "valid_until": "2026-05-04", + "asset_rating": 80, + "language_code": 1, + "property_type": "B1 Offices and Workshop businesses", + "address_line_1": "Some Unit", + "address_line_2": "2 Lonely Street", + "address_line_3": "Some Area", + "address_line_4": "Some County", + "assessment_type": "CEPC", + "inspection_date": "2020-05-04", + "ac_questionnaire": { + "ac_present": "No", + "ac_rated_output": { + "ac_kw_rating": 100 + }, + "ac_estimated_output": 3, + "ac_inspection_commissioned": 1 + }, + "calculation_tool": "Casio fx-39", + "is_heritage_site": "N", + "or_building_data": { + "hvac_system": "Yes", + "other_hvac_system": "Yes", + "internal_environment": "Yes", + "assessment_period_alignment": "Yes" + }, + "transaction_type": 1, + "or_benchmark_data": { + "benchmarks": [ + { + "floor_area": 403, + "benchmark_id": 1 + } + ] + }, + "registration_date": "2020-05-04", + "building_complexity": "Level 3", + "new_build_benchmark": 28, + "or_usable_floor_area": { + "ufa_1": { + "floor_area": 100 + } + }, + "or_energy_consumption": { + "anthracite": { + "end_date": "2030-12-25", + "estimate": 3, + "start_date": "2019-12-25", + "consumption": 12.6 + } + }, + "technical_information": { + "floor_area": 403, + "building_level": 3, + "main_heating_fuel": "Natural Gas", + "renewable_sources": "Renewable sources test", + "special_energy_uses": "Test sp", + "building_environment": "Air Conditioning", + "or_availability_date": "2020-01-04", + "other_fuel_description": "Test" + }, + "or_assessment_end_date": "2030-05-14", + "summary_of_performance": { + "building_data": [ + { + "area": 402.6, + "weather": "LON", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "building_w_k": 411.86, + "hvac_systems": [ + { + "area": 402.6, + "type": "Fan coil systems", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 1.963, + "heating_sseff": 0.827, + "kwh_m2_cooling": 25.3865, + "kwh_m2_heating": 41.0355, + "cooling_gen_seer": 2.5, + "heating_gen_seff": 0.89, + "kwh_m2_auxiliary": 39.0832, + "mj_m2_cooling_dem": 179.401, + "mj_m2_heating_dem": 122.171 + } + ], + "analysis_type": "ACTUAL", + "area_exterior": 1058.32, + "building_alpha": 18.3412, + "building_w_m2k": 0.389164, + "q50_infiltration": 10, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 2.77834, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 3.07665, + "kwh_m2_ses": 0.801605, + "kwh_m2_coal": 0, + "kwh_m2_wind": 3.02777, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 25.3865, + "kwh_m2_heating": 41.0355, + "kwh_m2_lighting": 54.0787, + "kwh_m2_supplied": 121.327, + "kwh_m2_auxiliary": 39.0832, + "kwh_m2_displaced": 6.10442, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 42.185, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 41.0355, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 402.6, + "weather": "LON", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "building_w_k": 362.524, + "hvac_systems": [ + { + "area": 402.6, + "type": "Fan coil systems", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 3.6, + "heating_sseff": 0.819, + "kwh_m2_cooling": 7.46769, + "kwh_m2_heating": 18.1581, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 14.0716, + "mj_m2_cooling_dem": 96.7814, + "mj_m2_heating_dem": 53.5375 + } + ], + "analysis_type": "NOTIONAL", + "area_exterior": 1058.32, + "building_alpha": 11.2489, + "building_w_m2k": 0.342547, + "q50_infiltration": 3, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 3.33761, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 3.33761, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 7.46769, + "kwh_m2_heating": 18.1582, + "kwh_m2_lighting": 14.4489, + "kwh_m2_supplied": 35.9883, + "kwh_m2_auxiliary": 14.0716, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 42.185, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 18.1582, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 402.6, + "weather": "LON", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "building_w_k": 718.761, + "hvac_systems": [ + { + "area": 402.6, + "type": "Fan coil systems", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 2.25, + "heating_sseff": 0.73, + "kwh_m2_cooling": 25.6538, + "kwh_m2_heating": 71.9516, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 2.16062, + "mj_m2_cooling_dem": 207.795, + "mj_m2_heating_dem": 189.088 + } + ], + "analysis_type": "REFERENCE", + "area_exterior": 1058.32, + "building_alpha": 10, + "building_w_m2k": 0.679153, + "q50_infiltration": 10, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 6.41192, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 25.6538, + "kwh_m2_heating": 71.9516, + "kwh_m2_lighting": 45.54, + "kwh_m2_supplied": 73.3544, + "kwh_m2_auxiliary": 2.16062, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 42.185, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 78.3634, + "kwh_m2_district_heating": 0 + } + } + ] + }, + "renewable_energy_source": [ + { + "name": "Solar panel", + "end_date": "2030-12-25", + "generation": 20, + "start_date": "2019-12-25", + "energy_type": 2 + } + ], + "existing_stock_benchmark": 81, + "or_assessment_start_date": "2020-05-14", + "related_certificate_number": "4192-1535-8427-8844-6702", + "current_energy_efficiency_band": "D" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-7.0/dec+rr.json b/backend/epc_api/json_samples/CEPC-7.0/dec+rr.json new file mode 100644 index 00000000..ed560a1b --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-7.0/dec+rr.json @@ -0,0 +1,128 @@ +{ + "uprn": 12457, + "status": "entered", + "postcode": "PT4 1SK", + "post_town": "POSTTOWN", + "dec_status": 0, + "issue_date": "2016-02-23", + "methodology": "ORCalc", + "reason_type": 2, + "report_type": 1, + "schema_type": "CEPC-7.0", + "uprn_source": "Energy Assessor", + "valid_until": "2017-04-24", + "language_code": 1, + "output_engine": "ORGen v3.6.2", + "property_type": "Schools And Seasonal Public Buildings; Swimming Pool Centre", + "address_line_1": "Mr Blobby's Sports Academy", + "address_line_2": "Mr Blobby's Academy", + "address_line_3": "Blobby Custard Lane", + "assessment_type": "DEC", + "inspection_date": "2016-04-11", + "this_assessment": { + "heating_co2": 153, + "energy_rating": 77, + "nominated_date": "2016-02-23", + "renewables_co2": 0, + "electricity_co2": 163 + }, + "ac_questionnaire": { + "ac_present": "Yes", + "ac_rated_output": { + "ac_rating_unknown_flag": 1 + }, + "ac_estimated_output": 2, + "ac_inspection_commissioned": 4 + }, + "calculation_tool": "Property-Tectonics Ltd, Lifespan DEC, v3.6.1", + "or_building_data": { + "hvac_system": "Radiators", + "internal_environment": "Mixed-mode with Natural Ventilation", + "assessment_period_alignment": "End Of Main Heating Fuel Period" + }, + "or_previous_data": { + "asset_rating": 0, + "previous_rating_1": { + "or": 95, + "ormm": 11, + "oryyyy": 2014 + }, + "previous_rating_2": { + "or": 113, + "ormm": 11, + "oryyyy": 2013 + } + }, + "year1_assessment": { + "heating_co2": 176, + "energy_rating": 95, + "nominated_date": "2014-11-30", + "renewables_co2": 0, + "electricity_co2": 266 + }, + "year2_assessment": { + "heating_co2": 238, + "energy_rating": 113, + "nominated_date": "2013-11-30", + "renewables_co2": 0, + "electricity_co2": 333 + }, + "building_category": "S3; H6", + "or_benchmark_data": { + "benchmarks": [ + { + "name": "Secondary school", + "tufa": 4646.48, + "benchmark": "Schools And Seasonal Public Buildings", + "floor_area": 4646.48, + "area_metric": "Gross floor area measured as RICS Gross Internal Area (GIA)", + "benchmark_id": 1, + "occupancy_level": "Extended Occupancy" + }, + { + "name": "Swimming pool", + "tufa": 254.52, + "benchmark": "Swimming Pool Centre", + "floor_area": 254.52, + "area_metric": "Gross floor area measured as RICS Gross Internal Area (GIA)", + "benchmark_id": 2, + "occupancy_level": "Extended Occupancy" + } + ], + "main_benchmark": "Schools And Seasonal Public Buildings" + }, + "registration_date": "2016-04-25", + "or_energy_consumption": { + "gas": { + "end_date": "2016-01-01", + "estimate": 0, + "start_date": "2015-01-01", + "consumption": 786655 + }, + "electricity": { + "end_date": "2015-12-31", + "estimate": 0, + "start_date": "2015-01-01", + "consumption": 296820 + } + }, + "technical_information": { + "floor_area": 4901, + "main_heating_fuel": "Natural Gas", + "building_environment": "Mixed-mode with Natural Ventilation", + "separately_metered_electric_heating": 0 + }, + "or_assessment_end_date": "2016-01-01", + "or_assessment_start_date": "2015-01-01", + "dec_annual_energy_summary": { + "typical_thermal_use": 244, + "renewables_electrical": 0, + "typical_electrical_use": 67, + "renewables_fuel_thermal": 0, + "annual_energy_use_electrical": 61, + "annual_energy_use_fuel_thermal": 161 + }, + "related_certificate_number": "3333-4444-5555-6666-7778", + "dec_related_party_disclosure": 3, + "current_energy_efficiency_band": "D" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-7.0/dec-rr.json b/backend/epc_api/json_samples/CEPC-7.0/dec-rr.json new file mode 100644 index 00000000..1974d031 --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-7.0/dec-rr.json @@ -0,0 +1,76 @@ +{ + "uprn": 12457, + "status": "entered", + "postcode": "SW1A 2AA", + "post_town": "Fulchester", + "issue_date": "2020-05-04", + "report_type": 2, + "schema_type": "CEPC-7.0", + "uprn_source": "Energy Assessor", + "valid_until": "2028-05-03", + "long_payback": [ + { + "co2_impact": "LOW", + "recommendation": "Consider replacing or improving glazing", + "recommendation_code": "ECP-F4" + } + ], + "language_code": 1, + "other_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Add a big wind turbine", + "recommendation_code": "ECP-H2" + } + ], + "property_type": "University campus", + "short_payback": [ + { + "co2_impact": "MEDIUM", + "recommendation": "Consider thinking about maybe possibly getting a solar panel but only one.", + "recommendation_code": "ECP-L5" + }, + { + "co2_impact": "LOW", + "recommendation": "Consider introducing variable speed drives (VSD) for fans, pumps and compressors.", + "recommendation_code": "EPC-L7" + } + ], + "site_services": { + "service_1": { + "quantity": 751445, + "description": "Electricity" + }, + "service_2": { + "quantity": 72956, + "description": "Gas" + }, + "service_3": { + "quantity": 0, + "description": "Not used" + } + }, + "address_line_1": "Some Unit", + "address_line_2": "2 Lonely Street", + "address_line_3": "Some Area", + "address_line_4": "Some County", + "medium_payback": [ + { + "co2_impact": "LOW", + "recommendation": "Engage experts to propose specific measures to reduce hot waterwastage and plan to carry this out.", + "recommendation_code": "ECP-C1" + } + ], + "assessment_type": "DEC-RR", + "inspection_date": "2020-05-04", + "inspection_type": "Physical", + "calculation_tool": "DCLG, ORCalc, v3.6.2", + "formatted_report": "ZGVmYXVsdA==", + "registration_date": "2020-05-04", + "technical_information": { + "floor_area": 10, + "renewable_sources": "Renewable source", + "special_energy_uses": "Special discount", + "building_environment": "Air Conditioning" + } +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-7.1/cepc+rr.json b/backend/epc_api/json_samples/CEPC-7.1/cepc+rr.json new file mode 100644 index 00000000..a41ca736 --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-7.1/cepc+rr.json @@ -0,0 +1,264 @@ +{ + "ber": 135.65, + "ser": 72.8, + "ter": 39.05, + "tyr": 114.44, + "uprn": 12457, + "status": "entered", + "postcode": "A1 1AA", + "post_town": "New Town", + "energy_use": { + "energy_consumption_current": 802.38 + }, + "issue_date": "2017-10-13", + "methodology": "SBEM", + "report_type": 3, + "schema_type": "CEPC-7.1", + "uprn_source": "Energy Assessor", + "valid_until": "2027-10-12", + "asset_rating": 93, + "language_code": 1, + "output_engine": "EPCgen, v5.3.a.0", + "property_type": "A1/A2 Retail and Financial/Professional services", + "address_line_1": "99a Address Street", + "address_line_2": "Place Location", + "assessment_type": "CEPC", + "inspection_date": "2017-10-10", + "inspection_type": "Physical", + "ac_questionnaire": { + "ac_present": "No", + "ac_rated_output": { + "ac_rating_unknown_flag": 1 + }, + "ac_inspection_commissioned": 4 + }, + "calculation_tool": "CLG, iSBEM, v5.3.a, SBEM, v5.3.a.0", + "is_heritage_site": "N", + "transaction_type": 6, + "registration_date": "2017-10-13", + "building_complexity": "Level 3", + "new_build_benchmark": 27, + "technical_information": { + "floor_area": 222, + "building_level": 3, + "main_heating_fuel": "Grid Supplied Electricity", + "building_environment": "Heating and Natural Ventilation" + }, + "summary_of_performance": { + "building_data": [ + { + "area": 221.66, + "weather": "NEW", + "activities": [ + { + "id": 1078, + "area": 6.84 + }, + { + "id": 1077, + "area": 3.42 + }, + { + "id": 1074, + "area": 43.89 + }, + { + "id": 1305, + "area": 167.51 + } + ], + "building_w_k": 756.843, + "hvac_systems": [ + { + "area": 54.15, + "type": "Other local room heater - fanned", + "fuel_type": "Grid Supplied Electricity", + "activities": [ + { + "id": 1078, + "area": 6.84 + }, + { + "id": 1077, + "area": 3.42 + }, + { + "id": 1074, + "area": 43.89 + } + ], + "heat_source": "Room heater", + "cooling_sseer": 0, + "heating_sseff": 0.8, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 221.413, + "cooling_gen_seer": 0, + "heating_gen_seff": 1, + "kwh_m2_auxiliary": 8.58942, + "mj_m2_cooling_dem": 82.8076, + "mj_m2_heating_dem": 637.668 + }, + { + "area": 167.51, + "type": "Other local room heater - unfanned", + "fuel_type": "Grid Supplied Electricity", + "activities": [ + { + "id": 1305, + "area": 167.51 + } + ], + "heat_source": "Room heater", + "cooling_sseer": 0, + "heating_sseff": 0.8, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 143.224, + "cooling_gen_seer": 0, + "heating_gen_seff": 1, + "kwh_m2_auxiliary": 0, + "mj_m2_cooling_dem": 277.439, + "mj_m2_heating_dem": 412.486 + } + ], + "analysis_type": "ACTUAL", + "area_exterior": 578.13, + "building_alpha": 5.48977, + "building_w_m2k": 1.30912, + "q50_infiltration": 25, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 1.42751, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 162.325, + "kwh_m2_lighting": 95.5093, + "kwh_m2_supplied": 261.361, + "kwh_m2_auxiliary": 2.09834, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 17.0788, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 0, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 221.66, + "weather": "NEW", + "activities": [ + { + "id": 1078, + "area": 6.84 + }, + { + "id": 1077, + "area": 3.42 + }, + { + "id": 1074, + "area": 43.89 + }, + { + "id": 1305, + "area": 167.51 + } + ], + "building_w_k": 141.605, + "hvac_systems": [ + { + "area": 54.15, + "type": "Other local room heater - fanned", + "fuel_type": "Oil", + "activities": [ + { + "id": 1078, + "area": 6.84 + }, + { + "id": 1077, + "area": 3.42 + }, + { + "id": 1074, + "area": 43.89 + } + ], + "heat_source": "Room heater", + "cooling_sseer": 0, + "heating_sseff": 0.819, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 83.8955, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 2.29051, + "mj_m2_cooling_dem": 32.988, + "mj_m2_heating_dem": 247.357 + }, + { + "area": 167.51, + "type": "Other local room heater - unfanned", + "fuel_type": "Oil", + "activities": [ + { + "id": 1305, + "area": 167.51 + } + ], + "heat_source": "Room heater", + "cooling_sseer": 0, + "heating_sseff": 0.819, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 18.2439, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 0, + "mj_m2_cooling_dem": 153.68, + "mj_m2_heating_dem": 53.7903 + } + ], + "analysis_type": "NOTIONAL", + "area_exterior": 578.13, + "building_alpha": 12.3616, + "building_w_m2k": 0.244936, + "q50_infiltration": 5, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 1.64373, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 35.9259, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 34.2821, + "kwh_m2_lighting": 53.9687, + "kwh_m2_supplied": 54.5286, + "kwh_m2_auxiliary": 0.559555, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 17.0788, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 0, + "kwh_m2_district_heating": 0 + } + } + ] + }, + "existing_stock_benchmark": 79, + "related_certificate_number": "9999-9999-3333-9999-2222", + "current_energy_efficiency_band": "D" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-7.1/cepc-rr.json b/backend/epc_api/json_samples/CEPC-7.1/cepc-rr.json new file mode 100644 index 00000000..be34f330 --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-7.1/cepc-rr.json @@ -0,0 +1,60 @@ +{ + "uprn": 12457, + "status": "cancelled", + "postcode": "SW1A 2AA", + "post_town": "Fulchester", + "issue_date": "2020-05-14", + "report_type": 4, + "schema_type": "CEPC-7.1", + "uprn_source": "Energy Assessor", + "valid_until": "2021-05-03", + "long_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Consider installing an air source heat pump.", + "recommendation_code": "EPC-R5" + } + ], + "language_code": 1, + "other_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Consider installing PV.", + "recommendation_code": "EPC-R4" + } + ], + "property_type": "B1 Offices and Workshop businesses", + "short_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Consider replacing T8 lamps with retrofit T5 conversion kit.", + "recommendation_code": "ECP-L5" + }, + { + "co2_impact": "LOW", + "recommendation": "Introduce HF (high frequency) ballasts for fluorescent tubes: Reduced number of fittings required.", + "recommendation_code": "EPC-L7" + } + ], + "address_line_1": "Some Unit", + "address_line_2": "2 Lonely Street", + "address_line_3": "Some Area", + "address_line_4": "Some County", + "medium_payback": [ + { + "co2_impact": "MEDIUM", + "recommendation": "Add optimum start/stop to the heating system.", + "recommendation_code": "EPC-H7" + } + ], + "assessment_type": "CEPC-RR", + "inspection_date": "2020-05-04", + "calculation_tool": "CEPC Compute v0.2", + "formatted_report": "ZGVmYXVsdA==", + "registration_date": "2020-05-05", + "technical_information": { + "floor_area": 10, + "building_environment": "Natural Ventilation Only" + }, + "related_certificate_number": "0000-0000-0000-0000-0001" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-7.1/cepc.json b/backend/epc_api/json_samples/CEPC-7.1/cepc.json new file mode 100644 index 00000000..f89954e6 --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-7.1/cepc.json @@ -0,0 +1,288 @@ +{ + "ber": 67.09, + "ser": 42.07, + "ter": 23.2, + "tyr": 67.98, + "uprn": 12457, + "status": "entered", + "postcode": "BT4 3SR", + "post_town": "Whitbury", + "energy_use": { + "energy_consumption_current": 413.22 + }, + "issue_date": "2020-05-14", + "report_type": 3, + "schema_type": "CEPC-7.1", + "uprn_source": "Energy Assessor", + "valid_until": "2026-05-04", + "asset_rating": 80, + "language_code": 1, + "property_type": "B1 Offices and Workshop businesses", + "address_line_1": "Some Unit", + "address_line_2": "2 Lonely Street", + "address_line_3": "Some Area", + "address_line_4": "Some County", + "assessment_type": "CEPC", + "inspection_date": "2020-05-04", + "ac_questionnaire": { + "ac_present": "No", + "ac_rated_output": { + "ac_kw_rating": 100 + }, + "ac_estimated_output": 3, + "ac_inspection_commissioned": 1 + }, + "calculation_tool": "Casio fx-39", + "is_heritage_site": "N", + "or_building_data": { + "hvac_system": "Yes", + "other_hvac_system": "Yes", + "internal_environment": "Yes", + "assessment_period_alignment": "Yes" + }, + "transaction_type": 1, + "or_benchmark_data": { + "benchmarks": [ + { + "floor_area": 403, + "benchmark_id": 1 + } + ] + }, + "registration_date": "2020-05-04", + "building_complexity": "Level 3", + "new_build_benchmark": 28, + "or_usable_floor_area": { + "ufa_1": { + "floor_area": 100 + } + }, + "or_energy_consumption": { + "anthracite": { + "end_date": "2030-12-25", + "estimate": 3, + "start_date": "2019-12-25", + "consumption": 12.6 + } + }, + "technical_information": { + "floor_area": 403, + "building_level": 3, + "main_heating_fuel": "Natural Gas", + "renewable_sources": "Renewable sources test", + "special_energy_uses": "Test sp", + "building_environment": "Air Conditioning", + "or_availability_date": "2020-01-04", + "other_fuel_description": "Test" + }, + "or_assessment_end_date": "2030-05-14", + "summary_of_performance": { + "building_data": [ + { + "area": 402.6, + "weather": "LON", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "building_w_k": 411.86, + "hvac_systems": [ + { + "area": 402.6, + "type": "Fan coil systems", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 1.963, + "heating_sseff": 0.827, + "kwh_m2_cooling": 25.3865, + "kwh_m2_heating": 41.0355, + "cooling_gen_seer": 2.5, + "heating_gen_seff": 0.89, + "kwh_m2_auxiliary": 39.0832, + "mj_m2_cooling_dem": 179.401, + "mj_m2_heating_dem": 122.171 + } + ], + "analysis_type": "ACTUAL", + "area_exterior": 1058.32, + "building_alpha": 18.3412, + "building_w_m2k": 0.389164, + "q50_infiltration": 10, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 2.77834, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 3.07665, + "kwh_m2_ses": 0.801605, + "kwh_m2_coal": 0, + "kwh_m2_wind": 3.02777, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 25.3865, + "kwh_m2_heating": 41.0355, + "kwh_m2_lighting": 54.0787, + "kwh_m2_supplied": 121.327, + "kwh_m2_auxiliary": 39.0832, + "kwh_m2_displaced": 6.10442, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 42.185, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 41.0355, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 402.6, + "weather": "LON", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "building_w_k": 362.524, + "hvac_systems": [ + { + "area": 402.6, + "type": "Fan coil systems", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 3.6, + "heating_sseff": 0.819, + "kwh_m2_cooling": 7.46769, + "kwh_m2_heating": 18.1581, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 14.0716, + "mj_m2_cooling_dem": 96.7814, + "mj_m2_heating_dem": 53.5375 + } + ], + "analysis_type": "NOTIONAL", + "area_exterior": 1058.32, + "building_alpha": 11.2489, + "building_w_m2k": 0.342547, + "q50_infiltration": 3, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 3.33761, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 3.33761, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 7.46769, + "kwh_m2_heating": 18.1582, + "kwh_m2_lighting": 14.4489, + "kwh_m2_supplied": 35.9883, + "kwh_m2_auxiliary": 14.0716, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 42.185, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 18.1582, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 402.6, + "weather": "LON", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "building_w_k": 718.761, + "hvac_systems": [ + { + "area": 402.6, + "type": "Fan coil systems", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 2.25, + "heating_sseff": 0.73, + "kwh_m2_cooling": 25.6538, + "kwh_m2_heating": 71.9516, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 2.16062, + "mj_m2_cooling_dem": 207.795, + "mj_m2_heating_dem": 189.088 + } + ], + "analysis_type": "REFERENCE", + "area_exterior": 1058.32, + "building_alpha": 10, + "building_w_m2k": 0.679153, + "q50_infiltration": 10, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 6.41192, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 25.6538, + "kwh_m2_heating": 71.9516, + "kwh_m2_lighting": 45.54, + "kwh_m2_supplied": 73.3544, + "kwh_m2_auxiliary": 2.16062, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 42.185, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 78.3634, + "kwh_m2_district_heating": 0 + } + } + ] + }, + "renewable_energy_source": [ + { + "name": "Solar panel", + "end_date": "2030-12-25", + "generation": 20, + "start_date": "2019-12-25", + "energy_type": 2 + } + ], + "existing_stock_benchmark": 81, + "or_assessment_start_date": "2020-05-14", + "related_certificate_number": "4192-1535-8427-8844-6702", + "current_energy_efficiency_band": "D" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-7.1/dec+rr.json b/backend/epc_api/json_samples/CEPC-7.1/dec+rr.json new file mode 100644 index 00000000..f03bd052 --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-7.1/dec+rr.json @@ -0,0 +1,118 @@ +{ + "uprn": 12457, + "status": "entered", + "postcode": "A1 1AA", + "post_town": "Town", + "dec_status": 0, + "issue_date": "2015-12-14", + "methodology": "ORCalc", + "reason_type": 1, + "report_type": 1, + "schema_type": "CEPC-7.1", + "uprn_source": "Energy Assessor", + "valid_until": "2016-12-29", + "language_code": 1, + "output_engine": "ORGen v3.6.2", + "property_type": "Schools And Seasonal Public Buildings", + "address_line_2": "Place Early Years Centre", + "address_line_3": "Address Road", + "assessment_type": "DEC", + "inspection_date": "2015-12-09", + "this_assessment": { + "heating_co2": 28, + "energy_rating": 80, + "nominated_date": "2015-12-30", + "renewables_co2": 0, + "electricity_co2": 33 + }, + "ac_questionnaire": { + "ac_present": "No", + "ac_rated_output": { + "ac_rating_unknown_flag": 1 + }, + "ac_inspection_commissioned": 4 + }, + "calculation_tool": "DCLG, ORCalc, v3.6.2", + "or_building_data": { + "hvac_system": "Convectors", + "internal_environment": "Heating and Natural Ventilation", + "assessment_period_alignment": "End Of Main Heating Fuel Period" + }, + "or_previous_data": { + "previous_rating_1": { + "or": 75, + "ormm": 12, + "oryyyy": 2014 + }, + "previous_rating_2": { + "or": 75, + "ormm": 12, + "oryyyy": 2013 + } + }, + "year1_assessment": { + "heating_co2": 24, + "energy_rating": 75, + "nominated_date": "2014-12-01", + "renewables_co2": 0, + "electricity_co2": 30 + }, + "year2_assessment": { + "heating_co2": 29, + "energy_rating": 75, + "nominated_date": "2013-12-01", + "renewables_co2": 0, + "electricity_co2": 31 + }, + "building_category": "S3;", + "or_benchmark_data": { + "benchmarks": [ + { + "name": "Nursery or kindergarten", + "tufa": 1219.2, + "benchmark": "Schools And Seasonal Public Buildings", + "floor_area": 1219.2, + "area_metric": "Gross floor area measured as RICS Gross Internal Area (GIA)", + "benchmark_id": 1, + "occupancy_level": "Extended Occupancy", + "total_equivalent": 2437.5 + } + ], + "main_benchmark": "Schools And Seasonal Public Buildings" + }, + "registration_date": "2015-12-14", + "location_description": "Refurbished and extended nursery school building with flat and pitched roof areas, double glazed windows and cavity walls. The building is heated by gas fired boilers.", + "or_energy_consumption": { + "gas": { + "end_date": "2015-09-30", + "estimate": 0, + "start_date": "2014-09-30", + "consumption": 143533 + }, + "electricity": { + "end_date": "2015-10-01", + "estimate": 0, + "start_date": "2014-10-01", + "consumption": 59477 + } + }, + "technical_information": { + "floor_area": 1219.2, + "main_heating_fuel": "Natural Gas", + "building_environment": "Heating and Natural Ventilation", + "separately_metered_electric_heating": 0 + }, + "or_assessment_end_date": "2015-09-30", + "or_assessment_start_date": "2014-09-30", + "dec_annual_energy_summary": { + "typical_thermal_use": 176, + "renewables_electrical": 0, + "typical_electrical_use": 51, + "renewables_fuel_thermal": 0, + "annual_energy_use_electrical": 49, + "annual_energy_use_fuel_thermal": 118 + }, + "related_certificate_number": "0000-0000-0000-0000-0006", + "dec_related_party_disclosure": 4, + "current_energy_efficiency_band": "D" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-7.1/dec-rr.json b/backend/epc_api/json_samples/CEPC-7.1/dec-rr.json new file mode 100644 index 00000000..95accca6 --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-7.1/dec-rr.json @@ -0,0 +1,76 @@ +{ + "uprn": 12457, + "status": "entered", + "postcode": "SW1A 2AA", + "post_town": "Fulchester", + "issue_date": "2020-05-04", + "report_type": 2, + "schema_type": "CEPC-7.1", + "uprn_source": "Energy Assessor", + "valid_until": "2028-05-03", + "long_payback": [ + { + "co2_impact": "LOW", + "recommendation": "Consider replacing or improving glazing", + "recommendation_code": "ECP-F4" + } + ], + "language_code": 1, + "other_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Add a big wind turbine", + "recommendation_code": "ECP-H2" + } + ], + "property_type": "University campus", + "short_payback": [ + { + "co2_impact": "MEDIUM", + "recommendation": "Consider thinking about maybe possibly getting a solar panel but only one.", + "recommendation_code": "ECP-L5" + }, + { + "co2_impact": "LOW", + "recommendation": "Consider introducing variable speed drives (VSD) for fans, pumps and compressors.", + "recommendation_code": "EPC-L7" + } + ], + "site_services": { + "service_1": { + "quantity": 751445, + "description": "Electricity" + }, + "service_2": { + "quantity": 72956, + "description": "Gas" + }, + "service_3": { + "quantity": 0, + "description": "Not used" + } + }, + "address_line_1": "Some Unit", + "address_line_2": "2 Lonely Street", + "address_line_3": "Some Area", + "address_line_4": "Some County", + "medium_payback": [ + { + "co2_impact": "LOW", + "recommendation": "Engage experts to propose specific measures to reduce hot waterwastage and plan to carry this out.", + "recommendation_code": "ECP-C1" + } + ], + "assessment_type": "DEC-RR", + "inspection_date": "2020-05-04", + "inspection_type": "Physical", + "calculation_tool": "DCLG, ORCalc, v3.6.2", + "formatted_report": "ZGVmYXVsdA==", + "registration_date": "2020-05-04", + "technical_information": { + "floor_area": 10, + "renewable_sources": "Renewable source", + "special_energy_uses": "Special discount", + "building_environment": "Air Conditioning" + } +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-8.0.0/cepc+rr.json b/backend/epc_api/json_samples/CEPC-8.0.0/cepc+rr.json new file mode 100644 index 00000000..9ff6ca3a --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-8.0.0/cepc+rr.json @@ -0,0 +1,468 @@ +{ + "ber": 76.29, + "ser": 45.61, + "ter": 31.05, + "tyr": 90.98, + "uprn": 12457, + "status": "entered", + "postcode": "NE0 0AA", + "post_town": "Big Rock", + "energy_use": { + "energy_consumption_current": 451.27 + }, + "issue_date": "2021-03-19", + "methodology": "SBEM", + "report_type": 3, + "schema_type": "CEPC-8.0.0", + "uprn_source": "Energy Assessor", + "valid_until": "2031-03-18", + "asset_rating": 84, + "language_code": 1, + "output_engine": "EPCgen, v5.6.b.0", + "property_type": "A1/A2 Retail and Financial/Professional services", + "address_line_1": "60 Maple Syrup Road", + "address_line_2": "Candy Mountain", + "assessment_type": "CEPC", + "inspection_date": "2021-03-19", + "inspection_type": "Physical", + "ac_questionnaire": { + "ac_present": "No", + "ac_rated_output": { + "ac_rating_unknown_flag": 1 + }, + "ac_inspection_commissioned": 4 + }, + "calculation_tool": "G-ISBEM Ltd, G-ISBEM, v24.0, SBEM, v5.6.b.0", + "is_heritage_site": "N", + "transaction_type": 1, + "registration_date": "2021-03-19", + "building_complexity": "Level 3", + "new_build_benchmark": 34, + "technical_information": { + "floor_area": 951, + "building_level": 3, + "main_heating_fuel": "Grid Supplied Electricity", + "building_environment": "Air Conditioning" + }, + "summary_of_performance": { + "building_data": [ + { + "area": 951.34, + "weather": "NEW", + "activities": [ + { + "id": 1067, + "area": 81.32 + }, + { + "id": 1070, + "area": 13.9 + }, + { + "id": 1074, + "area": 258.42 + }, + { + "id": 1078, + "area": 76.99 + }, + { + "id": 1071, + "area": 490.62 + }, + { + "id": 1077, + "area": 30.09 + } + ], + "building_w_k": 314.665, + "hvac_systems": [ + { + "area": 353.64, + "type": "No Heating or Cooling", + "fuel_type": "Oil", + "activities": [ + { + "id": 1067, + "area": 81.32 + }, + { + "id": 1070, + "area": 13.9 + }, + { + "id": 1074, + "area": 258.42 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 0, + "heating_sseff": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 0, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 4.9793, + "mj_m2_cooling_dem": 44.0312, + "mj_m2_heating_dem": 79.1732 + }, + { + "area": 567.61, + "type": "Split or multi-split system", + "fuel_type": "Grid Supplied Electricity", + "activities": [ + { + "id": 1078, + "area": 76.99 + }, + { + "id": 1071, + "area": 490.62 + } + ], + "heat_source": "Heat pump: air source", + "cooling_sseer": 2.457, + "heating_sseff": 3.466, + "kwh_m2_cooling": 65.3618, + "kwh_m2_heating": 2.17225, + "cooling_gen_seer": 3.46, + "heating_gen_seff": 3.72, + "kwh_m2_auxiliary": 6.9318, + "mj_m2_cooling_dem": 578.138, + "mj_m2_heating_dem": 27.1043 + }, + { + "area": 30.09, + "type": "Other local room heater - unfanned", + "fuel_type": "Grid Supplied Electricity", + "activities": [ + { + "id": 1077, + "area": 30.09 + } + ], + "heat_source": "Direct or storage electric heater", + "cooling_sseer": 0, + "heating_sseff": 0.8, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 101.261, + "cooling_gen_seer": 0, + "heating_gen_seff": 1, + "kwh_m2_auxiliary": 28.4481, + "mj_m2_cooling_dem": 72.0834, + "mj_m2_heating_dem": 291.63 + } + ], + "analysis_type": "ACTUAL", + "area_exterior": 509.17, + "building_alpha": 4.09978, + "building_w_m2k": 0.617996, + "q50_infiltration": 25, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 1.61017, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 38.9976, + "kwh_m2_heating": 4.49883, + "kwh_m2_lighting": 94.9991, + "kwh_m2_supplied": 146.993, + "kwh_m2_auxiliary": 6.88654, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 19.5261, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 0, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 951.34, + "weather": "NEW", + "activities": [ + { + "id": 1067, + "area": 81.32 + }, + { + "id": 1070, + "area": 13.9 + }, + { + "id": 1074, + "area": 258.42 + }, + { + "id": 1078, + "area": 76.99 + }, + { + "id": 1071, + "area": 490.62 + }, + { + "id": 1077, + "area": 30.09 + } + ], + "building_w_k": 106.722, + "hvac_systems": [ + { + "area": 353.64, + "type": "No Heating or Cooling", + "fuel_type": "Oil", + "activities": [ + { + "id": 1067, + "area": 81.32 + }, + { + "id": 1070, + "area": 13.9 + }, + { + "id": 1074, + "area": 258.42 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 0, + "heating_sseff": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 0, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 2.98759, + "mj_m2_cooling_dem": 22.3547, + "mj_m2_heating_dem": 24.8966 + }, + { + "area": 567.61, + "type": "Split or multi-split system", + "fuel_type": "Grid Supplied Electricity", + "activities": [ + { + "id": 1078, + "area": 76.99 + }, + { + "id": 1071, + "area": 490.62 + } + ], + "heat_source": "Heat pump: air source", + "cooling_sseer": 3.6, + "heating_sseff": 2.43, + "kwh_m2_cooling": 23.8278, + "kwh_m2_heating": 0.00251615, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 3.77382, + "mj_m2_cooling_dem": 308.809, + "mj_m2_heating_dem": 0.0220112 + }, + { + "area": 30.09, + "type": "Other local room heater - unfanned", + "fuel_type": "Oil", + "activities": [ + { + "id": 1077, + "area": 30.09 + } + ], + "heat_source": "Direct or storage electric heater", + "cooling_sseer": 0, + "heating_sseff": 0.819, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 35.9561, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 7.58617, + "mj_m2_cooling_dem": 78.2393, + "mj_m2_heating_dem": 106.013 + } + ], + "analysis_type": "NOTIONAL", + "area_exterior": 509.17, + "building_alpha": 12.5855, + "building_w_m2k": 0.2096, + "q50_infiltration": 3, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 1.45546, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 2.59272, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 14.2167, + "kwh_m2_heating": 1.13875, + "kwh_m2_lighting": 41.9026, + "kwh_m2_supplied": 59.7228, + "kwh_m2_auxiliary": 3.60214, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 19.5261, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 0, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 951.34, + "weather": "NEW", + "activities": [ + { + "id": 1067, + "area": 81.32 + }, + { + "id": 1070, + "area": 13.9 + }, + { + "id": 1074, + "area": 258.42 + }, + { + "id": 1078, + "area": 76.99 + }, + { + "id": 1071, + "area": 490.62 + }, + { + "id": 1077, + "area": 30.09 + } + ], + "building_w_k": 193.204, + "hvac_systems": [ + { + "area": 353.64, + "type": "No Heating or Cooling", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1067, + "area": 81.32 + }, + { + "id": 1070, + "area": 13.9 + }, + { + "id": 1074, + "area": 258.42 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 0, + "heating_sseff": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 0, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 0, + "mj_m2_cooling_dem": 13.1255, + "mj_m2_heating_dem": 71.1616 + }, + { + "area": 567.61, + "type": "Split or multi-split system", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1078, + "area": 76.99 + }, + { + "id": 1071, + "area": 490.62 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 2.25, + "heating_sseff": 0.73, + "kwh_m2_cooling": 49.5997, + "kwh_m2_heating": 6.49072, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 2.47934, + "mj_m2_cooling_dem": 401.756, + "mj_m2_heating_dem": 17.0576 + }, + { + "area": 30.09, + "type": "Other local room heater - unfanned", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1077, + "area": 30.09 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 2.25, + "heating_sseff": 0.73, + "kwh_m2_cooling": 25.4056, + "kwh_m2_heating": 84.553, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 12.1545, + "mj_m2_cooling_dem": 205.785, + "mj_m2_heating_dem": 222.205 + } + ], + "analysis_type": "REFERENCE", + "area_exterior": 509.17, + "building_alpha": 10, + "building_w_m2k": 0.379449, + "q50_infiltration": 10, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 2.7961, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 30.3968, + "kwh_m2_heating": 6.54699, + "kwh_m2_lighting": 78.7393, + "kwh_m2_supplied": 110.999, + "kwh_m2_auxiliary": 1.86372, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 19.5261, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 9.34308, + "kwh_m2_district_heating": 0 + } + } + ] + }, + "existing_stock_benchmark": 100, + "related_certificate_number": "0000-0000-0000-0000-0001", + "current_energy_efficiency_band": "D" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-8.0.0/cepc-rr.json b/backend/epc_api/json_samples/CEPC-8.0.0/cepc-rr.json new file mode 100644 index 00000000..065f54bb --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-8.0.0/cepc-rr.json @@ -0,0 +1,60 @@ +{ + "uprn": 12457, + "status": "cancelled", + "postcode": "SW1A 2AA", + "post_town": "Fulchester", + "issue_date": "2020-05-14", + "report_type": 4, + "schema_type": "CEPC-8.0.0", + "uprn_source": "Energy Assessor", + "valid_until": "2021-05-03", + "long_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Consider installing an air source heat pump.", + "recommendation_code": "EPC-R5" + } + ], + "language_code": 1, + "other_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Consider installing PV.", + "recommendation_code": "EPC-R4" + } + ], + "property_type": "B1 Offices and Workshop businesses", + "short_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Consider replacing T8 lamps with retrofit T5 conversion kit.", + "recommendation_code": "ECP-L5" + }, + { + "co2_impact": "LOW", + "recommendation": "Introduce HF (high frequency) ballasts for fluorescent tubes: Reduced number of fittings required.", + "recommendation_code": "EPC-L7" + } + ], + "address_line_1": "Some Unit", + "address_line_2": "2 Lonely Street", + "address_line_3": "Some Area", + "address_line_4": "Some County", + "medium_payback": [ + { + "co2_impact": "MEDIUM", + "recommendation": "Add optimum start/stop to the heating system.", + "recommendation_code": "EPC-H7" + } + ], + "assessment_type": "CEPC-RR", + "inspection_date": "2020-05-04", + "calculation_tool": "CEPC Compute v0.2", + "formatted_report": "ZGVmYXVsdA==", + "registration_date": "2020-05-05", + "technical_information": { + "floor_area": 10, + "building_environment": "Natural Ventilation Only" + }, + "related_certificate_number": "0000-0000-0000-0000-0001" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-8.0.0/cepc.json b/backend/epc_api/json_samples/CEPC-8.0.0/cepc.json new file mode 100644 index 00000000..ec747ee7 --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-8.0.0/cepc.json @@ -0,0 +1,334 @@ +{ + "ber": 76.29, + "ser": 45.61, + "ter": 31.05, + "tyr": 90.98, + "uprn": 12457, + "status": "entered", + "postcode": "NE0 0AA", + "post_town": "Big Rock", + "energy_use": { + "energy_consumption_current": 451.27 + }, + "issue_date": "2021-03-19", + "methodology": "SBEM", + "report_type": 3, + "schema_type": "CEPC-8.0.0", + "uprn_source": "Energy Assessor", + "valid_until": "2031-03-18", + "asset_rating": 84, + "language_code": 1, + "output_engine": "EPCgen, v5.6.b.0", + "property_type": "A1/A2 Retail and Financial/Professional services", + "address_line_1": "60 Maple Syrup Road", + "address_line_2": "Candy Mountain", + "assessment_type": "CEPC", + "inspection_date": "2021-03-19", + "inspection_type": "Physical", + "ac_questionnaire": { + "ac_present": "No", + "ac_rated_output": { + "ac_kw_rating": 100, + "ac_rating_unknown_flag": 0 + }, + "ac_estimated_output": 3, + "ac_inspection_commissioned": 4 + }, + "calculation_tool": "G-ISBEM Ltd, G-ISBEM, v24.0, SBEM, v5.6.b.0", + "is_heritage_site": "N", + "transaction_type": 1, + "registration_date": "2021-03-19", + "building_complexity": "Level 3", + "new_build_benchmark": 34, + "technical_information": { + "floor_area": 951, + "building_level": 3, + "main_heating_fuel": "Grid Supplied Electricity", + "renewable_sources": "Renewable sources test", + "special_energy_uses": "Test sp", + "building_environment": "Air Conditioning", + "other_fuel_description": "Other fuel test" + }, + "summary_of_performance": { + "building_data": [ + { + "area": 951.34, + "weather": "NEW", + "activities": [ + { + "id": 1067, + "area": 81.32 + }, + { + "id": 1070, + "area": 13.9 + }, + { + "id": 1074, + "area": 258.42 + }, + { + "id": 1078, + "area": 76.99 + }, + { + "id": 1071, + "area": 490.62 + }, + { + "id": 1077, + "area": 30.09 + } + ], + "building_w_k": 193.204, + "hvac_systems": [ + { + "area": 353.64, + "type": "No Heating or Cooling", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1067, + "area": 81.32 + }, + { + "id": 1070, + "area": 13.9 + }, + { + "id": 1074, + "area": 258.42 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 0, + "heating_sseff": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 0, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 0, + "mj_m2_cooling_dem": 13.1255, + "mj_m2_heating_dem": 71.1616 + }, + { + "area": 567.61, + "type": "Split or multi-split system", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1078, + "area": 76.99 + }, + { + "id": 1071, + "area": 490.62 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 2.25, + "heating_sseff": 0.73, + "kwh_m2_cooling": 49.5997, + "kwh_m2_heating": 6.49072, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 2.47934, + "mj_m2_cooling_dem": 401.756, + "mj_m2_heating_dem": 17.0576 + }, + { + "area": 30.09, + "type": "Other local room heater - unfanned", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1077, + "area": 30.09 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 2.25, + "heating_sseff": 0.73, + "kwh_m2_cooling": 25.4056, + "kwh_m2_heating": 84.553, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 12.1545, + "mj_m2_cooling_dem": 205.785, + "mj_m2_heating_dem": 222.205 + } + ], + "analysis_type": "REFERENCE", + "area_exterior": 509.17, + "building_alpha": 10, + "building_w_m2k": 0.379449, + "q50_infiltration": 10, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 2.7961, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 30.3968, + "kwh_m2_heating": 6.54699, + "kwh_m2_lighting": 78.7393, + "kwh_m2_supplied": 110.999, + "kwh_m2_auxiliary": 1.86372, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 19.5261, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 9.34308, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 951.34, + "weather": "NEW", + "activities": [ + { + "id": 1067, + "area": 81.32 + }, + { + "id": 1070, + "area": 13.9 + }, + { + "id": 1074, + "area": 258.42 + }, + { + "id": 1078, + "area": 76.99 + }, + { + "id": 1071, + "area": 490.62 + }, + { + "id": 1077, + "area": 30.09 + } + ], + "building_w_k": 193.204, + "hvac_systems": [ + { + "area": 353.64, + "type": "No Heating or Cooling", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1067, + "area": 81.32 + }, + { + "id": 1070, + "area": 13.9 + }, + { + "id": 1074, + "area": 258.42 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 0, + "heating_sseff": 0, + "kwh_m2_cooling": 0, + "kwh_m2_heating": 0, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 0, + "mj_m2_cooling_dem": 13.1255, + "mj_m2_heating_dem": 71.1616 + }, + { + "area": 567.61, + "type": "Split or multi-split system", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1078, + "area": 76.99 + }, + { + "id": 1071, + "area": 490.62 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 2.25, + "heating_sseff": 0.73, + "kwh_m2_cooling": 49.5997, + "kwh_m2_heating": 6.49072, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 2.47934, + "mj_m2_cooling_dem": 401.756, + "mj_m2_heating_dem": 17.0576 + }, + { + "area": 30.09, + "type": "Other local room heater - unfanned", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1077, + "area": 30.09 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 2.25, + "heating_sseff": 0.73, + "kwh_m2_cooling": 25.4056, + "kwh_m2_heating": 84.553, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 12.1545, + "mj_m2_cooling_dem": 205.785, + "mj_m2_heating_dem": 222.205 + } + ], + "analysis_type": "ACTUAL", + "area_exterior": 509.17, + "building_alpha": 10, + "building_w_m2k": 0.379449, + "q50_infiltration": 10, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 2.7961, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 30.3968, + "kwh_m2_heating": 6.54699, + "kwh_m2_lighting": 78.7393, + "kwh_m2_supplied": 110.999, + "kwh_m2_auxiliary": 1.86372, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 19.5261, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 9.34308, + "kwh_m2_district_heating": 0 + } + } + ] + }, + "existing_stock_benchmark": 100, + "current_energy_efficiency_band": "D" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-8.0.0/dec+rr.json b/backend/epc_api/json_samples/CEPC-8.0.0/dec+rr.json new file mode 100644 index 00000000..83cfac0c --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-8.0.0/dec+rr.json @@ -0,0 +1,131 @@ +{ + "uprn": 12457, + "status": "entered", + "postcode": "A00 0AA", + "post_town": "Floatering", + "dec_status": 0, + "issue_date": "2021-10-12", + "methodology": "ORCalc", + "reason_type": 1, + "report_type": 1, + "schema_type": "CEPC-8.0.0", + "uprn_source": "Energy Assessor", + "valid_until": "2022-01-31", + "language_code": 1, + "output_engine": "ORGen v4.0.4", + "property_type": "Fitness And Health Centre; Swimming Pool Centre", + "address_line_1": "Swim & Fitness Centre", + "address_line_2": "Swimming Lane", + "assessment_type": "DEC", + "inspection_date": "2021-09-02", + "this_assessment": { + "heating_co2": 156, + "energy_rating": 42, + "nominated_date": "2021-09-01", + "renewables_co2": 13, + "electricity_co2": 70 + }, + "ac_questionnaire": { + "ac_present": "Yes", + "ac_rated_output": { + "ac_kw_rating": 30 + }, + "ac_inspection_commissioned": 1 + }, + "calculation_tool": "CLG, ORCalc, v4.0.4", + "or_building_data": { + "hvac_system": "Radiators", + "internal_environment": "Heating and Mechanical Ventilation", + "assessment_period_alignment": "End Of Main Heating Fuel Period" + }, + "or_previous_data": { + "asset_rating": 45 + }, + "building_category": "H7; H6;", + "or_benchmark_data": { + "benchmarks": [ + { + "name": "Gymnasium", + "tufa": 110.382, + "benchmark": "Fitness And Health Centre", + "floor_area": 110.382, + "area_metric": "Gross floor area measured as RICS Gross Internal Area (GIA)", + "benchmark_id": 1, + "occupancy_level": "Standard Occupancy" + }, + { + "name": "Swimming pool", + "tufa": 1358.936, + "benchmark": "Swimming Pool Centre", + "floor_area": 1358.936, + "area_metric": "Gross floor area measured as RICS Gross Internal Area (GIA)", + "benchmark_id": 2, + "occupancy_level": "Standard Occupancy" + } + ], + "main_benchmark": "Swimming Pool Centre" + }, + "registration_date": "2021-10-12", + "location_description": "Swimming pool with gumnasium.", + "or_usable_floor_area": { + "ufa_1": { + "name": "Basement Plant", + "floor_area": 118.16 + }, + "ufa_2": { + "name": "Basement void around pool", + "floor_area": 179.673 + }, + "ufa_3": { + "name": "Ground Plant", + "floor_area": 146.223 + }, + "ufa_4": { + "name": "First Floor Plant", + "floor_area": 49.807 + }, + "total_ufa": 493.863 + }, + "or_energy_consumption": { + "gas": { + "end_date": "2021-08-31", + "estimate": 0, + "start_date": "2020-09-01", + "consumption": 805167 + }, + "electricity": { + "end_date": "2021-08-31", + "estimate": 0, + "start_date": "2020-09-01", + "consumption": 126161 + } + }, + "technical_information": { + "floor_area": 1469.318, + "main_heating_fuel": "Natural Gas", + "building_environment": "Heating and Mechanical Ventilation", + "separately_metered_electric_heating": 0 + }, + "or_assessment_end_date": "2021-08-31", + "renewable_energy_source": [ + { + "name": "CHP", + "end_date": "2021-08-31", + "generation": 24589, + "start_date": "2020-09-01", + "energy_type": 0 + } + ], + "or_assessment_start_date": "2020-08-31", + "dec_annual_energy_summary": { + "typical_thermal_use": 1196.24, + "renewables_electrical": 16.3, + "typical_electrical_use": 238.61, + "renewables_fuel_thermal": 0, + "annual_energy_use_electrical": 86.1, + "annual_energy_use_fuel_thermal": 548.82 + }, + "related_certificate_number": "0000-0000-0000-0000-0001", + "dec_related_party_disclosure": 1, + "current_energy_efficiency_band": "B" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-8.0.0/dec-rr.json b/backend/epc_api/json_samples/CEPC-8.0.0/dec-rr.json new file mode 100644 index 00000000..94d8b78a --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-8.0.0/dec-rr.json @@ -0,0 +1,76 @@ +{ + "uprn": 12457, + "status": "entered", + "postcode": "SW1A 2AA", + "post_town": "Fulchester", + "issue_date": "2020-05-04", + "report_type": 2, + "schema_type": "CEPC-8.0.0", + "uprn_source": "Energy Assessor", + "valid_until": "2028-05-03", + "long_payback": [ + { + "co2_impact": "LOW", + "recommendation": "Consider replacing or improving glazing", + "recommendation_code": "ECP-F4" + } + ], + "language_code": 1, + "other_payback": [ + { + "co2_impact": "HIGH", + "recommendation": "Add a big wind turbine", + "recommendation_code": "ECP-H2" + } + ], + "property_type": "University campus", + "short_payback": [ + { + "co2_impact": "MEDIUM", + "recommendation": "Consider thinking about maybe possibly getting a solar panel but only one.", + "recommendation_code": "ECP-L5" + }, + { + "co2_impact": "LOW", + "recommendation": "Consider introducing variable speed drives (VSD) for fans, pumps and compressors.", + "recommendation_code": "EPC-L7" + } + ], + "site_services": { + "service_1": { + "quantity": 751445, + "description": "Electricity" + }, + "service_2": { + "quantity": 72956, + "description": "Gas" + }, + "service_3": { + "quantity": 0, + "description": "Not used" + } + }, + "address_line_1": "Some Unit", + "address_line_2": "2 Lonely Street", + "address_line_3": "Some Area", + "address_line_4": "Some County", + "medium_payback": [ + { + "co2_impact": "LOW", + "recommendation": "Engage experts to propose specific measures to reduce hot waterwastage and plan to carry this out.", + "recommendation_code": "ECP-C1" + } + ], + "assessment_type": "DEC-RR", + "inspection_date": "2020-05-04", + "inspection_type": "Physical", + "calculation_tool": "DCLG, ORCalc, v3.6.2", + "formatted_report": "ZGVmYXVsdA==", + "registration_date": "2020-05-04", + "technical_information": { + "floor_area": 10, + "renewable_sources": "Renewable source", + "special_energy_uses": "Special discount", + "building_environment": "Air Conditioning" + } +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/CEPC-8.0.0/dec.json b/backend/epc_api/json_samples/CEPC-8.0.0/dec.json new file mode 100644 index 00000000..898e34cb --- /dev/null +++ b/backend/epc_api/json_samples/CEPC-8.0.0/dec.json @@ -0,0 +1,299 @@ +{ + "uprn": 12457, + "status": "entered", + "postcode": "SW1A 2AA", + "post_town": "Whitbury", + "dec_status": 1, + "issue_date": "2020-05-14", + "reason_type": 6, + "report_type": 1, + "schema_type": "CEPC-8.0.0", + "uprn_source": "Energy Assessor", + "valid_until": "2026-05-04", + "language_code": 1, + "output_engine": "MWW-91.1.1", + "property_type": "B1 Offices and Workshop businesses", + "address_line_1": "Some Unit", + "address_line_2": "2 Lonely Street", + "address_line_3": "Some Area", + "address_line_4": "Some County", + "assessment_type": "DEC", + "inspection_date": "2020-05-04", + "this_assessment": { + "heating_co2": 3, + "energy_rating": 1, + "nominated_date": "2020-01-01", + "renewables_co2": 0, + "electricity_co2": 7 + }, + "ac_questionnaire": { + "ac_present": "Yes", + "ac_rated_output": { + "ac_kw_rating": 1 + }, + "ac_estimated_output": 1, + "ac_inspection_commissioned": 1 + }, + "calculation_tool": "DCLG, ORCalc, v3.6.3", + "is_heritage_site": "N", + "or_previous_data": { + "asset_rating": 100 + }, + "year1_assessment": { + "heating_co2": 5, + "energy_rating": 24, + "nominated_date": "2019-01-01", + "renewables_co2": 1, + "electricity_co2": 10 + }, + "year2_assessment": { + "heating_co2": 10, + "energy_rating": 40, + "nominated_date": "2018-01-01", + "renewables_co2": 2, + "electricity_co2": 15 + }, + "building_category": "C1", + "or_benchmark_data": { + "benchmarks": [ + { + "name": "Library", + "tufa": 1840, + "floor_area": 15, + "benchmark_id": 1, + "occupancy_level": "level" + } + ] + }, + "registration_date": "2020-05-04", + "building_complexity": "Level 3", + "or_energy_consumption": { + "gas": { + "end_date": "2007-12-18", + "estimate": 0, + "start_date": "2007-01-18", + "consumption": 310400 + }, + "electricity": { + "end_date": "2008-01-31", + "estimate": 1, + "start_date": "2007-01-31", + "consumption": 422480 + } + }, + "technical_information": { + "floor_area": 99, + "main_heating_fuel": "Natural Gas", + "special_energy_uses": "special", + "building_environment": "Heating and Natural Ventilation", + "other_fuel_description": "other" + }, + "or_assessment_end_date": "2020-05-01", + "summary_of_performance": { + "building_data": [ + { + "area": 402.6, + "weather": "LON", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "building_w_k": 411.86, + "hvac_systems": [ + { + "area": 402.6, + "type": "Fan coil systems", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 1.963, + "heating_sseff": 0.827, + "kwh_m2_cooling": 25.3865, + "kwh_m2_heating": 41.0355, + "cooling_gen_seer": 2.5, + "heating_gen_seff": 0.89, + "kwh_m2_auxiliary": 39.0832, + "mj_m2_cooling_dem": 179.401, + "mj_m2_heating_dem": 122.171 + } + ], + "analysis_type": "ACTUAL", + "area_exterior": 1058.32, + "building_alpha": 18.3412, + "building_w_m2k": 0.389164, + "q50_infiltration": 10, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 2.77834, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 3.07665, + "kwh_m2_ses": 0.801605, + "kwh_m2_coal": 0, + "kwh_m2_wind": 3.02777, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 25.3865, + "kwh_m2_heating": 41.0355, + "kwh_m2_lighting": 54.0787, + "kwh_m2_supplied": 121.327, + "kwh_m2_auxiliary": 39.0832, + "kwh_m2_displaced": 6.10442, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 42.185, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 41.0355, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 402.6, + "weather": "LON", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "building_w_k": 362.524, + "hvac_systems": [ + { + "area": 402.6, + "type": "Fan coil systems", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 3.6, + "heating_sseff": 0.819, + "kwh_m2_cooling": 7.46769, + "kwh_m2_heating": 18.1581, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 14.0716, + "mj_m2_cooling_dem": 96.7814, + "mj_m2_heating_dem": 53.5375 + } + ], + "analysis_type": "NOTIONAL", + "area_exterior": 1058.32, + "building_alpha": 11.2489, + "building_w_m2k": 0.342547, + "q50_infiltration": 3, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 3.33761, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 3.33761, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 7.46769, + "kwh_m2_heating": 18.1582, + "kwh_m2_lighting": 14.4489, + "kwh_m2_supplied": 35.9883, + "kwh_m2_auxiliary": 14.0716, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 42.185, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 18.1582, + "kwh_m2_district_heating": 0 + } + }, + { + "area": 402.6, + "weather": "LON", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "building_w_k": 718.761, + "hvac_systems": [ + { + "area": 402.6, + "type": "Fan coil systems", + "fuel_type": "Natural Gas", + "activities": [ + { + "id": 1005, + "area": 402.6 + } + ], + "heat_source": "LTHW boiler", + "cooling_sseer": 2.25, + "heating_sseff": 0.73, + "kwh_m2_cooling": 25.6538, + "kwh_m2_heating": 71.9516, + "cooling_gen_seer": 0, + "heating_gen_seff": 0, + "kwh_m2_auxiliary": 2.16062, + "mj_m2_cooling_dem": 207.795, + "mj_m2_heating_dem": 189.088 + } + ], + "analysis_type": "REFERENCE", + "area_exterior": 1058.32, + "building_alpha": 10, + "building_w_m2k": 0.679153, + "q50_infiltration": 10, + "global_performance": { + "kwh_m2_chp": 0, + "kwh_m2_dhw": 6.41192, + "kwh_m2_lpg": 0, + "kwh_m2_oil": 0, + "kwh_m2_pvs": 0, + "kwh_m2_ses": 0, + "kwh_m2_coal": 0, + "kwh_m2_wind": 0, + "kwh_m2_biogas": 0, + "kwh_m2_biomass": 0, + "kwh_m2_cooling": 25.6538, + "kwh_m2_heating": 71.9516, + "kwh_m2_lighting": 45.54, + "kwh_m2_supplied": 73.3544, + "kwh_m2_auxiliary": 2.16062, + "kwh_m2_displaced": 0, + "kwh_m2_dual_fuel": 0, + "kwh_m2_equipment": 42.185, + "kwh_m2_smokeless": 0, + "kwh_m2_anthracite": 0, + "kwh_m2_waste_heat": 0, + "kwh_m2_natural_gas": 78.3634, + "kwh_m2_district_heating": 0 + } + } + ] + }, + "or_assessment_start_date": "2020-05-01", + "dec_annual_energy_summary": { + "typical_thermal_use": 1, + "renewables_electrical": 1, + "typical_electrical_use": 1, + "renewables_fuel_thermal": 1, + "annual_energy_use_electrical": 1, + "annual_energy_use_fuel_thermal": 1 + }, + "dec_related_party_disclosure": 4, + "current_energy_efficiency_band": "A" +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/RdSAP-Schema-17.0/epc.json b/backend/epc_api/json_samples/RdSAP-Schema-17.0/epc.json new file mode 100644 index 00000000..e1d48ffc --- /dev/null +++ b/backend/epc_api/json_samples/RdSAP-Schema-17.0/epc.json @@ -0,0 +1,352 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": { + "value": "(another dwelling above)", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "walls": [ + { + "description": { + "value": "System built, with internal insulation", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": { + "value": "(another dwelling below)", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 2, + "window": { + "description": { + "value": "Fully double glazed", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "lighting": { + "description": { + "value": "Low energy lighting in 57% of fixed outlets", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "postcode": "PT5 4RZ", + "hot_water": { + "description": { + "value": "Electric immersion, off-peak", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 2 + }, + "post_town": "POSTTOWN", + "built_form": 2, + "door_count": 2, + "glazed_area": 1, + "glazing_gap": "16+", + "region_code": 3, + "report_type": 2, + "sap_heating": { + "cylinder_size": 2, + "water_heating_code": 903, + "water_heating_fuel": 29, + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 29, + "heat_emitter_type": 0, + "emitter_temperature": "NA", + "main_heating_number": 1, + "main_heating_control": 2401, + "main_heating_category": 7, + "main_heating_fraction": 1, + "sap_main_heating_code": 402, + "main_heating_data_source": 2 + } + ], + "immersion_heating_type": 1, + "cylinder_insulation_type": 0, + "has_fixed_air_conditioning": "false" + }, + "sap_version": 9.92, + "schema_type": "RdSAP-Schema-17.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": { + "value": "Electric storage heaters", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 1 + } + ], + "dwelling_type": { + "value": "Mid-floor flat", + "language": "1" + }, + "language_code": 1, + "property_type": 2, + "address_line_1": "42, Moria Mines Lane", + "assessment_type": "RdSAP", + "completion_date": "2016-01-12", + "inspection_date": "2016-01-12", + "extensions_count": 0, + "measurement_type": 1, + "sap_flat_details": { + "level": 2, + "top_storey": "N", + "flat_location": 1, + "heat_loss_corridor": 0 + }, + "total_floor_area": 55, + "transaction_type": 8, + "conservatory_type": 1, + "heated_room_count": 1, + "pvc_window_frames": "true", + "registration_date": "2016-01-12", + "sap_energy_source": { + "mains_gas": "N", + "meter_type": 1, + "photovoltaic_supply": { + "none_or_no_details": { + "pv_connection": 0, + "percent_roof_area": 0 + } + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": { + "value": "Portable electric heaters (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 11 + ], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 240, + "floor_heat_loss": 6, + "roof_construction": 3, + "wall_construction": 8, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.4, + "quantity": "metres" + }, + "total_floor_area": { + "value": 54.6, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 7.3, + "quantity": "metres" + }, + "heat_loss_perimeter": { + "value": 23.3, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 3, + "construction_age_band": "D", + "party_wall_construction": 0, + "wall_thickness_measured": "Y", + "roof_insulation_location": "ND", + "roof_insulation_thickness": "ND", + "wall_insulation_thickness": "50mm" + } + ], + "low_energy_lighting": 57, + "solar_water_heating": "N", + "habitable_room_count": 3, + "heating_cost_current": { + "value": 214, + "currency": "GBP" + }, + "insulated_door_count": 0, + "co2_emissions_current": 3.9, + "energy_rating_average": 60, + "energy_rating_current": 66, + "lighting_cost_current": { + "value": 61, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": { + "value": "Manual charge control", + "language": "1" + }, + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + } + ], + "multiple_glazing_type": 3, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 216, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 396, + "currency": "GBP" + }, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 158, + "currency": "GBP" + }, + "indicative_cost": "£15 - £30", + "improvement_type": "C", + "improvement_details": { + "improvement_number": 1 + }, + "improvement_category": 5, + "energy_performance_rating": 74, + "environmental_impact_rating": 60 + }, + { + "sequence": 2, + "typical_saving": { + "value": 14, + "currency": "GBP" + }, + "indicative_cost": "£15", + "improvement_type": "E", + "improvement_details": { + "improvement_number": 35 + }, + "improvement_category": 5, + "energy_performance_rating": 74, + "environmental_impact_rating": 60 + }, + { + "sequence": 3, + "typical_saving": { + "value": 64, + "currency": "GBP" + }, + "indicative_cost": "£1,200 - £1,800", + "improvement_type": "L2", + "improvement_details": { + "improvement_number": 60 + }, + "improvement_category": 5, + "energy_performance_rating": 78, + "environmental_impact_rating": 64 + }, + { + "sequence": 4, + "typical_saving": { + "value": 22, + "currency": "GBP" + }, + "indicative_cost": "£1,000", + "improvement_type": "X", + "improvement_details": { + "improvement_number": 48 + }, + "improvement_category": 5, + "energy_performance_rating": 79, + "environmental_impact_rating": 66 + } + ], + "co2_emissions_potential": 2.5, + "energy_rating_potential": 79, + "lighting_cost_potential": { + "value": 42, + "currency": "GBP" + }, + "schema_version_original": "LIG-17.0", + "alternative_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 141, + "currency": "GBP" + }, + "improvement_type": "J2", + "improvement_details": { + "improvement_number": 54 + }, + "improvement_category": 6, + "energy_performance_rating": 81, + "environmental_impact_rating": 96 + }, + { + "sequence": 2, + "typical_saving": { + "value": 118, + "currency": "GBP" + }, + "improvement_type": "Z1", + "improvement_details": { + "improvement_number": 51 + }, + "improvement_category": 6, + "energy_performance_rating": 80, + "environmental_impact_rating": 83 + } + ], + "hot_water_cost_potential": { + "value": 154, + "currency": "GBP" + }, + "renewable_heat_incentive": { + "water_heating": 4818, + "space_heating_existing_dwelling": 2415 + }, + "energy_consumption_current": 427, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "9.0.0", + "energy_consumption_potential": 267, + "environmental_impact_current": 48, + "fixed_lighting_outlets_count": 7, + "current_energy_efficiency_band": "D", + "environmental_impact_potential": 66, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 72, + "low_energy_fixed_lighting_outlets_count": 4 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/RdSAP-Schema-17.1/epc.json b/backend/epc_api/json_samples/RdSAP-Schema-17.1/epc.json new file mode 100644 index 00000000..35db05fd --- /dev/null +++ b/backend/epc_api/json_samples/RdSAP-Schema-17.1/epc.json @@ -0,0 +1,472 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": { + "value": "Pitched, 100 mm loft insulation", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + { + "description": { + "value": "Pitched, insulated (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": { + "value": "Cavity wall, filled cavity", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + { + "description": { + "value": "Cavity wall, as built, insulated (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": { + "value": "Solid, no insulation (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 1, + "window": { + "description": { + "value": "Fully double glazed", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "lighting": { + "description": { + "value": "Low energy lighting in 23% of fixed outlets", + "language": "1" + }, + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + }, + "postcode": "PT42 5HL", + "hot_water": { + "description": { + "value": "From main system", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "post_town": "POSTTOWN", + "built_form": 1, + "door_count": 4, + "glazed_area": 1, + "glazing_gap": "16+", + "region_code": 17, + "report_type": 2, + "sap_heating": { + "cylinder_size": 2, + "water_heating_code": 901, + "water_heating_fuel": 28, + "cylinder_thermostat": "Y", + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 1 + }, + "secondary_fuel_type": 29, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 28, + "boiler_flue_type": 1, + "fan_flue_present": "N", + "heat_emitter_type": 1, + "emitter_temperature": "NA", + "main_heating_number": 1, + "main_heating_control": 2104, + "main_heating_category": 2, + "main_heating_fraction": 1, + "mcs_installed_heat_pump": "false", + "main_heating_data_source": 1, + "main_heating_index_number": 9049 + } + ], + "immersion_heating_type": "NA", + "secondary_heating_type": 691, + "cylinder_insulation_type": 1, + "has_fixed_air_conditioning": "false", + "cylinder_insulation_thickness": 12 + }, + "sap_version": 9.92, + "schema_type": "RdSAP-Schema-17.1", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": { + "value": "Boiler and radiators, oil", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + } + ], + "dwelling_type": { + "value": "Detached house", + "language": "1" + }, + "language_code": 1, + "property_type": 0, + "address_line_1": "15, Hedge Lane", + "address_line_2": "Lower Moria", + "assessment_type": "RdSAP", + "completion_date": "2018-05-29", + "inspection_date": "2018-05-29", + "extensions_count": 1, + "measurement_type": 2, + "total_floor_area": 101, + "transaction_type": 1, + "conservatory_type": 1, + "heated_room_count": 7, + "pvc_window_frames": "true", + "registration_date": "2018-05-27", + "sap_energy_source": { + "mains_gas": "N", + "meter_type": 2, + "photovoltaic_supply": { + "none_or_no_details": { + "pv_connection": 0, + "percent_roof_area": 0 + } + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": { + "value": "Room heaters, electric", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 11 + ], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 270, + "floor_heat_loss": 7, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.4, + "quantity": "metres" + }, + "floor_insulation": 1, + "total_floor_area": { + "value": 48.45, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 0, + "quantity": "metres" + }, + "floor_construction": 1, + "heat_loss_perimeter": { + "value": 22.3, + "quantity": "metres" + } + }, + { + "floor": 1, + "room_height": { + "value": 2.4, + "quantity": "metres" + }, + "total_floor_area": { + "value": 48.45, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 0, + "quantity": "metres" + }, + "heat_loss_perimeter": { + "value": 28.4, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 2, + "construction_age_band": "G", + "party_wall_construction": "NA", + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "100mm" + }, + { + "identifier": "Extension 1", + "wall_dry_lined": "N", + "wall_thickness": 260, + "floor_heat_loss": 7, + "roof_construction": 5, + "wall_construction": 4, + "building_part_number": 2, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.4, + "quantity": "metres" + }, + "floor_insulation": 1, + "total_floor_area": { + "value": 21.84, + "quantity": "square metres" + }, + "party_wall_length": 0, + "floor_construction": 1, + "heat_loss_perimeter": { + "value": 16.6, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 4, + "construction_age_band": "H", + "party_wall_construction": "NA", + "wall_thickness_measured": "Y", + "roof_insulation_location": 4, + "roof_insulation_thickness": 0, + "wall_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 23, + "solar_water_heating": "N", + "habitable_room_count": 7, + "heating_cost_current": { + "value": 659, + "currency": "GBP" + }, + "insulated_door_count": 0, + "co2_emissions_current": 5.8, + "energy_rating_average": 60, + "energy_rating_current": 53, + "lighting_cost_current": { + "value": 115, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": { + "value": "Programmer and room thermostat", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + } + ], + "multiple_glazing_type": 3, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 470, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 161, + "currency": "GBP" + }, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 25, + "currency": "GBP" + }, + "indicative_cost": "£100 - £350", + "improvement_type": "A", + "improvement_details": { + "improvement_number": 5 + }, + "improvement_category": 5, + "energy_performance_rating": 54, + "environmental_impact_rating": 47 + }, + { + "sequence": 2, + "typical_saving": { + "value": 87, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "W2", + "improvement_details": { + "improvement_number": 58 + }, + "improvement_category": 5, + "energy_performance_rating": 58, + "environmental_impact_rating": 52 + }, + { + "sequence": 3, + "typical_saving": { + "value": 16, + "currency": "GBP" + }, + "indicative_cost": "£15 - £30", + "improvement_type": "C", + "improvement_details": { + "improvement_number": 3 + }, + "improvement_category": 5, + "energy_performance_rating": 59, + "environmental_impact_rating": 53 + }, + { + "sequence": 4, + "typical_saving": { + "value": 42, + "currency": "GBP" + }, + "indicative_cost": "£50", + "improvement_type": "E", + "improvement_details": { + "improvement_number": 35 + }, + "improvement_category": 5, + "energy_performance_rating": 61, + "environmental_impact_rating": 54 + }, + { + "sequence": 5, + "typical_saving": { + "value": 38, + "currency": "GBP" + }, + "indicative_cost": "£350 - £450", + "improvement_type": "G", + "improvement_details": { + "improvement_number": 13 + }, + "improvement_category": 5, + "energy_performance_rating": 62, + "environmental_impact_rating": 56 + }, + { + "sequence": 6, + "typical_saving": { + "value": 48, + "currency": "GBP" + }, + "indicative_cost": "£2,200 - £3,000", + "improvement_type": "I", + "improvement_details": { + "improvement_number": 20 + }, + "improvement_category": 5, + "energy_performance_rating": 65, + "environmental_impact_rating": 59 + }, + { + "sequence": 7, + "typical_saving": { + "value": 41, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 68, + "environmental_impact_rating": 62 + }, + { + "sequence": 8, + "typical_saving": { + "value": 27, + "currency": "GBP" + }, + "indicative_cost": "£2,000", + "improvement_type": "X", + "improvement_details": { + "improvement_number": 48 + }, + "improvement_category": 5, + "energy_performance_rating": 69, + "environmental_impact_rating": 64 + }, + { + "sequence": 9, + "typical_saving": { + "value": 297, + "currency": "GBP" + }, + "indicative_cost": "£5,000 - £8,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 78, + "environmental_impact_rating": 72 + } + ], + "co2_emissions_potential": 2.7, + "energy_rating_potential": 78, + "lighting_cost_potential": { + "value": 65, + "currency": "GBP" + }, + "schema_version_original": "LIG-17.1", + "hot_water_cost_potential": { + "value": 77, + "currency": "GBP" + }, + "renewable_heat_incentive": { + "water_heating": 3301, + "impact_of_loft_insulation": -565, + "space_heating_existing_dwelling": 11351 + }, + "energy_consumption_current": 234, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "v92.0.1.1", + "energy_consumption_potential": 95, + "environmental_impact_current": 46, + "fixed_lighting_outlets_count": 13, + "current_energy_efficiency_band": "E", + "environmental_impact_potential": 72, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 57, + "low_energy_fixed_lighting_outlets_count": 3 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/RdSAP-Schema-18.0/epc.json b/backend/epc_api/json_samples/RdSAP-Schema-18.0/epc.json new file mode 100644 index 00000000..44761b86 --- /dev/null +++ b/backend/epc_api/json_samples/RdSAP-Schema-18.0/epc.json @@ -0,0 +1,413 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": { + "value": "Pitched, 100 mm loft insulation", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + { + "description": { + "value": "Flat, insulated", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + { + "description": { + "value": "Roof room(s), insulated (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": { + "value": "Solid brick, as built, no insulation (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 1, + "environmental_efficiency_rating": 1 + }, + { + "description": { + "value": "Cavity wall, as built, insulated (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": { + "value": "Solid, no insulation (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 1, + "window": { + "description": { + "value": "Fully double glazed", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "lighting": { + "description": { + "value": "Low energy lighting in 67% of fixed outlets", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "postcode": "PT11 4RF", + "hot_water": { + "description": { + "value": "From main system", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "POSTTOWN", + "built_form": 4, + "door_count": 2, + "glazed_area": 1, + "glazing_gap": 12, + "region_code": 1, + "report_type": 2, + "sap_heating": { + "cylinder_size": 1, + "water_heating_code": 901, + "water_heating_fuel": 26, + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "central_heating_pump_age": 0, + "main_heating_data_source": 1, + "main_heating_index_number": 16137 + } + ], + "immersion_heating_type": "NA", + "has_fixed_air_conditioning": "false" + }, + "sap_version": 9.92, + "schema_type": "RdSAP-Schema-18.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": { + "value": "Boiler and radiators, mains gas", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": { + "value": "Mid-terrace house", + "language": "1" + }, + "language_code": 1, + "property_type": 0, + "address_line_1": "1, Bagshot Lane", + "address_line_2": "Village", + "assessment_type": "RdSAP", + "completion_date": "2017-03-19", + "inspection_date": "2017-03-19", + "extensions_count": 1, + "measurement_type": 1, + "total_floor_area": 93, + "transaction_type": 5, + "conservatory_type": 1, + "heated_room_count": 5, + "pvc_window_frames": "true", + "registration_date": "2017-03-19", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 1, + "photovoltaic_supply": { + "none_or_no_details": { + "pv_connection": 0, + "percent_roof_area": 0 + } + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": { + "value": "None", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 11 + ], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 330, + "floor_heat_loss": 7, + "sap_room_in_roof": { + "floor_area": { + "value": 9, + "quantity": "square metres" + }, + "insulation": "AB", + "roof_room_connected": "N", + "construction_age_band": "G" + }, + "roof_construction": 4, + "wall_construction": 3, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.4, + "quantity": "metres" + }, + "floor_insulation": 1, + "total_floor_area": { + "value": 29.12, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 11.2, + "quantity": "metres" + }, + "floor_construction": 1, + "heat_loss_perimeter": { + "value": 5.2, + "quantity": "metres" + } + }, + { + "floor": 1, + "room_height": { + "value": 2.4, + "quantity": "metres" + }, + "total_floor_area": { + "value": 29.12, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 11.2, + "quantity": "metres" + }, + "heat_loss_perimeter": { + "value": 10.4, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 4, + "construction_age_band": "C", + "party_wall_construction": 0, + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "100mm", + "wall_insulation_thickness": "NI" + }, + { + "identifier": "Extension", + "wall_dry_lined": "N", + "wall_thickness": 290, + "floor_heat_loss": 7, + "roof_construction": 1, + "wall_construction": 4, + "building_part_number": 2, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.4, + "quantity": "metres" + }, + "floor_insulation": 1, + "total_floor_area": { + "value": 15.6, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 6, + "quantity": "metres" + }, + "floor_construction": 1, + "heat_loss_perimeter": { + "value": 5.2, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 4, + "construction_age_band": "G", + "party_wall_construction": 0, + "wall_thickness_measured": "Y", + "roof_insulation_location": 6, + "wall_insulation_thickness": "NI", + "flat_roof_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 67, + "solar_water_heating": "N", + "habitable_room_count": 5, + "heating_cost_current": { + "value": 619, + "currency": "GBP" + }, + "insulated_door_count": 0, + "co2_emissions_current": 3.4, + "energy_rating_average": 60, + "energy_rating_current": 69, + "lighting_cost_current": { + "value": 81, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": { + "value": "Programmer, room thermostat and TRVs", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "multiple_glazing_type": 3, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "false", + "heating_cost_potential": { + "value": 534, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 100, + "currency": "GBP" + }, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 88, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £14,000", + "improvement_type": "Q", + "improvement_details": { + "improvement_number": 7 + }, + "improvement_category": 5, + "energy_performance_rating": 72, + "environmental_impact_rating": 71 + }, + { + "sequence": 2, + "typical_saving": { + "value": 18, + "currency": "GBP" + }, + "indicative_cost": "£15", + "improvement_type": "E", + "improvement_details": { + "improvement_number": 35 + }, + "improvement_category": 5, + "energy_performance_rating": 73, + "environmental_impact_rating": 71 + }, + { + "sequence": 3, + "typical_saving": { + "value": 33, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 74, + "environmental_impact_rating": 73 + }, + { + "sequence": 4, + "typical_saving": { + "value": 302, + "currency": "GBP" + }, + "indicative_cost": "£5,000 - £8,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 85, + "environmental_impact_rating": 83 + } + ], + "co2_emissions_potential": 1.7, + "energy_rating_potential": 85, + "lighting_cost_potential": { + "value": 61, + "currency": "GBP" + }, + "schema_version_original": "LIG-17.0", + "hot_water_cost_potential": { + "value": 68, + "currency": "GBP" + }, + "renewable_heat_incentive": { + "water_heating": 2087, + "impact_of_loft_insulation": -214, + "impact_of_solid_wall_insulation": -1864, + "space_heating_existing_dwelling": 10483 + }, + "energy_consumption_current": 230, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "2.0.0.0", + "energy_consumption_potential": 115, + "environmental_impact_current": 66, + "fixed_lighting_outlets_count": 9, + "current_energy_efficiency_band": "C", + "environmental_impact_potential": 83, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "B", + "co2_emissions_current_per_floor_area": 41, + "low_energy_fixed_lighting_outlets_count": 6 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/RdSAP-Schema-19.0/epc.json b/backend/epc_api/json_samples/RdSAP-Schema-19.0/epc.json new file mode 100644 index 00000000..ce4e44b4 --- /dev/null +++ b/backend/epc_api/json_samples/RdSAP-Schema-19.0/epc.json @@ -0,0 +1,386 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": { + "value": "Pitched, 150 mm loft insulation", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + { + "description": { + "value": "Pitched, insulated (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + } + ], + "walls": [ + { + "description": { + "value": "Cavity wall, filled cavity", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + { + "description": { + "value": "Cavity wall, as built, insulated (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": { + "value": "Solid, no insulation (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 3, + "window": { + "description": { + "value": "Fully double glazed", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "lighting": { + "description": { + "value": "Low energy lighting in 87% of fixed outlets", + "language": "1" + }, + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "A1 1AA", + "hot_water": { + "description": { + "value": "From main system", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Town", + "built_form": 2, + "door_count": 1, + "glazed_area": 1, + "region_code": 19, + "report_type": 2, + "sap_heating": { + "cylinder_size": 1, + "water_heating_code": 901, + "water_heating_fuel": 26, + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "secondary_fuel_type": 9, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "central_heating_pump_age": 0, + "main_heating_data_source": 1, + "main_heating_index_number": 15274 + } + ], + "immersion_heating_type": "NA", + "secondary_heating_type": 631, + "has_fixed_air_conditioning": "false" + }, + "sap_version": 9.94, + "schema_type": "RdSAP-Schema-19.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": { + "value": "Boiler and radiators, mains gas", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": { + "value": "Semi-detached house", + "language": "1" + }, + "language_code": 1, + "property_type": 0, + "address_line_1": "15, Address Lane", + "address_line_2": "New Town", + "assessment_type": "RdSAP", + "completion_date": "2020-06-04", + "inspection_date": "2020-06-03", + "extensions_count": 1, + "measurement_type": 1, + "total_floor_area": 94, + "transaction_type": 8, + "conservatory_type": 2, + "heated_room_count": 5, + "pvc_window_frames": "false", + "registration_date": "2020-06-04", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 3, + "photovoltaic_supply": { + "none_or_no_details": { + "pv_connection": 0, + "percent_roof_area": 0 + } + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": { + "value": "Room heaters, dual fuel (mineral and wood)", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 11, + 9 + ], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 300, + "floor_heat_loss": 7, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.47, + "quantity": "metres" + }, + "floor_insulation": 1, + "total_floor_area": { + "value": 39.91, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 8.81, + "quantity": "metres" + }, + "floor_construction": 1, + "heat_loss_perimeter": { + "value": 13.65, + "quantity": "metres" + } + }, + { + "floor": 1, + "room_height": { + "value": 2.36, + "quantity": "metres" + }, + "total_floor_area": { + "value": 39.91, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 8.81, + "quantity": "metres" + }, + "heat_loss_perimeter": { + "value": 17.87, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 2, + "construction_age_band": "D", + "party_wall_construction": 1, + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "150mm", + "wall_insulation_thickness": "NI", + "floor_insulation_thickness": "NI" + }, + { + "identifier": "Extension", + "wall_dry_lined": "N", + "wall_thickness": 300, + "floor_heat_loss": 7, + "roof_construction": 5, + "wall_construction": 4, + "building_part_number": 2, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.34, + "quantity": "metres" + }, + "floor_insulation": 1, + "total_floor_area": { + "value": 13.88, + "quantity": "square metres" + }, + "party_wall_length": 0, + "floor_construction": 1, + "heat_loss_perimeter": { + "value": 10.8, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 4, + "construction_age_band": "G", + "party_wall_construction": "NA", + "wall_thickness_measured": "Y", + "roof_insulation_location": 4, + "roof_insulation_thickness": "ND", + "wall_insulation_thickness": "NI", + "floor_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 87, + "solar_water_heating": "N", + "habitable_room_count": 5, + "heating_cost_current": { + "value": 666, + "currency": "GBP" + }, + "insulated_door_count": 0, + "co2_emissions_current": 3.8, + "energy_rating_average": 60, + "energy_rating_current": 66, + "lighting_cost_current": { + "value": 81, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": { + "value": "Programmer, room thermostat and TRVs", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "multiple_glazing_type": 3, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "false", + "heating_cost_potential": { + "value": 615, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 107, + "currency": "GBP" + }, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 51, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "W2", + "improvement_details": { + "improvement_number": 58 + }, + "improvement_category": 5, + "energy_performance_rating": 68, + "environmental_impact_rating": 65 + }, + { + "sequence": 2, + "typical_saving": { + "value": 33, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 70, + "environmental_impact_rating": 67 + }, + { + "sequence": 3, + "typical_saving": { + "value": 316, + "currency": "GBP" + }, + "indicative_cost": "£3,500 - £5,500", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 79, + "environmental_impact_rating": 75 + } + ], + "co2_emissions_potential": 2.4, + "energy_rating_potential": 79, + "lighting_cost_potential": { + "value": 81, + "currency": "GBP" + }, + "schema_version_original": "LIG-19.0", + "hot_water_cost_potential": { + "value": 74, + "currency": "GBP" + }, + "renewable_heat_incentive": { + "water_heating": 2207, + "impact_of_loft_insulation": -394, + "space_heating_existing_dwelling": 9825 + }, + "energy_consumption_current": 222, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "2.1.1.0", + "energy_consumption_potential": 137, + "environmental_impact_current": 62, + "fixed_lighting_outlets_count": 15, + "windows_transmission_details": { + "u_value": 3.1, + "data_source": 2, + "solar_transmittance": 0.76 + }, + "current_energy_efficiency_band": "D", + "environmental_impact_potential": 75, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 41, + "low_energy_fixed_lighting_outlets_count": 13 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/RdSAP-Schema-20.0.0/epc.json b/backend/epc_api/json_samples/RdSAP-Schema-20.0.0/epc.json new file mode 100644 index 00000000..bcb3618f --- /dev/null +++ b/backend/epc_api/json_samples/RdSAP-Schema-20.0.0/epc.json @@ -0,0 +1,340 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Pitched, 25 mm loft insulation", + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + }, + { + "description": "Pitched, 250 mm loft insulation", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": "Solid brick, as built, no insulation (assumed)", + "energy_efficiency_rating": 1, + "environmental_efficiency_rating": 1 + }, + { + "description": "Cavity wall, as built, insulated (assumed)", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": "Suspended, no insulation (assumed)", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + { + "description": "Solid, insulated (assumed)", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 1, + "window": { + "description": "Fully double glazed", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "addendum": { + "stone_walls": "true", + "system_build": "true", + "addendum_numbers": [ + 1, + 8 + ] + }, + "lighting": { + "description": "Low energy lighting in 50% of fixed outlets", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "postcode": "A0 0AA", + "hot_water": { + "description": "From main system", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Whitbury", + "built_form": 2, + "door_count": 2, + "glazed_area": 1, + "region_code": 1, + "report_type": 2, + "sap_heating": { + "cylinder_size": 1, + "water_heating_code": 901, + "water_heating_fuel": 26, + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "secondary_fuel_type": 25, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "N", + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "sap_main_heating_code": 101, + "central_heating_pump_age": 0, + "main_heating_data_source": 1, + "main_heating_index_number": 17507 + } + ], + "immersion_heating_type": "NA", + "has_fixed_air_conditioning": "false" + }, + "sap_version": 9.8, + "sap_windows": [ + { + "orientation": 1, + "window_area": 200.1, + "window_type": 2, + "glazing_type": 1, + "window_location": 0 + }, + { + "orientation": 2, + "window_area": 180.2, + "window_type": 1, + "glazing_type": 2, + "window_location": 1 + } + ], + "schema_type": "RdSAP-Schema-20.0.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": "Boiler and radiators, anthracite", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 1 + }, + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "property_type": 0, + "address_line_1": "1 Some Street", + "assessment_type": "RdSAP", + "completion_date": "2020-05-04", + "inspection_date": "2020-05-04", + "extensions_count": 0, + "measurement_type": 1, + "sap_flat_details": { + "level": 1, + "top_storey": "N", + "storey_count": 3, + "flat_location": 1, + "heat_loss_corridor": 2, + "unheated_corridor_length": 10 + }, + "total_floor_area": 55, + "transaction_type": 1, + "conservatory_type": 1, + "heated_room_count": 5, + "registration_date": "2020-05-04", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 2, + "photovoltaic_supply": { + "none_or_no_details": { + "pv_connection": 0, + "percent_roof_area": 50 + } + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": "Room heaters, electric", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 11 + ], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "floor_heat_loss": 7, + "sap_room_in_roof": { + "floor_area": 100, + "insulation": "AB", + "roof_room_connected": "N", + "construction_age_band": "B" + }, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.45, + "quantity": "metres" + }, + "floor_insulation": 1, + "total_floor_area": { + "value": 45.82, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 7.9, + "quantity": "metres" + }, + "floor_construction": 1, + "heat_loss_perimeter": { + "value": 19.5, + "quantity": "metres" + } + }, + { + "floor": 1, + "room_height": { + "value": 2.59, + "quantity": "metres" + }, + "total_floor_area": { + "value": 45.82, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 7.9, + "quantity": "metres" + }, + "heat_loss_perimeter": { + "value": 19.5, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 2, + "construction_age_band": "K", + "party_wall_construction": 0, + "wall_thickness_measured": "N", + "roof_insulation_location": 2, + "roof_insulation_thickness": "200mm", + "wall_insulation_thickness": "NI", + "floor_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 100, + "solar_water_heating": "N", + "habitable_room_count": 5, + "heating_cost_current": 365.98, + "insulated_door_count": 2, + "co2_emissions_current": 2.4, + "energy_rating_average": 60, + "energy_rating_current": 50, + "lighting_cost_current": 123.45, + "main_heating_controls": [ + { + "description": "Programmer, room thermostat and TRVs", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + { + "description": "Time and temperature zone control", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "multiple_glazing_type": 2, + "open_fireplaces_count": 0, + "heating_cost_potential": 250.34, + "hot_water_cost_current": 200.4, + "insulated_door_u_value": 3, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": 360, + "indicative_cost": "£100 - £350", + "improvement_type": "Z3", + "improvement_details": { + "improvement_number": 5 + }, + "improvement_category": 6, + "energy_performance_rating": 50, + "environmental_impact_rating": 50 + }, + { + "sequence": 2, + "typical_saving": 99, + "indicative_cost": 2000, + "improvement_type": "Z2", + "improvement_details": { + "improvement_number": 1 + }, + "improvement_category": 2, + "energy_performance_rating": 60, + "environmental_impact_rating": 64 + }, + { + "sequence": 3, + "typical_saving": 99, + "indicative_cost": 1000, + "improvement_type": "Z2", + "improvement_details": { + "improvement_texts": { + "improvement_summary": "An improvement summary", + "improvement_description": "An improvement desc" + } + }, + "improvement_category": 2, + "energy_performance_rating": 60, + "environmental_impact_rating": 64 + } + ], + "co2_emissions_potential": 1.4, + "energy_rating_potential": 72, + "lighting_cost_potential": 84.23, + "schema_version_original": "SAP-19.0", + "hot_water_cost_potential": 180.43, + "renewable_heat_incentive": { + "water_heating": 2285, + "impact_of_loft_insulation": -2114, + "impact_of_cavity_insulation": -122, + "impact_of_solid_wall_insulation": -3560, + "space_heating_existing_dwelling": 13120 + }, + "energy_consumption_current": 230, + "multiple_glazed_proportion": 100, + "calculation_software_version": "13.05r16", + "energy_consumption_potential": 88, + "environmental_impact_current": 52, + "fixed_lighting_outlets_count": 16, + "windows_transmission_details": { + "u_value": 2, + "data_source": 2, + "solar_transmittance": 0.72 + }, + "multiple_glazed_proportion_nr": "NR", + "current_energy_efficiency_band": "E", + "environmental_impact_potential": 74, + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 20, + "low_energy_fixed_lighting_outlets_count": 16 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/RdSAP-Schema-21.0.0/epc.json b/backend/epc_api/json_samples/RdSAP-Schema-21.0.0/epc.json new file mode 100644 index 00000000..3f9793b0 --- /dev/null +++ b/backend/epc_api/json_samples/RdSAP-Schema-21.0.0/epc.json @@ -0,0 +1,403 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Pitched, 25 mm loft insulation", + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + }, + { + "description": "Pitched, 250 mm loft insulation", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": "Solid brick, as built, no insulation (assumed)", + "energy_efficiency_rating": 1, + "environmental_efficiency_rating": 1 + }, + { + "description": "Cavity wall, as built, insulated (assumed)", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": "Suspended, no insulation (assumed)", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + { + "description": "Solid, insulated (assumed)", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 1, + "window": { + "description": "Fully double glazed", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "addendum": { + "stone_walls": "true", + "system_build": "true", + "addendum_numbers": [ + 1, + 8 + ] + }, + "lighting": { + "description": "Low energy lighting in 50% of fixed outlets", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "postcode": "A0 0AA", + "hot_water": { + "description": "From main system", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Whitbury", + "built_form": 2, + "door_count": 3, + "region_code": 1, + "report_type": 2, + "sap_heating": { + "cylinder_size": 1, + "shower_outlets": { + "shower_outlet": { + "shower_wwhrs": 1, + "shower_outlet_type": 1 + } + }, + "water_heating_code": 901, + "water_heating_fuel": 26, + "instantaneous_wwhrs": { + "wwhrs_index_number1": 1, + "wwhrs_index_number2": 2 + }, + "secondary_fuel_type": 25, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "N", + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "boiler_ignition_type": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "sap_main_heating_code": 101, + "central_heating_pump_age": 0, + "main_heating_data_source": 1, + "main_heating_index_number": 17507 + } + ], + "immersion_heating_type": "NA", + "has_fixed_air_conditioning": "false" + }, + "sap_version": 10.2, + "sap_windows": [ + { + "pvc_frame": "false", + "glazing_gap": 6, + "orientation": 1, + "window_type": 2, + "frame_factor": 1.0, + "glazing_type": 14, + "window_width": 1.2, + "window_height": 2.0, + "draught_proofed": "true", + "window_location": 0, + "window_wall_type": 6, + "permanent_shutters_present": "N", + "window_transmission_details": { + "u_value": 1.0, + "data_source": 2, + "solar_transmittance": 1.0 + }, + "permanent_shutters_insulated": "N" + }, + { + "pvc_frame": "false", + "glazing_gap": 6, + "orientation": 1, + "window_type": 2, + "frame_factor": 1.0, + "glazing_type": 1, + "window_width": 1.2, + "window_height": 2.0, + "draught_proofed": "true", + "window_location": 1, + "window_wall_type": 6, + "permanent_shutters_present": "N", + "window_transmission_details": { + "u_value": 1.0, + "data_source": 2, + "solar_transmittance": 1.0 + }, + "permanent_shutters_insulated": "N" + } + ], + "schema_type": "RdSAP-Schema-21.0.0", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + { + "description": "Boiler and radiators, anthracite", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 1 + }, + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "pressure_test": 6, + "property_type": 0, + "address_line_1": "1 Some Street", + "assessment_type": "RdSAP", + "completion_date": "2023-12-01", + "inspection_date": "2023-12-01", + "wet_rooms_count": 0, + "extensions_count": 0, + "measurement_type": 1, + "sap_flat_details": { + "level": 1, + "top_storey": "N", + "storey_count": 3, + "flat_location": 1, + "heat_loss_corridor": 2, + "unheated_corridor_length": 10 + }, + "total_floor_area": 55, + "transaction_type": 16, + "conservatory_type": 1, + "heated_room_count": 5, + "registration_date": "2023-12-01", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 2, + "pv_batteries": { + "pv_battery": { + "battery_capacity": 5 + } + }, + "pv_connection": 0, + "pv_battery_count": 1, + "photovoltaic_supply": { + "none_or_no_details": { + "percent_roof_area": 0 + } + }, + "wind_turbines_count": 0, + "wind_turbine_details": { + "hub_height": 0, + "rotor_diameter": 0 + }, + "gas_smart_meter_present": "false", + "is_dwelling_export_capable": "false", + "wind_turbines_terrain_type": 4, + "electricity_smart_meter_present": "true" + }, + "secondary_heating": { + "description": "Room heaters, electric", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "floor_heat_loss": 7, + "sap_room_in_roof": { + "floor_area": 100, + "construction_age_band": "B" + }, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.45, + "quantity": "metres" + }, + "floor_insulation": 1, + "total_floor_area": { + "value": 45.82, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 7.9, + "quantity": "metres" + }, + "floor_construction": 1, + "heat_loss_perimeter": { + "value": 19.5, + "quantity": "metres" + } + }, + { + "floor": 1, + "room_height": { + "value": 2.59, + "quantity": "metres" + }, + "total_floor_area": { + "value": 45.82, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 7.9, + "quantity": "metres" + }, + "heat_loss_perimeter": { + "value": 19.5, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 2, + "construction_age_band": "M", + "sap_alternative_wall_1": { + "wall_area": 10.4, + "wall_dry_lined": "N", + "wall_construction": 4, + "wall_insulation_type": 2, + "wall_thickness_measured": "N" + }, + "sap_alternative_wall_2": { + "wall_area": 10.8, + "wall_dry_lined": "N", + "wall_construction": 4, + "wall_insulation_type": 2, + "wall_thickness_measured": "N" + }, + "party_wall_construction": 0, + "wall_thickness_measured": "N", + "roof_insulation_location": 2, + "roof_insulation_thickness": "200mm", + "wall_insulation_thickness": "NI", + "floor_insulation_thickness": "NI" + } + ], + "open_chimneys_count": 1, + "solar_water_heating": "N", + "habitable_room_count": 5, + "heating_cost_current": 365.98, + "insulated_door_count": 2, + "co2_emissions_current": 2.4, + "energy_rating_average": 60, + "energy_rating_current": 50, + "lighting_cost_current": 123.45, + "main_heating_controls": [ + { + "description": "Programmer, room thermostat and TRVs", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + { + "description": "Time and temperature zone control", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": 250.34, + "hot_water_cost_current": 200.4, + "insulated_door_u_value": 3, + "mechanical_ventilation": 6, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": 360, + "indicative_cost": "£100 - £350", + "improvement_type": "Z3", + "improvement_details": { + "improvement_number": 5 + }, + "improvement_category": 6, + "energy_performance_rating": 50, + "environmental_impact_rating": 50 + }, + { + "sequence": 2, + "typical_saving": 99, + "indicative_cost": 2000, + "improvement_type": "Z2", + "improvement_details": { + "improvement_number": 1 + }, + "improvement_category": 2, + "energy_performance_rating": 60, + "environmental_impact_rating": 64 + }, + { + "sequence": 3, + "typical_saving": 99, + "indicative_cost": 1000, + "improvement_type": "Z2", + "improvement_details": { + "improvement_texts": { + "improvement_description": "Improvement desc" + } + }, + "improvement_category": 2, + "energy_performance_rating": 60, + "environmental_impact_rating": 64 + } + ], + "co2_emissions_potential": 1.4, + "energy_rating_potential": 72, + "lighting_cost_potential": 84.23, + "schema_version_original": "SAP-19.0", + "hot_water_cost_potential": 180.43, + "renewable_heat_incentive": { + "water_heating": 2285, + "impact_of_loft_insulation": -2114, + "impact_of_cavity_insulation": -122, + "impact_of_solid_wall_insulation": -3560, + "space_heating_existing_dwelling": 13120 + }, + "draughtproofed_door_count": 1, + "mechanical_vent_duct_type": 3, + "energy_consumption_current": 230, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "13.05r16", + "energy_consumption_potential": 88, + "environmental_impact_current": 52, + "windows_transmission_details": { + "u_value": 2, + "data_source": 2, + "solar_transmittance": 0.72 + }, + "cfl_fixed_lighting_bulbs_count": 5, + "current_energy_efficiency_band": "E", + "environmental_impact_potential": 74, + "led_fixed_lighting_bulbs_count": 10, + "mechanical_vent_duct_placement": 2, + "mechanical_vent_duct_insulation": 2, + "potential_energy_efficiency_band": "C", + "pressure_test_certificate_number": 0, + "mechanical_ventilation_index_number": 12, + "co2_emissions_current_per_floor_area": 20, + "low_energy_fixed_lighting_bulbs_count": 16, + "mechanical_vent_duct_insulation_level": 2, + "mechanical_vent_measured_installation": "false", + "incandescent_fixed_lighting_bulbs_count": 5 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/RdSAP-Schema-21.0.1/epc.json b/backend/epc_api/json_samples/RdSAP-Schema-21.0.1/epc.json new file mode 100644 index 00000000..166e60a0 --- /dev/null +++ b/backend/epc_api/json_samples/RdSAP-Schema-21.0.1/epc.json @@ -0,0 +1,446 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": { + "value": "Pitched, 25 mm loft insulation", + "language": "1" + }, + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + }, + { + "description": { + "value": "Pitched, 250 mm loft insulation", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": { + "value": "Solid brick, as built, no insulation (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 1, + "environmental_efficiency_rating": 1 + }, + { + "description": { + "value": "Cavity wall, as built, insulated (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": { + "value": "Suspended, no insulation (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + { + "description": { + "value": "Solid, insulated (assumed)", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 1, + "window": { + "description": { + "value": "Fully double glazed", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "addendum": { + "stone_walls": "true", + "system_build": "true", + "addendum_numbers": [ + 1, + 13 + ] + }, + "lighting": { + "description": { + "value": "Low energy lighting in 50% of fixed outlets", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "postcode": "A0 0AA", + "hot_water": { + "description": { + "value": "From main system", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Whitbury", + "built_form": 2, + "door_count": 3, + "region_code": 1, + "report_type": 2, + "sap_heating": { + "cylinder_size": 1, + "shower_outlets": { + "shower_outlet": { + "shower_wwhrs": 1, + "shower_outlet_type": 1 + } + }, + "water_heating_code": 901, + "water_heating_fuel": 26, + "instantaneous_wwhrs": { + "wwhrs_index_number1": 1, + "wwhrs_index_number2": 2 + }, + "secondary_fuel_type": 25, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "N", + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "boiler_ignition_type": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "sap_main_heating_code": 101, + "central_heating_pump_age": 0, + "main_heating_data_source": 1, + "main_heating_index_number": 17507 + } + ], + "immersion_heating_type": "NA", + "has_fixed_air_conditioning": "false" + }, + "sap_version": 10.2, + "sap_windows": [ + { + "pvc_frame": "false", + "glazing_gap": 6, + "orientation": 1, + "window_type": 2, + "frame_factor": 1.0, + "glazing_type": 14, + "window_width": 1.2, + "window_height": 2.0, + "draught_proofed": "true", + "window_location": 0, + "window_wall_type": 6, + "permanent_shutters_present": "N", + "window_transmission_details": { + "u_value": 1.0, + "data_source": 2, + "solar_transmittance": 1.0 + }, + "permanent_shutters_insulated": "N" + }, + { + "pvc_frame": "false", + "glazing_gap": 6, + "orientation": 1, + "window_type": 2, + "frame_factor": 1.0, + "glazing_type": 1, + "window_width": 1.2, + "window_height": 2.0, + "draught_proofed": "true", + "window_location": 1, + "window_wall_type": 6, + "permanent_shutters_present": "N", + "window_transmission_details": { + "u_value": 1.0, + "data_source": 2, + "solar_transmittance": 1.0 + }, + "permanent_shutters_insulated": "N" + } + ], + "schema_type": "RdSAP-Schema-21.0.1", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + { + "description": { + "value": "Boiler and radiators, anthracite", + "language": "1" + }, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 1 + }, + { + "description": { + "value": "Boiler and radiators, mains gas", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "pressure_test": 6, + "property_type": 0, + "address_line_1": "1 Some Street", + "assessment_type": "RdSAP", + "completion_date": "2025-04-04", + "inspection_date": "2025-04-04", + "wet_rooms_count": 0, + "extensions_count": 0, + "measurement_type": 1, + "sap_flat_details": { + "level": 1, + "top_storey": "N", + "storey_count": 3, + "flat_location": 1, + "heat_loss_corridor": 2, + "unheated_corridor_length": { + "value": 10, + "quantity": "metres" + } + }, + "total_floor_area": 55, + "transaction_type": 16, + "conservatory_type": 1, + "heated_room_count": 5, + "registration_date": "2025-04-04", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 2, + "pv_batteries": { + "pv_battery": { + "battery_capacity": 5 + } + }, + "pv_connection": 0, + "pv_battery_count": 1, + "photovoltaic_supply": { + "none_or_no_details": { + "percent_roof_area": 0 + } + }, + "wind_turbines_count": 0, + "wind_turbine_details": { + "hub_height": 0, + "rotor_diameter": 0 + }, + "gas_smart_meter_present": "false", + "is_dwelling_export_capable": "false", + "wind_turbines_terrain_type": 4, + "electricity_smart_meter_present": "true" + }, + "secondary_heating": { + "description": { + "value": "Room heaters, electric", + "language": "1" + }, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "floor_heat_loss": 7, + "sap_room_in_roof": { + "floor_area": 100, + "construction_age_band": "B" + }, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": { + "value": 2.45, + "quantity": "metres" + }, + "floor_insulation": 1, + "total_floor_area": { + "value": 45.82, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 7.9, + "quantity": "metres" + }, + "floor_construction": 1, + "heat_loss_perimeter": { + "value": 19.5, + "quantity": "metres" + } + }, + { + "floor": 1, + "room_height": { + "value": 2.59, + "quantity": "metres" + }, + "total_floor_area": { + "value": 45.82, + "quantity": "square metres" + }, + "party_wall_length": { + "value": 7.9, + "quantity": "metres" + }, + "heat_loss_perimeter": { + "value": 19.5, + "quantity": "metres" + } + } + ], + "wall_insulation_type": 2, + "construction_age_band": "M", + "sap_alternative_wall_1": { + "wall_area": 10.4, + "wall_dry_lined": "N", + "wall_construction": 4, + "wall_insulation_type": 2, + "wall_thickness_measured": "N" + }, + "sap_alternative_wall_2": { + "wall_area": 10.8, + "wall_dry_lined": "N", + "wall_construction": 4, + "wall_insulation_type": 2, + "wall_thickness_measured": "N" + }, + "party_wall_construction": 0, + "wall_thickness_measured": "N", + "roof_insulation_location": 2, + "roof_insulation_thickness": "200mm", + "wall_insulation_thickness": "NI", + "floor_insulation_thickness": "NI" + } + ], + "open_chimneys_count": 1, + "solar_water_heating": "N", + "habitable_room_count": 5, + "heating_cost_current": 365.98, + "insulated_door_count": 2, + "co2_emissions_current": 2.4, + "energy_rating_average": 60, + "energy_rating_current": 50, + "lighting_cost_current": 123.45, + "main_heating_controls": [ + { + "description": { + "value": "Programmer, room thermostat and TRVs", + "language": "1" + }, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + { + "description": { + "value": "Time and temperature zone control", + "language": "1" + }, + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": 250.34, + "hot_water_cost_current": 200.4, + "insulated_door_u_value": 3, + "mechanical_ventilation": 6, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": 139, + "indicative_cost": "£220 - £250", + "improvement_type": "G", + "improvement_details": { + "improvement_number": 66 + }, + "improvement_category": 5, + "energy_performance_rating": 70, + "environmental_impact_rating": 70 + }, + { + "sequence": 2, + "typical_saving": 49, + "indicative_cost": "£4,000 - £7,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 72, + "environmental_impact_rating": 73 + }, + { + "sequence": 3, + "typical_saving": 286, + "indicative_cost": "£8,000 - £10,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 83, + "environmental_impact_rating": 75 + } + ], + "co2_emissions_potential": 1.4, + "energy_rating_potential": 72, + "lighting_cost_potential": 84.23, + "schema_version_original": "SAP-19.0", + "hot_water_cost_potential": 180.43, + "renewable_heat_incentive": { + "water_heating": 2285, + "impact_of_loft_insulation": -2114, + "impact_of_cavity_insulation": -122, + "impact_of_solid_wall_insulation": -3560, + "space_heating_existing_dwelling": 13120 + }, + "draughtproofed_door_count": 1, + "mechanical_vent_duct_type": 3, + "energy_consumption_current": 230, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "13.05r16", + "energy_consumption_potential": 88, + "environmental_impact_current": 52, + "windows_transmission_details": { + "u_value": 2, + "data_source": 2, + "solar_transmittance": 0.72 + }, + "cfl_fixed_lighting_bulbs_count": 5, + "current_energy_efficiency_band": "E", + "environmental_impact_potential": 74, + "led_fixed_lighting_bulbs_count": 10, + "mechanical_vent_duct_placement": 2, + "mechanical_vent_duct_insulation": 2, + "potential_energy_efficiency_band": "C", + "pressure_test_certificate_number": 0, + "mechanical_ventilation_index_number": 12, + "co2_emissions_current_per_floor_area": 20, + "low_energy_fixed_lighting_bulbs_count": 16, + "mechanical_vent_duct_insulation_level": 2, + "mechanical_vent_measured_installation": "false", + "incandescent_fixed_lighting_bulbs_count": 0 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-16.0/rdsap.json b/backend/epc_api/json_samples/SAP-Schema-16.0/rdsap.json new file mode 100644 index 00000000..f1caacf4 --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-16.0/rdsap.json @@ -0,0 +1,299 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Pitched, 300+ mm loft insulation", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Cavity wall, filled cavity", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + { + "description": "Cavity wall, as built, insulated (assumed)", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": "Solid, no insulation (assumed)", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "windows": [ + { + "description": "Fully double glazed", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "lighting": { + "description": "Low energy lighting in 67% of fixed outlets", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "postcode": "AA1 1AA", + "hot_water": { + "description": "From main system", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Town", + "built_form": 1, + "door_count": 2, + "glazed_area": 1, + "region_code": 12, + "report_type": 2, + "sap_heating": { + "wwhrs": { + "rooms_with_bath_and_or_shower": 2, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "cylinder_size": 1, + "water_heating_code": 901, + "water_heating_fuel": 26, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "Y", + "heat_emitter_type": 1, + "boiler_index_number": 10265, + "main_heating_number": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "main_heating_data_source": 1 + } + ], + "has_fixed_air_conditioning": "false" + }, + "sap_version": 9.91, + "schema_type": "SAP-Schema-16.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": "Detached bungalow", + "language_code": 1, + "property_type": 1, + "address_line_1": "11, Street Road", + "schema_version": "LIG-16.0", + "assessment_type": "RdSAP", + "completion_date": "2012-09-30", + "inspection_date": "2012-09-27", + "extensions_count": 1, + "measurement_type": 1, + "total_floor_area": 107, + "transaction_type": 1, + "conservatory_type": 1, + "heated_room_count": 5, + "registration_date": "2012-09-30", + "restricted_access": 0, + "sap_energy_source": { + "main_gas": "Y", + "meter_type": 2, + "photovoltaic_supply": { + "percent_roof_area": 0 + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 11 + ], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 300, + "floor_heat_loss": 7, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": 2.33, + "floor_insulation": 1, + "total_floor_area": 97.96, + "floor_construction": 1, + "heat_loss_perimeter": 37 + } + ], + "wall_insulation_type": 2, + "construction_age_band": "E", + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "300mm+" + }, + { + "identifier": "Extension", + "wall_dry_lined": "N", + "floor_heat_loss": 7, + "roof_construction": 5, + "wall_construction": 4, + "building_part_number": 2, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": 3.08, + "floor_insulation": 1, + "total_floor_area": 8.63, + "floor_construction": 1, + "heat_loss_perimeter": 7.42 + } + ], + "wall_insulation_type": 4, + "construction_age_band": "K", + "wall_thickness_measured": "N", + "roof_insulation_location": 4, + "roof_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 67, + "solar_water_heating": "N", + "bedf_revision_number": 329, + "habitable_room_count": 5, + "heating_cost_current": { + "value": 494, + "currency": "GBP" + }, + "insulated_door_count": 0, + "co2_emissions_current": 2.9, + "energy_rating_average": 60, + "energy_rating_current": 73, + "lighting_cost_current": { + "value": 73, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Programmer, room thermostat and TRVs", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "multiple_glazing_type": 2, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "false", + "heating_cost_potential": { + "value": 428, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 90, + "currency": "GBP" + }, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 68, + "currency": "GBP" + }, + "indicative_cost": "£800 - £1,200", + "improvement_type": "W", + "improvement_details": { + "improvement_number": 47 + }, + "improvement_category": 5, + "energy_performance_rating": 76, + "environmental_impact_rating": 76 + }, + { + "sequence": 2, + "typical_saving": { + "value": 16, + "currency": "GBP" + }, + "indicative_cost": "£25", + "improvement_type": "E", + "improvement_details": { + "improvement_number": 35 + }, + "improvement_category": 5, + "energy_performance_rating": 76, + "environmental_impact_rating": 76 + }, + { + "sequence": 3, + "typical_saving": { + "value": 26, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 77, + "environmental_impact_rating": 78 + }, + { + "sequence": 4, + "typical_saving": { + "value": 238, + "currency": "GBP" + }, + "indicative_cost": "£9,000 - £14,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 86, + "environmental_impact_rating": 86 + } + ], + "co2_emissions_potential": 1.4, + "energy_rating_potential": 86, + "lighting_cost_potential": { + "value": 55, + "currency": "GBP" + }, + "hot_water_cost_potential": { + "value": 64, + "currency": "GBP" + }, + "renewable_heat_incentive": { + "water_heating": 2264, + "space_heating_existing_dwelling": 9324 + }, + "seller_commission_report": "Y", + "energy_consumption_current": 144, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": 8.0, + "energy_consumption_potential": 64, + "environmental_impact_current": 72, + "fixed_lighting_outlets_count": 15, + "current_energy_efficiency_band": "C", + "environmental_impact_potential": 86, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "B", + "co2_emissions_current_per_floor_area": 28, + "low_energy_fixed_lighting_outlets_count": 10 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-16.0/sap.json b/backend/epc_api/json_samples/SAP-Schema-16.0/sap.json new file mode 100644 index 00000000..3fdf06bc --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-16.0/sap.json @@ -0,0 +1,348 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "(other premises above)", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.34 W/m²K", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.25 W/m²K", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "status": "entered", + "windows": { + "description": "Fully double glazed", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "lighting": { + "description": "Low energy lighting in 57% of fixed outlets", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "postcode": "AA1 1AA", + "data_type": 2, + "hot_water": { + "description": "From main system", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Town", + "built_form": 1, + "living_area": 31.53, + "orientation": 0, + "region_code": 17, + "report_type": 3, + "sap_heating": { + "thermal_store": 1, + "has_solar_panel": "false", + "water_fuel_type": 1, + "water_heating_code": 901, + "hot_water_store_size": 145, + "main_heating_details": [ + { + "main_fuel_type": 1, + "heat_emitter_type": 1, + "boiler_index_number": 9206, + "is_flue_fan_present": "true", + "main_heating_number": 1, + "main_heating_control": 2106, + "is_interlocked_system": "true", + "main_heating_category": 2, + "main_heating_fraction": 1, + "main_heating_data_source": 1, + "has_delayed_start_thermostat": "false", + "load_or_weather_compensation": 0, + "is_central_heating_pump_in_heated_space": "true" + } + ], + "has_hot_water_cylinder": "true", + "has_cylinder_thermostat": "true", + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 1, + "is_cylinder_in_heated_space": "true", + "is_hot_water_separately_timed": "true", + "is_primary_pipework_insulated": "true", + "hot_water_store_insulation_type": 1, + "hot_water_store_heat_loss_source": 3, + "hot_water_store_insulation_thickness": 50 + }, + "sap_version": 9.9, + "schema_type": "SAP-Schema-16.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "air_tightness": { + "description": "Air permeability 2.7 m³/h.m² (as tested)", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "dwelling_type": "Mid-floor flat", + "language_code": 1, + "property_type": 2, + "address_line_1": "28, Place Drive", + "schema_version": "LIG-16.0", + "assessment_date": "2012-09-29", + "assessment_type": "SAP", + "completion_date": "2012-09-29", + "inspection_date": "2012-09-29", + "sap_ventilation": { + "psv_count": 0, + "pressure_test": 1, + "air_permeability": 2.72, + "open_flues_count": 0, + "ventilation_type": 1, + "extract_fans_count": 3, + "open_fireplaces_count": 0, + "sheltered_sides_count": 2, + "flueless_gas_fires_count": 0 + }, + "sap_data_version": 9.81, + "sap_flat_details": { + "level": 2 + }, + "total_floor_area": 80, + "transaction_type": 6, + "conservatory_type": 1, + "registration_date": "2012-09-29", + "restricted_access": 0, + "sap_energy_source": { + "electricity_tariff": 1, + "wind_turbines_count": 0, + "wind_turbine_terrain_type": 1, + "fixed_lighting_outlets_count": 7, + "low_energy_fixed_lighting_outlets_count": 4, + "low_energy_fixed_lighting_outlets_percentage": 57 + }, + "sap_opening_types": [ + { + "name": 1, + "type": 2, + "u_value": 2, + "data_source": 2, + "description": "Door", + "glazing_type": 6 + }, + { + "name": 2, + "type": 4, + "u_value": 1.8, + "data_source": 2, + "description": "Window", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + } + ], + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 11 + ], + "sap_building_parts": [ + { + "sap_walls": [ + { + "name": "Wall 1", + "u_value": 0.35, + "wall_type": 2, + "description": "Wall 5 render", + "total_wall_area": 58.29, + "is_curtain_walling": "false" + }, + { + "name": "Wall 2", + "u_value": 0.27, + "wall_type": 2, + "description": "Wall 2 common area", + "total_wall_area": 9.28, + "is_curtain_walling": "false" + }, + { + "name": "Wall 3", + "u_value": 0.35, + "wall_type": 2, + "description": "Wall 3 lift shaft", + "total_wall_area": 8.9, + "is_curtain_walling": "false" + } + ], + "identifier": "Main Dwelling", + "overshading": 2, + "sap_openings": [ + { + "name": 1, + "type": 1, + "width": 0.91, + "height": 2.1, + "location": "Wall 2", + "orientation": 0 + }, + { + "name": 2, + "type": 2, + "width": 1.15, + "height": 1.4, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 3, + "type": 2, + "width": 0.9, + "height": 1.4, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 4, + "type": 2, + "width": 1.3, + "height": 1.4, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 5, + "type": 2, + "width": 1.3, + "height": 1.4, + "location": "Wall 1", + "orientation": 5 + }, + { + "name": 6, + "type": 2, + "width": 1.15, + "height": 1.4, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 7, + "type": 2, + "width": 0.55, + "height": 1.05, + "location": "Wall 1", + "orientation": 4 + }, + { + "name": 8, + "type": 2, + "width": 1.15, + "height": 1.4, + "location": "Wall 1", + "orientation": 4 + }, + { + "name": 9, + "type": 2, + "width": 1.15, + "height": 1.4, + "location": "Wall 1", + "orientation": 4 + }, + { + "name": 10, + "type": 2, + "width": 1.3, + "height": 1.05, + "location": "Wall 1", + "orientation": 4 + } + ], + "construction_year": 2011, + "sap_thermal_bridges": { + "thermal_bridge_code": 4, + "user_defined_y_value": 0.08, + "calculation_reference": "SAP 2005 record" + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 1, + "u_value": 0.25, + "floor_type": 4, + "description": "Floor 2", + "storey_height": 2.32, + "heat_loss_area": 5.19, + "total_floor_area": 79.9 + } + ], + "thermal_mass_parameter": 250 + } + ], + "bedf_revision_number": 329, + "heating_cost_current": 213, + "co2_emissions_current": 1.3, + "energy_rating_average": 60, + "energy_rating_current": 82, + "lighting_cost_current": 66, + "main_heating_controls": [ + { + "description": "Programmer, room thermostat and TRVs", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": 215, + "hot_water_cost_current": 94, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": 18, + "indicative_cost": "£8", + "improvement_type": "E", + "improvement_details": { + "improvement_number": 35 + }, + "improvement_category": 1, + "energy_performance_rating": 83, + "environmental_impact_rating": 87 + } + ], + "co2_emissions_potential": 1.3, + "energy_rating_potential": 83, + "lighting_cost_potential": 46, + "hot_water_cost_potential": 94, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 1780, + "water_heating": 2306 + } + }, + "seller_commission_report": "Y", + "energy_consumption_current": 87, + "has_fixed_air_conditioning": "false", + "calculation_software_version": 5.4, + "energy_consumption_potential": 82, + "environmental_impact_current": 86, + "current_energy_efficiency_band": "B", + "environmental_impact_potential": 87, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "B", + "co2_emissions_current_per_floor_area": 16 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-16.1/rdsap.json b/backend/epc_api/json_samples/SAP-Schema-16.1/rdsap.json new file mode 100644 index 00000000..e6a3bb0a --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-16.1/rdsap.json @@ -0,0 +1,400 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Pitched, 300+ mm loft insulation", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Cavity wall, filled cavity", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + { + "description": "System built, as built, no insulation (assumed)", + "energy_efficiency_rating": 1, + "environmental_efficiency_rating": 1 + }, + { + "description": "Solid brick, as built, no insulation (assumed)", + "energy_efficiency_rating": 1, + "environmental_efficiency_rating": 1 + } + ], + "floors": [ + { + "description": "Suspended, no insulation (assumed)", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 2, + "windows": [ + { + "description": "Fully double glazed", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + } + ], + "addendum": { + "system_build": "true" + }, + "lighting": { + "description": "Low energy lighting in 36% of fixed outlets", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "postcode": "AA1 1AA", + "hot_water": { + "description": "From main system", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Town", + "built_form": 4, + "door_count": 2, + "glazed_area": 1, + "region_code": 14, + "report_type": 2, + "sap_heating": { + "wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "cylinder_size": 2, + "water_heating_code": 901, + "water_heating_fuel": 26, + "cylinder_thermostat": "Y", + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "Y", + "heat_emitter_type": 1, + "main_heating_number": 1, + "main_heating_control": 2103, + "main_heating_category": 2, + "main_heating_fraction": 1, + "sap_main_heating_code": 101, + "main_heating_data_source": 2 + } + ], + "cylinder_insulation_type": 1, + "has_fixed_air_conditioning": "false", + "cylinder_insulation_thickness": 38 + }, + "sap_version": 9.91, + "schema_type": "SAP-Schema-16.1", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "property_type": 0, + "address_line_1": "15, What the Dickens Road", + "schema_version": "LIG-16.1", + "assessment_type": "RdSAP", + "completion_date": "2013-01-13", + "inspection_date": "2013-01-09", + "extensions_count": 1, + "measurement_type": 1, + "total_floor_area": 72, + "transaction_type": 8, + "conservatory_type": 1, + "heated_room_count": 5, + "registration_date": "2013-01-13", + "restricted_access": 0, + "sap_energy_source": { + "main_gas": "Y", + "meter_type": 2, + "photovoltaic_supply": { + "percent_roof_area": 0 + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 1 + }, + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 9 + ], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 300, + "floor_heat_loss": 7, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_alternative_wall": { + "wall_area": 18.9, + "wall_dry_lined": "N", + "wall_construction": 3, + "wall_insulation_type": 4, + "wall_thickness_measured": "N" + }, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": 2.43, + "floor_insulation": 1, + "total_floor_area": 34.2, + "floor_construction": 2, + "heat_loss_perimeter": 14.9 + }, + { + "floor": 1, + "room_height": 2.47, + "total_floor_area": 34.2, + "heat_loss_perimeter": 9.84 + } + ], + "wall_insulation_type": 2, + "construction_age_band": "C", + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "300mm+" + }, + { + "identifier": "Extension 1", + "wall_dry_lined": "N", + "wall_thickness": 200, + "floor_heat_loss": 7, + "roof_construction": 1, + "wall_construction": 8, + "building_part_number": 2, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": 2.26, + "floor_insulation": 0, + "total_floor_area": 3.5, + "floor_construction": 0, + "heat_loss_perimeter": 5.7 + } + ], + "wall_insulation_type": 4, + "construction_age_band": "E", + "wall_thickness_measured": "Y", + "roof_insulation_location": 4, + "roof_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 36, + "solar_water_heating": "N", + "bedf_revision_number": 333, + "habitable_room_count": 5, + "heating_cost_current": { + "value": 546, + "currency": "GBP" + }, + "insulated_door_count": 0, + "co2_emissions_current": 3.4, + "energy_rating_average": 60, + "energy_rating_current": 61, + "lighting_cost_current": { + "value": 69, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Room thermostat only", + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + } + ], + "multiple_glazing_type": 3, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 378, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 127, + "currency": "GBP" + }, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 66, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £14,000", + "improvement_type": "Q", + "improvement_details": { + "improvement_number": 7 + }, + "improvement_category": 5, + "energy_performance_rating": 65, + "environmental_impact_rating": 63 + }, + { + "sequence": 2, + "typical_saving": { + "value": 36, + "currency": "GBP" + }, + "indicative_cost": "£800 - £1,200", + "improvement_type": "W", + "improvement_details": { + "improvement_number": 47 + }, + "improvement_category": 5, + "energy_performance_rating": 67, + "environmental_impact_rating": 66 + }, + { + "sequence": 3, + "typical_saving": { + "value": 23, + "currency": "GBP" + }, + "indicative_cost": "£35", + "improvement_type": "E", + "improvement_details": { + "improvement_number": 35 + }, + "improvement_category": 5, + "energy_performance_rating": 68, + "environmental_impact_rating": 67 + }, + { + "sequence": 4, + "typical_saving": { + "value": 21, + "currency": "GBP" + }, + "indicative_cost": "£350 - £450", + "improvement_type": "G", + "improvement_details": { + "improvement_number": 15 + }, + "improvement_category": 5, + "energy_performance_rating": 69, + "environmental_impact_rating": 68 + }, + { + "sequence": 5, + "typical_saving": { + "value": 75, + "currency": "GBP" + }, + "indicative_cost": "£2,200 - £3,000", + "improvement_type": "I", + "improvement_details": { + "improvement_number": 20 + }, + "improvement_category": 5, + "energy_performance_rating": 73, + "environmental_impact_rating": 73 + }, + { + "sequence": 6, + "typical_saving": { + "value": 34, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 74, + "environmental_impact_rating": 76 + }, + { + "sequence": 7, + "typical_saving": { + "value": 247, + "currency": "GBP" + }, + "indicative_cost": "£9,000 - £14,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 85, + "environmental_impact_rating": 86 + } + ], + "co2_emissions_potential": 1.0, + "energy_rating_potential": 85, + "lighting_cost_potential": { + "value": 42, + "currency": "GBP" + }, + "alternative_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 57, + "currency": "GBP" + }, + "improvement_type": "Z1", + "improvement_details": { + "improvement_number": 51 + }, + "improvement_category": 6, + "energy_performance_rating": 72, + "environmental_impact_rating": 74 + }, + { + "sequence": 2, + "typical_saving": { + "value": 85, + "currency": "GBP" + }, + "improvement_type": "Z3", + "improvement_details": { + "improvement_number": 53 + }, + "improvement_category": 6, + "energy_performance_rating": 73, + "environmental_impact_rating": 72 + } + ], + "hot_water_cost_potential": { + "value": 68, + "currency": "GBP" + }, + "renewable_heat_incentive": { + "water_heating": 2530, + "space_heating_existing_dwelling": 8564 + }, + "seller_commission_report": "Y", + "energy_consumption_current": 244, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "6.2.0.6", + "energy_consumption_potential": 66, + "environmental_impact_current": 59, + "fixed_lighting_outlets_count": 11, + "current_energy_efficiency_band": "D", + "environmental_impact_potential": 86, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "B", + "co2_emissions_current_per_floor_area": 47, + "low_energy_fixed_lighting_outlets_count": 4 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-16.1/sap.json b/backend/epc_api/json_samples/SAP-Schema-16.1/sap.json new file mode 100644 index 00000000..ac083fe4 --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-16.1/sap.json @@ -0,0 +1,365 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Average thermal transmittance 0.15 W/m²K", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.30 W/m²K", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.18 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "status": "entered", + "windows": { + "description": "Fully double glazed", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "lighting": { + "description": "Low energy lighting in 33% of fixed outlets", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "postcode": "AA1 1AA", + "data_type": 2, + "hot_water": { + "description": "From main system", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Town", + "built_form": 4, + "living_area": 12.04, + "orientation": 0, + "region_code": 15, + "report_type": 3, + "sap_heating": { + "thermal_store": 1, + "has_solar_panel": "false", + "water_fuel_type": 1, + "water_heating_code": 901, + "main_heating_details": [ + { + "main_fuel_type": 1, + "heat_emitter_type": 1, + "boiler_index_number": 9901, + "is_flue_fan_present": "true", + "main_heating_number": 1, + "main_heating_control": 2106, + "is_interlocked_system": "true", + "main_heating_category": 2, + "main_heating_fraction": 1, + "main_heating_data_source": 1, + "has_delayed_start_thermostat": "false", + "load_or_weather_compensation": 0, + "is_central_heating_pump_in_heated_space": "true" + } + ], + "has_hot_water_cylinder": "false", + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 1 + }, + "sap_version": 9.9, + "schema_type": "SAP-Schema-16.1", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "air_tightness": { + "description": "Air permeability 7.7 m³/h.m² (as tested)", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "property_type": 0, + "address_line_1": "28, Place Drive", + "schema_version": "LIG-16.0", + "assessment_date": "2013-01-12", + "assessment_type": "SAP", + "completion_date": "2013-01-12", + "inspection_date": "2013-01-12", + "sap_ventilation": { + "psv_count": 0, + "pressure_test": 1, + "air_permeability": 7.7, + "open_flues_count": 0, + "ventilation_type": 1, + "extract_fans_count": 3, + "open_fireplaces_count": 0, + "sheltered_sides_count": 2, + "flueless_gas_fires_count": 0 + }, + "total_floor_area": 59, + "transaction_type": 6, + "conservatory_type": 1, + "registration_date": "2013-01-12", + "restricted_access": 0, + "sap_energy_source": { + "electricity_tariff": 1, + "wind_turbines_count": 0, + "wind_turbine_terrain_type": 2, + "fixed_lighting_outlets_count": 9, + "low_energy_fixed_lighting_outlets_count": 3, + "low_energy_fixed_lighting_outlets_percentage": 33 + }, + "sap_opening_types": [ + { + "name": 1, + "type": 1, + "u_value": 1.57, + "data_source": 2, + "glazing_type": 1 + }, + { + "name": 2, + "type": 4, + "u_value": 1.9, + "frame_type": 2, + "data_source": 3, + "glazing_gap": 3, + "frame_factor": 0.7, + "glazing_type": 6, + "isargonfilled": "false", + "solar_transmittance": 0.63 + }, + { + "name": 4, + "type": 2, + "u_value": 1.9, + "data_source": 2, + "glazing_type": 6 + } + ], + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 9 + ], + "sap_building_parts": [ + { + "sap_roofs": [ + { + "name": "Roof 1", + "u_value": 0.15, + "roof_type": 2, + "description": "Roof 1", + "total_roof_area": 29.25 + } + ], + "sap_walls": [ + { + "name": "Wall 1", + "u_value": 0.3, + "wall_type": 2, + "description": "Wall 1", + "total_wall_area": 38.69, + "is_curtain_walling": "false" + }, + { + "name": "Wall 2", + "u_value": 0.3, + "wall_type": 2, + "description": "High step", + "total_wall_area": 2.25, + "is_curtain_walling": "false" + }, + { + "name": "Wall 3", + "u_value": 0.3, + "wall_type": 2, + "description": "low step", + "total_wall_area": 2.25, + "is_curtain_walling": "false" + } + ], + "identifier": "Main Dwelling", + "overshading": 2, + "sap_openings": [ + { + "name": 1, + "type": 1, + "width": 0.93, + "height": 2.1, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 2, + "type": 2, + "width": 1.77, + "height": 1.35, + "location": "Wall 1", + "orientation": 1 + }, + { + "name": 3, + "type": 2, + "width": 1.2, + "height": 1.05, + "location": "Wall 1", + "orientation": 1 + }, + { + "name": 4, + "type": 4, + "width": 0.93, + "height": 2.1, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 5, + "type": 2, + "width": 1.2, + "height": 1.05, + "location": "Wall 1", + "orientation": 5 + }, + { + "name": 6, + "type": 2, + "width": 1.77, + "height": 1.05, + "location": "Wall 1", + "orientation": 5 + } + ], + "construction_year": 2012, + "sap_thermal_bridges": { + "thermal_bridge_code": 4, + "user_defined_y_value": 0.08, + "calculation_reference": "ACDs" + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 0, + "u_value": 0.18, + "floor_type": 2, + "description": "Floor 1", + "storey_height": 2.37, + "heat_loss_area": 29.25, + "total_floor_area": 29.25 + }, + { + "storey": 1, + "u_value": 0, + "floor_type": 3, + "storey_height": 2.59, + "heat_loss_area": 0, + "total_floor_area": 29.25 + } + ], + "thermal_mass_parameter": 250 + } + ], + "bedf_revision_number": 333, + "heating_cost_current": 236, + "co2_emissions_current": 1.3, + "energy_rating_average": 60, + "energy_rating_current": 79, + "lighting_cost_current": 66, + "main_heating_controls": [ + { + "description": "Programmer, room thermostat and TRVs", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "has_hot_water_cylinder": "false", + "heating_cost_potential": 240, + "hot_water_cost_current": 76, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": 23, + "indicative_cost": "£15", + "improvement_type": "E", + "improvement_details": { + "improvement_number": 35 + }, + "improvement_category": 1, + "energy_performance_rating": 80, + "environmental_impact_rating": 84 + }, + { + "sequence": 2, + "typical_saving": 25, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 3, + "energy_performance_rating": 81, + "environmental_impact_rating": 86 + }, + { + "sequence": 3, + "typical_saving": 226, + "indicative_cost": "£11,000 - £20,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 3, + "energy_performance_rating": 94, + "environmental_impact_rating": 98 + }, + { + "sequence": 4, + "typical_saving": 19, + "indicative_cost": "£1,500 - £4,000", + "improvement_type": "V", + "improvement_details": { + "improvement_number": 44 + }, + "improvement_category": 3, + "energy_performance_rating": 95, + "environmental_impact_rating": 99 + } + ], + "co2_emissions_potential": 1.2, + "energy_rating_potential": 80, + "lighting_cost_potential": 39, + "hot_water_cost_potential": 76, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 1867, + "water_heating": 1839 + } + }, + "seller_commission_report": "Y", + "energy_consumption_current": 119, + "has_fixed_air_conditioning": "false", + "calculation_software_version": 5.4, + "energy_consumption_potential": 110, + "environmental_impact_current": 83, + "current_energy_efficiency_band": "C", + "environmental_impact_potential": 84, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 22 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-16.2/rdsap.json b/backend/epc_api/json_samples/SAP-Schema-16.2/rdsap.json new file mode 100644 index 00000000..cea3d9c2 --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-16.2/rdsap.json @@ -0,0 +1,931 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Pitched, 75 mm loft insulation", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + } + ], + "walls": [ + { + "description": "Cavity wall, as built, no insulation (assumed)", + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + }, + { + "description": "Cavity wall, as built, insulated (assumed)", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": "Suspended, no insulation (assumed)", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + { + "description": "To unheated space, limited insulation (assumed)", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 1, + "windows": [ + { + "description": "Partial double glazing", + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + } + ], + "addendum": { + "cavity_fill_recommended": "true" + }, + "lighting": { + "description": "Low energy lighting in 13% of fixed outlets", + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + }, + "postcode": "AA1 1AA", + "hot_water": { + "description": "From main system", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Town", + "built_form": 1, + "door_count": 4, + "glazed_area": 4, + "region_code": 3, + "report_type": 2, + "sap_heating": { + "wwhrs": { + "rooms_with_bath_and_or_shower": 3, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 3 + }, + "cylinder_size": 4, + "water_heating_code": 901, + "water_heating_fuel": 26, + "cylinder_thermostat": "Y", + "secondary_fuel_type": 33, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "N", + "heat_emitter_type": 1, + "main_heating_number": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 0.5, + "sap_main_heating_code": 101, + "main_heating_data_source": 2 + }, + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "Y", + "heat_emitter_type": 1, + "main_heating_number": 2, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 0.5, + "sap_main_heating_code": 101, + "main_heating_data_source": 2 + } + ], + "secondary_heating_type": 631, + "cylinder_insulation_type": 1, + "has_fixed_air_conditioning": "false", + "cylinder_insulation_thickness": 50 + }, + "sap_version": 9.91, + "sap_windows": [ + { + "u_value": 3.1, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 3, + "window_location": 2, + "solar_transmittance": 0.76 + }, + { + "u_value": 3.1, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 3, + "window_location": 2, + "solar_transmittance": 0.76 + }, + { + "u_value": 3.1, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 3, + "window_location": 2, + "solar_transmittance": 0.76 + }, + { + "u_value": 3.1, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 3, + "window_location": 2, + "solar_transmittance": 0.76 + }, + { + "u_value": 3.1, + "data_source": 2, + "orientation": 1, + "window_area": 1, + "window_type": 1, + "glazing_type": 3, + "window_location": 2, + "solar_transmittance": 0.76 + }, + { + "u_value": 3.1, + "data_source": 2, + "orientation": 1, + "window_area": 1, + "window_type": 1, + "glazing_type": 3, + "window_location": 0, + "solar_transmittance": 0.76 + }, + { + "u_value": 3.1, + "data_source": 2, + "orientation": 3, + "window_area": 1, + "window_type": 1, + "glazing_type": 3, + "window_location": 2, + "solar_transmittance": 0.76 + }, + { + "u_value": 3.1, + "data_source": 2, + "orientation": 3, + "window_area": 1, + "window_type": 1, + "glazing_type": 3, + "window_location": 2, + "solar_transmittance": 0.76 + }, + { + "u_value": 3.1, + "data_source": 2, + "orientation": 3, + "window_area": 1, + "window_type": 1, + "glazing_type": 3, + "window_location": 2, + "solar_transmittance": 0.76 + }, + { + "u_value": 3.1, + "data_source": 2, + "orientation": 3, + "window_area": 1, + "window_type": 1, + "glazing_type": 3, + "window_location": 2, + "solar_transmittance": 0.76 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 3, + "window_area": 2, + "window_type": 1, + "glazing_type": 5, + "window_location": 2, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 3, + "window_area": 2, + "window_type": 1, + "glazing_type": 5, + "window_location": 2, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 3, + "window_area": 2, + "window_type": 1, + "glazing_type": 5, + "window_location": 2, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 3, + "window_area": 2, + "window_type": 1, + "glazing_type": 5, + "window_location": 2, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 3, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 2, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 4, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 4, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 4, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 3, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 7, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 3, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 3, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 3, + "window_area": 5, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 3, + "window_area": 5, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 3, + "window_area": 1, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 3.1, + "data_source": 2, + "orientation": 5, + "window_area": 2, + "window_type": 1, + "glazing_type": 3, + "window_location": 0, + "solar_transmittance": 0.76 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 3, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 3, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 3, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 8, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 4.8, + "data_source": 2, + "orientation": 5, + "window_area": 3, + "window_type": 1, + "glazing_type": 5, + "window_location": 0, + "solar_transmittance": 0.85 + }, + { + "u_value": 2, + "data_source": 2, + "orientation": 3, + "window_area": 2, + "window_type": 1, + "glazing_type": 2, + "window_location": 0, + "solar_transmittance": 0.72 + }, + { + "u_value": 2, + "data_source": 2, + "orientation": 3, + "window_area": 3, + "window_type": 1, + "glazing_type": 2, + "window_location": 0, + "solar_transmittance": 0.72 + }, + { + "u_value": 2, + "data_source": 2, + "orientation": 3, + "window_area": 3, + "window_type": 1, + "glazing_type": 2, + "window_location": 0, + "solar_transmittance": 0.72 + }, + { + "u_value": 2, + "data_source": 2, + "orientation": 7, + "window_area": 6, + "window_type": 1, + "glazing_type": 2, + "window_location": 0, + "solar_transmittance": 0.72 + }, + { + "u_value": 2, + "data_source": 2, + "orientation": 7, + "window_area": 8, + "window_type": 1, + "glazing_type": 2, + "window_location": 0, + "solar_transmittance": 0.72 + }, + { + "u_value": 2, + "data_source": 2, + "orientation": 7, + "window_area": 8, + "window_type": 1, + "glazing_type": 2, + "window_location": 0, + "solar_transmittance": 0.72 + }, + { + "u_value": 2, + "data_source": 2, + "orientation": 5, + "window_area": 9, + "window_type": 1, + "glazing_type": 2, + "window_location": 0, + "solar_transmittance": 0.72 + } + ], + "schema_type": "SAP-Schema-16.2", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": "Detached house", + "language_code": 1, + "property_type": 0, + "address_line_1": "11, Some Close", + "address_line_2": "Long Itchington", + "schema_version": "LIG-16.1", + "assessment_type": "RdSAP", + "completion_date": "2013-07-12", + "inspection_date": "2013-06-27", + "extensions_count": 2, + "measurement_type": 1, + "total_floor_area": 1563, + "transaction_type": 5, + "conservatory_type": 2, + "heated_room_count": 15, + "registration_date": "2013-07-12", + "restricted_access": 0, + "sap_energy_source": { + "main_gas": "Y", + "meter_type": 1, + "photovoltaic_supply": { + "percent_roof_area": 0 + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 3 + }, + "secondary_heating": { + "description": "Room heaters, coal", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 9 + ], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 320, + "floor_heat_loss": 7, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": 2.52, + "floor_insulation": 1, + "total_floor_area": 668.41, + "floor_construction": 2, + "heat_loss_perimeter": 96.43 + }, + { + "floor": 1, + "room_height": 2.87, + "total_floor_area": 691.43, + "heat_loss_perimeter": 106.57 + } + ], + "wall_insulation_type": 4, + "construction_age_band": "C", + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "75mm" + }, + { + "identifier": "Extension 1", + "wall_dry_lined": "N", + "wall_thickness": 500, + "floor_heat_loss": 7, + "roof_construction": 5, + "wall_construction": 4, + "building_part_number": 2, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": 2.54, + "floor_insulation": 1, + "total_floor_area": 82.03, + "floor_construction": 1, + "heat_loss_perimeter": 34.27 + } + ], + "wall_insulation_type": 4, + "construction_age_band": "H", + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "75mm" + }, + { + "identifier": "Extension 2", + "wall_dry_lined": "N", + "wall_thickness": 280, + "floor_heat_loss": 2, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 3, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": 3.21, + "floor_insulation": 1, + "total_floor_area": 121.31, + "floor_construction": 2, + "heat_loss_perimeter": 37.71 + } + ], + "wall_insulation_type": 4, + "construction_age_band": "H", + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "75mm" + } + ], + "low_energy_lighting": 13, + "solar_water_heating": "N", + "bedf_revision_number": 340, + "habitable_room_count": 15, + "heating_cost_current": { + "value": 13144, + "currency": "GBP" + }, + "insulated_door_count": 0, + "co2_emissions_current": 78, + "energy_rating_average": 60, + "energy_rating_current": 52, + "lighting_cost_current": { + "value": 635, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Programmer, room thermostat and TRVs", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "multiple_glazing_type": "ND", + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 8582, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 188, + "currency": "GBP" + }, + "mechanical_ventilation": 2, + "percent_draughtproofed": 0, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 637.67, + "currency": "GBP" + }, + "indicative_cost": "£100 - £350", + "improvement_type": "A", + "improvement_details": { + "improvement_number": 5 + }, + "improvement_category": 5, + "energy_performance_rating": 55, + "environmental_impact_rating": 42 + }, + { + "sequence": 2, + "typical_saving": { + "value": 1224.21, + "currency": "GBP" + }, + "indicative_cost": "£500 - £1,500", + "improvement_type": "B", + "improvement_details": { + "improvement_number": 6 + }, + "improvement_category": 5, + "energy_performance_rating": 59, + "environmental_impact_rating": 46 + }, + { + "sequence": 3, + "typical_saving": { + "value": 476.28, + "currency": "GBP" + }, + "indicative_cost": "£800 - £1,200", + "improvement_type": "W", + "improvement_details": { + "improvement_number": 47 + }, + "improvement_category": 5, + "energy_performance_rating": 60, + "environmental_impact_rating": 48 + }, + { + "sequence": 4, + "typical_saving": { + "value": 787.42, + "currency": "GBP" + }, + "indicative_cost": "£80 - £120", + "improvement_type": "D", + "improvement_details": { + "improvement_number": 10 + }, + "improvement_category": 5, + "energy_performance_rating": 63, + "environmental_impact_rating": 51 + }, + { + "sequence": 5, + "typical_saving": { + "value": 234.75, + "currency": "GBP" + }, + "indicative_cost": "£305", + "improvement_type": "E", + "improvement_details": { + "improvement_number": 35 + }, + "improvement_category": 5, + "energy_performance_rating": 64, + "environmental_impact_rating": 51 + }, + { + "sequence": 6, + "typical_saving": { + "value": 1219.36, + "currency": "GBP" + }, + "indicative_cost": "£2,200 - £3,000", + "improvement_type": "I", + "improvement_details": { + "improvement_number": 20 + }, + "improvement_category": 5, + "energy_performance_rating": 68, + "environmental_impact_rating": 56 + }, + { + "sequence": 7, + "typical_saving": { + "value": 310.6, + "currency": "GBP" + }, + "indicative_cost": "£3,300 - £6,500", + "improvement_type": "O", + "improvement_details": { + "improvement_number": 8 + }, + "improvement_category": 5, + "energy_performance_rating": 69, + "environmental_impact_rating": 58 + } + ], + "co2_emissions_potential": 51, + "energy_rating_potential": 69, + "lighting_cost_potential": { + "value": 344, + "currency": "GBP" + }, + "hot_water_cost_potential": { + "value": 151, + "currency": "GBP" + }, + "renewable_heat_incentive": { + "water_heating": 3640, + "impact_of_loft_insulation": -12585, + "impact_of_cavity_insulation": -21982, + "space_heating_existing_dwelling": 207769 + }, + "seller_commission_report": "Y", + "energy_consumption_current": 239, + "has_fixed_air_conditioning": "false", + "calculation_software_version": "1.4.1.0", + "energy_consumption_potential": 153, + "environmental_impact_current": 40, + "fixed_lighting_outlets_count": 70, + "current_energy_efficiency_band": "E", + "environmental_impact_potential": 58, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 50, + "low_energy_fixed_lighting_outlets_count": 9 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-16.2/sap.json b/backend/epc_api/json_samples/SAP-Schema-16.2/sap.json new file mode 100644 index 00000000..b6db5a2b --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-16.2/sap.json @@ -0,0 +1,2112 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Average thermal transmittance 0.14 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.17 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.13 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "status": "entered", + "windows": { + "description": "High performance glazing", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "lighting": { + "description": "Low energy lighting in all fixed outlets", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "AA1 1AA", + "data_type": 2, + "hot_water": { + "description": "From main system, plus solar", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 5 + }, + "post_town": "Town", + "built_form": 1, + "living_area": 44.4, + "orientation": 0, + "region_code": 16, + "report_type": 3, + "sap_heating": { + "has_solar_panel": "true", + "water_fuel_type": 39, + "solar_store_volume": 100, + "water_heating_code": 901, + "secondary_fuel_type": 20, + "hot_water_store_size": 300, + "main_heating_details": [ + { + "main_fuel_type": 39, + "heat_emitter_type": 2, + "main_heating_code": 201, + "main_heating_number": 1, + "main_heating_control": 2207, + "main_heating_category": 4, + "main_heating_fraction": 1, + "main_heating_data_source": 3, + "has_delayed_start_thermostat": "false", + "load_or_weather_compensation": 0, + "underfloor_heat_emitter_type": 2, + "is_main_heating_hetas_approved": "false", + "is_central_heating_pump_in_heated_space": "true" + } + ], + "has_hot_water_cylinder": "true", + "has_solar_powered_pump": "false", + "secondary_heating_code": 633, + "has_cylinder_thermostat": "true", + "solar_panel_aperture_area": 5.26, + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 10, + "solar_panel_collector_type": 3, + "is_cylinder_in_heated_space": "true", + "solar_panel_collector_pitch": 3, + "is_hot_water_separately_timed": "true", + "is_primary_pipework_insulated": "true", + "secondary_heating_data_source": 3, + "hot_water_store_insulation_type": 1, + "hot_water_store_heat_loss_source": 3, + "is_solar_store_combined_cylinder": "true", + "solar_panel_collector_data_source": 2, + "solar_panel_collector_orientation": 3, + "solar_panel_collector_overshading": 1, + "is_heat_pump_assisted_by_immersion": "false", + "is_secondary_heating_hetas_approved": "false", + "hot_water_store_insulation_thickness": 80, + "solar_panel_collector_heat_loss_rate": 3, + "solar_panel_collector_zero_loss_efficiency": 60 + }, + "sap_version": 9.9, + "schema_type": "SAP-Schema-16.2", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": "Ground source heat pump, underfloor, electric", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 5 + } + ], + "air_tightness": { + "description": "Air permeability 6.4 m³/h.m² (as tested)", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "dwelling_type": "Detached house", + "language_code": 1, + "property_type": 0, + "address_line_1": "17, Street Terrace", + "schema_version": "LIG-16.0", + "assessment_date": "2013-02-06", + "assessment_type": "SAP", + "completion_date": "2013-02-06", + "inspection_date": "2013-02-06", + "sap_ventilation": { + "psv_count": 0, + "pressure_test": 1, + "air_permeability": 6.373, + "open_flues_count": 0, + "ventilation_type": 1, + "extract_fans_count": 9, + "open_fireplaces_count": 0, + "sheltered_sides_count": 0, + "flueless_gas_fires_count": 0 + }, + "total_floor_area": 709, + "transaction_type": 6, + "conservatory_type": 1, + "registration_date": "2013-02-06", + "restricted_access": 0, + "sap_energy_source": { + "pv_arrays": [ + { + "pitch": 3, + "peak_power": 3.6, + "orientation": 5, + "overshading": 1 + } + ], + "electricity_tariff": 1, + "wind_turbines_count": 0, + "wind_turbine_terrain_type": 3, + "fixed_lighting_outlets_count": 40, + "low_energy_fixed_lighting_outlets_count": 40, + "low_energy_fixed_lighting_outlets_percentage": 100 + }, + "sap_opening_types": [ + { + "name": 1, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D7", + "glazing_type": 6 + }, + { + "name": 2, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D12", + "glazing_type": 6 + }, + { + "name": 3, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W27", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 4, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W28A", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 5, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W29A", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 6, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W30", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 7, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W31", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 8, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W32", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 9, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W25", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 10, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W26", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 11, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D3", + "glazing_type": 6 + }, + { + "name": 12, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D11", + "glazing_type": 6 + }, + { + "name": 13, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W34", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 14, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W33", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 15, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W20", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 16, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W21", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 17, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W35", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 18, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W36", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 19, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W37", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 20, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W22", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 21, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W23", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 22, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W24", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 23, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W24A", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 24, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W38", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 25, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W39", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 26, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W40", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 27, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W41", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 28, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W42", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 29, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W1A", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 30, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W2A", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 31, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W1B", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 32, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W2B", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 33, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W18", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 34, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W19", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 35, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D4", + "glazing_type": 6 + }, + { + "name": 36, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D6", + "glazing_type": 6 + }, + { + "name": 37, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D5", + "glazing_type": 6 + }, + { + "name": 38, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D9", + "glazing_type": 6 + }, + { + "name": 39, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D10", + "glazing_type": 6 + }, + { + "name": 40, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W17", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 41, + "type": 5, + "u_value": 1.4, + "data_source": 2, + "description": "W14", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 42, + "type": 5, + "u_value": 1.4, + "data_source": 2, + "description": "W16", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 43, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D1", + "glazing_type": 6 + }, + { + "name": 44, + "type": 2, + "u_value": 1.4, + "data_source": 2, + "description": "D2", + "glazing_type": 6 + }, + { + "name": 45, + "type": 1, + "u_value": 1.4, + "data_source": 2, + "description": "D8", + "glazing_type": 1 + }, + { + "name": 46, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W9", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 47, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W29", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 48, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W28", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 49, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W13", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 50, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W9A", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 51, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W15", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 52, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WF", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 53, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WG", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 54, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WH", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 55, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WI", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 56, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WJ", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 57, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W30A", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 58, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W31A", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 59, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WLL", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 60, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WMM", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 61, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WAA", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 62, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WBB", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 63, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WCC", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 64, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WDD", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 65, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WO", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 66, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WP", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 67, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WQ", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 68, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W1", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 69, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W10", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 70, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W2", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 71, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W3", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 72, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W4", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 73, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W5", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 74, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W6", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 75, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W7", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 76, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W11", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 77, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "W12", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 78, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WA", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 79, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WB", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 80, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WC", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 81, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WD", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 82, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WE", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 83, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WK", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 84, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WL", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 85, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WM", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 86, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WN", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 87, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WFF", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 88, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WGG", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 89, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WHH", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 90, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WII", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 91, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WJJ", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 92, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WKK", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 93, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WR", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 94, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WS", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 95, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WT", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 96, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WU", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 97, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WV", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 98, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WW", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 99, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WX", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 100, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WY", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + }, + { + "name": 101, + "type": 4, + "u_value": 1.4, + "data_source": 2, + "description": "WZ", + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + } + ], + "secondary_heating": { + "description": "Room heaters, wood logs", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 4, + 7, + 10, + 11 + ], + "sap_building_parts": [ + { + "sap_roofs": [ + { + "name": "Roof 1", + "u_value": 0.129, + "roof_type": 2, + "description": "Roof 1 ZINC FLAT", + "total_roof_area": 145 + }, + { + "name": "Roof 2", + "u_value": 0.139, + "roof_type": 2, + "description": "Roof 2 THATCH ROOF", + "total_roof_area": 93.03 + }, + { + "name": "Roof 3", + "u_value": 0.19, + "roof_type": 2, + "description": "Roof 3 DORMER", + "total_roof_area": 4.8 + }, + { + "name": "Roof 4", + "u_value": 0.151, + "roof_type": 2, + "description": "Roof 4 SLATE", + "total_roof_area": 157.54 + } + ], + "sap_walls": [ + { + "name": "Wall 1", + "u_value": 0.166, + "wall_type": 2, + "description": "Wall 1 BRICK & FLINT", + "total_wall_area": 407.31, + "is_curtain_walling": "false" + }, + { + "name": "Wall 2", + "u_value": 0.168, + "wall_type": 1, + "description": "Wall 2 CONCRETE", + "total_wall_area": 122.42, + "is_curtain_walling": "false" + }, + { + "name": "Wall 3", + "u_value": 0.181, + "wall_type": 2, + "description": "Wall 3 INT WALL", + "total_wall_area": 17.33, + "is_curtain_walling": "false" + }, + { + "name": "Wall 4", + "u_value": 0.158, + "wall_type": 2, + "description": "Wall 4 TF", + "total_wall_area": 236.58, + "is_curtain_walling": "false" + } + ], + "identifier": "Main Dwelling", + "overshading": 1, + "sap_openings": [ + { + "name": 1, + "type": 1, + "width": 0.91, + "height": 2.1, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 2, + "type": 2, + "width": 0.91, + "height": 2.1, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 3, + "type": 3, + "width": 3.99, + "height": 0.78, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 4, + "type": 4, + "width": 3.33, + "height": 0.97, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 5, + "type": 5, + "width": 3.17, + "height": 0.97, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 6, + "type": 6, + "width": 1.28, + "height": 0.97, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 7, + "type": 7, + "width": 2.31, + "height": 0.97, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 8, + "type": 8, + "width": 2.31, + "height": 0.97, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 9, + "type": 9, + "width": 5.86, + "height": 1.36, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 10, + "type": 10, + "width": 3.95, + "height": 1.36, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 11, + "type": 11, + "width": 1.68, + "height": 2.75, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 12, + "type": 12, + "width": 1.99, + "height": 2.2, + "location": "Wall 4", + "orientation": 0 + }, + { + "name": 13, + "type": 13, + "width": 1.24, + "height": 0.95, + "location": "Wall 1", + "orientation": 8 + }, + { + "name": 14, + "type": 14, + "width": 1.99, + "height": 0.95, + "location": "Wall 1", + "orientation": 2 + }, + { + "name": 15, + "type": 15, + "width": 2.92, + "height": 2.09, + "location": "Wall 4", + "orientation": 2 + }, + { + "name": 16, + "type": 16, + "width": 1.38, + "height": 2.09, + "location": "Wall 4", + "orientation": 4 + }, + { + "name": 17, + "type": 17, + "width": 1.36, + "height": 2.4, + "location": "Wall 4", + "orientation": 1 + }, + { + "name": 18, + "type": 18, + "width": 2.09, + "height": 2.4, + "location": "Wall 4", + "orientation": 3 + }, + { + "name": 19, + "type": 19, + "width": 1.88, + "height": 2.4, + "location": "Wall 4", + "orientation": 3 + }, + { + "name": 20, + "type": 20, + "width": 0.68, + "height": 0.99, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 21, + "type": 21, + "width": 0.68, + "height": 0.99, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 22, + "type": 22, + "width": 0.68, + "height": 0.99, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 23, + "type": 23, + "width": 0.68, + "height": 0.99, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 24, + "type": 24, + "width": 0.54, + "height": 0.99, + "location": "Wall 1", + "orientation": 5 + }, + { + "name": 25, + "type": 25, + "width": 0.54, + "height": 0.99, + "location": "Wall 1", + "orientation": 5 + }, + { + "name": 26, + "type": 26, + "width": 0.54, + "height": 0.99, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 27, + "type": 27, + "width": 0.54, + "height": 0.99, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 28, + "type": 28, + "width": 0.54, + "height": 0.99, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 29, + "type": 29, + "width": 1.45, + "height": 1.42, + "location": "Wall 4", + "orientation": 3 + }, + { + "name": 30, + "type": 30, + "width": 1.68, + "height": 1.42, + "location": "Wall 4", + "orientation": 3 + }, + { + "name": 31, + "type": 31, + "width": 1.45, + "height": 1.42, + "location": "Wall 4", + "orientation": 5 + }, + { + "name": 32, + "type": 32, + "width": 1.68, + "height": 1.42, + "location": "Wall 4", + "orientation": 1 + }, + { + "name": 33, + "type": 33, + "width": 1.35, + "height": 2.09, + "location": "Wall 4", + "orientation": 2 + }, + { + "name": 34, + "type": 34, + "width": 1.35, + "height": 2.09, + "location": "Wall 4", + "orientation": 2 + }, + { + "name": 35, + "type": 35, + "width": 3.84, + "height": 2.8, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 36, + "type": 36, + "width": 4.74, + "height": 2.8, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 37, + "type": 37, + "width": 2.81, + "height": 2.8, + "location": "Wall 4", + "orientation": 0 + }, + { + "name": 38, + "type": 38, + "width": 6.45, + "height": 2.4, + "location": "Wall 4", + "orientation": 0 + }, + { + "name": 39, + "type": 39, + "width": 3.84, + "height": 2.4, + "location": "Wall 4", + "orientation": 0 + }, + { + "name": 40, + "type": 40, + "width": 1.4, + "height": 2.76, + "location": "Wall 1", + "orientation": 2 + }, + { + "name": 41, + "type": 41, + "width": 0.6, + "height": 0.89, + "location": "Roof 2", + "orientation": 8 + }, + { + "name": 42, + "type": 42, + "width": 7.25, + "height": 0.89, + "location": "Roof 2", + "orientation": 4 + }, + { + "name": 43, + "type": 43, + "width": 1.81, + "height": 2.1, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 44, + "type": 44, + "width": 2.49, + "height": 2.1, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 45, + "type": 45, + "width": 3.08, + "height": 2.62, + "location": "Wall 1", + "orientation": 0 + }, + { + "name": 46, + "type": 46, + "width": 1.68, + "height": 1.36, + "location": "Wall 1", + "orientation": 2 + }, + { + "name": 47, + "type": 47, + "width": 1.18, + "height": 2.44, + "location": "Wall 4", + "orientation": 3 + }, + { + "name": 48, + "type": 48, + "width": 1.18, + "height": 1.23, + "location": "Wall 4", + "orientation": 3 + }, + { + "name": 49, + "type": 49, + "width": 4.27, + "height": 2.85, + "location": "Wall 1", + "orientation": 2 + }, + { + "name": 50, + "type": 50, + "width": 1.68, + "height": 4.15, + "location": "Wall 1", + "orientation": 2 + }, + { + "name": 51, + "type": 51, + "width": 1.74, + "height": 2.62, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 52, + "type": 52, + "width": 2.36, + "height": 0.38, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 53, + "type": 53, + "width": 1.5, + "height": 0.38, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 54, + "type": 54, + "width": 0.7, + "height": 0.38, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 55, + "type": 55, + "width": 0.92, + "height": 0.38, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 56, + "type": 56, + "width": 0.9, + "height": 0.38, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 57, + "type": 57, + "width": 1.18, + "height": 2.98, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 58, + "type": 58, + "width": 1.18, + "height": 2.98, + "location": "Wall 1", + "orientation": 3 + }, + { + "name": 59, + "type": 59, + "width": 0.62, + "height": 0.38, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 60, + "type": 60, + "width": 1.69, + "height": 0.38, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 61, + "type": 61, + "width": 2.38, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 62, + "type": 62, + "width": 1.13, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 63, + "type": 63, + "width": 0.73, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 64, + "type": 64, + "width": 1.94, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 65, + "type": 65, + "width": 1.39, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 66, + "type": 66, + "width": 1.59, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 67, + "type": 67, + "width": 1.65, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 68, + "type": 68, + "width": 0.46, + "height": 0.54, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 69, + "type": 69, + "width": 0.46, + "height": 0.54, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 70, + "type": 70, + "width": 1.14, + "height": 1.06, + "location": "Wall 1", + "orientation": 8 + }, + { + "name": 71, + "type": 71, + "width": 1.14, + "height": 1.06, + "location": "Wall 1", + "orientation": 8 + }, + { + "name": 72, + "type": 72, + "width": 1.14, + "height": 1.06, + "location": "Wall 1", + "orientation": 8 + }, + { + "name": 73, + "type": 73, + "width": 1.14, + "height": 1.06, + "location": "Wall 1", + "orientation": 8 + }, + { + "name": 74, + "type": 74, + "width": 1.14, + "height": 1.06, + "location": "Wall 1", + "orientation": 4 + }, + { + "name": 75, + "type": 75, + "width": 1.14, + "height": 1.06, + "location": "Wall 1", + "orientation": 4 + }, + { + "name": 76, + "type": 76, + "width": 2, + "height": 1.09, + "location": "Wall 4", + "orientation": 8 + }, + { + "name": 77, + "type": 77, + "width": 2, + "height": 1.09, + "location": "Wall 4", + "orientation": 8 + }, + { + "name": 78, + "type": 78, + "width": 0.76, + "height": 0.38, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 79, + "type": 79, + "width": 0.76, + "height": 0.38, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 80, + "type": 80, + "width": 0.76, + "height": 0.38, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 81, + "type": 81, + "width": 2.57, + "height": 0.38, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 82, + "type": 82, + "width": 2.57, + "height": 0.38, + "location": "Wall 4", + "orientation": 6 + }, + { + "name": 83, + "type": 83, + "width": 0.88, + "height": 0.38, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 84, + "type": 84, + "width": 0.88, + "height": 0.38, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 85, + "type": 85, + "width": 0.88, + "height": 0.38, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 86, + "type": 86, + "width": 0.88, + "height": 0.38, + "location": "Wall 1", + "orientation": 6 + }, + { + "name": 87, + "type": 87, + "width": 1.28, + "height": 0.38, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 88, + "type": 88, + "width": 1.28, + "height": 0.38, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 89, + "type": 89, + "width": 1.28, + "height": 0.38, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 90, + "type": 90, + "width": 1.35, + "height": 0.38, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 91, + "type": 91, + "width": 1.35, + "height": 0.38, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 92, + "type": 92, + "width": 1.35, + "height": 0.38, + "location": "Wall 4", + "orientation": 7 + }, + { + "name": 93, + "type": 93, + "width": 0.55, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 94, + "type": 94, + "width": 0.55, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 95, + "type": 95, + "width": 0.55, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 96, + "type": 96, + "width": 0.55, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 97, + "type": 97, + "width": 0.55, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 98, + "type": 98, + "width": 0.92, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 99, + "type": 99, + "width": 0.92, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 100, + "type": 100, + "width": 1.23, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + }, + { + "name": 101, + "type": 101, + "width": 1.23, + "height": 0.38, + "location": "Wall 1", + "orientation": 7 + } + ], + "construction_year": 2010, + "sap_thermal_bridges": { + "thermal_bridge_code": 1 + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 0, + "u_value": 0.13, + "floor_type": 1, + "description": "Floor 1", + "storey_height": 2.65, + "heat_loss_area": 341.2, + "total_floor_area": 127.88 + }, + { + "storey": 1, + "u_value": 0, + "floor_type": 3, + "storey_height": 2.88, + "heat_loss_area": 0, + "total_floor_area": 213.32 + }, + { + "storey": 2, + "u_value": 0, + "floor_type": 3, + "storey_height": 3.07, + "heat_loss_area": 0, + "total_floor_area": 347.69 + }, + { + "storey": 3, + "u_value": 0, + "floor_type": 3, + "storey_height": 2.3, + "heat_loss_area": 0, + "total_floor_area": 20.45 + } + ], + "thermal_mass_parameter": 250 + } + ], + "bedf_revision_number": 333, + "heating_cost_current": 2132, + "co2_emissions_current": 6.5, + "energy_rating_average": 60, + "energy_rating_current": 85, + "lighting_cost_current": 154, + "main_heating_controls": [ + { + "description": "Time and temperature zone control", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": 2132, + "hot_water_cost_current": 105, + "co2_emissions_potential": 6.5, + "energy_rating_potential": 85, + "lighting_cost_potential": 154, + "hot_water_cost_potential": 105, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 41938, + "water_heating": 2876 + } + }, + "seller_commission_report": "Y", + "energy_consumption_current": 63, + "has_fixed_air_conditioning": "false", + "calculation_software_version": 5.4, + "energy_consumption_potential": 63, + "environmental_impact_current": 88, + "current_energy_efficiency_band": "B", + "environmental_impact_potential": 88, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "B", + "co2_emissions_current_per_floor_area": 9 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-16.3/rdsap.json b/backend/epc_api/json_samples/SAP-Schema-16.3/rdsap.json new file mode 100644 index 00000000..caa7b681 --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-16.3/rdsap.json @@ -0,0 +1,371 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Pitched, 200 mm loft insulation", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": "Cavity wall, as built, insulated (assumed)", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": "Suspended, no insulation (assumed)", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 2, + "windows": [ + { + "description": "Fully double glazed", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + } + ], + "lighting": { + "description": "Low energy lighting in 71% of fixed outlets", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "AA1 1AA", + "hot_water": { + "description": "Electric immersion, off-peak", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 1 + }, + "post_town": "Town", + "built_form": 2, + "door_count": 2, + "glazed_area": 1, + "region_code": 6, + "report_type": 2, + "sap_heating": { + "wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "cylinder_size": 4, + "water_heating_code": 903, + "water_heating_fuel": 29, + "secondary_fuel_type": 29, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 29, + "heat_emitter_type": 0, + "main_heating_number": 1, + "main_heating_control": 2401, + "main_heating_category": 7, + "main_heating_fraction": 1, + "sap_main_heating_code": 401, + "main_heating_data_source": 2 + } + ], + "immersion_heating_type": 1, + "secondary_heating_type": 691, + "cylinder_insulation_type": 2, + "has_fixed_air_conditioning": "false", + "cylinder_insulation_thickness": 25 + }, + "sap_version": 9.91, + "schema_type": "SAP-Schema-16.3", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": "Electric storage heaters", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 1 + } + ], + "dwelling_type": "Semi-detached house", + "language_code": 1, + "property_type": 0, + "address_line_1": "11, Some Close", + "address_line_2": "Long Itchington", + "schema_version": "LIG-16.1", + "assessment_type": "RdSAP", + "completion_date": "2014-12-06", + "inspection_date": "2014-12-05", + "extensions_count": 0, + "measurement_type": 1, + "total_floor_area": 70, + "transaction_type": 8, + "conservatory_type": 1, + "heated_room_count": 3, + "registration_date": "2014-12-06", + "restricted_access": 0, + "sap_energy_source": { + "main_gas": "N", + "meter_type": 1, + "photovoltaic_supply": { + "percent_roof_area": 0 + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": "Room heaters, electric", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 9 + ], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 270, + "floor_heat_loss": 7, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": 2.3, + "floor_insulation": 1, + "total_floor_area": 35.159, + "floor_construction": 3, + "heat_loss_perimeter": 16.837 + }, + { + "floor": 1, + "room_height": 2.3, + "total_floor_area": 35.159, + "heat_loss_perimeter": 16.837 + } + ], + "wall_insulation_type": 4, + "construction_age_band": "H", + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "200mm" + } + ], + "low_energy_lighting": 71, + "solar_water_heating": "N", + "bedf_revision_number": 370, + "habitable_room_count": 3, + "heating_cost_current": { + "value": 611, + "currency": "GBP" + }, + "insulated_door_count": 0, + "co2_emissions_current": 5.9, + "energy_rating_average": 60, + "energy_rating_current": 59, + "lighting_cost_current": { + "value": 65, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Manual charge control", + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + } + ], + "multiple_glazing_type": 1, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 471, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 226, + "currency": "GBP" + }, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 88, + "currency": "GBP" + }, + "indicative_cost": "£800 - £1,200", + "improvement_type": "W", + "improvement_details": { + "improvement_number": 47 + }, + "improvement_category": 5, + "energy_performance_rating": 63, + "environmental_impact_rating": 41 + }, + { + "sequence": 2, + "typical_saving": { + "value": 35, + "currency": "GBP" + }, + "indicative_cost": "£15 - £30", + "improvement_type": "C", + "improvement_details": { + "improvement_number": 2 + }, + "improvement_category": 5, + "energy_performance_rating": 65, + "environmental_impact_rating": 43 + }, + { + "sequence": 3, + "typical_saving": { + "value": 11, + "currency": "GBP" + }, + "indicative_cost": "£10", + "improvement_type": "E", + "improvement_details": { + "improvement_number": 35 + }, + "improvement_category": 5, + "energy_performance_rating": 65, + "environmental_impact_rating": 44 + }, + { + "sequence": 4, + "typical_saving": { + "value": 100, + "currency": "GBP" + }, + "indicative_cost": "£900 - £1,200", + "improvement_type": "L", + "improvement_details": { + "improvement_number": 25 + }, + "improvement_category": 5, + "energy_performance_rating": 69, + "environmental_impact_rating": 47 + }, + { + "sequence": 5, + "typical_saving": { + "value": 44, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 71, + "environmental_impact_rating": 52 + }, + { + "sequence": 6, + "typical_saving": { + "value": 261, + "currency": "GBP" + }, + "indicative_cost": "£9,000 - £14,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 83, + "environmental_impact_rating": 62 + }, + { + "sequence": 7, + "typical_saving": { + "value": 22, + "currency": "GBP" + }, + "indicative_cost": "£1,500 - £4,000", + "improvement_type": "V", + "improvement_details": { + "improvement_number": 44 + }, + "improvement_category": 5, + "energy_performance_rating": 84, + "environmental_impact_rating": 63 + } + ], + "co2_emissions_potential": 3.1, + "energy_rating_potential": 84, + "lighting_cost_potential": { + "value": 51, + "currency": "GBP" + }, + "alternative_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 50, + "currency": "GBP" + }, + "improvement_type": "J2", + "improvement_details": { + "improvement_number": 54 + }, + "improvement_category": 6, + "energy_performance_rating": 68, + "environmental_impact_rating": 88 + }, + { + "sequence": 2, + "typical_saving": { + "value": 178, + "currency": "GBP" + }, + "improvement_type": "Z1", + "improvement_details": { + "improvement_number": 51 + }, + "improvement_category": 6, + "energy_performance_rating": 75, + "environmental_impact_rating": 76 + }, + { + "sequence": 3, + "typical_saving": { + "value": 179, + "currency": "GBP" + }, + "improvement_type": "Z3", + "improvement_details": { + "improvement_number": 53 + }, + "improvement_category": 6, + "energy_performance_rating": 73, + "environmental_impact_rating": 72 + } + ], + "hot_water_cost_potential": { + "value": 105, + "currency": "GBP" + }, + "renewable_heat_incentive": { + "water_heating": 3424, + "space_heating_existing_dwelling": 7554 + }, + "seller_commission_report": "Y", + "energy_consumption_current": 473, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": 8.3, + "energy_consumption_potential": 247, + "environmental_impact_current": 37, + "fixed_lighting_outlets_count": 7, + "current_energy_efficiency_band": "D", + "environmental_impact_potential": 63, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "B", + "co2_emissions_current_per_floor_area": 84, + "low_energy_fixed_lighting_outlets_count": 5 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-16.3/sap.json b/backend/epc_api/json_samples/SAP-Schema-16.3/sap.json new file mode 100644 index 00000000..d5005cc7 --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-16.3/sap.json @@ -0,0 +1,456 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Average thermal transmittance 0.12 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.28 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.17 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "status": "entered", + "windows": { + "description": "High performance glazing", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "lighting": { + "description": "Low energy lighting in all fixed outlets", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "AA1 1AA", + "data_type": 2, + "hot_water": { + "description": "From main system", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Town", + "built_form": 4, + "living_area": 20.15, + "orientation": 7, + "region_code": 3, + "report_type": 3, + "sap_heating": { + "thermal_store": 1, + "has_solar_panel": "false", + "water_fuel_type": 1, + "water_heating_code": 901, + "hot_water_store_size": 180, + "main_heating_details": [ + { + "burner_control": 3, + "main_fuel_type": 1, + "heat_emitter_type": 1, + "boiler_index_number": 16179, + "is_flue_fan_present": "true", + "main_heating_number": 1, + "main_heating_control": 2110, + "is_interlocked_system": "true", + "main_heating_category": 2, + "main_heating_fraction": 1, + "main_heating_flue_type": 2, + "main_heating_data_source": 1, + "has_delayed_start_thermostat": "true", + "load_or_weather_compensation": 2, + "is_central_heating_pump_in_heated_space": "true" + } + ], + "has_hot_water_cylinder": "true", + "has_cylinder_thermostat": "true", + "hot_water_store_heat_loss": 1.63, + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 1, + "is_cylinder_in_heated_space": "true", + "is_hot_water_separately_timed": "true", + "is_primary_pipework_insulated": "true", + "hot_water_store_heat_loss_source": 2 + }, + "sap_version": 9.9, + "schema_type": "SAP-Schema-16.3", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "air_tightness": { + "description": "Air permeability 3.9 m³/h.m² (assumed)", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "property_type": 0, + "address_line_1": "17, Street Terrace", + "schema_version": "LIG-16.0", + "assessment_date": "2014-04-04", + "assessment_type": "SAP", + "completion_date": "2014-04-04", + "inspection_date": "2014-04-04", + "sap_ventilation": { + "psv_count": 0, + "pressure_test": 1, + "air_permeability": 3.89, + "open_flues_count": 0, + "ventilation_type": 1, + "extract_fans_count": 3, + "open_fireplaces_count": 0, + "sheltered_sides_count": 2, + "flueless_gas_fires_count": 0 + }, + "design_water_use": 1, + "total_floor_area": 96, + "transaction_type": 6, + "conservatory_type": 1, + "registration_date": "2014-04-05", + "restricted_access": 0, + "sap_energy_source": { + "electricity_tariff": 1, + "wind_turbines_count": 0, + "wind_turbine_terrain_type": 1, + "fixed_lighting_outlets_count": 16, + "low_energy_fixed_lighting_outlets_count": 16, + "low_energy_fixed_lighting_outlets_percentage": 100 + }, + "sap_opening_types": [ + { + "name": "Doors (1)", + "type": 1, + "u_value": 1.4, + "data_source": 3, + "glazing_type": 1 + }, + { + "name": "Doors (2)", + "type": 1, + "u_value": 1.5, + "data_source": 2, + "glazing_type": 1 + }, + { + "name": "Windows (1)", + "type": 4, + "u_value": 1.5, + "data_source": 2, + "frame_factor": 0.7, + "glazing_type": 6, + "solar_transmittance": 0.63 + } + ], + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 9 + ], + "sap_building_parts": [ + { + "sap_roofs": [ + { + "name": "Roof (1)", + "u_value": 0.11, + "roof_type": 2, + "description": "Pitched roof insulation at ceiling level", + "kappa_value": 8.75, + "total_roof_area": 41.96 + }, + { + "name": "Roof (2)", + "u_value": 0.2, + "roof_type": 2, + "description": "Flat roof - internal insulation", + "kappa_value": 8.75, + "total_roof_area": 4.69 + } + ], + "sap_walls": [ + { + "name": "Walls (1)", + "u_value": 0.29, + "wall_type": 2, + "description": "Masonry cavity wall", + "kappa_value": 46.25, + "total_wall_area": 26.5, + "is_curtain_walling": "false" + }, + { + "name": "Walls (2)", + "u_value": 0.28, + "wall_type": 2, + "description": "Timber frame wall", + "kappa_value": 8.75, + "total_wall_area": 48, + "is_curtain_walling": "false" + }, + { + "name": "Walls (3)", + "u_value": 0.22, + "wall_type": 3, + "description": "Insulated wall to garage", + "kappa_value": 8.75, + "total_wall_area": 7.29, + "is_curtain_walling": "false" + }, + { + "name": "Party wall (1)", + "u_value": 0, + "wall_type": 4, + "kappa_value": 17.5, + "total_wall_area": 48.6 + } + ], + "identifier": "Main Dwelling", + "overshading": 2, + "sap_openings": [ + { + "name": 1, + "type": "Doors (2)", + "width": 2.69, + "height": 2.1, + "location": "Walls (2)", + "orientation": 7 + }, + { + "name": 2, + "type": "Windows (1)", + "width": 0.46, + "height": 1.88, + "location": "Walls (2)", + "orientation": 7 + }, + { + "name": 3, + "type": "Windows (1)", + "width": 0.67, + "height": 0.9, + "location": "Walls (2)", + "orientation": 3 + }, + { + "name": 4, + "type": "Windows (1)", + "width": 0.46, + "height": 1.88, + "location": "Walls (2)", + "orientation": 7 + }, + { + "name": 5, + "type": "Windows (1)", + "width": 1.45, + "height": 1.35, + "location": "Walls (2)", + "orientation": 3 + }, + { + "name": 6, + "type": "Windows (1)", + "width": 1.45, + "height": 1.35, + "location": "Walls (2)", + "orientation": 3 + }, + { + "name": 7, + "type": "Doors (1)", + "width": 1.01, + "height": 2, + "location": "Walls (3)", + "orientation": 0 + }, + { + "name": 8, + "type": "Doors (2)", + "width": 1.74, + "height": 2.1, + "location": "Walls (2)", + "orientation": 7 + }, + { + "name": 9, + "type": "Doors (2)", + "width": 1.79, + "height": 2.1, + "location": "Walls (2)", + "orientation": 3 + }, + { + "name": 10, + "type": "Doors (1)", + "width": 1.01, + "height": 2.1, + "location": "Walls (1)", + "orientation": 0 + } + ], + "construction_year": 2013, + "sap_thermal_bridges": { + "thermal_bridges": [ + { + "length": 9.8, + "psi_value": 0.06, + "psi_value_source": 2, + "thermal_bridge_type": 10 + }, + { + "length": 4.2, + "psi_value": 0.04, + "psi_value_source": 2, + "thermal_bridge_type": 19 + }, + { + "length": 2.6, + "psi_value": 0.28, + "psi_value_source": 2, + "thermal_bridge_type": 20 + }, + { + "length": 6, + "psi_value": 0.08, + "psi_value_source": 2, + "thermal_bridge_type": 22 + }, + { + "length": 33.6, + "psi_value": 0, + "psi_value_source": 2, + "thermal_bridge_type": 23 + }, + { + "length": 25.86, + "psi_value": 0.05, + "psi_value_source": 2, + "thermal_bridge_type": 4 + }, + { + "length": 12.73, + "psi_value": 0.3, + "psi_value_source": 2, + "thermal_bridge_type": 2 + }, + { + "length": 12.73, + "psi_value": 0.04, + "psi_value_source": 2, + "thermal_bridge_type": 3 + } + ], + "thermal_bridge_code": 5 + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 0, + "u_value": 0.16, + "floor_type": 2, + "kappa_value": 30, + "storey_height": 2.4, + "heat_loss_area": 11.8, + "total_floor_area": 11.72 + }, + { + "storey": 1, + "u_value": 0.17, + "floor_type": 3, + "kappa_value": 30, + "storey_height": 2.4, + "heat_loss_area": 24.68, + "total_floor_area": 41.95 + }, + { + "storey": 2, + "u_value": 0, + "floor_type": 4, + "kappa_value": 0, + "storey_height": 2.4, + "heat_loss_area": 0, + "total_floor_area": 41.95 + } + ] + } + ], + "bedf_revision_number": 333, + "heating_cost_current": 260, + "co2_emissions_current": 1.5, + "energy_rating_average": 60, + "energy_rating_current": 83, + "lighting_cost_current": 64, + "main_heating_controls": [ + { + "description": "Time and temperature zone control", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": 260, + "hot_water_cost_current": 93, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": 37, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 3, + "energy_performance_rating": 84, + "environmental_impact_rating": 88 + }, + { + "sequence": 2, + "typical_saving": 226, + "indicative_cost": "£11,000 - £20,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 3, + "energy_performance_rating": 94, + "environmental_impact_rating": 96 + } + ], + "co2_emissions_potential": 1.5, + "energy_rating_potential": 83, + "lighting_cost_potential": 64, + "hot_water_cost_potential": 93, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 3127, + "water_heating": 2230 + } + }, + "seller_commission_report": "N", + "energy_consumption_current": 85, + "has_fixed_air_conditioning": "false", + "calculation_software_version": 3.59, + "energy_consumption_potential": 85, + "environmental_impact_current": 85, + "current_energy_efficiency_band": "B", + "environmental_impact_potential": 85, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "B", + "co2_emissions_current_per_floor_area": 16 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-17.0/epc.json b/backend/epc_api/json_samples/SAP-Schema-17.0/epc.json new file mode 100644 index 00000000..12957001 --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-17.0/epc.json @@ -0,0 +1,375 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Average thermal transmittance 0.14 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.28 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.19 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "status": "entered", + "tenure": "ND", + "windows": { + "description": "High performance glazing", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "lighting": { + "description": "Low energy lighting in all fixed outlets", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "PT34 5BG", + "data_type": 2, + "hot_water": { + "description": "From main system", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "POSTTOWN", + "built_form": 1, + "living_area": 18.13, + "orientation": 1, + "region_code": 6, + "report_type": 3, + "sap_heating": { + "thermal_store": 1, + "water_fuel_type": 1, + "water_heating_code": 901, + "hot_water_store_size": 180, + "main_heating_details": [ + { + "main_fuel_type": 1, + "heat_emitter_type": 1, + "emitter_temperature": 1, + "is_flue_fan_present": "true", + "main_heating_number": 1, + "main_heating_control": 2110, + "is_interlocked_system": "true", + "main_heating_category": 2, + "main_heating_fraction": 1, + "main_heating_flue_type": 2, + "central_heating_pump_age": 2, + "main_heating_data_source": 1, + "main_heating_index_number": 10321, + "has_separate_delayed_start": "false", + "load_or_weather_compensation": 0, + "is_central_heating_pump_in_heated_space": "true" + } + ], + "has_hot_water_cylinder": "true", + "has_cylinder_thermostat": "true", + "hot_water_store_heat_loss": 1.48, + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 1, + "is_cylinder_in_heated_space": "true", + "primary_pipework_insulation": 4, + "is_hot_water_separately_timed": "true", + "hot_water_store_heat_loss_source": 2 + }, + "sap_version": 9.92, + "schema_type": "SAP-Schema-17.0", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "air_tightness": { + "description": "Air permeability 4.9 m³/h.m² (as tested)", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "dwelling_type": "Detached house", + "language_code": 1, + "property_type": 0, + "address_line_1": "2, Place Park", + "address_line_2": "Central District", + "assessment_date": "2013-07-08", + "assessment_type": "SAP", + "completion_date": "2015-06-30", + "inspection_date": "2015-06-30", + "sap_ventilation": { + "psv_count": 0, + "pressure_test": 1, + "air_permeability": 4.94, + "open_flues_count": 0, + "ventilation_type": 1, + "extract_fans_count": 5, + "open_fireplaces_count": 0, + "sheltered_sides_count": 2, + "flueless_gas_fires_count": 0 + }, + "sap_data_version": 9.9, + "sap_flat_details": { + "level": 1 + }, + "total_floor_area": 139, + "transaction_type": 6, + "conservatory_type": 1, + "registration_date": "2015-06-30", + "sap_energy_source": { + "electricity_tariff": 1, + "wind_turbines_count": 0, + "wind_turbine_terrain_type": 1, + "fixed_lighting_outlets_count": 20, + "low_energy_fixed_lighting_outlets_count": 20, + "low_energy_fixed_lighting_outlets_percentage": 100 + }, + "sap_opening_types": [ + { + "name": "Opening Type 1", + "type": 4, + "u_value": 1.5, + "data_source": 2, + "frame_factor": 0.7, + "glazing_type": 3, + "solar_transmittance": 0.76 + }, + { + "name": "Opening Type 2", + "type": 5, + "u_value": 1.5, + "data_source": 2, + "frame_factor": 0.7, + "glazing_type": 3, + "solar_transmittance": 0.76 + } + ], + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 10 + ], + "sap_building_parts": [ + { + "sap_roofs": [ + { + "name": "Roof 1", + "u_value": 0.14, + "roof_type": 2, + "description": "External Roof 1", + "total_roof_area": 69.63 + } + ], + "sap_walls": [ + { + "name": "External Wall 1", + "u_value": 0.28, + "wall_type": 2, + "description": "External Wall 1", + "total_wall_area": 197.76, + "is_curtain_walling": "false" + } + ], + "identifier": "Main Dwelling", + "overshading": 2, + "sap_openings": [ + { + "name": "Opening 1", + "type": "Opening Type 1", + "width": 7.47, + "height": 1, + "location": "External Wall 1", + "orientation": 1 + }, + { + "name": "Opening 2", + "type": "Opening Type 1", + "width": 6.88, + "height": 1, + "location": "External Wall 1", + "orientation": 5 + }, + { + "name": "Opening 3", + "type": "Opening Type 1", + "width": 5.44, + "height": 1, + "location": "External Wall 1", + "orientation": 7 + }, + { + "name": "Opening 4", + "type": "Opening Type 1", + "width": 6.88, + "height": 1, + "location": "External Wall 1", + "orientation": 3 + }, + { + "name": "Opening 5", + "type": "Opening Type 2", + "pitch": 35, + "width": 1.77, + "height": 1, + "location": "Roof 1", + "orientation": 7 + }, + { + "name": "Opening 6", + "type": "Opening Type 2", + "pitch": 35, + "width": 0.59, + "height": 1, + "location": "Roof 1", + "orientation": 3 + } + ], + "construction_year": 2013, + "sap_thermal_bridges": { + "thermal_bridges": [ + { + "length": 41.2, + "psi_value": 0.32, + "psi_value_source": 4, + "thermal_bridge_type": "E5" + }, + { + "length": 41.2, + "psi_value": 0.14, + "psi_value_source": 4, + "thermal_bridge_type": "E6" + }, + { + "length": 19.2, + "psi_value": 0.18, + "psi_value_source": 4, + "thermal_bridge_type": "E16" + } + ], + "thermal_bridge_code": 5 + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 0, + "u_value": 0.19, + "floor_type": 2, + "description": "Heat Loss Floor 1", + "storey_height": 2.4, + "heat_loss_area": 69.63, + "total_floor_area": 69.63 + }, + { + "storey": 1, + "u_value": 0, + "floor_type": 3, + "storey_height": 2.4, + "heat_loss_area": 0, + "total_floor_area": 69.63 + } + ], + "thermal_mass_parameter": 100 + } + ], + "heating_cost_current": { + "value": 378, + "currency": "GBP" + }, + "co2_emissions_current": 2.2, + "energy_rating_average": 60, + "energy_rating_current": 84, + "lighting_cost_current": { + "value": 73, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Time and temperature zone control", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 379, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 114, + "currency": "GBP" + }, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 52, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 85, + "environmental_impact_rating": 86 + }, + { + "sequence": 2, + "typical_saving": { + "value": 275, + "currency": "GBP" + }, + "indicative_cost": "£5,000 - £8,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 92, + "environmental_impact_rating": 92 + } + ], + "co2_emissions_potential": 1.0, + "energy_rating_potential": 92, + "lighting_cost_potential": { + "value": 73, + "currency": "GBP" + }, + "schema_version_original": "LIG-17.0", + "hot_water_cost_potential": { + "value": 62, + "currency": "GBP" + }, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 5622, + "water_heating": 2279 + } + }, + "seller_commission_report": "Y", + "energy_consumption_current": 91, + "has_fixed_air_conditioning": "false", + "multiple_glazed_percentage": 100, + "calculation_software_version": "4.03r03", + "energy_consumption_potential": 39, + "environmental_impact_current": 84, + "current_energy_efficiency_band": "B", + "environmental_impact_potential": 92, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "A", + "co2_emissions_current_per_floor_area": 16 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-17.1/epc.json b/backend/epc_api/json_samples/SAP-Schema-17.1/epc.json new file mode 100644 index 00000000..b690d220 --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-17.1/epc.json @@ -0,0 +1,562 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Average thermal transmittance 0.11 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.27 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.14 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "status": "entered", + "tenure": "ND", + "windows": { + "description": "High performance glazing", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "lighting": { + "description": "Low energy lighting in all fixed outlets", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "A1 1AA", + "data_type": 2, + "hot_water": { + "description": "From main system", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Town", + "built_form": 1, + "living_area": 20.36, + "orientation": 1, + "region_code": 6, + "report_type": 3, + "sap_heating": { + "thermal_store": 1, + "water_fuel_type": 1, + "water_heating_code": 901, + "hot_water_store_size": 180, + "main_heating_details": [ + { + "main_fuel_type": 1, + "heat_emitter_type": 1, + "emitter_temperature": 1, + "is_flue_fan_present": "true", + "main_heating_number": 1, + "main_heating_control": 2110, + "is_interlocked_system": "true", + "main_heating_category": 2, + "main_heating_fraction": 1, + "main_heating_flue_type": 2, + "central_heating_pump_age": 2, + "main_heating_data_source": 1, + "main_heating_index_number": 18042, + "has_separate_delayed_start": "true", + "load_or_weather_compensation": 0, + "is_central_heating_pump_in_heated_space": "true" + } + ], + "has_hot_water_cylinder": "true", + "has_cylinder_thermostat": "true", + "hot_water_store_heat_loss": 1.2, + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 1, + "is_cylinder_in_heated_space": "true", + "primary_pipework_insulation": 4, + "is_hot_water_separately_timed": "true", + "hot_water_store_heat_loss_source": 2 + }, + "sap_version": 9.92, + "schema_type": "SAP-Schema-17.1", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + { + "description": "Boiler and radiators, mains gas", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "air_tightness": { + "description": "Air permeability 4.9 m³/h.m² (as tested)", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "dwelling_type": "Detached house", + "language_code": 1, + "property_type": 0, + "address_line_1": "47, Address Lane", + "address_line_2": "Long Itchington", + "assessment_date": "2020-08-17", + "assessment_type": "SAP", + "completion_date": "2020-08-17", + "inspection_date": "2020-08-17", + "sap_ventilation": { + "psv_count": 0, + "pressure_test": 1, + "air_permeability": 4.94, + "open_flues_count": 0, + "ventilation_type": 1, + "extract_fans_count": 5, + "open_fireplaces_count": 0, + "sheltered_sides_count": 1, + "flueless_gas_fires_count": 0 + }, + "design_water_use": 1, + "sap_data_version": 9.92, + "sap_flat_details": { + "level": 1 + }, + "total_floor_area": 146, + "transaction_type": 6, + "conservatory_type": 1, + "registration_date": "2020-08-17", + "sap_energy_source": { + "electricity_tariff": 1, + "wind_turbines_count": 0, + "wind_turbine_terrain_type": 2, + "fixed_lighting_outlets_count": 14, + "low_energy_fixed_lighting_outlets_count": 14, + "low_energy_fixed_lighting_outlets_percentage": 100 + }, + "sap_opening_types": [ + { + "name": "Opening Type 1", + "type": 2, + "u_value": 1.5, + "data_source": 2, + "glazing_type": 4 + }, + { + "name": "Opening Type 2", + "type": 4, + "u_value": 1.41, + "data_source": 2, + "frame_factor": 0.7, + "glazing_type": 4, + "solar_transmittance": 0.71 + }, + { + "name": "Opening Type 4", + "type": 4, + "u_value": 1.41, + "data_source": 2, + "frame_factor": 0.7, + "glazing_type": 4, + "solar_transmittance": 0.72 + } + ], + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 11 + ], + "sap_building_parts": [ + { + "sap_roofs": [ + { + "name": "Roof 1", + "u_value": 0.11, + "roof_type": 2, + "description": "400mm Mineral Wool", + "total_roof_area": 72.6 + }, + { + "name": "Roof 2", + "u_value": 0.17, + "roof_type": 2, + "description": "Flat Roof", + "total_roof_area": 1.07 + } + ], + "sap_walls": [ + { + "name": "External Wall 1", + "u_value": 0.27, + "wall_type": 2, + "description": "50mm A Platinum", + "total_wall_area": 186.21, + "is_curtain_walling": "false" + } + ], + "identifier": "Main Dwelling", + "overshading": 2, + "sap_openings": [ + { + "name": "D1", + "type": "Opening Type 1", + "width": 1.01, + "height": 2.33, + "location": "External Wall 1", + "orientation": 0 + }, + { + "name": "D2", + "type": "Opening Type 2", + "width": 2.4, + "height": 2.1, + "location": "External Wall 1", + "orientation": 6 + }, + { + "name": "D3", + "type": "Opening Type 2", + "width": 2.4, + "height": 2.1, + "location": "External Wall 1", + "orientation": 4 + }, + { + "name": "W1", + "type": "Opening Type 4", + "width": 1.14, + "height": 1.5, + "location": "External Wall 1", + "orientation": 2 + }, + { + "name": "W1a", + "type": "Opening Type 4", + "width": 0.56, + "height": 1.5, + "location": "External Wall 1", + "orientation": 3 + }, + { + "name": "W1b", + "type": "Opening Type 4", + "width": 0.56, + "height": 1.5, + "location": "External Wall 1", + "orientation": 1 + }, + { + "name": "W2", + "type": "Opening Type 4", + "width": 1.14, + "height": 1.5, + "location": "External Wall 1", + "orientation": 2 + }, + { + "name": "W2a", + "type": "Opening Type 4", + "width": 0.56, + "height": 1.5, + "location": "External Wall 1", + "orientation": 3 + }, + { + "name": "W2b", + "type": "Opening Type 4", + "width": 0.56, + "height": 1.5, + "location": "External Wall 1", + "orientation": 1 + }, + { + "name": "W3", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.35, + "location": "External Wall 1", + "orientation": 8 + }, + { + "name": "W4", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.35, + "location": "External Wall 1", + "orientation": 8 + }, + { + "name": "W5", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.35, + "location": "External Wall 1", + "orientation": 8 + }, + { + "name": "W6", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.35, + "location": "External Wall 1", + "orientation": 6 + }, + { + "name": "W7", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.35, + "location": "External Wall 1", + "orientation": 2 + }, + { + "name": "W8", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.2, + "location": "External Wall 1", + "orientation": 2 + }, + { + "name": "W9", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.35, + "location": "External Wall 1", + "orientation": 2 + }, + { + "name": "W10", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.35, + "location": "External Wall 1", + "orientation": 8 + }, + { + "name": "W11", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.35, + "location": "External Wall 1", + "orientation": 8 + }, + { + "name": "W12", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.35, + "location": "External Wall 1", + "orientation": 8 + }, + { + "name": "W13", + "type": "Opening Type 4", + "width": 0.63, + "height": 1.05, + "location": "External Wall 1", + "orientation": 4 + }, + { + "name": "W14", + "type": "Opening Type 4", + "width": 1.02, + "height": 1.35, + "location": "External Wall 1", + "orientation": 6 + } + ], + "construction_year": 2017, + "sap_thermal_bridges": { + "thermal_bridges": [ + { + "length": 21.8, + "psi_value": 0.211, + "psi_value_source": 3, + "thermal_bridge_type": "E2" + }, + { + "length": 6.12, + "psi_value": 0.049, + "psi_value_source": 3, + "thermal_bridge_type": "E2" + }, + { + "length": 20.79, + "psi_value": 0.023, + "psi_value_source": 3, + "thermal_bridge_type": "E3" + }, + { + "length": 63.76, + "psi_value": 0.028, + "psi_value_source": 3, + "thermal_bridge_type": "E4" + }, + { + "length": 38.46, + "psi_value": 0.047, + "psi_value_source": 3, + "thermal_bridge_type": "E5" + }, + { + "length": 3.48, + "psi_value": 0.14, + "psi_value_source": 4, + "thermal_bridge_type": "E6" + }, + { + "length": 34.33, + "psi_value": 0.003, + "psi_value_source": 3, + "thermal_bridge_type": "E6" + }, + { + "length": 26.68, + "psi_value": 0.094, + "psi_value_source": 3, + "thermal_bridge_type": "E10" + }, + { + "length": 9.4, + "psi_value": 0.049, + "psi_value_source": 3, + "thermal_bridge_type": "E12" + }, + { + "length": 4.41, + "psi_value": 0.08, + "psi_value_source": 4, + "thermal_bridge_type": "E14" + }, + { + "length": 34.2, + "psi_value": 0.047, + "psi_value_source": 3, + "thermal_bridge_type": "E16" + }, + { + "length": 15.36, + "psi_value": -0.097, + "psi_value_source": 3, + "thermal_bridge_type": "E17" + } + ], + "thermal_bridge_code": 5 + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 0, + "u_value": 0.14, + "floor_type": 2, + "description": "B&B", + "storey_height": 2.33, + "heat_loss_area": 73.67, + "total_floor_area": 73.67 + }, + { + "storey": 1, + "u_value": 0, + "floor_type": 3, + "storey_height": 2.56, + "heat_loss_area": 0, + "total_floor_area": 72.6 + } + ], + "thermal_mass_parameter": 134.43 + } + ], + "heating_cost_current": { + "value": 339, + "currency": "GBP" + }, + "co2_emissions_current": 2.1, + "energy_rating_average": 60, + "energy_rating_current": 85, + "lighting_cost_current": { + "value": 93, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Time and temperature zone control", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 340, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 99, + "currency": "GBP" + }, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 43, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 86, + "environmental_impact_rating": 87 + }, + { + "sequence": 2, + "typical_saving": { + "value": 340, + "currency": "GBP" + }, + "indicative_cost": "£3,500 - £5,500", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 93, + "environmental_impact_rating": 93 + } + ], + "co2_emissions_potential": 0.9, + "energy_rating_potential": 93, + "lighting_cost_potential": { + "value": 93, + "currency": "GBP" + }, + "schema_version_original": "LIG-17.0", + "hot_water_cost_potential": { + "value": 55, + "currency": "GBP" + }, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 5361, + "water_heating": 2143 + } + }, + "seller_commission_report": "Y", + "energy_consumption_current": 82, + "has_fixed_air_conditioning": "false", + "multiple_glazed_percentage": 100, + "calculation_software_version": "4.12r02", + "energy_consumption_potential": 34, + "environmental_impact_current": 85, + "current_energy_efficiency_band": "B", + "environmental_impact_potential": 93, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "A", + "co2_emissions_current_per_floor_area": 14 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-18.0.0/epc.json b/backend/epc_api/json_samples/SAP-Schema-18.0.0/epc.json new file mode 100644 index 00000000..b2d27bbf --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-18.0.0/epc.json @@ -0,0 +1,510 @@ +{ + "name": "-", + "uprn": 12457, + "roofs": [ + { + "description": "Average thermal transmittance 0.10 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.10 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.09 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "status": "entered", + "tenure": "ND", + "windows": { + "description": "High performance glazing", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "addendum": { + "stone_walls": "true", + "system_build": "true" + }, + "lighting": { + "description": "Low energy lighting in all fixed outlets", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "LS0 0AA", + "data_type": 2, + "hot_water": { + "description": "Electric immersion, standard tariff", + "energy_efficiency_rating": 1, + "environmental_efficiency_rating": 2 + }, + "post_town": "Town", + "built_form": 4, + "living_area": 38.91, + "orientation": 6, + "region_code": 3, + "report_type": 3, + "sap_heating": { + "water_fuel_type": 39, + "water_heating_code": 903, + "hot_water_store_size": 210, + "main_heating_details": [ + { + "main_fuel_type": 39, + "main_heating_code": 691, + "emitter_temperature": "NA", + "main_heating_number": 1, + "main_heating_control": 2603, + "main_heating_category": 10, + "main_heating_fraction": 1, + "main_heating_data_source": 3 + } + ], + "has_hot_water_cylinder": "true", + "immersion_heating_type": 1, + "has_cylinder_thermostat": "true", + "hot_water_store_heat_loss": 1.31, + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 1, + "is_cylinder_in_heated_space": "true", + "hot_water_store_heat_loss_source": 2 + }, + "sap_version": 9.92, + "schema_type": "SAP-Schema-18.0.0", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + { + "description": "Room heaters, electric", + "energy_efficiency_rating": 1, + "environmental_efficiency_rating": 2 + } + ], + "air_tightness": { + "description": "Air permeability 0.7 m³/h.m² (as tested)", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "property_type": 0, + "address_line_1": "30 Lovely Place", + "address_line_2": "Nice Road", + "assessment_date": "2021-01-01", + "assessment_type": "SAP", + "completion_date": "2021-01-01", + "inspection_date": "2021-01-01", + "sap_ventilation": { + "psv_count": 0, + "pressure_test": 1, + "wet_rooms_count": 3, + "air_permeability": 0.69, + "open_flues_count": 0, + "ventilation_type": 8, + "extract_fans_count": 0, + "open_fireplaces_count": 0, + "sheltered_sides_count": 3, + "flueless_gas_fires_count": 0, + "mechanical_vent_duct_type": 2, + "mechanical_vent_duct_insulation": 2, + "mechanical_ventilation_data_source": 1, + "mechanical_vent_system_index_number": 500482, + "is_mechanical_vent_approved_installer_scheme": "true" + }, + "design_water_use": 1, + "sap_data_version": 9.92, + "sap_flat_details": { + "level": 1 + }, + "total_floor_area": 117, + "transaction_type": 6, + "conservatory_type": 1, + "registration_date": "2021-01-01", + "sap_energy_source": { + "pv_arrays": [ + { + "pitch": 2, + "peak_power": 0.75, + "orientation": 6, + "overshading": 1, + "pv_connection": 2 + } + ], + "electricity_tariff": 1, + "wind_turbines_count": 0, + "wind_turbine_terrain_type": 1, + "fixed_lighting_outlets_count": 20, + "low_energy_fixed_lighting_outlets_count": 20, + "low_energy_fixed_lighting_outlets_percentage": 100 + }, + "sap_opening_types": [ + { + "name": "Doors", + "type": 1, + "u_value": 0.75, + "data_source": 2, + "glazing_type": 1 + }, + { + "name": "Windows - W-001A", + "type": 4, + "u_value": 0.72, + "data_source": 2, + "frame_factor": 0.85, + "glazing_type": 12, + "solar_transmittance": 0.52 + }, + { + "name": "Windows - W-001B", + "type": 4, + "u_value": 0.73, + "data_source": 2, + "frame_factor": 0.84, + "glazing_type": 12, + "solar_transmittance": 0.52 + }, + { + "name": "W-003 Louvre system", + "type": 4, + "u_value": 1.02, + "data_source": 2, + "frame_factor": 0.01, + "glazing_type": 12, + "solar_transmittance": 0 + }, + { + "name": "Window - W-003R", + "type": 4, + "u_value": 0.82, + "data_source": 2, + "frame_factor": 0.78, + "glazing_type": 12, + "solar_transmittance": 0.53 + }, + { + "name": "Sliding door - EX002A", + "type": 4, + "u_value": 0.86, + "data_source": 2, + "frame_factor": 0.85, + "glazing_type": 12, + "solar_transmittance": 0.52 + }, + { + "name": "EX002B Louvre system", + "type": 4, + "u_value": 1, + "data_source": 2, + "frame_factor": 0.01, + "glazing_type": 12, + "solar_transmittance": 0 + }, + { + "name": "Window - EX002B", + "type": 4, + "u_value": 0.76, + "data_source": 2, + "frame_factor": 0.84, + "glazing_type": 12, + "solar_transmittance": 0.53 + } + ], + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [ + 11, + 9 + ], + "sap_building_parts": [ + { + "sap_roofs": [ + { + "name": "Roof 1", + "u_value": 0.1, + "roof_type": 2, + "description": "External Roof 1", + "total_roof_area": 38.91 + } + ], + "sap_walls": [ + { + "name": "External Wall 1", + "u_value": 0.1, + "wall_type": 2, + "description": "External Wall 1", + "total_wall_area": 77.31, + "is_curtain_walling": "false" + }, + { + "name": "Party Wall 0", + "u_value": 0, + "wall_type": 4, + "total_wall_area": 129.44 + } + ], + "identifier": "Main Dwelling", + "overshading": 2, + "sap_openings": [ + { + "name": "Opening 1", + "type": "Doors", + "width": 2.23, + "height": 1, + "location": "External Wall 1", + "orientation": 0 + }, + { + "name": "Opening 2", + "type": "Windows - W-001A", + "width": 2.3, + "height": 1, + "location": "External Wall 1", + "orientation": 6 + }, + { + "name": "Opening 3", + "type": "W-003 Louvre system", + "width": 0.63, + "height": 1, + "location": "External Wall 1", + "orientation": 6 + }, + { + "name": "Opening 4", + "type": "Window - W-003R", + "width": 2.38, + "height": 1, + "location": "External Wall 1", + "orientation": 6 + }, + { + "name": "Opening 5", + "type": "Sliding door - EX002A", + "width": 5.37, + "height": 1, + "location": "External Wall 1", + "orientation": 6 + }, + { + "name": "Opening 6", + "type": "Windows - W-001A", + "width": 2.3, + "height": 1, + "location": "External Wall 1", + "orientation": 2 + }, + { + "name": "Opening 7", + "type": "Windows - W-001B", + "width": 2.06, + "height": 1, + "location": "External Wall 1", + "orientation": 2 + }, + { + "name": "Opening 8", + "type": "Window - W-003R", + "width": 1.18, + "height": 1, + "location": "External Wall 1", + "orientation": 2 + }, + { + "name": "Opening 9", + "type": "EX002B Louvre system", + "width": 0.95, + "height": 1, + "location": "External Wall 1", + "orientation": 2 + }, + { + "name": "Opening 10", + "type": "Window - EX002B", + "width": 4.73, + "height": 1, + "location": "External Wall 1", + "orientation": 2 + } + ], + "construction_year": 2017, + "sap_thermal_bridges": { + "thermal_bridges": [ + { + "length": 13.13, + "psi_value": 0.07, + "psi_value_source": 3, + "thermal_bridge_type": "E2" + }, + { + "length": 9.7, + "psi_value": 0.055, + "psi_value_source": 3, + "thermal_bridge_type": "E3" + }, + { + "length": 31.33, + "psi_value": 0.066, + "psi_value_source": 3, + "thermal_bridge_type": "E4" + }, + { + "length": 9.64, + "psi_value": 0.074, + "psi_value_source": 3, + "thermal_bridge_type": "E20" + }, + { + "length": 19.28, + "psi_value": 0.049, + "psi_value_source": 3, + "thermal_bridge_type": "E6" + }, + { + "length": 9.84, + "psi_value": 0.097, + "psi_value_source": 3, + "thermal_bridge_type": "E15" + }, + { + "length": 32.08, + "psi_value": 0.02, + "psi_value_source": 3, + "thermal_bridge_type": "E18" + }, + { + "length": 32.28, + "psi_value": 0, + "psi_value_source": 4, + "thermal_bridge_type": "P2" + }, + { + "length": 16.14, + "psi_value": 0.16, + "psi_value_source": 4, + "thermal_bridge_type": "P7" + }, + { + "length": 16.14, + "psi_value": 0.005, + "psi_value_source": 3, + "thermal_bridge_type": "P4" + } + ], + "thermal_bridge_code": 5 + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 0, + "u_value": 0.09, + "floor_type": 3, + "description": "Heat Loss Floor 1", + "storey_height": 2.48, + "heat_loss_area": 38.91, + "total_floor_area": 38.91 + }, + { + "storey": 1, + "u_value": 0, + "floor_type": 3, + "storey_height": 2.8, + "heat_loss_area": 0, + "total_floor_area": 38.91 + }, + { + "storey": 2, + "u_value": 0, + "floor_type": 3, + "storey_height": 2.74, + "heat_loss_area": 0, + "total_floor_area": 38.91 + } + ], + "thermal_mass_parameter": 100 + } + ], + "heating_cost_current": { + "value": 133, + "currency": "GBP" + }, + "co2_emissions_current": 1.3, + "energy_rating_average": 60, + "energy_rating_current": 88, + "lighting_cost_current": { + "value": 86, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Programmer and appliance thermostats", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 136, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 361, + "currency": "GBP" + }, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 184, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 93, + "environmental_impact_rating": 94 + } + ], + "co2_emissions_potential": 0.8, + "energy_rating_potential": 93, + "lighting_cost_potential": { + "value": 86, + "currency": "GBP" + }, + "schema_version_original": "18.0.0", + "hot_water_cost_potential": { + "value": 175, + "currency": "GBP" + }, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 413, + "water_heating": 1890 + } + }, + "seller_commission_report": "Y", + "energy_consumption_current": 64, + "has_fixed_air_conditioning": "false", + "multiple_glazed_percentage": 100, + "calculation_software_version": "4.14r16", + "energy_consumption_potential": 39, + "environmental_impact_current": 89, + "current_energy_efficiency_band": "B", + "environmental_impact_potential": 94, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "A", + "co2_emissions_current_per_floor_area": 11 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-19.0.0/epc.json b/backend/epc_api/json_samples/SAP-Schema-19.0.0/epc.json new file mode 100644 index 00000000..66e58247 --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-19.0.0/epc.json @@ -0,0 +1,610 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Average thermal transmittance 0.13 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.18 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.12 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "status": "entered", + "tenure": 1, + "windows": { + "description": "High performance glazing", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 5 + }, + "addendum": { + "stone_walls": "true" + }, + "lighting": { + "description": "Energy saving bulbs", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "postcode": "A0 0AA", + "data_type": 1, + "hot_water": { + "description": "From main system, waste water heat recovery", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 3 + }, + "post_town": "Whitbury", + "built_form": 4, + "living_area": 41.35, + "orientation": 0, + "region_code": 16, + "report_type": 3, + "sap_heating": { + "thermal_store": 1, + "shower_outlets": [ + { + "shower_wwhrs": 1, + "shower_flow_rate": 7, + "shower_outlet_type": 1 + }, + { + "shower_wwhrs": 2, + "shower_flow_rate": 7, + "shower_outlet_type": 1 + } + ], + "water_fuel_type": 39, + "water_heating_code": 901, + "instantaneous_wwhrs": { + "wwhrs_index_number1": 491123 + }, + "hot_water_store_size": 600, + "main_heating_details": [ + { + "has_fghrs": "false", + "main_fuel_type": 39, + "combi_boiler_type": 4, + "heat_emitter_type": 1, + "main_heating_code": 192, + "main_heating_number": 1, + "is_condensing_boiler": "false", + "main_heating_control": 2106, + "is_interlocked_system": "false", + "main_heating_category": 2, + "main_heating_fraction": 1, + "central_heating_pump_age": 2, + "main_heating_data_source": 3, + "has_separate_delayed_start": "true", + "is_oil_pump_in_heated_space": "false", + "is_main_heating_hetas_approved": "false", + "electric_cpsu_operating_temperature": 80, + "is_central_heating_pump_in_heated_space": "true" + } + ], + "has_hot_water_cylinder": "true", + "has_cylinder_thermostat": "true", + "hot_water_store_heat_loss": 2.45, + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 1, + "is_immersion_for_summer_use": "false", + "is_hot_water_separately_timed": "false", + "hot_water_store_heat_loss_source": 2, + "is_heat_pump_assisted_by_immersion": "false" + }, + "sap_version": 10.2, + "schema_type": "SAP-Schema-19.0.0", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + { + "description": "Boiler and radiators, electric", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 2 + } + ], + "sap_lighting": [ + [ + { + "lighting_power": 60, + "lighting_outlets": 1, + "lighting_efficacy": 11.2 + }, + { + "lighting_power": 14, + "lighting_outlets": 10, + "lighting_efficacy": 66.9 + }, + { + "lighting_power": 15, + "lighting_outlets": 7, + "lighting_efficacy": 69.9 + } + ] + ], + "terrain_type": 1, + "air_tightness": { + "description": "Air permeability 2.0 m³/h.m² (assumed)", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "property_type": 0, + "address_line_1": "1 Some Street", + "address_line_2": "Some Area", + "address_line_3": "Some County", + "assessment_date": "2022-05-09", + "assessment_type": "SAP", + "completion_date": "2022-05-09", + "inspection_date": "2022-05-09", + "sap_ventilation": { + "psv_count": 0, + "wall_type": 2, + "pressure_test": 2, + "wet_rooms_count": 2, + "air_permeability": 2, + "open_flues_count": 0, + "ventilation_type": 8, + "has_draught_lobby": "false", + "other_flues_count": 0, + "closed_flues_count": 0, + "extract_fans_count": 0, + "boilers_flues_count": 0, + "open_chimneys_count": 0, + "sheltered_sides_count": 2, + "blocked_chimneys_count": 0, + "flueless_gas_fires_count": 0, + "mechanical_vent_duct_type": 2, + "mechanical_vent_duct_placement": 1, + "mechanical_ventilation_data_source": 1, + "mechanical_vent_system_index_number": 500206, + "mechanical_vent_duct_insulation_level": 2, + "is_mechanical_vent_approved_installer_scheme": "true" + }, + "sap_data_version": 10.2, + "sap_flat_details": { + "level": 1, + "storeys": 1 + }, + "total_floor_area": 165, + "transaction_type": 1, + "cold_water_source": 1, + "conservatory_type": 1, + "registration_date": "2022-05-09", + "sap_energy_source": { + "wind_turbines": [ + { + "wind_turbine_hub_height": 3, + "wind_turbine_rotor_diameter": 1.7 + } + ], + "electricity_tariff": 4 + }, + "sap_opening_types": [ + { + "name": "Doors", + "type": 1, + "u_value": 1.5, + "data_source": 2, + "glazing_type": 1, + "isargonfilled": "false" + }, + { + "name": "Windows (1)", + "type": 4, + "u_value": 1.4, + "frame_type": 1, + "data_source": 3, + "glazing_gap": 3, + "frame_factor": 0.7, + "glazing_type": 11, + "isargonfilled": "true", + "solar_transmittance": 0.57 + }, + { + "name": "Windows (2)", + "type": 4, + "u_value": 1.5, + "frame_type": 1, + "data_source": 3, + "glazing_gap": 3, + "frame_factor": 0.7, + "glazing_type": 9, + "isargonfilled": "true", + "solar_transmittance": 0.64 + } + ], + "secondary_heating": { + "description": "Electric heater", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lowest_storey_area": 57.4, + "lzc_energy_sources": [ + 11 + ], + "sap_building_parts": [ + { + "sap_roofs": [ + { + "name": "Roof (1)", + "u_value": 0.13, + "roof_type": 2, + "kappa_value": 9, + "total_roof_area": 57.4 + } + ], + "sap_walls": [ + { + "name": "Walls (1)", + "u_value": 0.18, + "wall_type": 2, + "kappa_value": 60, + "total_wall_area": 111.34, + "is_curtain_walling": "false" + }, + { + "name": "Party wall", + "u_value": 0, + "wall_type": 4, + "kappa_value": 0, + "total_wall_area": 167, + "is_curtain_walling": "false" + }, + { + "name": "Internal wall (1)", + "u_value": 0, + "wall_type": 5, + "kappa_value": 0, + "total_wall_area": 320, + "is_curtain_walling": "false" + } + ], + "identifier": "Main Dwelling", + "sap_openings": [ + { + "name": 1, + "type": "Doors", + "width": 1, + "height": 2.25, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 2, + "type": "Doors", + "width": 0.8, + "height": 2.1, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 3, + "type": "Windows (2)", + "width": 1.6, + "height": 2.7, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 4, + "type": "Windows (1)", + "width": 1.1, + "height": 1.75, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 5, + "type": "Windows (1)", + "width": 1.1, + "height": 1.75, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 6, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 7, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 8, + "type": "Windows (1)", + "width": 1.1, + "height": 1.8, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 9, + "type": "Windows (1)", + "width": 1.1, + "height": 1.75, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 10, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 11, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 12, + "type": "Windows (1)", + "width": 0.6, + "height": 0.8, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 13, + "type": "Windows (1)", + "width": 0.8, + "height": 1.25, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 14, + "type": "Windows (1)", + "width": 0.8, + "height": 1.25, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 15, + "type": "Windows (1)", + "width": 1.1, + "height": 1.25, + "location": "Walls (1)", + "orientation": 7 + } + ], + "construction_year": 1750, + "sap_thermal_bridges": { + "thermal_bridges": [ + { + "length": 15.5, + "psi_value": 0.3, + "psi_value_source": 2, + "thermal_bridge_type": "E2" + }, + { + "length": 12.1, + "psi_value": 0.04, + "psi_value_source": 2, + "thermal_bridge_type": "E3" + }, + { + "length": 52.1, + "psi_value": 0.05, + "psi_value_source": 2, + "thermal_bridge_type": "E4" + }, + { + "length": 14.4, + "psi_value": 0.16, + "psi_value_source": 2, + "thermal_bridge_type": "E5" + }, + { + "length": 25.8, + "psi_value": 0.07, + "psi_value_source": 2, + "thermal_bridge_type": "E6" + }, + { + "length": 10.4, + "psi_value": 0.06, + "psi_value_source": 2, + "thermal_bridge_type": "E10" + }, + { + "length": 5.4, + "psi_value": 0.06, + "psi_value_source": 3, + "thermal_bridge_type": "E14" + }, + { + "length": 5.8, + "psi_value": 0.09, + "psi_value_source": 2, + "thermal_bridge_type": "E16" + }, + { + "length": 5.8, + "psi_value": -0.09, + "psi_value_source": 2, + "thermal_bridge_type": "E17" + }, + { + "length": 34, + "psi_value": 0.06, + "psi_value_source": 2, + "thermal_bridge_type": "E18" + }, + { + "length": 20.6, + "psi_value": 0.12, + "psi_value_source": 3, + "thermal_bridge_type": "P1" + }, + { + "length": 20.6, + "psi_value": 0, + "psi_value_source": 4, + "thermal_bridge_type": "P2" + }, + { + "length": 20.6, + "psi_value": 0.12, + "psi_value_source": 3, + "thermal_bridge_type": "P4" + } + ], + "thermal_bridge_code": 5 + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 0, + "u_value": 0.12, + "floor_type": 2, + "kappa_value": 80, + "storey_height": 2.8, + "heat_loss_area": 57.4, + "total_floor_area": 57.4 + }, + { + "storey": 1, + "u_value": 0, + "floor_type": 3, + "kappa_value": 18, + "storey_height": 3, + "heat_loss_area": 0, + "total_floor_area": 57.4, + "kappa_value_from_below": 9 + }, + { + "storey": 2, + "u_value": 0, + "floor_type": 3, + "kappa_value": 19, + "storey_height": 2.7, + "heat_loss_area": 0, + "total_floor_area": 57.4, + "kappa_value_from_below": 9 + } + ], + "construction_age_band": "A" + } + ], + "user_interface_name": "BRE SAP interface 10.2", + "windows_overshading": 2, + "heating_cost_current": { + "value": 365.98, + "currency": "GBP" + }, + "co2_emissions_current": 2.4, + "energy_rating_average": 60, + "energy_rating_current": 72, + "lighting_cost_current": { + "value": 123.45, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Programmer, room thermostat and TRVs", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "has_hot_water_cylinder": "false", + "heating_cost_potential": { + "value": 250.34, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 200.4, + "currency": "GBP" + }, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 88, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 74, + "environmental_impact_rating": 94 + }, + { + "sequence": 2, + "typical_saving": { + "value": 88, + "currency": "GBP" + }, + "indicative_cost": "£9,000 - £14,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 80, + "environmental_impact_rating": 96 + } + ], + "user_interface_version": "1.0.1-alpha", + "co2_emissions_potential": 1.4, + "energy_rating_potential": 72, + "gas_smart_meter_present": "false", + "lighting_cost_potential": { + "value": 84.23, + "currency": "GBP" + }, + "schema_version_original": "SAP-Schema-19.0.0", + "hot_water_cost_potential": { + "value": 180.43, + "currency": "GBP" + }, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 13120, + "water_heating": 2285 + } + }, + "seller_commission_report": "Y", + "energy_consumption_current": 59, + "has_fixed_air_conditioning": "false", + "is_dwelling_export_capable": "true", + "multiple_glazed_percentage": 100, + "calculation_software_version": "13.05r16", + "energy_consumption_potential": 53, + "environmental_impact_current": 94, + "current_energy_efficiency_band": "C", + "environmental_impact_potential": 96, + "electricity_smart_meter_present": "false", + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 5.6 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-19.1.0/epc.json b/backend/epc_api/json_samples/SAP-Schema-19.1.0/epc.json new file mode 100644 index 00000000..2e94810f --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-19.1.0/epc.json @@ -0,0 +1,613 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Average thermal transmittance 0.13 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.18 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.12 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "status": "entered", + "tenure": 1, + "windows": { + "description": "High performance glazing", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "lighting": { + "description": "Low energy lighting in 91% of fixed outlets", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "A0 0AA", + "data_type": 1, + "hot_water": { + "description": "From main system, waste water heat recovery", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 3 + }, + "post_town": "Whitbury", + "built_form": 4, + "living_area": 41.35, + "orientation": 0, + "region_code": 16, + "report_type": 3, + "sap_heating": { + "thermal_store": 1, + "shower_outlets": [ + { + "shower_wwhrs": 1, + "shower_flow_rate": 7, + "shower_outlet_type": 1 + }, + { + "shower_wwhrs": 2, + "shower_flow_rate": 7, + "shower_outlet_type": 1 + } + ], + "water_fuel_type": 39, + "water_heating_code": 901, + "instantaneous_wwhrs": { + "wwhrs_index_number1": 491123 + }, + "hot_water_store_size": 600, + "main_heating_details": [ + { + "has_fghrs": "false", + "main_fuel_type": 47, + "combi_boiler_type": 4, + "heat_emitter_type": 1, + "main_heating_code": 192, + "main_heating_number": 1, + "is_condensing_boiler": "false", + "main_heating_control": 2106, + "is_interlocked_system": "false", + "main_heating_category": 2, + "main_heating_fraction": 1, + "central_heating_pump_age": 2, + "main_heating_data_source": 3, + "has_separate_delayed_start": "true", + "is_oil_pump_in_heated_space": "false", + "is_main_heating_hetas_approved": "false", + "electric_cpsu_operating_temperature": 80, + "is_central_heating_pump_in_heated_space": "true" + } + ], + "has_hot_water_cylinder": "true", + "has_cylinder_thermostat": "true", + "hot_water_store_heat_loss": 2.45, + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 1, + "is_immersion_for_summer_use": "false", + "is_hot_water_separately_timed": "false", + "sap_community_heating_systems": [ + { + "sub_network_name": "Test 1", + "community_heating_use": 3, + "heat_network_existing": "true", + "heat_network_index_number": 496402, + "heat_network_assessed_as_new": "true", + "community_heating_distribution_type": 5, + "community_heating_distribution_loss_factor": 1.26 + } + ], + "hot_water_store_heat_loss_source": 2, + "is_heat_pump_assisted_by_immersion": "false" + }, + "sap_version": 10.2, + "schema_type": "SAP-Schema-19.1.0", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + { + "description": "Boiler and radiators, electric", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 2 + } + ], + "sap_lighting": [ + [ + { + "lighting_power": 60, + "lighting_outlets": 1, + "lighting_efficacy": 11.2 + }, + { + "lighting_power": 14, + "lighting_outlets": 10, + "lighting_efficacy": 66.9 + } + ] + ], + "terrain_type": 1, + "air_tightness": { + "description": "Air permeability 2.0 m³/h.m² (assumed)", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "property_type": 0, + "address_line_1": "1 Some Street", + "address_line_2": "Some Area", + "address_line_3": "Some County", + "assessment_date": "2022-05-09", + "assessment_type": "SAP", + "completion_date": "2022-05-09", + "inspection_date": "2022-05-09", + "sap_ventilation": { + "psv_count": 0, + "wall_type": 2, + "pressure_test": 2, + "wet_rooms_count": 2, + "air_permeability": 2, + "open_flues_count": 0, + "ventilation_type": 8, + "has_draught_lobby": "false", + "other_flues_count": 0, + "closed_flues_count": 0, + "extract_fans_count": 0, + "boilers_flues_count": 0, + "open_chimneys_count": 0, + "sheltered_sides_count": 2, + "blocked_chimneys_count": 0, + "flueless_gas_fires_count": 0, + "mechanical_vent_duct_type": 2, + "mechanical_vent_duct_placement": 1, + "mechanical_ventilation_data_source": 1, + "mechanical_vent_system_index_number": 500206, + "mechanical_vent_duct_insulation_level": 2, + "is_mechanical_vent_approved_installer_scheme": "true" + }, + "sap_data_version": 10.2, + "sap_flat_details": { + "level": 1, + "storeys": 1 + }, + "total_floor_area": 165, + "transaction_type": 1, + "cold_water_source": 1, + "conservatory_type": 1, + "registration_date": "2022-05-09", + "sap_energy_source": { + "wind_turbines": [ + { + "wind_turbine_hub_height": 3, + "wind_turbine_rotor_diameter": 1.7 + }, + { + "wind_turbine_hub_height": 3, + "wind_turbine_rotor_diameter": 1.7 + } + ], + "electricity_tariff": 5 + }, + "sap_opening_types": [ + { + "name": "Doors", + "type": 1, + "u_value": 1.5, + "data_source": 2, + "glazing_type": 1, + "isargonfilled": "false" + }, + { + "name": "Windows (1)", + "type": 4, + "u_value": 1.4, + "frame_type": 1, + "data_source": 3, + "glazing_gap": 3, + "frame_factor": 0.7, + "glazing_type": 11, + "isargonfilled": "true", + "solar_transmittance": 0.57 + }, + { + "name": "Windows (2)", + "type": 4, + "u_value": 1.5, + "frame_type": 1, + "data_source": 3, + "glazing_gap": 3, + "frame_factor": 0.7, + "glazing_type": 9, + "isargonfilled": "true", + "solar_transmittance": 0.64 + } + ], + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lowest_storey_area": 57.4, + "sap_building_parts": [ + { + "sap_roofs": [ + { + "name": "Roof (1)", + "u_value": 0.13, + "roof_type": 2, + "kappa_value": 9, + "total_roof_area": 57.4 + } + ], + "sap_walls": [ + { + "name": "Walls (1)", + "u_value": 0.18, + "wall_type": 2, + "kappa_value": 60, + "total_wall_area": 111.34, + "is_curtain_walling": "false" + }, + { + "name": "Party wall", + "u_value": 0, + "wall_type": 4, + "kappa_value": 0, + "total_wall_area": 167, + "is_curtain_walling": "false" + }, + { + "name": "Internal wall (1)", + "u_value": 0, + "wall_type": 5, + "kappa_value": 0, + "total_wall_area": 320, + "is_curtain_walling": "false" + } + ], + "identifier": "Main Dwelling", + "sap_openings": [ + { + "name": 1, + "type": "Doors", + "width": 1, + "height": 2.25, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 2, + "type": "Doors", + "width": 0.8, + "height": 2.1, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 3, + "type": "Windows (2)", + "width": 1.6, + "height": 2.7, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 4, + "type": "Windows (1)", + "width": 1.1, + "height": 1.75, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 5, + "type": "Windows (1)", + "width": 1.1, + "height": 1.75, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 6, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 7, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 8, + "type": "Windows (1)", + "width": 1.1, + "height": 1.8, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 9, + "type": "Windows (1)", + "width": 1.1, + "height": 1.75, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 10, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 11, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 12, + "type": "Windows (1)", + "width": 0.6, + "height": 0.8, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 13, + "type": "Windows (1)", + "width": 0.8, + "height": 1.25, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 14, + "type": "Windows (1)", + "width": 0.8, + "height": 1.25, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 15, + "type": "Windows (1)", + "width": 1.1, + "height": 1.25, + "location": "Walls (1)", + "orientation": 7 + } + ], + "sap_thermal_bridges": { + "thermal_bridges": [ + { + "length": 15.5, + "psi_value": 0.3, + "psi_value_source": 2, + "thermal_bridge_type": "E2" + }, + { + "length": 12.1, + "psi_value": 0.04, + "psi_value_source": 2, + "thermal_bridge_type": "E3" + }, + { + "length": 52.1, + "psi_value": 0.05, + "psi_value_source": 2, + "thermal_bridge_type": "E4" + }, + { + "length": 14.4, + "psi_value": 0.16, + "psi_value_source": 2, + "thermal_bridge_type": "E5" + }, + { + "length": 25.8, + "psi_value": 0.07, + "psi_value_source": 2, + "thermal_bridge_type": "E6" + }, + { + "length": 10.4, + "psi_value": 0.06, + "psi_value_source": 2, + "thermal_bridge_type": "E10" + }, + { + "length": 5.4, + "psi_value": 0.06, + "psi_value_source": 3, + "thermal_bridge_type": "E14" + }, + { + "length": 5.8, + "psi_value": 0.09, + "psi_value_source": 2, + "thermal_bridge_type": "E16" + }, + { + "length": 5.8, + "psi_value": -0.09, + "psi_value_source": 2, + "thermal_bridge_type": "E17" + }, + { + "length": 34, + "psi_value": 0.06, + "psi_value_source": 2, + "thermal_bridge_type": "E18" + }, + { + "length": 20.6, + "psi_value": 0.12, + "psi_value_source": 3, + "thermal_bridge_type": "P1" + }, + { + "length": 20.6, + "psi_value": 0, + "psi_value_source": 4, + "thermal_bridge_type": "P2" + }, + { + "length": 20.6, + "psi_value": 0.12, + "psi_value_source": 3, + "thermal_bridge_type": "P4" + } + ], + "thermal_bridge_code": 5 + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 0, + "u_value": 0.12, + "floor_type": 2, + "kappa_value": 80, + "storey_height": 2.8, + "heat_loss_area": 57.4, + "total_floor_area": 57.4 + }, + { + "storey": 1, + "u_value": 0, + "floor_type": 3, + "kappa_value": 18, + "storey_height": 3, + "heat_loss_area": 0, + "total_floor_area": 57.4, + "kappa_value_from_below": 9 + }, + { + "storey": 2, + "u_value": 0, + "floor_type": 3, + "kappa_value": 19, + "storey_height": 2.7, + "heat_loss_area": 0, + "total_floor_area": 57.4, + "kappa_value_from_below": 9 + } + ], + "construction_age_band": "A" + } + ], + "user_interface_name": "BRE SAP interface 10.2", + "windows_overshading": 2, + "heating_cost_current": { + "value": 365.98, + "currency": "GBP" + }, + "co2_emissions_current": 2.4, + "energy_rating_average": 60, + "energy_rating_current": 72, + "lighting_cost_current": { + "value": 123.45, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Programmer, room thermostat and TRVs", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 250.34, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 200.4, + "currency": "GBP" + }, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 88, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 74, + "environmental_impact_rating": 94 + }, + { + "sequence": 2, + "typical_saving": { + "value": 88, + "currency": "GBP" + }, + "indicative_cost": "£9,000 - £14,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 80, + "environmental_impact_rating": 96 + } + ], + "user_interface_version": "1.0.1-alpha", + "co2_emissions_potential": 1.4, + "energy_rating_potential": 72, + "gas_smart_meter_present": "false", + "lighting_cost_potential": { + "value": 84.23, + "currency": "GBP" + }, + "schema_version_original": "SAP-Schema-19.1.0", + "hot_water_cost_potential": { + "value": 180.43, + "currency": "GBP" + }, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 2666, + "water_heating": 2650 + } + }, + "seller_commission_report": "Y", + "energy_consumption_current": 59, + "has_fixed_air_conditioning": "false", + "is_dwelling_export_capable": "true", + "multiple_glazed_percentage": 100, + "calculation_software_version": "13.05r16", + "energy_consumption_potential": 53, + "environmental_impact_current": 94, + "current_energy_efficiency_band": "C", + "environmental_impact_potential": 96, + "electricity_smart_meter_present": "false", + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 5.6 +} \ No newline at end of file diff --git a/backend/epc_api/json_samples/SAP-Schema-19.2.0/epc.json b/backend/epc_api/json_samples/SAP-Schema-19.2.0/epc.json new file mode 100644 index 00000000..26a1cd4d --- /dev/null +++ b/backend/epc_api/json_samples/SAP-Schema-19.2.0/epc.json @@ -0,0 +1,612 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": "Average thermal transmittance 0.13 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "walls": [ + { + "description": "Average thermal transmittance 0.18 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "floors": [ + { + "description": "Average thermal transmittance 0.12 W/m²K", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + } + ], + "status": "entered", + "tenure": 1, + "windows": { + "description": "High performance glazing", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "lighting": { + "description": "Low energy lighting in 91% of fixed outlets", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "A0 0AA", + "data_type": 1, + "hot_water": { + "description": "From main system, waste water heat recovery", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 3 + }, + "post_town": "Whitbury", + "built_form": 4, + "living_area": 41.35, + "orientation": 0, + "region_code": 16, + "report_type": 3, + "sap_heating": { + "thermal_store": 1, + "shower_outlets": [ + { + "shower_wwhrs": 1, + "shower_flow_rate": 7, + "shower_outlet_type": 1 + }, + { + "shower_wwhrs": 2, + "shower_flow_rate": 7, + "shower_outlet_type": 1 + } + ], + "water_fuel_type": 39, + "water_heating_code": 901, + "instantaneous_wwhrs": { + "wwhrs_index_number1": 491123 + }, + "hot_water_store_size": 600, + "main_heating_details": [ + { + "has_fghrs": "false", + "main_fuel_type": 5, + "combi_boiler_type": 4, + "heat_emitter_type": 1, + "main_heating_code": 192, + "main_heating_number": 1, + "is_condensing_boiler": "false", + "main_heating_control": 2106, + "is_interlocked_system": "false", + "main_heating_category": 2, + "main_heating_fraction": 1, + "central_heating_pump_age": 2, + "main_heating_data_source": 3, + "has_separate_delayed_start": "true", + "is_oil_pump_in_heated_space": "false", + "is_main_heating_hetas_approved": "false", + "electric_cpsu_operating_temperature": 80, + "is_central_heating_pump_in_heated_space": "true" + } + ], + "has_hot_water_cylinder": "true", + "has_cylinder_thermostat": "true", + "hot_water_store_heat_loss": 2.45, + "has_fixed_air_conditioning": "false", + "secondary_heating_category": 1, + "is_immersion_for_summer_use": "false", + "is_hot_water_separately_timed": "false", + "sap_community_heating_systems": [ + { + "sub_network_name": "Test 1", + "heat_network_sleeved": 1, + "community_heating_use": 3, + "heat_network_existing": "true", + "heat_network_index_number": 496402, + "heat_network_assessed_as_new": "true", + "heat_network_community_heating": 0, + "community_heating_distribution_type": 5, + "community_heating_distribution_loss_factor": 1.26 + } + ], + "hot_water_store_heat_loss_source": 2, + "is_heat_pump_assisted_by_immersion": "false" + }, + "sap_version": 10.3, + "schema_type": "SAP-Schema-19.2.0", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + { + "description": "Boiler and radiators, electric", + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 2 + } + ], + "sap_lighting": [ + [ + { + "lighting_power": 60, + "lighting_outlets": 1, + "lighting_efficacy": 11.2 + }, + { + "lighting_power": 14, + "lighting_outlets": 10, + "lighting_efficacy": 66.9 + } + ] + ], + "terrain_type": 1, + "air_tightness": { + "description": "Air permeability 2.0 m³/h.m² (assumed)", + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "property_type": 0, + "address_line_1": "1 Some Street", + "address_line_2": "Some Area", + "address_line_3": "Some County", + "assessment_date": "2022-05-09", + "assessment_type": "SAP", + "completion_date": "2022-05-09", + "inspection_date": "2022-05-09", + "sap_ventilation": { + "psv_count": 0, + "wall_type": 2, + "pressure_test": 2, + "wet_rooms_count": 2, + "air_permeability": 2, + "open_flues_count": 0, + "ventilation_type": 8, + "has_draught_lobby": "false", + "other_flues_count": 0, + "closed_flues_count": 0, + "extract_fans_count": 0, + "boilers_flues_count": 0, + "open_chimneys_count": 0, + "sheltered_sides_count": 2, + "blocked_chimneys_count": 0, + "flueless_gas_fires_count": 0, + "mechanical_vent_duct_type": 2, + "mechanical_vent_duct_placement": 1, + "mechanical_ventilation_data_source": 1, + "mechanical_vent_system_index_number": 500206, + "mechanical_vent_duct_insulation_level": 2, + "is_mechanical_vent_approved_installer_scheme": "true" + }, + "sap_data_version": 10.3, + "total_floor_area": 165, + "transaction_type": 1, + "cold_water_source": 1, + "conservatory_type": 1, + "registration_date": "2022-05-09", + "sap_energy_source": { + "wind_turbines": [ + { + "wind_turbine_hub_height": 3, + "wind_turbine_rotor_diameter": 1.7 + }, + { + "wind_turbine_hub_height": 3, + "wind_turbine_rotor_diameter": 1.7 + } + ], + "electricity_tariff": 5 + }, + "sap_opening_types": [ + { + "name": "Doors", + "type": 1, + "u_value": 1.5, + "data_source": 2, + "glazing_type": 1, + "isargonfilled": "false" + }, + { + "name": "Windows (1)", + "type": 4, + "u_value": 1.4, + "frame_type": 1, + "data_source": 3, + "glazing_gap": 3, + "frame_factor": 0.7, + "glazing_type": 11, + "isargonfilled": "true", + "solar_transmittance": 0.57 + }, + { + "name": "Windows (2)", + "type": 4, + "u_value": 1.5, + "frame_type": 1, + "data_source": 3, + "glazing_gap": 3, + "frame_factor": 0.7, + "glazing_type": 9, + "isargonfilled": "true", + "solar_transmittance": 0.64 + } + ], + "secondary_heating": { + "description": "None", + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lowest_storey_area": 57.4, + "sap_building_parts": [ + { + "sap_roofs": [ + { + "name": "Roof (1)", + "u_value": 0.13, + "roof_type": 2, + "kappa_value": 9, + "total_roof_area": 57.4 + } + ], + "sap_walls": [ + { + "name": "Walls (1)", + "u_value": 0.18, + "wall_type": 2, + "kappa_value": 60, + "total_wall_area": 111.34, + "is_curtain_walling": "false" + }, + { + "name": "Party wall", + "u_value": 0, + "wall_type": 4, + "kappa_value": 0, + "total_wall_area": 167, + "is_curtain_walling": "false" + }, + { + "name": "Internal wall (1)", + "u_value": 0, + "wall_type": 5, + "kappa_value": 0, + "total_wall_area": 320, + "is_curtain_walling": "false" + } + ], + "identifier": "Main Dwelling", + "sap_openings": [ + { + "name": 1, + "type": "Doors", + "width": 1, + "height": 2.25, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 2, + "type": "Doors", + "width": 0.8, + "height": 2.1, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 3, + "type": "Windows (2)", + "width": 1.6, + "height": 2.7, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 4, + "type": "Windows (1)", + "width": 1.1, + "height": 1.75, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 5, + "type": "Windows (1)", + "width": 1.1, + "height": 1.75, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 6, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 7, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 8, + "type": "Windows (1)", + "width": 1.1, + "height": 1.8, + "location": "Walls (1)", + "orientation": 3 + }, + { + "name": 9, + "type": "Windows (1)", + "width": 1.1, + "height": 1.75, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 10, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 11, + "type": "Windows (1)", + "width": 1.1, + "height": 1.85, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 12, + "type": "Windows (1)", + "width": 0.6, + "height": 0.8, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 13, + "type": "Windows (1)", + "width": 0.8, + "height": 1.25, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 14, + "type": "Windows (1)", + "width": 0.8, + "height": 1.25, + "location": "Walls (1)", + "orientation": 7 + }, + { + "name": 15, + "type": "Windows (1)", + "width": 1.1, + "height": 1.25, + "location": "Walls (1)", + "orientation": 7 + } + ], + "sap_thermal_bridges": { + "thermal_bridges": [ + { + "length": 15.5, + "psi_value": 0.3, + "psi_value_source": 2, + "thermal_bridge_type": "E2" + }, + { + "length": 12.1, + "psi_value": 0.04, + "psi_value_source": 2, + "thermal_bridge_type": "E3" + }, + { + "length": 52.1, + "psi_value": 0.05, + "psi_value_source": 2, + "thermal_bridge_type": "E4" + }, + { + "length": 14.4, + "psi_value": 0.16, + "psi_value_source": 2, + "thermal_bridge_type": "E5" + }, + { + "length": 25.8, + "psi_value": 0.07, + "psi_value_source": 2, + "thermal_bridge_type": "E6" + }, + { + "length": 10.4, + "psi_value": 0.06, + "psi_value_source": 2, + "thermal_bridge_type": "E10" + }, + { + "length": 5.4, + "psi_value": 0.06, + "psi_value_source": 3, + "thermal_bridge_type": "E14" + }, + { + "length": 5.8, + "psi_value": 0.09, + "psi_value_source": 2, + "thermal_bridge_type": "E16" + }, + { + "length": 5.8, + "psi_value": -0.09, + "psi_value_source": 2, + "thermal_bridge_type": "E17" + }, + { + "length": 34, + "psi_value": 0.06, + "psi_value_source": 2, + "thermal_bridge_type": "E18" + }, + { + "length": 20.6, + "psi_value": 0.12, + "psi_value_source": 3, + "thermal_bridge_type": "P1" + }, + { + "length": 20.6, + "psi_value": 0, + "psi_value_source": 4, + "thermal_bridge_type": "P2" + }, + { + "length": 20.6, + "psi_value": 0.12, + "psi_value_source": 3, + "thermal_bridge_type": "P4" + } + ], + "thermal_bridge_code": 5 + }, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "storey": 0, + "u_value": 0.12, + "floor_type": 2, + "kappa_value": 80, + "storey_height": 2.8, + "heat_loss_area": 57.4, + "total_floor_area": 57.4 + }, + { + "storey": 1, + "u_value": 0, + "floor_type": 3, + "kappa_value": 18, + "storey_height": 3, + "heat_loss_area": 0, + "total_floor_area": 57.4, + "kappa_value_from_below": 9 + }, + { + "storey": 2, + "u_value": 0, + "floor_type": 3, + "kappa_value": 19, + "storey_height": 2.7, + "heat_loss_area": 0, + "total_floor_area": 57.4, + "kappa_value_from_below": 9 + } + ], + "construction_age_band": "A" + } + ], + "user_interface_name": "BRE SAP interface 10.3", + "windows_overshading": 2, + "heating_cost_current": { + "value": 365.98, + "currency": "GBP" + }, + "co2_emissions_current": 2.4, + "energy_rating_average": 60, + "energy_rating_current": 72, + "lighting_cost_current": { + "value": 123.45, + "currency": "GBP" + }, + "main_heating_controls": [ + { + "description": "Programmer, room thermostat and TRVs", + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": { + "value": 250.34, + "currency": "GBP" + }, + "hot_water_cost_current": { + "value": 200.4, + "currency": "GBP" + }, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": { + "value": 88, + "currency": "GBP" + }, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "N", + "improvement_details": { + "improvement_number": 19 + }, + "improvement_category": 5, + "energy_performance_rating": 74, + "environmental_impact_rating": 94 + }, + { + "sequence": 2, + "typical_saving": { + "value": 88, + "currency": "GBP" + }, + "indicative_cost": "£9,000 - £14,000", + "improvement_type": "U", + "improvement_details": { + "improvement_number": 34 + }, + "improvement_category": 5, + "energy_performance_rating": 80, + "environmental_impact_rating": 96 + } + ], + "user_interface_version": "1.0.1-alpha", + "co2_emissions_potential": 1.4, + "energy_rating_potential": 72, + "gas_smart_meter_present": "false", + "lighting_cost_potential": { + "value": 84.23, + "currency": "GBP" + }, + "part_o_cooling_required": 1, + "schema_version_original": "SAP-Schema-19.2.0", + "hot_water_cost_potential": { + "value": 180.43, + "currency": "GBP" + }, + "is_in_smoke_control_area": "unknown", + "renewable_heat_incentive": { + "rhi_new_dwelling": { + "space_heating": 2666, + "water_heating": 2650 + } + }, + "seller_commission_report": "Y", + "energy_consumption_current": 59, + "has_fixed_air_conditioning": "false", + "is_dwelling_export_capable": "true", + "multiple_glazed_percentage": 100, + "calculation_software_version": "13.05r16", + "energy_consumption_potential": 53, + "environmental_impact_current": 94, + "current_energy_efficiency_band": "C", + "environmental_impact_potential": 96, + "electricity_smart_meter_present": "false", + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 5.6 +} \ No newline at end of file diff --git a/backend/utils/subtasks.py b/backend/utils/subtasks.py index 041494e9..6be3a742 100644 --- a/backend/utils/subtasks.py +++ b/backend/utils/subtasks.py @@ -5,7 +5,8 @@ from typing import Callable, Any from uuid import UUID import json -from backend.app.db.functions.tasks.Tasks import SubTaskInterface +from backend.app.db.functions.tasks.Tasks import SubTaskInterface, TasksInterface +from utils.logger import setup_logger def subtask_handler(): @@ -93,3 +94,93 @@ def subtask_handler(): return wrapper return decorator + + +def task_handler(): + """ + Decorator that wraps a Lambda handler and automatically: + + - Parses body from the first SQS record (or uses the event dict directly) + - Creates a fresh Task + SubTask in the database + - Marks the subtask as in progress + - Executes the handler, passing the parsed body + - Marks complete on success, failed on exception (and re-raises) + """ + + def decorator(func: Callable[..., Any]): + + task_source = f"{func.__module__}.{func.__qualname__}" + + @wraps(func) + def wrapper(event: dict[str, Any], context: Any, *args, **kwargs): + logger = setup_logger() + + records = event.get("Records", [event]) # fallback for non-SQS + + results = [] + failures = [] + + for record in records: + # Parse body + raw_body = record.get("body", record) + + if isinstance(raw_body, str): + try: + body = json.loads(raw_body) + except Exception: + body = {} + else: + body = raw_body or {} + + # Create task per message + logger.info("Creating task for source: %s", task_source) + task_id, subtask_id = TasksInterface.create_task( + task_source=task_source, + inputs=body, + ) + + logger.info("Created task_id=%s subtask_id=%s", task_id, subtask_id) + + interface = SubTaskInterface() + + interface.update_subtask_status( + subtask_id=subtask_id, + status="in progress", + ) + + try: + result = func(body, context, *args, **kwargs) + + interface.update_subtask_status( + subtask_id=subtask_id, + status="complete", + outputs={"result": result} if result else None, + ) + + logger.info("Task %s completed successfully", task_id) + results.append(result) + + except Exception as e: + logger.exception("Task %s failed: %s", task_id, e) + + interface.update_subtask_status( + subtask_id=subtask_id, + status="failed", + outputs={"error": str(e)}, + ) + + if "Records" in event: + failures.append({"itemIdentifier": record["messageId"]}) + else: + # Handle non-SQS events + raise + + if "Records" in event: + return {"batchItemFailures": failures} + + # Handle non-SQS events + return results + + return wrapper + + return decorator diff --git a/datatypes/epc/schema/__init__.py b/datatypes/epc/schema/__init__.py new file mode 100644 index 00000000..e49f2317 --- /dev/null +++ b/datatypes/epc/schema/__init__.py @@ -0,0 +1,17 @@ +from .rdsap_schema_17_0 import RdSapSchema17_0 +from .rdsap_schema_17_1 import RdSapSchema17_1 +from .rdsap_schema_18_0 import RdSapSchema18_0 +from .rdsap_schema_19_0 import RdSapSchema19_0 +from .rdsap_schema_20_0_0 import RdSapSchema20_0_0 +from .rdsap_schema_21_0_0 import RdSapSchema21_0_0 +from .rdsap_schema_21_0_1 import RdSapSchema21_0_1 + +__all__ = [ + "RdSapSchema17_0", + "RdSapSchema17_1", + "RdSapSchema18_0", + "RdSapSchema19_0", + "RdSapSchema20_0_0", + "RdSapSchema21_0_0", + "RdSapSchema21_0_1", +] diff --git a/datatypes/epc/schema/common.py b/datatypes/epc/schema/common.py new file mode 100644 index 00000000..aa9ea512 --- /dev/null +++ b/datatypes/epc/schema/common.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass + + +@dataclass +class Measurement: + """A numeric value with a physical unit, e.g. {"value": 2.4, "quantity": "metres"}.""" + value: float + quantity: str + + +@dataclass +class DescriptionV1: + """Localised description object used in schemas 17.x, 18.x, 19.x, and 21.0.1.""" + value: str + language: str + + +@dataclass +class CostAmount: + """Monetary amount used in schemas 17.x, 18.x, and 19.x.""" + value: int + currency: str diff --git a/datatypes/epc/schema/rdsap_schema_17_0.py b/datatypes/epc/schema/rdsap_schema_17_0.py new file mode 100644 index 00000000..22aaded4 --- /dev/null +++ b/datatypes/epc/schema/rdsap_schema_17_0.py @@ -0,0 +1,222 @@ +from dataclasses import dataclass +from typing import List, Optional, Union + +from .common import CostAmount, DescriptionV1, Measurement + + +@dataclass +class EnergyElement: + description: DescriptionV1 + energy_efficiency_rating: int + environmental_efficiency_rating: int + + +@dataclass +class InstantaneousWwhrs: + rooms_with_bath_and_or_shower: int + rooms_with_mixer_shower_no_bath: int + rooms_with_bath_and_mixer_shower: int + + +@dataclass +class MainHeatingDetail: + has_fghrs: str + main_fuel_type: int + heat_emitter_type: int + emitter_temperature: Union[int, str] + main_heating_number: int + main_heating_control: int + main_heating_category: int + main_heating_fraction: int + main_heating_data_source: int + sap_main_heating_code: Optional[int] = None + + +@dataclass +class SapHeating: + cylinder_size: int + water_heating_code: int + water_heating_fuel: int + instantaneous_wwhrs: InstantaneousWwhrs + main_heating_details: List[MainHeatingDetail] + immersion_heating_type: Union[int, str] + cylinder_insulation_type: int + has_fixed_air_conditioning: str + + +@dataclass +class PhotovoltaicSupplyNoneOrNoDetails: + pv_connection: int + percent_roof_area: int + + +@dataclass +class PhotovoltaicSupply: + none_or_no_details: PhotovoltaicSupplyNoneOrNoDetails + + +@dataclass +class SapEnergySource: + mains_gas: str + meter_type: int + photovoltaic_supply: PhotovoltaicSupply + wind_turbines_count: int + wind_turbines_terrain_type: int + + +@dataclass +class SapFloorDimension: + floor: int + room_height: Measurement + total_floor_area: Measurement + party_wall_length: Measurement + heat_loss_perimeter: Measurement + + +@dataclass +class SapBuildingPart: + identifier: str + wall_dry_lined: str + wall_thickness: int + floor_heat_loss: int + roof_construction: int + wall_construction: int + building_part_number: int + sap_floor_dimensions: List[SapFloorDimension] + wall_insulation_type: int + construction_age_band: str + party_wall_construction: Union[int, str] + wall_thickness_measured: str + roof_insulation_location: Union[int, str] + roof_insulation_thickness: str + wall_insulation_thickness: Optional[str] = None + + +@dataclass +class SapFlatDetails: + level: int + top_storey: str + flat_location: int + heat_loss_corridor: int + + +@dataclass +class ImprovementDetails: + improvement_number: int + + +@dataclass +class SuggestedImprovement: + sequence: int + typical_saving: CostAmount + indicative_cost: str + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class AlternativeImprovement: + sequence: int + typical_saving: CostAmount + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class RenewableHeatIncentive: + water_heating: int + space_heating_existing_dwelling: int + impact_of_loft_insulation: Optional[int] = None + impact_of_solid_wall_insulation: Optional[int] = None + + +@dataclass +class RdSapSchema17_0: + uprn: int + roofs: List[EnergyElement] + walls: List[EnergyElement] + floors: List[EnergyElement] + status: str + tenure: int + window: EnergyElement + lighting: EnergyElement + postcode: str + hot_water: EnergyElement + post_town: str + built_form: int + door_count: int + glazed_area: int + glazing_gap: str + region_code: int + report_type: int + sap_heating: SapHeating + sap_version: float + schema_type: str + uprn_source: str + country_code: str + main_heating: List[EnergyElement] + dwelling_type: DescriptionV1 + language_code: int + property_type: int + address_line_1: str + assessment_type: str + completion_date: str + inspection_date: str + extensions_count: int + measurement_type: int + total_floor_area: int + transaction_type: int + conservatory_type: int + heated_room_count: int + pvc_window_frames: str + registration_date: str + sap_energy_source: SapEnergySource + secondary_heating: EnergyElement + lzc_energy_sources: List[int] + sap_building_parts: List[SapBuildingPart] + low_energy_lighting: int + solar_water_heating: str + habitable_room_count: int + heating_cost_current: CostAmount + insulated_door_count: int + co2_emissions_current: float + energy_rating_average: int + energy_rating_current: int + lighting_cost_current: CostAmount + main_heating_controls: List[EnergyElement] + multiple_glazing_type: int + open_fireplaces_count: int + has_hot_water_cylinder: str + heating_cost_potential: CostAmount + hot_water_cost_current: CostAmount + mechanical_ventilation: int + percent_draughtproofed: int + suggested_improvements: List[SuggestedImprovement] + co2_emissions_potential: float + energy_rating_potential: int + lighting_cost_potential: CostAmount + schema_version_original: str + hot_water_cost_potential: CostAmount + renewable_heat_incentive: RenewableHeatIncentive + energy_consumption_current: int + has_fixed_air_conditioning: str + multiple_glazed_proportion: int + calculation_software_version: str + energy_consumption_potential: int + environmental_impact_current: int + fixed_lighting_outlets_count: int + current_energy_efficiency_band: str + environmental_impact_potential: int + has_heated_separate_conservatory: str + potential_energy_efficiency_band: str + co2_emissions_current_per_floor_area: int + low_energy_fixed_lighting_outlets_count: int + sap_flat_details: Optional[SapFlatDetails] = None + address_line_2: Optional[str] = None + alternative_improvements: Optional[List[AlternativeImprovement]] = None diff --git a/datatypes/epc/schema/rdsap_schema_17_1.py b/datatypes/epc/schema/rdsap_schema_17_1.py new file mode 100644 index 00000000..a4c007ed --- /dev/null +++ b/datatypes/epc/schema/rdsap_schema_17_1.py @@ -0,0 +1,234 @@ +from dataclasses import dataclass +from typing import List, Optional, Union + +from .common import CostAmount, DescriptionV1, Measurement + + +@dataclass +class EnergyElement: + description: DescriptionV1 + energy_efficiency_rating: int + environmental_efficiency_rating: int + + +@dataclass +class InstantaneousWwhrs: + rooms_with_bath_and_or_shower: int + rooms_with_mixer_shower_no_bath: int + rooms_with_bath_and_mixer_shower: int + + +@dataclass +class MainHeatingDetail: + has_fghrs: str + main_fuel_type: int + heat_emitter_type: int + emitter_temperature: Union[int, str] + main_heating_number: int + main_heating_control: int + main_heating_category: int + main_heating_fraction: int + main_heating_data_source: int + boiler_flue_type: Optional[int] = None + fan_flue_present: Optional[str] = None + mcs_installed_heat_pump: Optional[str] = None + main_heating_index_number: Optional[int] = None + sap_main_heating_code: Optional[int] = None + + +@dataclass +class SapHeating: + cylinder_size: int + water_heating_code: int + water_heating_fuel: int + instantaneous_wwhrs: InstantaneousWwhrs + main_heating_details: List[MainHeatingDetail] + immersion_heating_type: Union[int, str] + cylinder_insulation_type: int + has_fixed_air_conditioning: str + cylinder_thermostat: Optional[str] = None + secondary_fuel_type: Optional[int] = None + secondary_heating_type: Optional[int] = None + cylinder_insulation_thickness: Optional[int] = None + + +@dataclass +class PhotovoltaicSupplyNoneOrNoDetails: + pv_connection: int + percent_roof_area: int + + +@dataclass +class PhotovoltaicSupply: + none_or_no_details: PhotovoltaicSupplyNoneOrNoDetails + + +@dataclass +class SapEnergySource: + mains_gas: str + meter_type: int + photovoltaic_supply: PhotovoltaicSupply + wind_turbines_count: int + wind_turbines_terrain_type: int + + +@dataclass +class SapFloorDimension: + floor: int + room_height: Measurement + total_floor_area: Measurement + # Can be a Measurement object or 0 (int) for party walls of zero length + party_wall_length: Union[Measurement, int] + heat_loss_perimeter: Measurement + floor_insulation: Optional[int] = None + floor_construction: Optional[int] = None + + +@dataclass +class SapBuildingPart: + identifier: str + wall_dry_lined: str + wall_thickness: int + floor_heat_loss: int + roof_construction: int + wall_construction: int + building_part_number: int + sap_floor_dimensions: List[SapFloorDimension] + wall_insulation_type: int + construction_age_band: str + party_wall_construction: Union[int, str] + wall_thickness_measured: str + roof_insulation_location: Union[int, str] + # Can be a thickness string (e.g. "100mm") or 0 for uninsulated flat roofs + roof_insulation_thickness: Union[str, int] + wall_insulation_thickness: Optional[str] = None + + +@dataclass +class SapFlatDetails: + level: int + top_storey: str + flat_location: int + heat_loss_corridor: int + + +@dataclass +class ImprovementDetails: + improvement_number: int + + +@dataclass +class SuggestedImprovement: + sequence: int + typical_saving: CostAmount + indicative_cost: str + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class AlternativeImprovement: + sequence: int + typical_saving: CostAmount + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class RenewableHeatIncentive: + water_heating: int + space_heating_existing_dwelling: int + impact_of_loft_insulation: Optional[int] = None + impact_of_solid_wall_insulation: Optional[int] = None + + +@dataclass +class RdSapSchema17_1: + uprn: int + roofs: List[EnergyElement] + walls: List[EnergyElement] + floors: List[EnergyElement] + status: str + tenure: int + window: EnergyElement + lighting: EnergyElement + postcode: str + hot_water: EnergyElement + post_town: str + built_form: int + door_count: int + glazed_area: int + glazing_gap: str + region_code: int + report_type: int + sap_heating: SapHeating + sap_version: float + schema_type: str + uprn_source: str + country_code: str + main_heating: List[EnergyElement] + dwelling_type: DescriptionV1 + language_code: int + property_type: int + address_line_1: str + assessment_type: str + completion_date: str + inspection_date: str + extensions_count: int + measurement_type: int + total_floor_area: int + transaction_type: int + conservatory_type: int + heated_room_count: int + pvc_window_frames: str + registration_date: str + sap_energy_source: SapEnergySource + secondary_heating: EnergyElement + lzc_energy_sources: List[int] + sap_building_parts: List[SapBuildingPart] + low_energy_lighting: int + solar_water_heating: str + habitable_room_count: int + heating_cost_current: CostAmount + insulated_door_count: int + co2_emissions_current: float + energy_rating_average: int + energy_rating_current: int + lighting_cost_current: CostAmount + main_heating_controls: List[EnergyElement] + multiple_glazing_type: int + open_fireplaces_count: int + has_hot_water_cylinder: str + heating_cost_potential: CostAmount + hot_water_cost_current: CostAmount + mechanical_ventilation: int + percent_draughtproofed: int + suggested_improvements: List[SuggestedImprovement] + co2_emissions_potential: float + energy_rating_potential: int + lighting_cost_potential: CostAmount + schema_version_original: str + hot_water_cost_potential: CostAmount + renewable_heat_incentive: RenewableHeatIncentive + energy_consumption_current: int + has_fixed_air_conditioning: str + multiple_glazed_proportion: int + calculation_software_version: str + energy_consumption_potential: int + environmental_impact_current: int + fixed_lighting_outlets_count: int + current_energy_efficiency_band: str + environmental_impact_potential: int + has_heated_separate_conservatory: str + potential_energy_efficiency_band: str + co2_emissions_current_per_floor_area: int + low_energy_fixed_lighting_outlets_count: int + sap_flat_details: Optional[SapFlatDetails] = None + address_line_2: Optional[str] = None + alternative_improvements: Optional[List[AlternativeImprovement]] = None diff --git a/datatypes/epc/schema/rdsap_schema_18_0.py b/datatypes/epc/schema/rdsap_schema_18_0.py new file mode 100644 index 00000000..a038dc9b --- /dev/null +++ b/datatypes/epc/schema/rdsap_schema_18_0.py @@ -0,0 +1,245 @@ +from dataclasses import dataclass +from typing import List, Optional, Union + +from .common import CostAmount, DescriptionV1, Measurement + + +@dataclass +class EnergyElement: + description: DescriptionV1 + energy_efficiency_rating: int + environmental_efficiency_rating: int + + +@dataclass +class InstantaneousWwhrs: + rooms_with_bath_and_or_shower: int + rooms_with_mixer_shower_no_bath: int + rooms_with_bath_and_mixer_shower: int + + +@dataclass +class MainHeatingDetail: + has_fghrs: str + main_fuel_type: int + heat_emitter_type: int + emitter_temperature: Union[int, str] + main_heating_number: int + main_heating_control: int + main_heating_category: int + main_heating_fraction: int + main_heating_data_source: int + boiler_flue_type: Optional[int] = None + fan_flue_present: Optional[str] = None + central_heating_pump_age: Optional[int] = None + main_heating_index_number: Optional[int] = None + sap_main_heating_code: Optional[int] = None + + +@dataclass +class SapHeating: + cylinder_size: int + water_heating_code: int + water_heating_fuel: int + instantaneous_wwhrs: InstantaneousWwhrs + main_heating_details: List[MainHeatingDetail] + immersion_heating_type: Union[int, str] + has_fixed_air_conditioning: str + cylinder_insulation_type: Optional[int] = None + cylinder_thermostat: Optional[str] = None + secondary_fuel_type: Optional[int] = None + secondary_heating_type: Optional[int] = None + cylinder_insulation_thickness: Optional[int] = None + + +@dataclass +class PhotovoltaicSupplyNoneOrNoDetails: + pv_connection: int + percent_roof_area: int + + +@dataclass +class PhotovoltaicSupply: + none_or_no_details: PhotovoltaicSupplyNoneOrNoDetails + + +@dataclass +class SapEnergySource: + mains_gas: str + meter_type: int + photovoltaic_supply: PhotovoltaicSupply + wind_turbines_count: int + wind_turbines_terrain_type: int + + +@dataclass +class SapFloorDimension: + floor: int + room_height: Measurement + total_floor_area: Measurement + party_wall_length: Union[Measurement, int] + heat_loss_perimeter: Measurement + floor_insulation: Optional[int] = None + floor_construction: Optional[int] = None + + +@dataclass +class SapRoomInRoof: + """Room-in-roof details. floor_area is a Measurement object in schema 18.0.""" + floor_area: Measurement + insulation: str + roof_room_connected: str + construction_age_band: str + + +@dataclass +class SapBuildingPart: + identifier: str + wall_dry_lined: str + wall_thickness: int + floor_heat_loss: int + roof_construction: int + wall_construction: int + building_part_number: int + sap_floor_dimensions: List[SapFloorDimension] + wall_insulation_type: int + construction_age_band: str + party_wall_construction: Union[int, str] + wall_thickness_measured: str + roof_insulation_location: Union[int, str] + roof_insulation_thickness: Union[str, int] + sap_room_in_roof: Optional[SapRoomInRoof] = None + wall_insulation_thickness: Optional[str] = None + floor_insulation_thickness: Optional[str] = None + flat_roof_insulation_thickness: Optional[Union[str, int]] = None + + +@dataclass +class SapFlatDetails: + level: int + top_storey: str + flat_location: int + heat_loss_corridor: int + + +@dataclass +class ImprovementDetails: + improvement_number: int + + +@dataclass +class SuggestedImprovement: + sequence: int + typical_saving: CostAmount + indicative_cost: str + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class AlternativeImprovement: + sequence: int + typical_saving: CostAmount + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class RenewableHeatIncentive: + water_heating: int + space_heating_existing_dwelling: int + impact_of_loft_insulation: Optional[int] = None + impact_of_solid_wall_insulation: Optional[int] = None + + +@dataclass +class RdSapSchema18_0: + uprn: int + roofs: List[EnergyElement] + walls: List[EnergyElement] + floors: List[EnergyElement] + status: str + tenure: int + window: EnergyElement + lighting: EnergyElement + postcode: str + hot_water: EnergyElement + post_town: str + built_form: int + door_count: int + glazed_area: int + # glazing_gap is an integer in 18.0 (e.g. 12 mm), unlike 17.x where it was a string + glazing_gap: int + region_code: int + report_type: int + sap_heating: SapHeating + sap_version: float + schema_type: str + uprn_source: str + country_code: str + main_heating: List[EnergyElement] + dwelling_type: DescriptionV1 + language_code: int + property_type: int + address_line_1: str + assessment_type: str + completion_date: str + inspection_date: str + extensions_count: int + measurement_type: int + total_floor_area: int + transaction_type: int + conservatory_type: int + heated_room_count: int + pvc_window_frames: str + registration_date: str + sap_energy_source: SapEnergySource + secondary_heating: EnergyElement + lzc_energy_sources: List[int] + sap_building_parts: List[SapBuildingPart] + low_energy_lighting: int + solar_water_heating: str + habitable_room_count: int + heating_cost_current: CostAmount + insulated_door_count: int + co2_emissions_current: float + energy_rating_average: int + energy_rating_current: int + lighting_cost_current: CostAmount + main_heating_controls: List[EnergyElement] + multiple_glazing_type: int + open_fireplaces_count: int + has_hot_water_cylinder: str + heating_cost_potential: CostAmount + hot_water_cost_current: CostAmount + mechanical_ventilation: int + percent_draughtproofed: int + suggested_improvements: List[SuggestedImprovement] + co2_emissions_potential: float + energy_rating_potential: int + lighting_cost_potential: CostAmount + schema_version_original: str + hot_water_cost_potential: CostAmount + renewable_heat_incentive: RenewableHeatIncentive + energy_consumption_current: int + has_fixed_air_conditioning: str + multiple_glazed_proportion: int + calculation_software_version: str + energy_consumption_potential: int + environmental_impact_current: int + fixed_lighting_outlets_count: int + current_energy_efficiency_band: str + environmental_impact_potential: int + has_heated_separate_conservatory: str + potential_energy_efficiency_band: str + co2_emissions_current_per_floor_area: int + low_energy_fixed_lighting_outlets_count: int + sap_flat_details: Optional[SapFlatDetails] = None + address_line_2: Optional[str] = None + alternative_improvements: Optional[List[AlternativeImprovement]] = None diff --git a/datatypes/epc/schema/rdsap_schema_19_0.py b/datatypes/epc/schema/rdsap_schema_19_0.py new file mode 100644 index 00000000..b94d9bb3 --- /dev/null +++ b/datatypes/epc/schema/rdsap_schema_19_0.py @@ -0,0 +1,251 @@ +from dataclasses import dataclass +from typing import List, Optional, Union + +from .common import CostAmount, DescriptionV1, Measurement + + +@dataclass +class EnergyElement: + description: DescriptionV1 + energy_efficiency_rating: int + environmental_efficiency_rating: int + + +@dataclass +class InstantaneousWwhrs: + rooms_with_bath_and_or_shower: int + rooms_with_mixer_shower_no_bath: int + rooms_with_bath_and_mixer_shower: int + + +@dataclass +class MainHeatingDetail: + has_fghrs: str + main_fuel_type: int + heat_emitter_type: int + emitter_temperature: Union[int, str] + main_heating_number: int + main_heating_control: int + main_heating_category: int + main_heating_fraction: int + main_heating_data_source: int + boiler_flue_type: Optional[int] = None + fan_flue_present: Optional[str] = None + central_heating_pump_age: Optional[int] = None + main_heating_index_number: Optional[int] = None + sap_main_heating_code: Optional[int] = None + + +@dataclass +class SapHeating: + cylinder_size: int + water_heating_code: int + water_heating_fuel: int + instantaneous_wwhrs: InstantaneousWwhrs + main_heating_details: List[MainHeatingDetail] + immersion_heating_type: Union[int, str] + has_fixed_air_conditioning: str + cylinder_insulation_type: Optional[int] = None + cylinder_thermostat: Optional[str] = None + secondary_fuel_type: Optional[int] = None + secondary_heating_type: Optional[int] = None + cylinder_insulation_thickness: Optional[int] = None + + +@dataclass +class PhotovoltaicSupplyNoneOrNoDetails: + pv_connection: int + percent_roof_area: int + + +@dataclass +class PhotovoltaicSupply: + none_or_no_details: PhotovoltaicSupplyNoneOrNoDetails + + +@dataclass +class SapEnergySource: + mains_gas: str + meter_type: int + photovoltaic_supply: PhotovoltaicSupply + wind_turbines_count: int + wind_turbines_terrain_type: int + + +@dataclass +class SapFloorDimension: + floor: int + room_height: Measurement + total_floor_area: Measurement + party_wall_length: Union[Measurement, int] + heat_loss_perimeter: Measurement + floor_insulation: Optional[int] = None + floor_construction: Optional[int] = None + + +@dataclass +class SapRoomInRoof: + floor_area: Measurement + insulation: str + roof_room_connected: str + construction_age_band: str + + +@dataclass +class SapBuildingPart: + identifier: str + wall_dry_lined: str + wall_thickness: int + floor_heat_loss: int + roof_construction: int + wall_construction: int + building_part_number: int + sap_floor_dimensions: List[SapFloorDimension] + wall_insulation_type: int + construction_age_band: str + party_wall_construction: Union[int, str] + wall_thickness_measured: str + roof_insulation_location: Union[int, str] + roof_insulation_thickness: Union[str, int] + sap_room_in_roof: Optional[SapRoomInRoof] = None + wall_insulation_thickness: Optional[str] = None + floor_insulation_thickness: Optional[str] = None + flat_roof_insulation_thickness: Optional[Union[str, int]] = None + + +@dataclass +class SapFlatDetails: + level: int + top_storey: str + flat_location: int + heat_loss_corridor: int + + +@dataclass +class WindowsTransmissionDetails: + u_value: float + data_source: int + solar_transmittance: float + + +@dataclass +class ImprovementDetails: + improvement_number: int + + +@dataclass +class SuggestedImprovement: + sequence: int + typical_saving: CostAmount + indicative_cost: str + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class AlternativeImprovement: + sequence: int + typical_saving: CostAmount + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class RenewableHeatIncentive: + water_heating: int + space_heating_existing_dwelling: int + impact_of_loft_insulation: Optional[int] = None + impact_of_solid_wall_insulation: Optional[int] = None + + +@dataclass +class RdSapSchema19_0: + uprn: int + roofs: List[EnergyElement] + walls: List[EnergyElement] + floors: List[EnergyElement] + status: str + tenure: int + window: EnergyElement + lighting: EnergyElement + postcode: str + hot_water: EnergyElement + post_town: str + built_form: int + door_count: int + glazed_area: int + region_code: int + report_type: int + sap_heating: SapHeating + sap_version: float + schema_type: str + uprn_source: str + country_code: str + main_heating: List[EnergyElement] + dwelling_type: DescriptionV1 + language_code: int + property_type: int + address_line_1: str + assessment_type: str + completion_date: str + inspection_date: str + extensions_count: int + measurement_type: int + total_floor_area: int + transaction_type: int + conservatory_type: int + heated_room_count: int + pvc_window_frames: str + registration_date: str + sap_energy_source: SapEnergySource + secondary_heating: EnergyElement + lzc_energy_sources: List[int] + sap_building_parts: List[SapBuildingPart] + low_energy_lighting: int + solar_water_heating: str + habitable_room_count: int + heating_cost_current: CostAmount + insulated_door_count: int + co2_emissions_current: float + energy_rating_average: int + energy_rating_current: int + lighting_cost_current: CostAmount + main_heating_controls: List[EnergyElement] + multiple_glazing_type: int + open_fireplaces_count: int + has_hot_water_cylinder: str + heating_cost_potential: CostAmount + hot_water_cost_current: CostAmount + mechanical_ventilation: int + percent_draughtproofed: int + suggested_improvements: List[SuggestedImprovement] + co2_emissions_potential: float + energy_rating_potential: int + lighting_cost_potential: CostAmount + schema_version_original: str + hot_water_cost_potential: CostAmount + renewable_heat_incentive: RenewableHeatIncentive + windows_transmission_details: WindowsTransmissionDetails + energy_consumption_current: int + has_fixed_air_conditioning: str + multiple_glazed_proportion: int + calculation_software_version: str + energy_consumption_potential: int + environmental_impact_current: int + fixed_lighting_outlets_count: int + current_energy_efficiency_band: str + environmental_impact_potential: int + has_heated_separate_conservatory: str + potential_energy_efficiency_band: str + co2_emissions_current_per_floor_area: int + low_energy_fixed_lighting_outlets_count: int + sap_flat_details: Optional[SapFlatDetails] = None + address_line_2: Optional[str] = None + glazing_gap: Optional[Union[str, int]] = None + alternative_improvements: Optional[List[AlternativeImprovement]] = None diff --git a/datatypes/epc/schema/rdsap_schema_20_0_0.py b/datatypes/epc/schema/rdsap_schema_20_0_0.py new file mode 100644 index 00000000..8f3986a2 --- /dev/null +++ b/datatypes/epc/schema/rdsap_schema_20_0_0.py @@ -0,0 +1,282 @@ +from dataclasses import dataclass +from typing import List, Optional, Union + +from .common import Measurement + + +@dataclass +class EnergyElement: + # description is a plain string in schema 20.0.0 onwards (no longer a localised object) + description: str + energy_efficiency_rating: int + environmental_efficiency_rating: int + + +@dataclass +class Addendum: + addendum_numbers: List[int] + stone_walls: Optional[str] = None + system_build: Optional[str] = None + + +@dataclass +class InstantaneousWwhrs: + rooms_with_bath_and_or_shower: int + rooms_with_mixer_shower_no_bath: int + rooms_with_bath_and_mixer_shower: int + + +@dataclass +class MainHeatingDetail: + has_fghrs: str + main_fuel_type: int + heat_emitter_type: int + emitter_temperature: Union[int, str] + main_heating_number: int + main_heating_control: int + main_heating_category: int + main_heating_fraction: int + main_heating_data_source: int + boiler_flue_type: Optional[int] = None + fan_flue_present: Optional[str] = None + central_heating_pump_age: Optional[int] = None + main_heating_index_number: Optional[int] = None + sap_main_heating_code: Optional[int] = None + + +@dataclass +class SapHeating: + cylinder_size: int + water_heating_code: int + water_heating_fuel: int + instantaneous_wwhrs: InstantaneousWwhrs + main_heating_details: List[MainHeatingDetail] + immersion_heating_type: Union[int, str] + has_fixed_air_conditioning: str + cylinder_insulation_type: Optional[int] = None + cylinder_thermostat: Optional[str] = None + secondary_fuel_type: Optional[int] = None + secondary_heating_type: Optional[int] = None + cylinder_insulation_thickness: Optional[int] = None + + +@dataclass +class PhotovoltaicSupplyNoneOrNoDetails: + pv_connection: int + percent_roof_area: int + + +@dataclass +class PhotovoltaicSupply: + none_or_no_details: PhotovoltaicSupplyNoneOrNoDetails + + +@dataclass +class SapEnergySource: + mains_gas: str + meter_type: int + photovoltaic_supply: PhotovoltaicSupply + wind_turbines_count: int + wind_turbines_terrain_type: int + + +@dataclass +class SapWindow: + orientation: int + window_area: float + window_type: int + glazing_type: int + window_location: int + + +@dataclass +class SapFloorDimension: + floor: int + room_height: Measurement + total_floor_area: Measurement + party_wall_length: Union[Measurement, int] + heat_loss_perimeter: Measurement + floor_insulation: Optional[int] = None + floor_construction: Optional[int] = None + + +@dataclass +class SapRoomInRoof: + """Room-in-roof details. floor_area is a plain number in schema 20.0.0 (not a Measurement object).""" + floor_area: Union[int, float] + insulation: str + roof_room_connected: str + construction_age_band: str + + +@dataclass +class SapAlternativeWall: + wall_area: float + wall_dry_lined: str + wall_construction: int + wall_insulation_type: int + wall_thickness_measured: str + wall_insulation_thickness: Optional[str] = None + + +@dataclass +class SapBuildingPart: + identifier: str + wall_dry_lined: str + floor_heat_loss: int + roof_construction: int + wall_construction: int + building_part_number: int + sap_floor_dimensions: List[SapFloorDimension] + wall_insulation_type: int + construction_age_band: str + party_wall_construction: Union[int, str] + wall_thickness_measured: str + roof_insulation_location: Union[int, str] + roof_insulation_thickness: Union[str, int] + sap_room_in_roof: Optional[SapRoomInRoof] = None + wall_thickness: Optional[int] = None + wall_insulation_thickness: Optional[str] = None + floor_insulation_thickness: Optional[str] = None + flat_roof_insulation_thickness: Optional[Union[str, int]] = None + + +@dataclass +class SapFlatDetails: + level: int + top_storey: str + flat_location: int + heat_loss_corridor: int + storey_count: Optional[int] = None + unheated_corridor_length: Optional[int] = None + + +@dataclass +class WindowsTransmissionDetails: + u_value: float + data_source: int + solar_transmittance: float + + +@dataclass +class ImprovementTexts: + improvement_description: str + improvement_summary: Optional[str] = None + + +@dataclass +class ImprovementDetails: + improvement_number: Optional[int] = None + improvement_texts: Optional[ImprovementTexts] = None + + +@dataclass +class SuggestedImprovement: + sequence: int + # typical_saving is a plain number in schema 20.0.0 (not a CostAmount object) + typical_saving: float + # indicative_cost can be a formatted string (e.g. "£100 - £350") or a plain integer + indicative_cost: Union[str, int] + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class RenewableHeatIncentive: + water_heating: int + space_heating_existing_dwelling: int + impact_of_loft_insulation: Optional[int] = None + impact_of_cavity_insulation: Optional[int] = None + impact_of_solid_wall_insulation: Optional[int] = None + + +@dataclass +class RdSapSchema20_0_0: + uprn: int + roofs: List[EnergyElement] + walls: List[EnergyElement] + floors: List[EnergyElement] + status: str + tenure: int + window: EnergyElement + lighting: EnergyElement + postcode: str + hot_water: EnergyElement + post_town: str + built_form: int + door_count: int + glazed_area: int + region_code: int + report_type: int + sap_heating: SapHeating + sap_version: float + sap_windows: List[SapWindow] + schema_type: str + uprn_source: str + country_code: str + main_heating: List[EnergyElement] + # dwelling_type is a plain string in schema 20.0.0 onwards + dwelling_type: str + language_code: int + property_type: int + address_line_1: str + assessment_type: str + completion_date: str + inspection_date: str + extensions_count: int + measurement_type: int + total_floor_area: int + transaction_type: int + conservatory_type: int + heated_room_count: int + registration_date: str + sap_energy_source: SapEnergySource + secondary_heating: EnergyElement + lzc_energy_sources: List[int] + sap_building_parts: List[SapBuildingPart] + low_energy_lighting: int + solar_water_heating: str + habitable_room_count: int + heating_cost_current: float + insulated_door_count: int + co2_emissions_current: float + energy_rating_average: int + energy_rating_current: int + lighting_cost_current: float + main_heating_controls: List[EnergyElement] + multiple_glazing_type: int + open_fireplaces_count: int + heating_cost_potential: float + hot_water_cost_current: float + insulated_door_u_value: float + mechanical_ventilation: int + percent_draughtproofed: int + suggested_improvements: List[SuggestedImprovement] + co2_emissions_potential: float + energy_rating_potential: int + lighting_cost_potential: float + schema_version_original: str + hot_water_cost_potential: float + renewable_heat_incentive: RenewableHeatIncentive + windows_transmission_details: WindowsTransmissionDetails + energy_consumption_current: int + multiple_glazed_proportion: int + calculation_software_version: str + energy_consumption_potential: int + environmental_impact_current: int + fixed_lighting_outlets_count: int + multiple_glazed_proportion_nr: Optional[str] + current_energy_efficiency_band: str + environmental_impact_potential: int + potential_energy_efficiency_band: str + co2_emissions_current_per_floor_area: int + low_energy_fixed_lighting_outlets_count: int + sap_flat_details: Optional[SapFlatDetails] = None + addendum: Optional[Addendum] = None + address_line_2: Optional[str] = None + has_hot_water_cylinder: Optional[str] = None + has_fixed_air_conditioning: Optional[str] = None + has_heated_separate_conservatory: Optional[str] = None diff --git a/datatypes/epc/schema/rdsap_schema_21_0_0.py b/datatypes/epc/schema/rdsap_schema_21_0_0.py new file mode 100644 index 00000000..eee00cb8 --- /dev/null +++ b/datatypes/epc/schema/rdsap_schema_21_0_0.py @@ -0,0 +1,339 @@ +from dataclasses import dataclass +from typing import List, Optional, Union + +from .common import Measurement + + +@dataclass +class EnergyElement: + # description is a plain string in schema 21.0.0 (no longer a localised object) + description: str + energy_efficiency_rating: int + environmental_efficiency_rating: int + + +@dataclass +class Addendum: + addendum_numbers: List[int] + stone_walls: Optional[str] = None + system_build: Optional[str] = None + + +@dataclass +class ShowerOutlet: + shower_wwhrs: int + shower_outlet_type: int + + +@dataclass +class ShowerOutlets: + shower_outlet: ShowerOutlet + + +@dataclass +class InstantaneousWwhrs: + """Changed in 21.0.0: references WWHRS product index numbers instead of room counts.""" + wwhrs_index_number1: Optional[int] = None + wwhrs_index_number2: Optional[int] = None + + +@dataclass +class MainHeatingDetail: + has_fghrs: str + main_fuel_type: int + heat_emitter_type: int + emitter_temperature: Union[int, str] + main_heating_number: int + main_heating_control: int + main_heating_category: int + main_heating_fraction: int + main_heating_data_source: int + boiler_flue_type: Optional[int] = None + fan_flue_present: Optional[str] = None + boiler_ignition_type: Optional[int] = None + central_heating_pump_age: Optional[int] = None + main_heating_index_number: Optional[int] = None + sap_main_heating_code: Optional[int] = None + + +@dataclass +class SapHeating: + cylinder_size: int + water_heating_code: int + water_heating_fuel: int + instantaneous_wwhrs: InstantaneousWwhrs + main_heating_details: List[MainHeatingDetail] + immersion_heating_type: Union[int, str] + has_fixed_air_conditioning: str + shower_outlets: Optional[ShowerOutlets] = None + cylinder_insulation_type: Optional[int] = None + cylinder_thermostat: Optional[str] = None + secondary_fuel_type: Optional[int] = None + secondary_heating_type: Optional[int] = None + cylinder_insulation_thickness: Optional[int] = None + + +@dataclass +class PvBattery: + battery_capacity: float + + +@dataclass +class PvBatteries: + pv_battery: PvBattery + + +@dataclass +class WindTurbineDetails: + hub_height: float + rotor_diameter: float + + +@dataclass +class PhotovoltaicSupplyNoneOrNoDetails: + percent_roof_area: int + + +@dataclass +class PhotovoltaicSupply: + none_or_no_details: PhotovoltaicSupplyNoneOrNoDetails + + +@dataclass +class SapEnergySource: + mains_gas: str + meter_type: int + pv_connection: int + pv_battery_count: int + photovoltaic_supply: PhotovoltaicSupply + wind_turbines_count: int + wind_turbine_details: WindTurbineDetails + gas_smart_meter_present: str + is_dwelling_export_capable: str + wind_turbines_terrain_type: int + electricity_smart_meter_present: str + pv_batteries: Optional[PvBatteries] = None + + +@dataclass +class WindowTransmissionDetails: + u_value: float + data_source: int + solar_transmittance: float + + +@dataclass +class SapWindow: + pvc_frame: str + glazing_gap: int + orientation: int + window_type: int + frame_factor: float + glazing_type: int + window_width: float + window_height: float + draught_proofed: str + window_location: int + window_wall_type: int + permanent_shutters_present: str + window_transmission_details: WindowTransmissionDetails + permanent_shutters_insulated: str + + +@dataclass +class SapFloorDimension: + floor: int + room_height: Measurement + total_floor_area: Measurement + party_wall_length: Union[Measurement, int] + heat_loss_perimeter: Measurement + floor_insulation: Optional[int] = None + floor_construction: Optional[int] = None + + +@dataclass +class SapRoomInRoof: + """Room-in-roof details. insulation and roof_room_connected removed in schema 21.0.0.""" + floor_area: Union[int, float] + construction_age_band: str + + +@dataclass +class SapAlternativeWall: + wall_area: float + wall_dry_lined: str + wall_construction: int + wall_insulation_type: int + wall_thickness_measured: str + wall_insulation_thickness: Optional[str] = None + + +@dataclass +class SapBuildingPart: + identifier: str + wall_dry_lined: str + floor_heat_loss: int + roof_construction: int + wall_construction: int + building_part_number: int + sap_floor_dimensions: List[SapFloorDimension] + wall_insulation_type: int + construction_age_band: str + party_wall_construction: Union[int, str] + wall_thickness_measured: str + roof_insulation_location: Union[int, str] + roof_insulation_thickness: Union[str, int] + sap_room_in_roof: Optional[SapRoomInRoof] = None + sap_alternative_wall_1: Optional[SapAlternativeWall] = None + sap_alternative_wall_2: Optional[SapAlternativeWall] = None + wall_thickness: Optional[int] = None + wall_insulation_thickness: Optional[str] = None + floor_insulation_thickness: Optional[str] = None + flat_roof_insulation_thickness: Optional[Union[str, int]] = None + + +@dataclass +class SapFlatDetails: + level: int + top_storey: str + flat_location: int + heat_loss_corridor: int + storey_count: Optional[int] = None + unheated_corridor_length: Optional[int] = None + + +@dataclass +class WindowsTransmissionDetails: + u_value: float + data_source: int + solar_transmittance: float + + +@dataclass +class ImprovementTexts: + improvement_description: str + improvement_summary: Optional[str] = None + + +@dataclass +class ImprovementDetails: + improvement_number: Optional[int] = None + improvement_texts: Optional[ImprovementTexts] = None + + +@dataclass +class SuggestedImprovement: + sequence: int + typical_saving: float + indicative_cost: Union[str, int] + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class RenewableHeatIncentive: + water_heating: int + space_heating_existing_dwelling: int + impact_of_loft_insulation: Optional[int] = None + impact_of_cavity_insulation: Optional[int] = None + impact_of_solid_wall_insulation: Optional[int] = None + + +@dataclass +class RdSapSchema21_0_0: + uprn: int + roofs: List[EnergyElement] + walls: List[EnergyElement] + floors: List[EnergyElement] + status: str + tenure: int + window: EnergyElement + lighting: EnergyElement + postcode: str + hot_water: EnergyElement + post_town: str + built_form: int + door_count: int + region_code: int + report_type: int + sap_heating: SapHeating + sap_version: float + sap_windows: List[SapWindow] + schema_type: str + uprn_source: str + country_code: str + main_heating: List[EnergyElement] + dwelling_type: str + language_code: int + pressure_test: int + property_type: int + address_line_1: str + assessment_type: str + completion_date: str + inspection_date: str + wet_rooms_count: int + extensions_count: int + measurement_type: int + total_floor_area: int + transaction_type: int + conservatory_type: int + heated_room_count: int + registration_date: str + sap_energy_source: SapEnergySource + secondary_heating: EnergyElement + sap_building_parts: List[SapBuildingPart] + open_chimneys_count: int + solar_water_heating: str + habitable_room_count: int + heating_cost_current: float + insulated_door_count: int + co2_emissions_current: float + energy_rating_average: int + energy_rating_current: int + lighting_cost_current: float + main_heating_controls: List[EnergyElement] + has_hot_water_cylinder: str + heating_cost_potential: float + hot_water_cost_current: float + insulated_door_u_value: float + mechanical_ventilation: int + percent_draughtproofed: int + suggested_improvements: List[SuggestedImprovement] + co2_emissions_potential: float + energy_rating_potential: int + lighting_cost_potential: float + schema_version_original: str + hot_water_cost_potential: float + renewable_heat_incentive: RenewableHeatIncentive + draughtproofed_door_count: int + mechanical_vent_duct_type: int + windows_transmission_details: WindowsTransmissionDetails + cfl_fixed_lighting_bulbs_count: int + energy_consumption_current: int + has_fixed_air_conditioning: str + multiple_glazed_proportion: int + calculation_software_version: str + energy_consumption_potential: int + environmental_impact_current: int + led_fixed_lighting_bulbs_count: int + mechanical_vent_duct_placement: int + mechanical_vent_duct_insulation: int + potential_energy_efficiency_band: str + pressure_test_certificate_number: int + mechanical_ventilation_index_number: int + co2_emissions_current_per_floor_area: int + current_energy_efficiency_band: str + environmental_impact_potential: int + low_energy_fixed_lighting_bulbs_count: int + mechanical_vent_duct_insulation_level: int + mechanical_vent_measured_installation: str + incandescent_fixed_lighting_bulbs_count: int + sap_flat_details: Optional[SapFlatDetails] = None + addendum: Optional[Addendum] = None + address_line_2: Optional[str] = None + has_heated_separate_conservatory: Optional[str] = None + fixed_lighting_outlets_count: Optional[int] = None + low_energy_fixed_lighting_outlets_count: Optional[int] = None diff --git a/datatypes/epc/schema/rdsap_schema_21_0_1.py b/datatypes/epc/schema/rdsap_schema_21_0_1.py new file mode 100644 index 00000000..046e4fec --- /dev/null +++ b/datatypes/epc/schema/rdsap_schema_21_0_1.py @@ -0,0 +1,340 @@ +from dataclasses import dataclass +from typing import List, Optional, Union + +from .common import DescriptionV1, Measurement + + +@dataclass +class EnergyElement: + # Descriptions revert to localised objects in schema 21.0.1 (were plain strings in 21.0.0) + description: DescriptionV1 + energy_efficiency_rating: int + environmental_efficiency_rating: int + + +@dataclass +class Addendum: + addendum_numbers: List[int] + stone_walls: Optional[str] = None + system_build: Optional[str] = None + + +@dataclass +class ShowerOutlet: + shower_wwhrs: int + shower_outlet_type: int + + +@dataclass +class ShowerOutlets: + shower_outlet: ShowerOutlet + + +@dataclass +class InstantaneousWwhrs: + """References WWHRS product index numbers (introduced in 21.0.0).""" + wwhrs_index_number1: Optional[int] = None + wwhrs_index_number2: Optional[int] = None + + +@dataclass +class MainHeatingDetail: + has_fghrs: str + main_fuel_type: int + heat_emitter_type: int + emitter_temperature: Union[int, str] + main_heating_number: int + main_heating_control: int + main_heating_category: int + main_heating_fraction: int + main_heating_data_source: int + boiler_flue_type: Optional[int] = None + fan_flue_present: Optional[str] = None + boiler_ignition_type: Optional[int] = None + central_heating_pump_age: Optional[int] = None + main_heating_index_number: Optional[int] = None + sap_main_heating_code: Optional[int] = None + + +@dataclass +class SapHeating: + cylinder_size: int + water_heating_code: int + water_heating_fuel: int + instantaneous_wwhrs: InstantaneousWwhrs + main_heating_details: List[MainHeatingDetail] + immersion_heating_type: Union[int, str] + has_fixed_air_conditioning: str + shower_outlets: Optional[ShowerOutlets] = None + cylinder_insulation_type: Optional[int] = None + cylinder_thermostat: Optional[str] = None + secondary_fuel_type: Optional[int] = None + secondary_heating_type: Optional[int] = None + cylinder_insulation_thickness: Optional[int] = None + + +@dataclass +class PvBattery: + battery_capacity: float + + +@dataclass +class PvBatteries: + pv_battery: PvBattery + + +@dataclass +class WindTurbineDetails: + hub_height: float + rotor_diameter: float + + +@dataclass +class PhotovoltaicSupplyNoneOrNoDetails: + percent_roof_area: int + + +@dataclass +class PhotovoltaicSupply: + none_or_no_details: PhotovoltaicSupplyNoneOrNoDetails + + +@dataclass +class SapEnergySource: + mains_gas: str + meter_type: int + pv_connection: int + pv_battery_count: int + photovoltaic_supply: PhotovoltaicSupply + wind_turbines_count: int + wind_turbine_details: WindTurbineDetails + gas_smart_meter_present: str + is_dwelling_export_capable: str + wind_turbines_terrain_type: int + electricity_smart_meter_present: str + pv_batteries: Optional[PvBatteries] = None + + +@dataclass +class WindowTransmissionDetails: + u_value: float + data_source: int + solar_transmittance: float + + +@dataclass +class SapWindow: + pvc_frame: str + glazing_gap: int + orientation: int + window_type: int + frame_factor: float + glazing_type: int + window_width: float + window_height: float + draught_proofed: str + window_location: int + window_wall_type: int + permanent_shutters_present: str + window_transmission_details: WindowTransmissionDetails + permanent_shutters_insulated: str + + +@dataclass +class SapFloorDimension: + floor: int + room_height: Measurement + total_floor_area: Measurement + party_wall_length: Union[Measurement, int] + heat_loss_perimeter: Measurement + floor_insulation: Optional[int] = None + floor_construction: Optional[int] = None + + +@dataclass +class SapRoomInRoof: + floor_area: Union[int, float] + construction_age_band: str + + +@dataclass +class SapAlternativeWall: + wall_area: float + wall_dry_lined: str + wall_construction: int + wall_insulation_type: int + wall_thickness_measured: str + wall_insulation_thickness: Optional[str] = None + + +@dataclass +class SapBuildingPart: + identifier: str + wall_dry_lined: str + floor_heat_loss: int + roof_construction: int + wall_construction: int + building_part_number: int + sap_floor_dimensions: List[SapFloorDimension] + wall_insulation_type: int + construction_age_band: str + party_wall_construction: Union[int, str] + wall_thickness_measured: str + roof_insulation_location: Union[int, str] + roof_insulation_thickness: Union[str, int] + sap_room_in_roof: Optional[SapRoomInRoof] = None + sap_alternative_wall_1: Optional[SapAlternativeWall] = None + sap_alternative_wall_2: Optional[SapAlternativeWall] = None + wall_thickness: Optional[int] = None + wall_insulation_thickness: Optional[str] = None + floor_insulation_thickness: Optional[str] = None + flat_roof_insulation_thickness: Optional[Union[str, int]] = None + + +@dataclass +class SapFlatDetails: + level: int + top_storey: str + flat_location: int + heat_loss_corridor: int + storey_count: Optional[int] = None + # Changed from plain int in 21.0.0 to a Measurement object in 21.0.1 + unheated_corridor_length: Optional[Union[Measurement, int]] = None + + +@dataclass +class WindowsTransmissionDetails: + u_value: float + data_source: int + solar_transmittance: float + + +@dataclass +class ImprovementTexts: + improvement_description: str + improvement_summary: Optional[str] = None + + +@dataclass +class ImprovementDetails: + improvement_number: Optional[int] = None + improvement_texts: Optional[ImprovementTexts] = None + + +@dataclass +class SuggestedImprovement: + sequence: int + typical_saving: float + indicative_cost: Union[str, int] + improvement_type: str + improvement_details: ImprovementDetails + improvement_category: int + energy_performance_rating: int + environmental_impact_rating: int + + +@dataclass +class RenewableHeatIncentive: + water_heating: int + space_heating_existing_dwelling: int + impact_of_loft_insulation: Optional[int] = None + impact_of_cavity_insulation: Optional[int] = None + impact_of_solid_wall_insulation: Optional[int] = None + + +@dataclass +class RdSapSchema21_0_1: + uprn: int + roofs: List[EnergyElement] + walls: List[EnergyElement] + floors: List[EnergyElement] + status: str + tenure: int + window: EnergyElement + lighting: EnergyElement + postcode: str + hot_water: EnergyElement + post_town: str + built_form: int + door_count: int + region_code: int + report_type: int + sap_heating: SapHeating + sap_version: float + sap_windows: List[SapWindow] + schema_type: str + uprn_source: str + country_code: str + main_heating: List[EnergyElement] + # dwelling_type remains a plain string (not reverted to DescriptionV1) in 21.0.1 + dwelling_type: str + language_code: int + pressure_test: int + property_type: int + address_line_1: str + assessment_type: str + completion_date: str + inspection_date: str + wet_rooms_count: int + extensions_count: int + measurement_type: int + total_floor_area: int + transaction_type: int + conservatory_type: int + heated_room_count: int + registration_date: str + sap_energy_source: SapEnergySource + secondary_heating: EnergyElement + sap_building_parts: List[SapBuildingPart] + open_chimneys_count: int + solar_water_heating: str + habitable_room_count: int + heating_cost_current: float + insulated_door_count: int + co2_emissions_current: float + energy_rating_average: int + energy_rating_current: int + lighting_cost_current: float + main_heating_controls: List[EnergyElement] + has_hot_water_cylinder: str + heating_cost_potential: float + hot_water_cost_current: float + insulated_door_u_value: float + mechanical_ventilation: int + percent_draughtproofed: int + suggested_improvements: List[SuggestedImprovement] + co2_emissions_potential: float + energy_rating_potential: int + lighting_cost_potential: float + schema_version_original: str + hot_water_cost_potential: float + renewable_heat_incentive: RenewableHeatIncentive + draughtproofed_door_count: int + mechanical_vent_duct_type: int + windows_transmission_details: WindowsTransmissionDetails + cfl_fixed_lighting_bulbs_count: int + energy_consumption_current: int + has_fixed_air_conditioning: str + multiple_glazed_proportion: int + calculation_software_version: str + energy_consumption_potential: int + environmental_impact_current: int + led_fixed_lighting_bulbs_count: int + mechanical_vent_duct_placement: int + mechanical_vent_duct_insulation: int + potential_energy_efficiency_band: str + pressure_test_certificate_number: int + mechanical_ventilation_index_number: int + co2_emissions_current_per_floor_area: int + current_energy_efficiency_band: str + environmental_impact_potential: int + low_energy_fixed_lighting_bulbs_count: int + mechanical_vent_duct_insulation_level: int + mechanical_vent_measured_installation: str + incandescent_fixed_lighting_bulbs_count: int + sap_flat_details: Optional[SapFlatDetails] = None + addendum: Optional[Addendum] = None + address_line_2: Optional[str] = None + has_heated_separate_conservatory: Optional[str] = None + fixed_lighting_outlets_count: Optional[int] = None + low_energy_fixed_lighting_outlets_count: Optional[int] = None diff --git a/datatypes/epc/schema/tests/__init__.py b/datatypes/epc/schema/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/datatypes/epc/schema/tests/fixtures/17_0.json b/datatypes/epc/schema/tests/fixtures/17_0.json new file mode 100644 index 00000000..b17ca7f3 --- /dev/null +++ b/datatypes/epc/schema/tests/fixtures/17_0.json @@ -0,0 +1,218 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": {"value": "(another dwelling above)", "language": "1"}, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "walls": [ + { + "description": {"value": "System built, with internal insulation", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": {"value": "(another dwelling below)", "language": "1"}, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 2, + "window": { + "description": {"value": "Fully double glazed", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "lighting": { + "description": {"value": "Low energy lighting in 57% of fixed outlets", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "postcode": "PT5 4RZ", + "hot_water": { + "description": {"value": "Electric immersion, off-peak", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 2 + }, + "post_town": "POSTTOWN", + "built_form": 2, + "door_count": 2, + "glazed_area": 1, + "glazing_gap": "16+", + "region_code": 3, + "report_type": 2, + "sap_heating": { + "cylinder_size": 2, + "water_heating_code": 903, + "water_heating_fuel": 29, + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 29, + "heat_emitter_type": 0, + "emitter_temperature": "NA", + "main_heating_number": 1, + "main_heating_control": 2401, + "main_heating_category": 7, + "main_heating_fraction": 1, + "sap_main_heating_code": 402, + "main_heating_data_source": 2 + } + ], + "immersion_heating_type": 1, + "cylinder_insulation_type": 0, + "has_fixed_air_conditioning": "false" + }, + "sap_version": 9.92, + "schema_type": "RdSAP-Schema-17.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": {"value": "Electric storage heaters", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 1 + } + ], + "dwelling_type": {"value": "Mid-floor flat", "language": "1"}, + "language_code": 1, + "property_type": 2, + "address_line_1": "42, Moria Mines Lane", + "assessment_type": "RdSAP", + "completion_date": "2016-01-12", + "inspection_date": "2016-01-12", + "extensions_count": 0, + "measurement_type": 1, + "sap_flat_details": { + "level": 2, + "top_storey": "N", + "flat_location": 1, + "heat_loss_corridor": 0 + }, + "total_floor_area": 55, + "transaction_type": 8, + "conservatory_type": 1, + "heated_room_count": 1, + "pvc_window_frames": "true", + "registration_date": "2016-01-12", + "sap_energy_source": { + "mains_gas": "N", + "meter_type": 1, + "photovoltaic_supply": { + "none_or_no_details": {"pv_connection": 0, "percent_roof_area": 0} + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": {"value": "Portable electric heaters (assumed)", "language": "1"}, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [11], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 240, + "floor_heat_loss": 6, + "roof_construction": 3, + "wall_construction": 8, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": {"value": 2.4, "quantity": "metres"}, + "total_floor_area": {"value": 54.6, "quantity": "square metres"}, + "party_wall_length": {"value": 7.3, "quantity": "metres"}, + "heat_loss_perimeter": {"value": 23.3, "quantity": "metres"} + } + ], + "wall_insulation_type": 3, + "construction_age_band": "D", + "party_wall_construction": 0, + "wall_thickness_measured": "Y", + "roof_insulation_location": "ND", + "roof_insulation_thickness": "ND", + "wall_insulation_thickness": "50mm" + } + ], + "low_energy_lighting": 57, + "solar_water_heating": "N", + "habitable_room_count": 3, + "heating_cost_current": {"value": 214, "currency": "GBP"}, + "insulated_door_count": 0, + "co2_emissions_current": 3.9, + "energy_rating_average": 60, + "energy_rating_current": 66, + "lighting_cost_current": {"value": 61, "currency": "GBP"}, + "main_heating_controls": [ + { + "description": {"value": "Manual charge control", "language": "1"}, + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + } + ], + "multiple_glazing_type": 3, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "true", + "heating_cost_potential": {"value": 216, "currency": "GBP"}, + "hot_water_cost_current": {"value": 396, "currency": "GBP"}, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": {"value": 158, "currency": "GBP"}, + "indicative_cost": "£15 - £30", + "improvement_type": "C", + "improvement_details": {"improvement_number": 1}, + "improvement_category": 5, + "energy_performance_rating": 74, + "environmental_impact_rating": 60 + } + ], + "co2_emissions_potential": 2.5, + "energy_rating_potential": 79, + "lighting_cost_potential": {"value": 42, "currency": "GBP"}, + "schema_version_original": "LIG-17.0", + "alternative_improvements": [ + { + "sequence": 1, + "typical_saving": {"value": 141, "currency": "GBP"}, + "improvement_type": "J2", + "improvement_details": {"improvement_number": 54}, + "improvement_category": 6, + "energy_performance_rating": 81, + "environmental_impact_rating": 96 + } + ], + "hot_water_cost_potential": {"value": 154, "currency": "GBP"}, + "renewable_heat_incentive": { + "water_heating": 4818, + "space_heating_existing_dwelling": 2415 + }, + "energy_consumption_current": 427, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "9.0.0", + "energy_consumption_potential": 267, + "environmental_impact_current": 48, + "fixed_lighting_outlets_count": 7, + "current_energy_efficiency_band": "D", + "environmental_impact_potential": 66, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 72, + "low_energy_fixed_lighting_outlets_count": 4 +} diff --git a/datatypes/epc/schema/tests/fixtures/17_1.json b/datatypes/epc/schema/tests/fixtures/17_1.json new file mode 100644 index 00000000..ef0613d1 --- /dev/null +++ b/datatypes/epc/schema/tests/fixtures/17_1.json @@ -0,0 +1,243 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": {"value": "Pitched, 100 mm loft insulation", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + { + "description": {"value": "Pitched, insulated (assumed)", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": {"value": "Cavity wall, filled cavity", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "floors": [ + { + "description": {"value": "Solid, no insulation (assumed)", "language": "1"}, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 1, + "window": { + "description": {"value": "Fully double glazed", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "lighting": { + "description": {"value": "Low energy lighting in 23% of fixed outlets", "language": "1"}, + "energy_efficiency_rating": 2, + "environmental_efficiency_rating": 2 + }, + "postcode": "PT42 5HL", + "hot_water": { + "description": {"value": "From main system", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "post_town": "POSTTOWN", + "built_form": 1, + "door_count": 4, + "glazed_area": 1, + "glazing_gap": "16+", + "region_code": 17, + "report_type": 2, + "sap_heating": { + "cylinder_size": 2, + "water_heating_code": 901, + "water_heating_fuel": 28, + "cylinder_thermostat": "Y", + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 1 + }, + "secondary_fuel_type": 29, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 28, + "boiler_flue_type": 1, + "fan_flue_present": "N", + "heat_emitter_type": 1, + "emitter_temperature": "NA", + "main_heating_number": 1, + "main_heating_control": 2104, + "main_heating_category": 2, + "main_heating_fraction": 1, + "mcs_installed_heat_pump": "false", + "main_heating_data_source": 1, + "main_heating_index_number": 9049 + } + ], + "immersion_heating_type": "NA", + "secondary_heating_type": 691, + "cylinder_insulation_type": 1, + "has_fixed_air_conditioning": "false", + "cylinder_insulation_thickness": 12 + }, + "sap_version": 9.92, + "schema_type": "RdSAP-Schema-17.1", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": {"value": "Boiler and radiators, oil", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + } + ], + "dwelling_type": {"value": "Detached house", "language": "1"}, + "language_code": 1, + "property_type": 0, + "address_line_1": "15, Hedge Lane", + "address_line_2": "Lower Moria", + "assessment_type": "RdSAP", + "completion_date": "2018-05-29", + "inspection_date": "2018-05-29", + "extensions_count": 1, + "measurement_type": 2, + "total_floor_area": 101, + "transaction_type": 1, + "conservatory_type": 1, + "heated_room_count": 7, + "pvc_window_frames": "true", + "registration_date": "2018-05-27", + "sap_energy_source": { + "mains_gas": "N", + "meter_type": 2, + "photovoltaic_supply": { + "none_or_no_details": {"pv_connection": 0, "percent_roof_area": 0} + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": {"value": "Room heaters, electric", "language": "1"}, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [11], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 270, + "floor_heat_loss": 7, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": {"value": 2.4, "quantity": "metres"}, + "floor_insulation": 1, + "total_floor_area": {"value": 48.45, "quantity": "square metres"}, + "party_wall_length": {"value": 0, "quantity": "metres"}, + "floor_construction": 1, + "heat_loss_perimeter": {"value": 22.3, "quantity": "metres"} + } + ], + "wall_insulation_type": 2, + "construction_age_band": "G", + "party_wall_construction": "NA", + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "100mm" + }, + { + "identifier": "Extension 1", + "wall_dry_lined": "N", + "wall_thickness": 260, + "floor_heat_loss": 7, + "roof_construction": 5, + "wall_construction": 4, + "building_part_number": 2, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": {"value": 2.4, "quantity": "metres"}, + "floor_insulation": 1, + "total_floor_area": {"value": 21.84, "quantity": "square metres"}, + "party_wall_length": 0, + "floor_construction": 1, + "heat_loss_perimeter": {"value": 16.6, "quantity": "metres"} + } + ], + "wall_insulation_type": 4, + "construction_age_band": "H", + "party_wall_construction": "NA", + "wall_thickness_measured": "Y", + "roof_insulation_location": 4, + "roof_insulation_thickness": 0, + "wall_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 23, + "solar_water_heating": "N", + "habitable_room_count": 7, + "heating_cost_current": {"value": 659, "currency": "GBP"}, + "insulated_door_count": 0, + "co2_emissions_current": 5.8, + "energy_rating_average": 60, + "energy_rating_current": 53, + "lighting_cost_current": {"value": 115, "currency": "GBP"}, + "main_heating_controls": [ + { + "description": {"value": "Programmer and room thermostat", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + } + ], + "multiple_glazing_type": 3, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "true", + "heating_cost_potential": {"value": 470, "currency": "GBP"}, + "hot_water_cost_current": {"value": 161, "currency": "GBP"}, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": {"value": 25, "currency": "GBP"}, + "indicative_cost": "£100 - £350", + "improvement_type": "A", + "improvement_details": {"improvement_number": 5}, + "improvement_category": 5, + "energy_performance_rating": 54, + "environmental_impact_rating": 47 + } + ], + "co2_emissions_potential": 2.7, + "energy_rating_potential": 78, + "lighting_cost_potential": {"value": 65, "currency": "GBP"}, + "schema_version_original": "LIG-17.1", + "hot_water_cost_potential": {"value": 77, "currency": "GBP"}, + "renewable_heat_incentive": { + "water_heating": 3301, + "impact_of_loft_insulation": -565, + "space_heating_existing_dwelling": 11351 + }, + "energy_consumption_current": 234, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "v92.0.1.1", + "energy_consumption_potential": 95, + "environmental_impact_current": 46, + "fixed_lighting_outlets_count": 13, + "current_energy_efficiency_band": "E", + "environmental_impact_potential": 72, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 57, + "low_energy_fixed_lighting_outlets_count": 3 +} diff --git a/datatypes/epc/schema/tests/fixtures/18_0.json b/datatypes/epc/schema/tests/fixtures/18_0.json new file mode 100644 index 00000000..502ac329 --- /dev/null +++ b/datatypes/epc/schema/tests/fixtures/18_0.json @@ -0,0 +1,251 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": {"value": "Pitched, 100 mm loft insulation", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + { + "description": {"value": "Flat, insulated", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + { + "description": {"value": "Roof room(s), insulated (assumed)", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": {"value": "Solid brick, as built, no insulation (assumed)", "language": "1"}, + "energy_efficiency_rating": 1, + "environmental_efficiency_rating": 1 + } + ], + "floors": [ + { + "description": {"value": "Solid, no insulation (assumed)", "language": "1"}, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 1, + "window": { + "description": {"value": "Fully double glazed", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "lighting": { + "description": {"value": "Low energy lighting in 67% of fixed outlets", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "postcode": "PT11 4RF", + "hot_water": { + "description": {"value": "From main system", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "POSTTOWN", + "built_form": 4, + "door_count": 2, + "glazed_area": 1, + "glazing_gap": 12, + "region_code": 1, + "report_type": 2, + "sap_heating": { + "cylinder_size": 1, + "water_heating_code": 901, + "water_heating_fuel": 26, + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "central_heating_pump_age": 0, + "main_heating_data_source": 1, + "main_heating_index_number": 16137 + } + ], + "immersion_heating_type": "NA", + "has_fixed_air_conditioning": "false" + }, + "sap_version": 9.92, + "schema_type": "RdSAP-Schema-18.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": {"value": "Boiler and radiators, mains gas", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": {"value": "Mid-terrace house", "language": "1"}, + "language_code": 1, + "property_type": 0, + "address_line_1": "1, Bagshot Lane", + "address_line_2": "Village", + "assessment_type": "RdSAP", + "completion_date": "2017-03-19", + "inspection_date": "2017-03-19", + "extensions_count": 1, + "measurement_type": 1, + "total_floor_area": 93, + "transaction_type": 5, + "conservatory_type": 1, + "heated_room_count": 5, + "pvc_window_frames": "true", + "registration_date": "2017-03-19", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 1, + "photovoltaic_supply": { + "none_or_no_details": {"pv_connection": 0, "percent_roof_area": 0} + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": {"value": "None", "language": "1"}, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [11], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 330, + "floor_heat_loss": 7, + "sap_room_in_roof": { + "floor_area": {"value": 9, "quantity": "square metres"}, + "insulation": "AB", + "roof_room_connected": "N", + "construction_age_band": "G" + }, + "roof_construction": 4, + "wall_construction": 3, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": {"value": 2.4, "quantity": "metres"}, + "floor_insulation": 1, + "total_floor_area": {"value": 29.12, "quantity": "square metres"}, + "party_wall_length": {"value": 11.2, "quantity": "metres"}, + "floor_construction": 1, + "heat_loss_perimeter": {"value": 5.2, "quantity": "metres"} + } + ], + "wall_insulation_type": 4, + "construction_age_band": "C", + "party_wall_construction": 0, + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "100mm", + "wall_insulation_thickness": "NI" + }, + { + "identifier": "Extension", + "wall_dry_lined": "N", + "wall_thickness": 290, + "floor_heat_loss": 7, + "roof_construction": 1, + "wall_construction": 4, + "building_part_number": 2, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": {"value": 2.4, "quantity": "metres"}, + "floor_insulation": 1, + "total_floor_area": {"value": 15.6, "quantity": "square metres"}, + "party_wall_length": {"value": 6, "quantity": "metres"}, + "floor_construction": 1, + "heat_loss_perimeter": {"value": 5.2, "quantity": "metres"} + } + ], + "wall_insulation_type": 4, + "construction_age_band": "G", + "party_wall_construction": 0, + "wall_thickness_measured": "Y", + "roof_insulation_location": 6, + "roof_insulation_thickness": "NI", + "wall_insulation_thickness": "NI", + "flat_roof_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 67, + "solar_water_heating": "N", + "habitable_room_count": 5, + "heating_cost_current": {"value": 619, "currency": "GBP"}, + "insulated_door_count": 0, + "co2_emissions_current": 3.4, + "energy_rating_average": 60, + "energy_rating_current": 69, + "lighting_cost_current": {"value": 81, "currency": "GBP"}, + "main_heating_controls": [ + { + "description": {"value": "Programmer, room thermostat and TRVs", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "multiple_glazing_type": 3, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "false", + "heating_cost_potential": {"value": 534, "currency": "GBP"}, + "hot_water_cost_current": {"value": 100, "currency": "GBP"}, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": {"value": 88, "currency": "GBP"}, + "indicative_cost": "£4,000 - £14,000", + "improvement_type": "Q", + "improvement_details": {"improvement_number": 7}, + "improvement_category": 5, + "energy_performance_rating": 72, + "environmental_impact_rating": 71 + } + ], + "co2_emissions_potential": 1.7, + "energy_rating_potential": 85, + "lighting_cost_potential": {"value": 61, "currency": "GBP"}, + "schema_version_original": "LIG-17.0", + "hot_water_cost_potential": {"value": 68, "currency": "GBP"}, + "renewable_heat_incentive": { + "water_heating": 2087, + "impact_of_loft_insulation": -214, + "impact_of_solid_wall_insulation": -1864, + "space_heating_existing_dwelling": 10483 + }, + "energy_consumption_current": 230, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "2.0.0.0", + "energy_consumption_potential": 115, + "environmental_impact_current": 66, + "fixed_lighting_outlets_count": 9, + "current_energy_efficiency_band": "C", + "environmental_impact_potential": 83, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "B", + "co2_emissions_current_per_floor_area": 41, + "low_energy_fixed_lighting_outlets_count": 6 +} diff --git a/datatypes/epc/schema/tests/fixtures/19_0.json b/datatypes/epc/schema/tests/fixtures/19_0.json new file mode 100644 index 00000000..f9995286 --- /dev/null +++ b/datatypes/epc/schema/tests/fixtures/19_0.json @@ -0,0 +1,213 @@ +{ + "uprn": 12457, + "roofs": [ + { + "description": {"value": "Pitched, 150 mm loft insulation", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "walls": [ + { + "description": {"value": "Cavity wall, filled cavity", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + } + ], + "floors": [ + { + "description": {"value": "Solid, no insulation (assumed)", "language": "1"}, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + } + ], + "status": "entered", + "tenure": 3, + "window": { + "description": {"value": "Fully double glazed", "language": "1"}, + "energy_efficiency_rating": 3, + "environmental_efficiency_rating": 3 + }, + "lighting": { + "description": {"value": "Low energy lighting in 87% of fixed outlets", "language": "1"}, + "energy_efficiency_rating": 5, + "environmental_efficiency_rating": 5 + }, + "postcode": "A1 1AA", + "hot_water": { + "description": {"value": "From main system", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + }, + "post_town": "Town", + "built_form": 2, + "door_count": 1, + "glazed_area": 1, + "region_code": 19, + "report_type": 2, + "sap_heating": { + "cylinder_size": 1, + "water_heating_code": 901, + "water_heating_fuel": 26, + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "secondary_fuel_type": 9, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "central_heating_pump_age": 0, + "main_heating_data_source": 1, + "main_heating_index_number": 15274 + } + ], + "immersion_heating_type": "NA", + "secondary_heating_type": 631, + "has_fixed_air_conditioning": "false" + }, + "sap_version": 9.94, + "schema_type": "RdSAP-Schema-19.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + { + "description": {"value": "Boiler and radiators, mains gas", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "dwelling_type": {"value": "Semi-detached house", "language": "1"}, + "language_code": 1, + "property_type": 0, + "address_line_1": "15, Address Lane", + "address_line_2": "New Town", + "assessment_type": "RdSAP", + "completion_date": "2020-06-04", + "inspection_date": "2020-06-03", + "extensions_count": 1, + "measurement_type": 1, + "total_floor_area": 94, + "transaction_type": 8, + "conservatory_type": 2, + "heated_room_count": 5, + "pvc_window_frames": "false", + "registration_date": "2020-06-04", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 3, + "photovoltaic_supply": { + "none_or_no_details": {"pv_connection": 0, "percent_roof_area": 0} + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": { + "description": {"value": "Room heaters, dual fuel (mineral and wood)", "language": "1"}, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "lzc_energy_sources": [11, 9], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "wall_thickness": 300, + "floor_heat_loss": 7, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": {"value": 2.47, "quantity": "metres"}, + "floor_insulation": 1, + "total_floor_area": {"value": 39.91, "quantity": "square metres"}, + "party_wall_length": {"value": 8.81, "quantity": "metres"}, + "floor_construction": 1, + "heat_loss_perimeter": {"value": 13.65, "quantity": "metres"} + } + ], + "wall_insulation_type": 2, + "construction_age_band": "D", + "party_wall_construction": 1, + "wall_thickness_measured": "Y", + "roof_insulation_location": 2, + "roof_insulation_thickness": "150mm", + "wall_insulation_thickness": "NI", + "floor_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 87, + "solar_water_heating": "N", + "habitable_room_count": 5, + "heating_cost_current": {"value": 666, "currency": "GBP"}, + "insulated_door_count": 0, + "co2_emissions_current": 3.8, + "energy_rating_average": 60, + "energy_rating_current": 66, + "lighting_cost_current": {"value": 81, "currency": "GBP"}, + "main_heating_controls": [ + { + "description": {"value": "Programmer, room thermostat and TRVs", "language": "1"}, + "energy_efficiency_rating": 4, + "environmental_efficiency_rating": 4 + } + ], + "multiple_glazing_type": 3, + "open_fireplaces_count": 0, + "has_hot_water_cylinder": "false", + "heating_cost_potential": {"value": 615, "currency": "GBP"}, + "hot_water_cost_current": {"value": 107, "currency": "GBP"}, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": {"value": 51, "currency": "GBP"}, + "indicative_cost": "£4,000 - £6,000", + "improvement_type": "W2", + "improvement_details": {"improvement_number": 58}, + "improvement_category": 5, + "energy_performance_rating": 68, + "environmental_impact_rating": 65 + } + ], + "co2_emissions_potential": 2.4, + "energy_rating_potential": 79, + "lighting_cost_potential": {"value": 81, "currency": "GBP"}, + "schema_version_original": "LIG-19.0", + "hot_water_cost_potential": {"value": 74, "currency": "GBP"}, + "renewable_heat_incentive": { + "water_heating": 2207, + "impact_of_loft_insulation": -394, + "space_heating_existing_dwelling": 9825 + }, + "energy_consumption_current": 222, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "2.1.1.0", + "energy_consumption_potential": 137, + "environmental_impact_current": 62, + "fixed_lighting_outlets_count": 15, + "windows_transmission_details": { + "u_value": 3.1, + "data_source": 2, + "solar_transmittance": 0.76 + }, + "current_energy_efficiency_band": "D", + "environmental_impact_potential": 75, + "has_heated_separate_conservatory": "false", + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 41, + "low_energy_fixed_lighting_outlets_count": 13 +} diff --git a/datatypes/epc/schema/tests/fixtures/20_0_0.json b/datatypes/epc/schema/tests/fixtures/20_0_0.json new file mode 100644 index 00000000..397c2758 --- /dev/null +++ b/datatypes/epc/schema/tests/fixtures/20_0_0.json @@ -0,0 +1,225 @@ +{ + "uprn": 12457, + "roofs": [ + {"description": "Pitched, 25 mm loft insulation", "energy_efficiency_rating": 2, "environmental_efficiency_rating": 2}, + {"description": "Pitched, 250 mm loft insulation", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4} + ], + "walls": [ + {"description": "Solid brick, as built, no insulation (assumed)", "energy_efficiency_rating": 1, "environmental_efficiency_rating": 1}, + {"description": "Cavity wall, as built, insulated (assumed)", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4} + ], + "floors": [ + {"description": "Suspended, no insulation (assumed)", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0}, + {"description": "Solid, insulated (assumed)", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0} + ], + "status": "entered", + "tenure": 1, + "window": {"description": "Fully double glazed", "energy_efficiency_rating": 3, "environmental_efficiency_rating": 3}, + "addendum": { + "stone_walls": "true", + "system_build": "true", + "addendum_numbers": [1, 8] + }, + "lighting": {"description": "Low energy lighting in 50% of fixed outlets", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}, + "postcode": "A0 0AA", + "hot_water": {"description": "From main system", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}, + "post_town": "Whitbury", + "built_form": 2, + "door_count": 2, + "glazed_area": 1, + "region_code": 1, + "report_type": 2, + "sap_heating": { + "cylinder_size": 1, + "water_heating_code": 901, + "water_heating_fuel": 26, + "instantaneous_wwhrs": { + "rooms_with_bath_and_or_shower": 1, + "rooms_with_mixer_shower_no_bath": 0, + "rooms_with_bath_and_mixer_shower": 0 + }, + "secondary_fuel_type": 25, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "N", + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "sap_main_heating_code": 101, + "central_heating_pump_age": 0, + "main_heating_data_source": 1, + "main_heating_index_number": 17507 + } + ], + "immersion_heating_type": "NA", + "has_fixed_air_conditioning": "false" + }, + "sap_version": 9.8, + "sap_windows": [ + {"orientation": 1, "window_area": 200.1, "window_type": 2, "glazing_type": 1, "window_location": 0}, + {"orientation": 2, "window_area": 180.2, "window_type": 1, "glazing_type": 2, "window_location": 1} + ], + "schema_type": "RdSAP-Schema-20.0.0", + "uprn_source": "Energy Assessor", + "country_code": "EAW", + "main_heating": [ + {"description": "Boiler and radiators, anthracite", "energy_efficiency_rating": 3, "environmental_efficiency_rating": 1}, + {"description": "Boiler and radiators, mains gas", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4} + ], + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "property_type": 0, + "address_line_1": "1 Some Street", + "assessment_type": "RdSAP", + "completion_date": "2020-05-04", + "inspection_date": "2020-05-04", + "extensions_count": 0, + "measurement_type": 1, + "sap_flat_details": { + "level": 1, + "top_storey": "N", + "storey_count": 3, + "flat_location": 1, + "heat_loss_corridor": 2, + "unheated_corridor_length": 10 + }, + "total_floor_area": 55, + "transaction_type": 1, + "conservatory_type": 1, + "heated_room_count": 5, + "registration_date": "2020-05-04", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 2, + "photovoltaic_supply": { + "none_or_no_details": {"pv_connection": 0, "percent_roof_area": 50} + }, + "wind_turbines_count": 0, + "wind_turbines_terrain_type": 2 + }, + "secondary_heating": {"description": "Room heaters, electric", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0}, + "lzc_energy_sources": [11], + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "floor_heat_loss": 7, + "sap_room_in_roof": { + "floor_area": 100, + "insulation": "AB", + "roof_room_connected": "N", + "construction_age_band": "B" + }, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": {"value": 2.45, "quantity": "metres"}, + "floor_insulation": 1, + "total_floor_area": {"value": 45.82, "quantity": "square metres"}, + "party_wall_length": {"value": 7.9, "quantity": "metres"}, + "floor_construction": 1, + "heat_loss_perimeter": {"value": 19.5, "quantity": "metres"} + } + ], + "wall_insulation_type": 2, + "construction_age_band": "K", + "party_wall_construction": 0, + "wall_thickness_measured": "N", + "roof_insulation_location": 2, + "roof_insulation_thickness": "200mm", + "wall_insulation_thickness": "NI", + "floor_insulation_thickness": "NI" + } + ], + "low_energy_lighting": 100, + "solar_water_heating": "N", + "habitable_room_count": 5, + "heating_cost_current": 365.98, + "insulated_door_count": 2, + "co2_emissions_current": 2.4, + "energy_rating_average": 60, + "energy_rating_current": 50, + "lighting_cost_current": 123.45, + "main_heating_controls": [ + {"description": "Programmer, room thermostat and TRVs", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}, + {"description": "Time and temperature zone control", "energy_efficiency_rating": 5, "environmental_efficiency_rating": 5} + ], + "multiple_glazing_type": 2, + "open_fireplaces_count": 0, + "heating_cost_potential": 250.34, + "hot_water_cost_current": 200.4, + "insulated_door_u_value": 3, + "mechanical_ventilation": 0, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": 360, + "indicative_cost": "£100 - £350", + "improvement_type": "Z3", + "improvement_details": {"improvement_number": 5}, + "improvement_category": 6, + "energy_performance_rating": 50, + "environmental_impact_rating": 50 + }, + { + "sequence": 2, + "typical_saving": 99, + "indicative_cost": 2000, + "improvement_type": "Z2", + "improvement_details": {"improvement_number": 1}, + "improvement_category": 2, + "energy_performance_rating": 60, + "environmental_impact_rating": 64 + }, + { + "sequence": 3, + "typical_saving": 99, + "indicative_cost": 1000, + "improvement_type": "Z2", + "improvement_details": { + "improvement_texts": { + "improvement_summary": "An improvement summary", + "improvement_description": "An improvement desc" + } + }, + "improvement_category": 2, + "energy_performance_rating": 60, + "environmental_impact_rating": 64 + } + ], + "co2_emissions_potential": 1.4, + "energy_rating_potential": 72, + "lighting_cost_potential": 84.23, + "schema_version_original": "SAP-19.0", + "hot_water_cost_potential": 180.43, + "renewable_heat_incentive": { + "water_heating": 2285, + "impact_of_loft_insulation": -2114, + "impact_of_cavity_insulation": -122, + "impact_of_solid_wall_insulation": -3560, + "space_heating_existing_dwelling": 13120 + }, + "energy_consumption_current": 230, + "multiple_glazed_proportion": 100, + "calculation_software_version": "13.05r16", + "energy_consumption_potential": 88, + "environmental_impact_current": 52, + "fixed_lighting_outlets_count": 16, + "windows_transmission_details": {"u_value": 2, "data_source": 2, "solar_transmittance": 0.72}, + "multiple_glazed_proportion_nr": "NR", + "current_energy_efficiency_band": "E", + "environmental_impact_potential": 74, + "potential_energy_efficiency_band": "C", + "co2_emissions_current_per_floor_area": 20, + "low_energy_fixed_lighting_outlets_count": 16 +} diff --git a/datatypes/epc/schema/tests/fixtures/21_0_0.json b/datatypes/epc/schema/tests/fixtures/21_0_0.json new file mode 100644 index 00000000..6781ac6e --- /dev/null +++ b/datatypes/epc/schema/tests/fixtures/21_0_0.json @@ -0,0 +1,245 @@ +{ + "uprn": 12457, + "roofs": [ + {"description": "Pitched, 25 mm loft insulation", "energy_efficiency_rating": 2, "environmental_efficiency_rating": 2}, + {"description": "Pitched, 250 mm loft insulation", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4} + ], + "walls": [ + {"description": "Solid brick, as built, no insulation (assumed)", "energy_efficiency_rating": 1, "environmental_efficiency_rating": 1}, + {"description": "Cavity wall, as built, insulated (assumed)", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4} + ], + "floors": [ + {"description": "Suspended, no insulation (assumed)", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0}, + {"description": "Solid, insulated (assumed)", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0} + ], + "status": "entered", + "tenure": 1, + "window": {"description": "Fully double glazed", "energy_efficiency_rating": 3, "environmental_efficiency_rating": 3}, + "addendum": {"stone_walls": "true", "system_build": "true", "addendum_numbers": [1, 8]}, + "lighting": {"description": "Low energy lighting in 50% of fixed outlets", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}, + "postcode": "A0 0AA", + "hot_water": {"description": "From main system", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}, + "post_town": "Whitbury", + "built_form": 2, + "door_count": 3, + "region_code": 1, + "report_type": 2, + "sap_heating": { + "cylinder_size": 1, + "shower_outlets": { + "shower_outlet": {"shower_wwhrs": 1, "shower_outlet_type": 1} + }, + "water_heating_code": 901, + "water_heating_fuel": 26, + "instantaneous_wwhrs": {"wwhrs_index_number1": 1, "wwhrs_index_number2": 2}, + "secondary_fuel_type": 25, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "N", + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "boiler_ignition_type": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "sap_main_heating_code": 101, + "central_heating_pump_age": 0, + "main_heating_data_source": 1, + "main_heating_index_number": 17507 + } + ], + "immersion_heating_type": "NA", + "has_fixed_air_conditioning": "false" + }, + "sap_version": 10.2, + "sap_windows": [ + { + "pvc_frame": "false", + "glazing_gap": 6, + "orientation": 1, + "window_type": 2, + "frame_factor": 1.0, + "glazing_type": 14, + "window_width": 1.2, + "window_height": 2.0, + "draught_proofed": "true", + "window_location": 0, + "window_wall_type": 6, + "permanent_shutters_present": "N", + "window_transmission_details": {"u_value": 1.0, "data_source": 2, "solar_transmittance": 1.0}, + "permanent_shutters_insulated": "N" + } + ], + "schema_type": "RdSAP-Schema-21.0.0", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + {"description": "Boiler and radiators, anthracite", "energy_efficiency_rating": 3, "environmental_efficiency_rating": 1}, + {"description": "Boiler and radiators, mains gas", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4} + ], + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "pressure_test": 6, + "property_type": 0, + "address_line_1": "1 Some Street", + "assessment_type": "RdSAP", + "completion_date": "2023-12-01", + "inspection_date": "2023-12-01", + "wet_rooms_count": 0, + "extensions_count": 0, + "measurement_type": 1, + "sap_flat_details": { + "level": 1, + "top_storey": "N", + "storey_count": 3, + "flat_location": 1, + "heat_loss_corridor": 2, + "unheated_corridor_length": 10 + }, + "total_floor_area": 55, + "transaction_type": 16, + "conservatory_type": 1, + "heated_room_count": 5, + "registration_date": "2023-12-01", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 2, + "pv_batteries": {"pv_battery": {"battery_capacity": 5}}, + "pv_connection": 0, + "pv_battery_count": 1, + "photovoltaic_supply": {"none_or_no_details": {"percent_roof_area": 0}}, + "wind_turbines_count": 0, + "wind_turbine_details": {"hub_height": 0, "rotor_diameter": 0}, + "gas_smart_meter_present": "false", + "is_dwelling_export_capable": "false", + "wind_turbines_terrain_type": 4, + "electricity_smart_meter_present": "true" + }, + "secondary_heating": {"description": "Room heaters, electric", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0}, + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "floor_heat_loss": 7, + "sap_room_in_roof": {"floor_area": 100, "construction_age_band": "B"}, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": {"value": 2.45, "quantity": "metres"}, + "floor_insulation": 1, + "total_floor_area": {"value": 45.82, "quantity": "square metres"}, + "party_wall_length": {"value": 7.9, "quantity": "metres"}, + "floor_construction": 1, + "heat_loss_perimeter": {"value": 19.5, "quantity": "metres"} + } + ], + "wall_insulation_type": 2, + "construction_age_band": "M", + "sap_alternative_wall_1": { + "wall_area": 10.4, + "wall_dry_lined": "N", + "wall_construction": 4, + "wall_insulation_type": 2, + "wall_thickness_measured": "N" + }, + "sap_alternative_wall_2": { + "wall_area": 10.8, + "wall_dry_lined": "N", + "wall_construction": 4, + "wall_insulation_type": 2, + "wall_thickness_measured": "N" + }, + "party_wall_construction": 0, + "wall_thickness_measured": "N", + "roof_insulation_location": 2, + "roof_insulation_thickness": "200mm", + "wall_insulation_thickness": "NI", + "floor_insulation_thickness": "NI" + } + ], + "open_chimneys_count": 1, + "solar_water_heating": "N", + "habitable_room_count": 5, + "heating_cost_current": 365.98, + "insulated_door_count": 2, + "co2_emissions_current": 2.4, + "energy_rating_average": 60, + "energy_rating_current": 50, + "lighting_cost_current": 123.45, + "main_heating_controls": [ + {"description": "Programmer, room thermostat and TRVs", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}, + {"description": "Time and temperature zone control", "energy_efficiency_rating": 5, "environmental_efficiency_rating": 5} + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": 250.34, + "hot_water_cost_current": 200.4, + "insulated_door_u_value": 3, + "mechanical_ventilation": 6, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": 360, + "indicative_cost": "£100 - £350", + "improvement_type": "Z3", + "improvement_details": {"improvement_number": 5}, + "improvement_category": 6, + "energy_performance_rating": 50, + "environmental_impact_rating": 50 + }, + { + "sequence": 3, + "typical_saving": 99, + "indicative_cost": 1000, + "improvement_type": "Z2", + "improvement_details": { + "improvement_texts": {"improvement_description": "Improvement desc"} + }, + "improvement_category": 2, + "energy_performance_rating": 60, + "environmental_impact_rating": 64 + } + ], + "co2_emissions_potential": 1.4, + "energy_rating_potential": 72, + "lighting_cost_potential": 84.23, + "schema_version_original": "SAP-19.0", + "hot_water_cost_potential": 180.43, + "renewable_heat_incentive": { + "water_heating": 2285, + "impact_of_loft_insulation": -2114, + "impact_of_cavity_insulation": -122, + "impact_of_solid_wall_insulation": -3560, + "space_heating_existing_dwelling": 13120 + }, + "draughtproofed_door_count": 1, + "mechanical_vent_duct_type": 3, + "energy_consumption_current": 230, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "13.05r16", + "energy_consumption_potential": 88, + "environmental_impact_current": 52, + "windows_transmission_details": {"u_value": 2, "data_source": 2, "solar_transmittance": 0.72}, + "cfl_fixed_lighting_bulbs_count": 5, + "current_energy_efficiency_band": "E", + "environmental_impact_potential": 74, + "led_fixed_lighting_bulbs_count": 10, + "mechanical_vent_duct_placement": 2, + "mechanical_vent_duct_insulation": 2, + "potential_energy_efficiency_band": "C", + "pressure_test_certificate_number": 0, + "mechanical_ventilation_index_number": 12, + "co2_emissions_current_per_floor_area": 20, + "low_energy_fixed_lighting_bulbs_count": 16, + "mechanical_vent_duct_insulation_level": 2, + "mechanical_vent_measured_installation": "false", + "incandescent_fixed_lighting_bulbs_count": 5 +} diff --git a/datatypes/epc/schema/tests/fixtures/21_0_1.json b/datatypes/epc/schema/tests/fixtures/21_0_1.json new file mode 100644 index 00000000..45361227 --- /dev/null +++ b/datatypes/epc/schema/tests/fixtures/21_0_1.json @@ -0,0 +1,222 @@ +{ + "uprn": 12457, + "roofs": [ + {"description": {"value": "Pitched, 25 mm loft insulation", "language": "1"}, "energy_efficiency_rating": 2, "environmental_efficiency_rating": 2}, + {"description": {"value": "Pitched, 250 mm loft insulation", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4} + ], + "walls": [ + {"description": {"value": "Solid brick, as built, no insulation (assumed)", "language": "1"}, "energy_efficiency_rating": 1, "environmental_efficiency_rating": 1}, + {"description": {"value": "Cavity wall, as built, insulated (assumed)", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4} + ], + "floors": [ + {"description": {"value": "Suspended, no insulation (assumed)", "language": "1"}, "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0} + ], + "status": "entered", + "tenure": 1, + "window": {"description": {"value": "Fully double glazed", "language": "1"}, "energy_efficiency_rating": 3, "environmental_efficiency_rating": 3}, + "addendum": {"stone_walls": "true", "system_build": "true", "addendum_numbers": [1, 13]}, + "lighting": {"description": {"value": "Low energy lighting in 50% of fixed outlets", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}, + "postcode": "A0 0AA", + "hot_water": {"description": {"value": "From main system", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}, + "post_town": "Whitbury", + "built_form": 2, + "door_count": 3, + "region_code": 1, + "report_type": 2, + "sap_heating": { + "cylinder_size": 1, + "shower_outlets": {"shower_outlet": {"shower_wwhrs": 1, "shower_outlet_type": 1}}, + "water_heating_code": 901, + "water_heating_fuel": 26, + "instantaneous_wwhrs": {"wwhrs_index_number1": 1, "wwhrs_index_number2": 2}, + "secondary_fuel_type": 25, + "main_heating_details": [ + { + "has_fghrs": "N", + "main_fuel_type": 26, + "boiler_flue_type": 2, + "fan_flue_present": "N", + "heat_emitter_type": 1, + "emitter_temperature": 0, + "main_heating_number": 1, + "boiler_ignition_type": 1, + "main_heating_control": 2106, + "main_heating_category": 2, + "main_heating_fraction": 1, + "sap_main_heating_code": 101, + "central_heating_pump_age": 0, + "main_heating_data_source": 1, + "main_heating_index_number": 17507 + } + ], + "immersion_heating_type": "NA", + "has_fixed_air_conditioning": "false" + }, + "sap_version": 10.2, + "sap_windows": [ + { + "pvc_frame": "false", + "glazing_gap": 6, + "orientation": 1, + "window_type": 2, + "frame_factor": 1.0, + "glazing_type": 14, + "window_width": 1.2, + "window_height": 2.0, + "draught_proofed": "true", + "window_location": 0, + "window_wall_type": 6, + "permanent_shutters_present": "N", + "window_transmission_details": {"u_value": 1.0, "data_source": 2, "solar_transmittance": 1.0}, + "permanent_shutters_insulated": "N" + } + ], + "schema_type": "RdSAP-Schema-21.0.1", + "uprn_source": "Energy Assessor", + "country_code": "ENG", + "main_heating": [ + {"description": {"value": "Boiler and radiators, anthracite", "language": "1"}, "energy_efficiency_rating": 3, "environmental_efficiency_rating": 1}, + {"description": {"value": "Boiler and radiators, mains gas", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4} + ], + "dwelling_type": "Mid-terrace house", + "language_code": 1, + "pressure_test": 6, + "property_type": 0, + "address_line_1": "1 Some Street", + "assessment_type": "RdSAP", + "completion_date": "2025-04-04", + "inspection_date": "2025-04-04", + "wet_rooms_count": 0, + "extensions_count": 0, + "measurement_type": 1, + "sap_flat_details": { + "level": 1, + "top_storey": "N", + "storey_count": 3, + "flat_location": 1, + "heat_loss_corridor": 2, + "unheated_corridor_length": {"value": 10, "quantity": "metres"} + }, + "total_floor_area": 55, + "transaction_type": 16, + "conservatory_type": 1, + "heated_room_count": 5, + "registration_date": "2025-04-04", + "sap_energy_source": { + "mains_gas": "Y", + "meter_type": 2, + "pv_batteries": {"pv_battery": {"battery_capacity": 5}}, + "pv_connection": 0, + "pv_battery_count": 1, + "photovoltaic_supply": {"none_or_no_details": {"percent_roof_area": 0}}, + "wind_turbines_count": 0, + "wind_turbine_details": {"hub_height": 0, "rotor_diameter": 0}, + "gas_smart_meter_present": "false", + "is_dwelling_export_capable": "false", + "wind_turbines_terrain_type": 4, + "electricity_smart_meter_present": "true" + }, + "secondary_heating": { + "description": {"value": "Room heaters, electric", "language": "1"}, + "energy_efficiency_rating": 0, + "environmental_efficiency_rating": 0 + }, + "sap_building_parts": [ + { + "identifier": "Main Dwelling", + "wall_dry_lined": "N", + "floor_heat_loss": 7, + "sap_room_in_roof": {"floor_area": 100, "construction_age_band": "B"}, + "roof_construction": 4, + "wall_construction": 4, + "building_part_number": 1, + "sap_floor_dimensions": [ + { + "floor": 0, + "room_height": {"value": 2.45, "quantity": "metres"}, + "floor_insulation": 1, + "total_floor_area": {"value": 45.82, "quantity": "square metres"}, + "party_wall_length": {"value": 7.9, "quantity": "metres"}, + "floor_construction": 1, + "heat_loss_perimeter": {"value": 19.5, "quantity": "metres"} + } + ], + "wall_insulation_type": 2, + "construction_age_band": "M", + "sap_alternative_wall_1": {"wall_area": 10.4, "wall_dry_lined": "N", "wall_construction": 4, "wall_insulation_type": 2, "wall_thickness_measured": "N"}, + "sap_alternative_wall_2": {"wall_area": 10.8, "wall_dry_lined": "N", "wall_construction": 4, "wall_insulation_type": 2, "wall_thickness_measured": "N"}, + "party_wall_construction": 0, + "wall_thickness_measured": "N", + "roof_insulation_location": 2, + "roof_insulation_thickness": "200mm", + "wall_insulation_thickness": "NI", + "floor_insulation_thickness": "NI" + } + ], + "open_chimneys_count": 1, + "solar_water_heating": "N", + "habitable_room_count": 5, + "heating_cost_current": 365.98, + "insulated_door_count": 2, + "co2_emissions_current": 2.4, + "energy_rating_average": 60, + "energy_rating_current": 50, + "lighting_cost_current": 123.45, + "main_heating_controls": [ + {"description": {"value": "Programmer, room thermostat and TRVs", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}, + {"description": {"value": "Time and temperature zone control", "language": "1"}, "energy_efficiency_rating": 5, "environmental_efficiency_rating": 5} + ], + "has_hot_water_cylinder": "true", + "heating_cost_potential": 250.34, + "hot_water_cost_current": 200.4, + "insulated_door_u_value": 3, + "mechanical_ventilation": 6, + "percent_draughtproofed": 100, + "suggested_improvements": [ + { + "sequence": 1, + "typical_saving": 139, + "indicative_cost": "£220 - £250", + "improvement_type": "G", + "improvement_details": {"improvement_number": 66}, + "improvement_category": 5, + "energy_performance_rating": 70, + "environmental_impact_rating": 70 + } + ], + "co2_emissions_potential": 1.4, + "energy_rating_potential": 72, + "lighting_cost_potential": 84.23, + "schema_version_original": "SAP-19.0", + "hot_water_cost_potential": 180.43, + "renewable_heat_incentive": { + "water_heating": 2285, + "impact_of_loft_insulation": -2114, + "impact_of_cavity_insulation": -122, + "impact_of_solid_wall_insulation": -3560, + "space_heating_existing_dwelling": 13120 + }, + "draughtproofed_door_count": 1, + "mechanical_vent_duct_type": 3, + "energy_consumption_current": 230, + "has_fixed_air_conditioning": "false", + "multiple_glazed_proportion": 100, + "calculation_software_version": "13.05r16", + "energy_consumption_potential": 88, + "environmental_impact_current": 52, + "windows_transmission_details": {"u_value": 2, "data_source": 2, "solar_transmittance": 0.72}, + "cfl_fixed_lighting_bulbs_count": 5, + "current_energy_efficiency_band": "E", + "environmental_impact_potential": 74, + "led_fixed_lighting_bulbs_count": 10, + "mechanical_vent_duct_placement": 2, + "mechanical_vent_duct_insulation": 2, + "potential_energy_efficiency_band": "C", + "pressure_test_certificate_number": 0, + "mechanical_ventilation_index_number": 12, + "co2_emissions_current_per_floor_area": 20, + "low_energy_fixed_lighting_bulbs_count": 16, + "mechanical_vent_duct_insulation_level": 2, + "mechanical_vent_measured_installation": "false", + "incandescent_fixed_lighting_bulbs_count": 0 +} diff --git a/datatypes/epc/schema/tests/helpers.py b/datatypes/epc/schema/tests/helpers.py new file mode 100644 index 00000000..677bd8b7 --- /dev/null +++ b/datatypes/epc/schema/tests/helpers.py @@ -0,0 +1,73 @@ +import dataclasses +import typing +from typing import Any, Dict, Type, TypeVar + +T = TypeVar("T") + + +def from_dict(cls: Type[T], data: Dict[str, Any]) -> T: + """ + Recursively convert a plain dict (e.g. from json.loads) into the given + dataclass type, using the field type hints to convert nested structures. + + Handles: + - Nested dataclasses + - List[SomeDataclass] + - Optional[X] / Union[X, None] + - Union[DataclassType, primitive] (e.g. Union[Measurement, int]) + - Primitive pass-through for Union[str, int] etc. + """ + return _from_dict_impl(cls, data) # type: ignore[return-value] + + +def _from_dict_impl(cls: Any, data: Any) -> Any: + hints = typing.get_type_hints(cls) + kwargs: Dict[str, Any] = {} + + for field in dataclasses.fields(cls): # type: ignore[arg-type] + has_default = ( + field.default is not dataclasses.MISSING + or field.default_factory is not dataclasses.MISSING # type: ignore[misc] + ) + if field.name not in data: + if has_default: + continue + raise ValueError(f"{cls.__name__}: missing required field '{field.name}'") + + kwargs[field.name] = _coerce(data[field.name], hints[field.name]) + + return cls(**kwargs) + + +def _coerce(value: Any, hint: Any) -> Any: + if value is None: + return None + + origin = typing.get_origin(hint) + args = typing.get_args(hint) + + # Union (includes Optional[X] which is Union[X, None]) + if origin is typing.Union: + if value is None: + return None + non_none_args = [a for a in args if a is not type(None)] + if len(non_none_args) == 1: + # Optional[X] — recurse so List[X] and nested dataclasses are handled + return _coerce(value, non_none_args[0]) + # Multi-type Union (e.g. Union[Measurement, int]): try dataclasses first + for arg in non_none_args: + if dataclasses.is_dataclass(arg) and isinstance(value, dict): + return _from_dict_impl(arg, value) + # All remaining args are primitives — return value as-is + return value + + # List[X] + if origin is list: + item_hint = args[0] + return [_coerce(item, item_hint) for item in value] + + # Plain dataclass + if dataclasses.is_dataclass(hint) and isinstance(value, dict): + return _from_dict_impl(hint, value) + + return value diff --git a/datatypes/epc/schema/tests/test_schema_loading.py b/datatypes/epc/schema/tests/test_schema_loading.py new file mode 100644 index 00000000..dc80ddc0 --- /dev/null +++ b/datatypes/epc/schema/tests/test_schema_loading.py @@ -0,0 +1,380 @@ +import json +import os +from typing import Any, Dict + +import pytest + +from datatypes.epc.schema import ( + RdSapSchema17_0, + RdSapSchema17_1, + RdSapSchema18_0, + RdSapSchema19_0, + RdSapSchema20_0_0, + RdSapSchema21_0_0, + RdSapSchema21_0_1, +) +from datatypes.epc.schema.common import CostAmount, DescriptionV1, Measurement +from datatypes.epc.schema.tests.helpers import from_dict + +FIXTURES = os.path.join(os.path.dirname(__file__), "fixtures") + + +def load(filename: str) -> Dict[str, Any]: + with open(os.path.join(FIXTURES, filename)) as f: + return json.load(f) # type: ignore[no-any-return] + + +class TestRdSapSchema17_0: + + @pytest.fixture + def epc(self) -> RdSapSchema17_0: + return from_dict(RdSapSchema17_0, load("17_0.json")) + + def test_schema_type(self, epc: RdSapSchema17_0) -> None: + assert epc.schema_type == "RdSAP-Schema-17.0" + + def test_uprn(self, epc: RdSapSchema17_0) -> None: + assert epc.uprn == 12457 + + def test_description_is_localised_object(self, epc: RdSapSchema17_0) -> None: + assert isinstance(epc.roofs[0].description, DescriptionV1) + assert epc.roofs[0].description.value == "(another dwelling above)" + assert epc.roofs[0].description.language == "1" + + def test_dwelling_type_is_localised_object(self, epc: RdSapSchema17_0) -> None: + assert isinstance(epc.dwelling_type, DescriptionV1) + assert epc.dwelling_type.value == "Mid-floor flat" + + def test_costs_are_cost_amount_objects(self, epc: RdSapSchema17_0) -> None: + assert isinstance(epc.heating_cost_current, CostAmount) + assert epc.heating_cost_current.value == 214 + assert epc.heating_cost_current.currency == "GBP" + + def test_suggested_improvement_saving_is_cost_amount(self, epc: RdSapSchema17_0) -> None: + assert isinstance(epc.suggested_improvements[0].typical_saving, CostAmount) + assert epc.suggested_improvements[0].typical_saving.value == 158 + + def test_flat_details_populated(self, epc: RdSapSchema17_0) -> None: + assert epc.sap_flat_details is not None + assert epc.sap_flat_details.level == 2 + + def test_floor_dimensions_use_measurement(self, epc: RdSapSchema17_0) -> None: + dim = epc.sap_building_parts[0].sap_floor_dimensions[0] + assert isinstance(dim.room_height, Measurement) + assert dim.room_height.value == 2.4 + assert dim.room_height.quantity == "metres" + + def test_alternative_improvements_present(self, epc: RdSapSchema17_0) -> None: + assert epc.alternative_improvements is not None + assert len(epc.alternative_improvements) == 1 + assert epc.alternative_improvements[0].improvement_type == "J2" + + def test_renewable_heat_incentive(self, epc: RdSapSchema17_0) -> None: + assert epc.renewable_heat_incentive.water_heating == 4818 + assert epc.renewable_heat_incentive.space_heating_existing_dwelling == 2415 + assert epc.renewable_heat_incentive.impact_of_loft_insulation is None + + def test_glazing_gap_is_string(self, epc: RdSapSchema17_0) -> None: + assert epc.glazing_gap == "16+" + + def test_lzc_energy_sources(self, epc: RdSapSchema17_0) -> None: + assert epc.lzc_energy_sources == [11] + + +class TestRdSapSchema17_1: + + @pytest.fixture + def epc(self) -> RdSapSchema17_1: + return from_dict(RdSapSchema17_1, load("17_1.json")) + + def test_schema_type(self, epc: RdSapSchema17_1) -> None: + assert epc.schema_type == "RdSAP-Schema-17.1" + + def test_description_is_localised_object(self, epc: RdSapSchema17_1) -> None: + assert isinstance(epc.roofs[0].description, DescriptionV1) + assert epc.roofs[0].description.value == "Pitched, 100 mm loft insulation" + + def test_address_line_2(self, epc: RdSapSchema17_1) -> None: + assert epc.address_line_2 == "Lower Moria" + + def test_cylinder_thermostat(self, epc: RdSapSchema17_1) -> None: + assert epc.sap_heating.cylinder_thermostat == "Y" + + def test_cylinder_insulation_thickness(self, epc: RdSapSchema17_1) -> None: + assert epc.sap_heating.cylinder_insulation_thickness == 12 + + def test_boiler_flue_type_in_heating_detail(self, epc: RdSapSchema17_1) -> None: + detail = epc.sap_heating.main_heating_details[0] + assert detail.boiler_flue_type == 1 + assert detail.main_heating_index_number == 9049 + + def test_extension_party_wall_length_is_int(self, epc: RdSapSchema17_1) -> None: + # Extension floor dimension has party_wall_length as a plain int (0) + extension = epc.sap_building_parts[1] + assert extension.identifier == "Extension 1" + dim = extension.sap_floor_dimensions[0] + assert dim.party_wall_length == 0 + + def test_extension_roof_insulation_thickness_is_int(self, epc: RdSapSchema17_1) -> None: + extension = epc.sap_building_parts[1] + assert extension.roof_insulation_thickness == 0 + + def test_renewable_heat_incentive_has_loft_impact(self, epc: RdSapSchema17_1) -> None: + assert epc.renewable_heat_incentive.impact_of_loft_insulation == -565 + + def test_multiple_roofs(self, epc: RdSapSchema17_1) -> None: + assert len(epc.roofs) == 2 + + +class TestRdSapSchema18_0: + + @pytest.fixture + def epc(self) -> RdSapSchema18_0: + return from_dict(RdSapSchema18_0, load("18_0.json")) + + def test_schema_type(self, epc: RdSapSchema18_0) -> None: + assert epc.schema_type == "RdSAP-Schema-18.0" + + def test_glazing_gap_is_integer(self, epc: RdSapSchema18_0) -> None: + assert epc.glazing_gap == 12 + assert isinstance(epc.glazing_gap, int) + + def test_room_in_roof_present(self, epc: RdSapSchema18_0) -> None: + main = epc.sap_building_parts[0] + assert main.sap_room_in_roof is not None + assert main.sap_room_in_roof.insulation == "AB" + assert main.sap_room_in_roof.construction_age_band == "G" + + def test_room_in_roof_floor_area_is_measurement(self, epc: RdSapSchema18_0) -> None: + room_in_roof = epc.sap_building_parts[0].sap_room_in_roof + assert room_in_roof is not None + assert isinstance(room_in_roof.floor_area, Measurement) + assert room_in_roof.floor_area.value == 9.0 + assert room_in_roof.floor_area.quantity == "square metres" + + def test_flat_roof_insulation_on_extension(self, epc: RdSapSchema18_0) -> None: + extension = epc.sap_building_parts[1] + assert extension.flat_roof_insulation_thickness == "NI" + + def test_description_is_localised_object(self, epc: RdSapSchema18_0) -> None: + assert isinstance(epc.walls[0].description, DescriptionV1) + + def test_three_roofs(self, epc: RdSapSchema18_0) -> None: + assert len(epc.roofs) == 3 + + def test_renewable_heat_incentive_has_solid_wall_impact(self, epc: RdSapSchema18_0) -> None: + assert epc.renewable_heat_incentive.impact_of_solid_wall_insulation == -1864 + + +class TestRdSapSchema19_0: + + @pytest.fixture + def epc(self) -> RdSapSchema19_0: + return from_dict(RdSapSchema19_0, load("19_0.json")) + + def test_schema_type(self, epc: RdSapSchema19_0) -> None: + assert epc.schema_type == "RdSAP-Schema-19.0" + + def test_windows_transmission_details(self, epc: RdSapSchema19_0) -> None: + wtd = epc.windows_transmission_details + assert wtd.u_value == 3.1 + assert wtd.data_source == 2 + assert wtd.solar_transmittance == 0.76 + + def test_description_is_localised_object(self, epc: RdSapSchema19_0) -> None: + assert isinstance(epc.roofs[0].description, DescriptionV1) + assert epc.roofs[0].description.value == "Pitched, 150 mm loft insulation" + + def test_glazing_gap_absent(self, epc: RdSapSchema19_0) -> None: + assert epc.glazing_gap is None + + def test_secondary_heating_type(self, epc: RdSapSchema19_0) -> None: + assert epc.sap_heating.secondary_heating_type == 631 + + def test_multiple_lzc_sources(self, epc: RdSapSchema19_0) -> None: + assert epc.lzc_energy_sources == [11, 9] + + def test_floor_insulation_thickness_on_building_part(self, epc: RdSapSchema19_0) -> None: + assert epc.sap_building_parts[0].floor_insulation_thickness == "NI" + + +class TestRdSapSchema20_0_0: + + @pytest.fixture + def epc(self) -> RdSapSchema20_0_0: + return from_dict(RdSapSchema20_0_0, load("20_0_0.json")) + + def test_schema_type(self, epc: RdSapSchema20_0_0) -> None: + assert epc.schema_type == "RdSAP-Schema-20.0.0" + + def test_description_is_plain_string(self, epc: RdSapSchema20_0_0) -> None: + assert isinstance(epc.roofs[0].description, str) + assert epc.roofs[0].description == "Pitched, 25 mm loft insulation" + + def test_dwelling_type_is_plain_string(self, epc: RdSapSchema20_0_0) -> None: + assert isinstance(epc.dwelling_type, str) + assert epc.dwelling_type == "Mid-terrace house" + + def test_costs_are_floats(self, epc: RdSapSchema20_0_0) -> None: + assert isinstance(epc.heating_cost_current, float) + assert epc.heating_cost_current == pytest.approx(365.98) + + def test_suggested_improvement_saving_is_numeric(self, epc: RdSapSchema20_0_0) -> None: + assert epc.suggested_improvements[0].typical_saving == 360 + + def test_indicative_cost_can_be_int(self, epc: RdSapSchema20_0_0) -> None: + # Second improvement has indicative_cost as a plain integer + assert epc.suggested_improvements[1].indicative_cost == 2000 + + def test_improvement_details_with_texts(self, epc: RdSapSchema20_0_0) -> None: + # Third improvement uses improvement_texts instead of improvement_number + third = epc.suggested_improvements[2] + assert third.improvement_details.improvement_number is None + assert third.improvement_details.improvement_texts is not None + assert third.improvement_details.improvement_texts.improvement_summary == "An improvement summary" + + def test_sap_windows_present(self, epc: RdSapSchema20_0_0) -> None: + assert len(epc.sap_windows) == 2 + assert epc.sap_windows[0].window_area == pytest.approx(200.1) + assert epc.sap_windows[0].glazing_type == 1 + + def test_addendum(self, epc: RdSapSchema20_0_0) -> None: + assert epc.addendum is not None + assert epc.addendum.stone_walls == "true" + assert epc.addendum.addendum_numbers == [1, 8] + + def test_flat_details_has_storey_count(self, epc: RdSapSchema20_0_0) -> None: + assert epc.sap_flat_details is not None + assert epc.sap_flat_details.storey_count == 3 + assert epc.sap_flat_details.unheated_corridor_length == 10 + + def test_room_in_roof_floor_area_is_plain_number(self, epc: RdSapSchema20_0_0) -> None: + room_in_roof = epc.sap_building_parts[0].sap_room_in_roof + assert room_in_roof is not None + assert room_in_roof.floor_area == 100 + assert not isinstance(room_in_roof.floor_area, Measurement) + + def test_renewable_heat_incentive_has_cavity_impact(self, epc: RdSapSchema20_0_0) -> None: + assert epc.renewable_heat_incentive.impact_of_cavity_insulation == -122 + + def test_multiple_glazed_proportion_nr(self, epc: RdSapSchema20_0_0) -> None: + assert epc.multiple_glazed_proportion_nr == "NR" + + +class TestRdSapSchema21_0_0: + + @pytest.fixture + def epc(self) -> RdSapSchema21_0_0: + return from_dict(RdSapSchema21_0_0, load("21_0_0.json")) + + def test_schema_type(self, epc: RdSapSchema21_0_0) -> None: + assert epc.schema_type == "RdSAP-Schema-21.0.0" + + def test_description_is_plain_string(self, epc: RdSapSchema21_0_0) -> None: + assert isinstance(epc.roofs[0].description, str) + + def test_pressure_test(self, epc: RdSapSchema21_0_0) -> None: + assert epc.pressure_test == 6 + + def test_wet_rooms_count(self, epc: RdSapSchema21_0_0) -> None: + assert epc.wet_rooms_count == 0 + + def test_instantaneous_wwhrs_uses_index_numbers(self, epc: RdSapSchema21_0_0) -> None: + wwhrs = epc.sap_heating.instantaneous_wwhrs + assert wwhrs.wwhrs_index_number1 == 1 + assert wwhrs.wwhrs_index_number2 == 2 + + def test_shower_outlets(self, epc: RdSapSchema21_0_0) -> None: + outlets = epc.sap_heating.shower_outlets + assert outlets is not None + assert outlets.shower_outlet.shower_wwhrs == 1 + assert outlets.shower_outlet.shower_outlet_type == 1 + + def test_boiler_ignition_type(self, epc: RdSapSchema21_0_0) -> None: + assert epc.sap_heating.main_heating_details[0].boiler_ignition_type == 1 + + def test_smart_meters(self, epc: RdSapSchema21_0_0) -> None: + src = epc.sap_energy_source + assert src.electricity_smart_meter_present == "true" + assert src.gas_smart_meter_present == "false" + + def test_pv_batteries(self, epc: RdSapSchema21_0_0) -> None: + assert epc.sap_energy_source.pv_batteries is not None + assert epc.sap_energy_source.pv_batteries.pv_battery.battery_capacity == 5 + + def test_alternative_walls(self, epc: RdSapSchema21_0_0) -> None: + part = epc.sap_building_parts[0] + assert part.sap_alternative_wall_1 is not None + assert part.sap_alternative_wall_1.wall_area == pytest.approx(10.4) + assert part.sap_alternative_wall_2 is not None + assert part.sap_alternative_wall_2.wall_area == pytest.approx(10.8) + + def test_detailed_sap_windows(self, epc: RdSapSchema21_0_0) -> None: + win = epc.sap_windows[0] + assert win.glazing_gap == 6 + assert win.window_width == pytest.approx(1.2) + assert win.window_transmission_details.u_value == pytest.approx(1.0) + + def test_mechanical_vent_fields(self, epc: RdSapSchema21_0_0) -> None: + assert epc.mechanical_vent_duct_type == 3 + assert epc.mechanical_ventilation_index_number == 12 + + def test_lighting_bulb_counts(self, epc: RdSapSchema21_0_0) -> None: + assert epc.led_fixed_lighting_bulbs_count == 10 + assert epc.cfl_fixed_lighting_bulbs_count == 5 + assert epc.incandescent_fixed_lighting_bulbs_count == 5 + + def test_open_chimneys_count(self, epc: RdSapSchema21_0_0) -> None: + assert epc.open_chimneys_count == 1 + + def test_room_in_roof_has_no_insulation_field(self, epc: RdSapSchema21_0_0) -> None: + room_in_roof = epc.sap_building_parts[0].sap_room_in_roof + assert room_in_roof is not None + assert room_in_roof.construction_age_band == "B" + assert not hasattr(room_in_roof, "insulation") + + +class TestRdSapSchema21_0_1: + + @pytest.fixture + def epc(self) -> RdSapSchema21_0_1: + return from_dict(RdSapSchema21_0_1, load("21_0_1.json")) + + def test_schema_type(self, epc: RdSapSchema21_0_1) -> None: + assert epc.schema_type == "RdSAP-Schema-21.0.1" + + def test_description_reverts_to_localised_object(self, epc: RdSapSchema21_0_1) -> None: + # Descriptions on energy elements revert to DescriptionV1 in 21.0.1 + assert isinstance(epc.roofs[0].description, DescriptionV1) + assert epc.roofs[0].description.value == "Pitched, 25 mm loft insulation" + + def test_main_heating_description_is_localised_object(self, epc: RdSapSchema21_0_1) -> None: + assert isinstance(epc.main_heating[0].description, DescriptionV1) + assert epc.main_heating[0].description.value == "Boiler and radiators, anthracite" + + def test_dwelling_type_remains_plain_string(self, epc: RdSapSchema21_0_1) -> None: + # dwelling_type does NOT revert — stays as plain str in 21.0.1 + assert isinstance(epc.dwelling_type, str) + assert epc.dwelling_type == "Mid-terrace house" + + def test_unheated_corridor_length_is_measurement(self, epc: RdSapSchema21_0_1) -> None: + # Changed from plain int (21.0.0) to Measurement object in 21.0.1 + assert epc.sap_flat_details is not None + corridor_length = epc.sap_flat_details.unheated_corridor_length + assert isinstance(corridor_length, Measurement) + assert corridor_length.value == 10 + assert corridor_length.quantity == "metres" + + def test_completion_date(self, epc: RdSapSchema21_0_1) -> None: + assert epc.completion_date == "2025-04-04" + + def test_addendum_numbers(self, epc: RdSapSchema21_0_1) -> None: + assert epc.addendum is not None + assert epc.addendum.addendum_numbers == [1, 13] + + def test_pv_battery_capacity(self, epc: RdSapSchema21_0_1) -> None: + assert epc.sap_energy_source.pv_batteries is not None + assert epc.sap_energy_source.pv_batteries.pv_battery.battery_capacity == 5 + + def test_incandescent_bulb_count(self, epc: RdSapSchema21_0_1) -> None: + assert epc.incandescent_fixed_lighting_bulbs_count == 0 diff --git a/etl/bill_savings/KwhData.py b/etl/bill_savings/KwhData.py index 266f4b72..30e11698 100644 --- a/etl/bill_savings/KwhData.py +++ b/etl/bill_savings/KwhData.py @@ -203,7 +203,7 @@ class KwhData: # TODO: New is a temporary parameter, which will transform the epc descriptions to their transformed features # in anticipation of the new model - data["lodgement-date"] = pd.to_datetime(data["lodgement-date"]) + data["lodgement-date"] = pd.to_datetime(data["lodgement-date"], format="mixed", errors="coerce") data["lodgement-year"] = data["lodgement-date"].dt.year data["lodgement-month"] = data["lodgement-date"].dt.month @@ -331,8 +331,8 @@ class KwhData: def prepare_epc(self, input_properties: list[Property]): scoring_data = pd.DataFrame([self._prepare_epc(p) for p in input_properties]) - scoring_data["lodgement-year"] = pd.to_datetime(scoring_data["lodgement-date"]).dt.year - scoring_data["lodgement-month"] = pd.to_datetime(scoring_data["lodgement-date"]).dt.month + scoring_data["lodgement-year"] = pd.to_datetime(scoring_data["lodgement-date"], format="mixed").dt.year + scoring_data["lodgement-month"] = pd.to_datetime(scoring_data["lodgement-date"], format="mixed").dt.month scoring_data["id"] = scoring_data["uprn"].copy() diff --git a/etl/epc/Record.py b/etl/epc/Record.py index defe13f4..8dbb5ba5 100644 --- a/etl/epc/Record.py +++ b/etl/epc/Record.py @@ -580,7 +580,22 @@ class EPCRecord: if existing is not None and v is not None and abs(existing - v) > 1: # 1m tolerance self.landlord_differences[k] = v else: - if v != self._prepared_epc.get(k) and (not pd.isnull(v)) and (not pd.isnull(self._prepared_epc.get(k))): + + # Check if something has been cleaned. We want to avoid triggering re-baselining if we cleaned + # a value. In the address meta, it will possibly contain the original value, so we'd pick up a + # diference if the original value was something to be cleaned, we clean that value and then end up + # comparing the original value to the new clean one + cleaned_value = self._prepared_epc.get(k) + original_value = self.original_epc.get(k.replace("_", "-")) + + # We check if the value has been cleaned + if cleaned_value != original_value: + # The thing we want to compare against, is the original value + compare_to = original_value + else: + compare_to = cleaned_value + + if v != compare_to and (not pd.isnull(v)) and (not pd.isnull(self._prepared_epc.get(k))): self.landlord_differences[k] = v self._prepared_epc.update(self.landlord_differences) diff --git a/etl/find_my_epc/RetrieveFindMyEpc.py b/etl/find_my_epc/RetrieveFindMyEpc.py index 392e6aaa..e6e4e5fd 100644 --- a/etl/find_my_epc/RetrieveFindMyEpc.py +++ b/etl/find_my_epc/RetrieveFindMyEpc.py @@ -472,8 +472,8 @@ class RetrieveFindMyEpc: address_response = requests.get(chosen_epc, headers=self.HEADERS) epc_page_source = address_response.text address_res = BeautifulSoup(address_response.text, features="html.parser") - elif self.rrn: - epc_certificate = self.rrn + elif self.rrn or rrn: + epc_certificate = self.rrn if self.rrn else rrn chosen_epc = f"{self.BASE_ENERGY_URL}/energy-certificate/{epc_certificate}" address_response = requests.get(chosen_epc, headers=self.HEADERS) epc_page_source = address_response.text @@ -497,7 +497,7 @@ class RetrieveFindMyEpc: current_sap = int(current_rating.split(' ')[-1]) if self.sap_rating: - if current_sap != self.sap_rating: + if current_sap != self.sap_rating and not rrn: # This means we likely have the wrong data. If we are in this scenario, we return nothing return { "epc_certificate": None, @@ -734,6 +734,8 @@ class RetrieveFindMyEpc: "Step 1:": [], "Step 2:": [], 'Step 3:': [], + 'Step 4:': [], + 'Step 5:': [], "Biomass stove with boiler": [], "Replace boiler with biomass boiler": [], "Heating controls (room thermostat and thermostatic radiator valves)": [ @@ -776,6 +778,12 @@ class RetrieveFindMyEpc: 'Air or ground source heat pump': ["air_source_heat_pump"], "Add PV Battery": ["solar_pv_battery"], "Add PV diverter": ["solar_pv_diverter"], # Don't have a recommendation yet + "Draughtproof single-glazed windows": ["double_glazing"], + "Upgrade heating controls": ["roomstat_programmer_trvs", "time_temperature_zone_control"], + "Low energy lighting recommendation": ["low_energy_lighting"], + "Install cavity wall insulation": ["cavity_wall_insulation"], + "Install solar water heating": ["solar_water_heating"], + 'Install photovoltaics, 25% of roof area': ["solar_pv"], } survey = True @@ -820,6 +828,10 @@ class RetrieveFindMyEpc: "recommendations": find_epc_data.get("recommendations", []), } + lodgment_date = find_epc_data.get("Date of certificate", None) + if not pd.isnull(lodgment_date): + lodgment_date = str(datetime.strptime(str(lodgment_date), "%d %B %Y")) + # We need to add the patch information patch = { "current-energy-rating": find_epc_data.get("current_epc_rating"), @@ -827,6 +839,7 @@ class RetrieveFindMyEpc: "potential-energy-rating": find_epc_data.get("potential_epc_rating"), "potential-energy-efficiency": find_epc_data.get("potential_epc_efficiency"), **find_epc_data.get("epc_data", {}), + "lodgement-date": lodgment_date } page_source = { diff --git a/etl/hubspot/company_data.py b/etl/hubspot/company_data.py new file mode 100644 index 00000000..13b2ee88 --- /dev/null +++ b/etl/hubspot/company_data.py @@ -0,0 +1,6 @@ +from typing import TypedDict + + +class CompanyData(TypedDict): + hs_object_id: str + name: str diff --git a/etl/hubspot/hubspotClient.py b/etl/hubspot/hubspotClient.py index 8bbe8a63..6bdf71ed 100644 --- a/etl/hubspot/hubspotClient.py +++ b/etl/hubspot/hubspotClient.py @@ -1,6 +1,7 @@ import os +import time from enum import Enum -from typing import Optional, cast +from typing import Optional, cast, Callable, Any from hubspot.client import Client # type: ignore[reportMissingTypeStubs] from hubspot.crm.associations import ApiException # type: ignore[reportMissingTypeStubs] @@ -25,10 +26,10 @@ from hubspot.crm.associations.v4.models import ( # type: ignore[reportMissingTy ForwardPaging as AssociationsPaging, NextPage as AssociationsPagingNext, ) -from etl.hubspot.hubspotDataTodB import CompanyData, HubspotDataToDb from backend.app.config import get_settings +from etl.hubspot.company_data import CompanyData from utils.logger import setup_logger import mimetypes @@ -83,6 +84,34 @@ class HubspotClient: # Sorry - not sorry but enjoy, Past Junte 13/03/2026 # self.client + def _call_with_retry(self, fn: Callable[[], Any], max_retries: int = 2) -> Any: + """ + Call fn(), retrying up to max_retries times on 429 rate-limit errors. + Waits the minimal amount: the remaining interval window reported by HubSpot headers. + Falls back to the full interval (10s) if headers are absent. + + Note: each HubSpot sub-module (deals, companies, etc.) ships its own ApiException + class with no shared base beyond Exception, so we detect 429s via duck-typing. + """ + for attempt in range(max_retries + 1): + try: + return fn() + except Exception as e: + status = getattr(e, "status", None) + if status != 429 or attempt == max_retries: + raise + headers = getattr(e, "headers", None) or {} + interval_ms = int( + headers.get("x-hubspot-ratelimit-interval-milliseconds", 10000) + ) + wait_s = interval_ms / 1000.0 + self.logger.warning( + f"HubSpot 429 (attempt {attempt + 1}/{max_retries}), " + f"waiting {wait_s:.1f}s before retry." + ) + time.sleep(wait_s) + raise RuntimeError("Unreachable") # pragma: no cover + def get_deal_ids_from_company(self, company_id: str) -> list[str]: associations_api: AssociationsBasicApi = ( # type: ignore[reportUnknownMemberType] self.client.crm.associations.v4.basic_api # type: ignore[reportUnknownMemberType] @@ -92,12 +121,14 @@ class HubspotClient: after: Optional[str] = None while True: - response: AssociationsPageResponse = associations_api.get_page( # type: ignore[reportUnknownMemberType] - object_type="companies", - object_id=company_id, - to_object_type="deals", - limit=100, - after=after, + response: AssociationsPageResponse = self._call_with_retry( + lambda: associations_api.get_page( # type: ignore[reportUnknownMemberType] + object_type="companies", + object_id=company_id, + to_object_type="deals", + limit=100, + after=after, + ) ) results: list[AssociationsResult] = cast(list[AssociationsResult], response.results) # type: ignore[reportUnknownMemberType] @@ -127,11 +158,13 @@ class HubspotClient: associations_api: AssociationsBasicApi = self.client.crm.associations.v4.basic_api # type: ignore[reportUnknownMemberType] # Fetch associations for this specific deal only - response: AssociationsPageResponse = associations_api.get_page( # type: ignore[reportUnknownMemberType] - object_type="deals", - object_id=deal_id, - to_object_type="companies", - limit=1, # Expect only one associated company + response: AssociationsPageResponse = self._call_with_retry( + lambda: associations_api.get_page( # type: ignore[reportUnknownMemberType] + object_type="deals", + object_id=deal_id, + to_object_type="companies", + limit=1, + ) ) results: list[AssociationsResult] = cast(list[AssociationsResult], response.results) # type: ignore[reportUnknownMemberType] @@ -161,11 +194,13 @@ class HubspotClient: listings_api: ObjectsBasicApi = self.client.crm.objects.basic_api # type: ignore[reportUnknownMemberType] # works for custom objects like "listing" # Fetch associated listing(s) - response: AssociationsPageResponse = associations_api.get_page( # type: ignore[reportUnknownMemberType] - object_type="deals", - object_id=deal_id, - to_object_type="0-420", # <-- to get an listing object - limit=1, + response: AssociationsPageResponse = self._call_with_retry( + lambda: associations_api.get_page( # type: ignore[reportUnknownMemberType] + object_type="deals", + object_id=deal_id, + to_object_type="0-420", + limit=1, + ) ) results: list[AssociationsResult] = cast(list[AssociationsResult], response.results) # type: ignore[reportUnknownMemberType] @@ -178,54 +213,81 @@ class HubspotClient: self.logger.info(f"Associated listing ID for deal {deal_id}: {listing_id}") # Fetch listing details (the "listing information") - listing: HubspotObject = listings_api.get_by_id( # type: ignore[reportUnknownMemberType] - object_type="0-420", # again, must match your HubSpot object name - object_id=listing_id, - properties=[ - "national_uprn", - "domna_property_id", - "owner_property_id", - ], + listing: HubspotObject = self._call_with_retry( + lambda: listings_api.get_by_id( # type: ignore[reportUnknownMemberType] + object_type="0-420", + object_id=listing_id, + properties=[ + "national_uprn", + "domna_property_id", + "owner_property_id", + ], + ) ) listing_info: dict[str, str] = cast(dict[str, str], listing.properties) # type: ignore[reportUnknownMemberType] + listing_info["listing_id"] = listing_id self.logger.info(f"Listing info for deal {deal_id}: {listing_info}") return listing_info - def from_deal_id_get_info(self, deal_id: str) -> dict[str, str]: + def from_deal_id_get_info( + self, deal_id: str + ) -> dict[str, str]: # TODO: add dataclass for this deals_api: DealsBasicApi = self.client.crm.deals.basic_api # type: ignore[reportUnknownMemberType] - deal: HubspotObject = deals_api.get_by_id( # type: ignore[reportUnknownMemberType] - deal_id, - properties=[ - "dealname", - "dealstage", - "pipeline", - "outcome", # outcome, - "outcome_notes", # outcome notes - "project_code", - "major_condition_issue_description", - "major_condition_issue_photos", - "coordination_status__stage_1_", # Coordiantion Status (Stage 1), - "retrofit_design_status", # Retrofit Design Status - ], + deal: HubspotObject = self._call_with_retry( + lambda: deals_api.get_by_id( # type: ignore[reportUnknownMemberType] + deal_id, + properties=[ + "dealname", + "dealstage", + "pipeline", + "outcome", + "outcome_notes", + "project_code", + "major_condition_issue_description", + "major_condition_issue_photos", + "coordination_status__stage_1_", + "coordination_comments", + "retrofit_design_status", + "pashub_link", + "sharepoint_link", + "dampmould_growth", + "damp_mould_and_repairs_comments", + "pre_sap", + "coordinator", + "mtp_completion_date", + "mtp_re_model_completion_date", + "ioe_v3_completion_date", + "proposed_measures", + "approved_package", + "designer", + "design_completion_date", + "actual_measures_installed", + "installer", + "installer_handover", + "lodgement_status", + "measures_lodgement_date", + "lodgement_date", + "expected_commencement_date", + "surveyor", + "confirmed_survey_date", + "confirmed_survey_time", + "surveyed_date", + "design_type", + ], + ) ) deal_info: dict[str, str] = cast(dict[str, str], deal.properties) # type: ignore[reportUnknownMemberType] return deal_info - def get_deal_info_for_db( + def get_deal_and_company_and_listing( self, deal_id: str ) -> tuple[dict[str, str], Optional[str], Optional[dict[str, str]]]: deal: dict[str, str] = self.from_deal_id_get_info(deal_id) company: Optional[str] = self.from_deal_id_get_associated_company_id(deal_id) - - if company: - company_data: CompanyData = self.get_company_information(company) - dbloader: HubspotDataToDb = HubspotDataToDb() - dbloader.upsert_company(company_data) - listing: Optional[dict[str, str]] = self.from_deal_id_get_associated_listing( deal_id ) @@ -235,11 +297,11 @@ class HubspotClient: def get_company_information(self, company_id: str) -> CompanyData: companies_api: CompaniesBasicApi = self.client.crm.companies.basic_api # type: ignore[reportUnknownMemberType] - company: HubspotObject = companies_api.get_by_id( # type: ignore[reportUnknownMemberType] - company_id, - properties=[ - "name", - ], + company: HubspotObject = self._call_with_retry( + lambda: companies_api.get_by_id( # type: ignore[reportUnknownMemberType] + company_id, + properties=["name"], + ) ) company_info: CompanyData = company.properties # type: ignore[reportUnknownMemberType] @@ -376,8 +438,10 @@ class HubspotClient: def create_line_item_from_product(self, product_id: str, quantity: int = 1) -> str: # Fetch product mapping products_api: ProductsBasicApi = self.client.crm.products.basic_api # type: ignore[reportUnknownMemberType] - product: HubspotObject = products_api.get_by_id( # type: ignore[reportUnknownMemberType] - product_id, properties=["name", "price", "hs_price"] + product: HubspotObject = self._call_with_retry( + lambda: products_api.get_by_id( # type: ignore[reportUnknownMemberType] + product_id, properties=["name", "price", "hs_price"] + ) ) properties: dict[str, str] = cast(dict[str, str], product.properties) # type: ignore[reportUnknownMemberType] @@ -398,7 +462,9 @@ class HubspotClient: # Create line item line_items_api: LineItemsBasicApi = self.client.crm.line_items.basic_api # type: ignore[reportUnknownMemberType] - line_item: HubspotObject = line_items_api.create(line_item_input) # type: ignore[reportUnknownMemberType] + line_item: HubspotObject = self._call_with_retry( + lambda: line_items_api.create(line_item_input) # type: ignore[reportUnknownMemberType] + ) return cast(str, line_item.id) # type: ignore[reportUnknownMemberType] def associate_line_item_to_deal(self, line_item_id: str, deal_id: str) -> None: @@ -406,17 +472,19 @@ class HubspotClient: association_api: AssociationsBasicApi = self.client.crm.associations.v4.basic_api # type: ignore[reportUnknownMemberType] - association_api.create( # type: ignore[reportUnknownMemberType] - "0-3", # to object type - deal_id, # to object id - "line_items", # from object type - line_item_id, # from object id - [ - AssociationSpec( - association_category="HUBSPOT_DEFINED", - association_type_id=19, # line_item → deal - ) - ], + self._call_with_retry( + lambda: association_api.create( # type: ignore[reportUnknownMemberType] + "0-3", + deal_id, + "line_items", + line_item_id, + [ + AssociationSpec( + association_category="HUBSPOT_DEFINED", + association_type_id=19, + ) + ], + ) ) def add_product_line_item_to_deal( diff --git a/etl/hubspot/hubspotDataTodB.py b/etl/hubspot/hubspotDataTodB.py index f7f79e46..9756833b 100644 --- a/etl/hubspot/hubspotDataTodB.py +++ b/etl/hubspot/hubspotDataTodB.py @@ -1,16 +1,19 @@ -from backend.app.db.connection import db_read_session -from backend.app.db.models.organisation import Organisation, HubspotDealData +import os from sqlmodel import select from datetime import datetime, timezone -from typing import TypedDict +from typing import Dict, Optional + +from backend.app.db.models.hubspot_deal_data import HubspotDealData +from etl.hubspot.company_data import CompanyData +from etl.hubspot.hubspotClient import HubspotClient from etl.hubspot.s3_uploader import S3Uploader -import hashlib -import os +from backend.app.db.connection import db_read_session +from backend.app.db.models.organisation import Organisation +from etl.hubspot.utils import parse_hs_date +from utils.logger import setup_logger -class CompanyData(TypedDict): - hs_object_id: str - name: str +logger = setup_logger() class HubspotDataToDb: @@ -31,7 +34,7 @@ class HubspotDataToDb: records = self.read_org_table(limit) return [org.name for org in records if org.name] - def upsert_company(self, company_data: CompanyData) -> Organisation: + def upsert_organisation(self, company_data: CompanyData) -> Organisation: """Upserts a company record. Updates if hubspot_company_id exists, otherwise creates new.""" with db_read_session() as session: hubspot_id = company_data.get("hs_object_id") @@ -61,11 +64,7 @@ class HubspotDataToDb: session.commit() return record - def new_record_to_hubspot_data(self, deal_data, company, listing, hubspot_client): - print("⚠️ Deprecated — use the new interface instead.") - return self.upsert_deal(deal_data, company, listing, hubspot_client) - - def find_all_deals_with_company_id(self, company_id): + def find_all_deals_with_company_id(self, company_id: str): """Returns a list of deals for a given company_id.""" with db_read_session() as session: return ( @@ -74,7 +73,7 @@ class HubspotDataToDb: .all() ) - def find_deal_with_deal_id(self, deal_id): + def find_deal_with_deal_id(self, deal_id: str) -> Optional[HubspotDealData]: with db_read_session() as session: return ( session.query(HubspotDealData) @@ -82,144 +81,13 @@ class HubspotDataToDb: .one_or_none() ) - def _sha256(self, file_path: str) -> str: - """Compute SHA-256 checksum of a file.""" - sha256 = hashlib.sha256() - with open(file_path, "rb") as f: - for chunk in iter(lambda: f.read(8192), b""): - sha256.update(chunk) - return sha256.hexdigest() - - def update_deal_with_checks(self, deal_in_db, hubspot_client) -> bool: - """ - Checks if a deal needs updating and syncs it with HubSpot. - Also handles major_condition_issue_photos file upload to S3 with integrity check. - """ - - def soft_assert(condition, message="Assertion Failed"): - if not condition: - print(f"⚠️ Soft Assert Failed: {message}") - return False - return True - - print(f"🔍 Checking if deal needs updating (deal_id={deal_in_db.deal_id})") - - hs_deal, hs_company_id, hs_listing = hubspot_client.get_deal_info_for_db( - deal_in_db.deal_id - ) - - # Soft compare key fields - checks = [ - soft_assert( - deal_in_db.deal_id == hs_deal.get("hs_object_id"), "deal_id mismatch" - ), - soft_assert(deal_in_db.company_id == hs_company_id, "company_id mismatch"), - soft_assert( - deal_in_db.landlord_property_id == hs_listing.get("owner_property_id"), - "landlord_property_id mismatch", - ), - soft_assert( - deal_in_db.outcome == hs_deal.get("outcome"), "outcome mismatch" - ), - soft_assert( - deal_in_db.dealstage == hs_deal.get("dealstage"), "dealstage mismatch" - ), - soft_assert( - deal_in_db.dealname == hs_deal.get("dealname"), "dealname mismatch" - ), - soft_assert( - deal_in_db.project_code == hs_deal.get("project_code"), - "project_code mismatch", - ), - soft_assert( - deal_in_db.uprn == hs_listing.get("national_uprn"), "uprn mismatch" - ), - soft_assert( - deal_in_db.outcome_notes == hs_deal.get("outcome_notes"), - "outcome_notes mismatch", - ), - soft_assert( - deal_in_db.major_condition_issue_description - == hs_deal.get("major_condition_issue_description"), - "major condition description mismatch", - ), - soft_assert( - deal_in_db.major_condition_issue_photos - == hs_deal.get("major_condition_issue_photos"), - "major condition issue photos mismatch", - ), - soft_assert( - deal_in_db.coordination_status - == hs_deal.get("coordination_status__stage_1_"), - "coordination stage 1 status mismatch", - ), - soft_assert( - deal_in_db.design_status == hs_deal.get("retrofit_design_status"), - "retrofit design mismatch", - ), - ] - - # If discrepancies found, update from HubSpot - if not all(checks): - print( - f"❗ Discrepancies found for deal_id {deal_in_db.deal_id} — syncing with HubSpot." - ) - self.upsert_deal(hs_deal, hs_company_id, hs_listing, hubspot_client) - return False - - # Handle photo upload if it exists but S3 URL is missing - if ( - deal_in_db.major_condition_issue_photos - and not deal_in_db.major_condition_issue_evidence_s3_url - ): - print( - f"🖼️ Found photo for deal_id {deal_in_db.deal_id} — uploading to S3..." - ) - - photo_url = hs_deal.get("major_condition_issue_photos") - if photo_url: - try: - # Download from HubSpot using fresh URL from hs_deal (not stale DB URL) - local_file = hubspot_client.download_file_from_url(photo_url) - - # Upload to S3 - bucket = "retrofit-data-dev" - s3_url = self.s3.upload_file( - local_file, bucket, prefix="hubspot/awaabs_law_evidence/" - ) - - # Download again to verify integrity - downloaded = self.s3.download_from_url(s3_url) - if self._sha256(local_file) == self._sha256(downloaded): - print("✅ SHA256 match verified — upload successful.") - else: - print("❌ SHA256 mismatch — integrity check failed.") - raise ValueError("File integrity check failed after S3 upload.") - - # Update DB record with S3 URL - with db_read_session() as session: - db_record = session.get(HubspotDealData, deal_in_db.id) - db_record.major_condition_issue_evidence_s3_url = s3_url - session.add(db_record) - session.commit() - print( - f"✅ Updated DB with S3 URL for deal_id={deal_in_db.deal_id}" - ) - return False - except Exception as e: - print( - f"⚠️ Failed to download/upload photo for deal_id {deal_in_db.deal_id}: {e}" - ) - # Continue without the file — don't crash the entire update - else: - print(f"⚠️ Photo URL missing for deal_id {deal_in_db.deal_id}") - - else: - print(f"✅ No update or upload required for deal_id {deal_in_db.deal_id}.") - - return True - - def upsert_deal(self, deal_data, company, listing, hubspot_client): + def upsert_deal( + self, + deal_data: Dict[str, str], + company: Optional[str], + listing: Optional[dict[str, str]], + hubspot_client: HubspotClient, + ): """ Inserts or updates a deal record. Also uploads photos if present and adds S3 URL. @@ -233,63 +101,10 @@ class HubspotDataToDb: existing = session.exec(statement).first() if existing: + self._handle_existing_photo_upload(existing, hubspot_client) + print(f"🔄 Updating existing deal (deal_id={deal_id})") - - for attr, value in { - "dealname": deal_data.get("dealname"), - "dealstage": deal_data.get("dealstage"), - "landlord_property_id": listing.get("owner_property_id"), - "uprn": listing.get("national_uprn"), - "outcome": deal_data.get("outcome"), - "outcome_notes": deal_data.get("outcome_notes"), - "project_code": deal_data.get("project_code"), - "company_id": company, - "major_condition_issue_description": deal_data.get( - "major_condition_issue_description" - ), - "major_condition_issue_photos": deal_data.get( - "major_condition_issue_photos" - ), - "major_condition_issue_description": deal_data.get( - "major_condition_issue_description" - ), - "major_condition_issue_photos": deal_data.get( - "major_condition_issue_photos" - ), - "coordination_status": deal_data.get( - "coordination_status__stage_1_" - ), - "design_status": deal_data.get("retrofit_design_status"), - }.items(): - setattr(existing, attr, value or getattr(existing, attr)) - - # Upload if photo exists but S3 link missing - if ( - existing.major_condition_issue_photos - and not existing.major_condition_issue_evidence_s3_url - ): - # Fetch fresh URL from HubSpot instead of using potentially expired stored URL - fresh_deal = hubspot_client.from_deal_id_get_info(existing.deal_id) - photo_url = fresh_deal.get("major_condition_issue_photos") - - if photo_url: - try: - local_file = hubspot_client.download_file_from_url( - photo_url - ) - s3_url = self.s3.upload_file( - local_file, - "retrofit-data-dev", - prefix="hubspot/awaabs_law_evidence/", - ) - existing.major_condition_issue_evidence_s3_url = s3_url - except Exception as e: - print( - f"⚠️ Failed to download photo for deal_id {existing.deal_id}: {e}" - ) - # Continue without the file — don't crash the update - else: - print(f"⚠️ Photo URL missing for deal_id {existing.deal_id}") + self._update_existing_deal(existing, deal_data, listing, company) session.add(existing) session.commit() @@ -298,45 +113,213 @@ class HubspotDataToDb: else: print(f"🆕 Inserting new deal (deal_id={deal_id})") - new_record = HubspotDealData( - deal_id=deal_id, - dealname=deal_data.get("dealname"), - dealstage=deal_data.get("dealstage"), - landlord_property_id=listing.get("owner_property_id"), - uprn=listing.get("national_uprn"), - outcome=deal_data.get("outcome"), - outcome_notes=deal_data.get("outcome_notes"), - project_code=deal_data.get("project_code"), - company_id=company, - major_condition_issue_description=deal_data.get( - "major_condition_issue_description" - ), - major_condition_issue_photos=deal_data.get( - "major_condition_issue_photos" - ), - coordination_status=deal_data.get("coordination_status__stage_1_"), - design_status=deal_data.get("retrofit_design_status"), + new_record: HubspotDealData = self._build_new_deal( + deal_id, deal_data, listing, company ) # Handle upload at insert time - if new_record.major_condition_issue_photos: - try: - local_file = hubspot_client.download_file_from_url( - new_record.major_condition_issue_photos - ) - s3_url = self.s3.upload_file( - local_file, - "retrofit-data-dev", - prefix="hubspot/awaabs_law_evidence/", - ) - new_record.major_condition_issue_evidence_s3_url = s3_url - except Exception as e: - print( - f"⚠️ Failed to download photo for deal_id {new_record.deal_id}: {e}" - ) - # Continue without the file — don't crash the insert + self._handle_new_photo_upload(new_record, hubspot_client) session.add(new_record) session.commit() session.refresh(new_record) return new_record + + def _update_existing_deal( + self, + existing: HubspotDealData, + deal_data: Dict[str, str], + listing: Optional[dict[str, str]], + company: Optional[str], + ): + for attr, value in { + "dealname": deal_data.get("dealname"), + "dealstage": deal_data.get("dealstage"), + "listing_id": listing.get("listing_id", None) if listing else None, + "landlord_property_id": ( + listing.get("owner_property_id", None) if listing else None + ), + "uprn": listing.get("national_uprn", None) if listing else None, + "outcome": deal_data.get("outcome"), + "outcome_notes": deal_data.get("outcome_notes"), + "project_code": deal_data.get("project_code"), + "company_id": company, + "major_condition_issue_description": deal_data.get( + "major_condition_issue_description" + ), + "major_condition_issue_photos": deal_data.get( + "major_condition_issue_photos" + ), + "coordination_status": deal_data.get("coordination_status__stage_1_"), + "design_status": deal_data.get("retrofit_design_status"), + "coordination_comments": deal_data.get("coordination_comments"), + "pashub_link": deal_data.get("pashub_link"), + "sharepoint_link": deal_data.get("sharepoint_link"), + "dampmould_growth": deal_data.get("dampmould_growth"), + "damp_mould_and_repairs_comments": deal_data.get( + "damp_mould_and_repairs_comments" + ), + "pre_sap": deal_data.get("pre_sap"), + "coordinator": deal_data.get("coordinator"), + "mtp_completion_date": parse_hs_date(deal_data.get("mtp_completion_date")), + "mtp_re_model_completion_date": parse_hs_date( + deal_data.get("mtp_re_model_completion_date") + ), + "ioe_v3_completion_date": parse_hs_date( + deal_data.get("ioe_v3_completion_date") + ), + "proposed_measures": deal_data.get("proposed_measures"), + "approved_package": deal_data.get("approved_package"), + "designer": deal_data.get("designer"), + "design_completion_date": parse_hs_date( + deal_data.get("design_completion_date") + ), + "actual_measures_installed": deal_data.get("actual_measures_installed"), + "installer": deal_data.get("installer"), + "installer_handover": deal_data.get("installer_handover"), + "lodgement_status": deal_data.get("lodgement_status"), + "measures_lodgement_date": parse_hs_date( + deal_data.get("measures_lodgement_date") + ), + "lodgement_date": parse_hs_date(deal_data.get("lodgement_date")), + "expected_commencement_date": parse_hs_date( + deal_data.get("expected_commencement_date") + ), + "surveyor": deal_data.get("surveyor"), + "confirmed_survey_date": parse_hs_date( + deal_data.get("confirmed_survey_date") + ), + "confirmed_survey_time": deal_data.get("confirmed_survey_time"), + "surveyed_date": parse_hs_date(deal_data.get("surveyed_date")), + "design_type": deal_data.get("design_type"), + }.items(): + setattr(existing, attr, value or getattr(existing, attr)) + + def _build_new_deal( + self, + deal_id: str, + deal_data: Dict[str, str], + listing: Optional[dict[str, str]], + company: Optional[str], + ) -> HubspotDealData: + return HubspotDealData( + deal_id=deal_id, + dealname=deal_data.get("dealname"), + dealstage=deal_data.get("dealstage"), + listing_id=listing.get("listing_id") if listing else None, + landlord_property_id=( + listing.get("owner_property_id") if listing else None + ), + uprn=listing.get("national_uprn") if listing else None, + outcome=deal_data.get("outcome"), + outcome_notes=deal_data.get("outcome_notes"), + project_code=deal_data.get("project_code"), + company_id=company, + major_condition_issue_description=deal_data.get( + "major_condition_issue_description" + ), + major_condition_issue_photos=deal_data.get("major_condition_issue_photos"), + coordination_status=deal_data.get("coordination_status__stage_1_"), + design_status=deal_data.get("retrofit_design_status"), + coordination_comments=deal_data.get("coordination_comments"), + pashub_link=deal_data.get("pashub_link"), + sharepoint_link=deal_data.get("sharepoint_link"), + dampmould_growth=deal_data.get("dampmould_growth"), + damp_mould_and_repairs_comments=deal_data.get( + "damp_mould_and_repairs_comments" + ), + pre_sap=deal_data.get("pre_sap"), + coordinator=deal_data.get("coordinator"), + mtp_completion_date=parse_hs_date(deal_data.get("mtp_completion_date")), + mtp_re_model_completion_date=parse_hs_date( + deal_data.get("mtp_re_model_completion_date") + ), + ioe_v3_completion_date=parse_hs_date( + deal_data.get("ioe_v3_completion_date") + ), + proposed_measures=deal_data.get("proposed_measures"), + approved_package=deal_data.get("approved_package"), + designer=deal_data.get("designer"), + design_completion_date=parse_hs_date( + deal_data.get("design_completion_date") + ), + actual_measures_installed=deal_data.get("actual_measures_installed"), + installer=deal_data.get("installer"), + installer_handover=deal_data.get("installer_handover"), + lodgement_status=deal_data.get("lodgement_status"), + measures_lodgement_date=parse_hs_date( + deal_data.get("measures_lodgement_date") + ), + lodgement_date=parse_hs_date(deal_data.get("lodgement_date")), + expected_commencement_date=parse_hs_date( + deal_data.get("expected_commencement_date") + ), + surveyor=deal_data.get("surveyor"), + confirmed_survey_date=parse_hs_date(deal_data.get("confirmed_survey_date")), + confirmed_survey_time=deal_data.get("confirmed_survey_time"), + surveyed_date=parse_hs_date(deal_data.get("surveyed_date")), + design_type=deal_data.get("design_type"), + ) + + def _handle_existing_photo_upload( + self, + existing_deal: HubspotDealData, + hubspot_client: HubspotClient, + ): + # if self._needs_photo_upload(existing): + + fresh_deal = hubspot_client.from_deal_id_get_info(existing_deal.deal_id) + fresh_photo_url = fresh_deal.get("major_condition_issue_photos") + + if not fresh_photo_url: + print(f"⚠️ Photo URL missing for deal_id {existing_deal.deal_id}") + return + + if fresh_photo_url != existing_deal.major_condition_issue_photos: + logger.info( + f"Hubspot image URL changed from {existing_deal.major_condition_issue_photos} to {fresh_photo_url}" + ) + self._upload_photo_to_s3(existing_deal, fresh_photo_url, hubspot_client) + else: + logger.info(f"Hubspot image URL unchanged: {fresh_photo_url}") + + def _handle_new_photo_upload( + self, + record: HubspotDealData, + hubspot_client: HubspotClient, + ): + if record.major_condition_issue_photos: + self._upload_photo_to_s3( + record, + record.major_condition_issue_photos, + hubspot_client, + ) + + def _upload_photo_to_s3( + self, + record: HubspotDealData, + hubspot_photo_url: str, + hubspot_client: HubspotClient, + ): + try: + local_file = hubspot_client.download_file_from_url(hubspot_photo_url) + + s3_url = self.s3.upload_file( + local_file, + "retrofit-data-dev", + prefix="hubspot/awaabs_law_evidence/", + ) + + record.major_condition_issue_evidence_s3_url = s3_url + + except Exception as e: + print(f"⚠️ Failed to upload photo for deal_id {record.deal_id}: {e}") + finally: + if "local_file" in locals() and os.path.exists(local_file): + os.remove(local_file) + + def _needs_photo_upload(self, old_deal: HubspotDealData) -> bool: + return bool( + old_deal.major_condition_issue_photos + and not old_deal.major_condition_issue_evidence_s3_url + ) diff --git a/etl/hubspot/hubspot_deal_differ.py b/etl/hubspot/hubspot_deal_differ.py new file mode 100644 index 00000000..b95b544c --- /dev/null +++ b/etl/hubspot/hubspot_deal_differ.py @@ -0,0 +1,177 @@ +from typing import Dict, List, Optional + +from backend.app.db.models.hubspot_deal_data import HubspotDealData +from etl.hubspot.utils import parse_hs_date + + +class HubspotDealDiffer: + COORDINATION_COMPLETE: List[str] = [ + "v1 ioe/mtp complete", + "v2 ioe/mtp complete", + "v3 ioe/mtp complete", + ] + RETROFIT_DESIGN_COMPLETE = "uploaded" + LODGEMENT_COMPLETE: List[str] = ["lodgement complete", "measures lodged"] + + @staticmethod + def check_for_db_update_trigger( + new_deal: Dict[str, str], + new_company: Optional[str], + new_listing: Optional[Dict[str, str]], + old_deal: HubspotDealData, + ) -> bool: + """ + Returns True if ANY difference exists between HubSpot data and DB. + Returns False if everything matches (i.e. no update needed). + """ + + # --- Deal ID --- + if str(old_deal.deal_id) != str(new_deal.get("hs_object_id")): + return True + + # --- Company --- + if new_company is not None: + if old_deal.company_id != new_company: + return True + + # --- Listing --- + hs_listing = new_listing or {} + + if old_deal.listing_id != hs_listing.get("listing_id"): + return True + + if old_deal.landlord_property_id != hs_listing.get("owner_property_id"): + return True + + if old_deal.uprn != hs_listing.get("national_uprn"): + return True + + # --- Field mappings --- + FIELD_MAP = { + "outcome": "outcome", + "dealstage": "dealstage", + "dealname": "dealname", + "project_code": "project_code", + "outcome_notes": "outcome_notes", + "major_condition_issue_description": "major_condition_issue_description", + "major_condition_issue_photos": "major_condition_issue_photos", + "coordination_status__stage_1_": "coordination_status", + "coordination_comments": "coordination_comments", + "retrofit_design_status": "design_status", + "pashub_link": "pashub_link", + "sharepoint_link": "sharepoint_link", + "dampmould_growth": "dampmould_growth", + "damp_mould_and_repairs_comments": "damp_mould_and_repairs_comments", + "pre_sap": "pre_sap", + "coordinator": "coordinator", + "proposed_measures": "proposed_measures", + "approved_package": "approved_package", + "designer": "designer", + "actual_measures_installed": "actual_measures_installed", + "installer": "installer", + "installer_handover": "installer_handover", + "lodgement_status": "lodgement_status", + "design_type": "design_type", + "surveyor": "surveyor", + "confirmed_survey_time": "confirmed_survey_time", + } + + for hs_field, db_field in FIELD_MAP.items(): + old_value = getattr(old_deal, db_field) + new_value = new_deal.get(hs_field) + + if old_value != new_value: + return True + + # --- Date fields --- + date_fields = [ + ("mtp_completion_date", "mtp_completion_date"), + ("mtp_re_model_completion_date", "mtp_re_model_completion_date"), + ("ioe_v3_completion_date", "ioe_v3_completion_date"), + ("design_completion_date", "design_completion_date"), + ("measures_lodgement_date", "measures_lodgement_date"), + ("lodgement_date", "lodgement_date"), + ("expected_commencement_date", "expected_commencement_date"), + ("confirmed_survey_date", "confirmed_survey_date"), + ("surveyed_date", "surveyed_date"), + ] + + for hs_field, db_field in date_fields: + old_value = getattr(old_deal, db_field) + new_value = parse_hs_date(new_deal.get(hs_field)) + + if old_value != new_value: + return True + + # --- Time field --- + if old_deal.confirmed_survey_time != new_deal.get("confirmed_survey_time"): + return True + + # No differences found + return False + + @staticmethod + def check_for_pashub_trigger( + new_deal: Dict[str, str], old_deal: HubspotDealData + ) -> bool: + new_pashub_link: str = new_deal.get("pashub_link", "") + + if not HubspotDealDiffer._has_valid_pashub_link(new_pashub_link): + return False + + if HubspotDealDiffer._new_or_updated_pashub_link(new_pashub_link, old_deal): + return True + + if HubspotDealDiffer._coordination_completed(new_deal, old_deal): + return True + + if HubspotDealDiffer._design_completed(new_deal, old_deal): + return True + + if HubspotDealDiffer._lodgement_completed(new_deal, old_deal): + return True + + return False + + @staticmethod + def _has_valid_pashub_link(new_pashub_link: str) -> bool: + return bool(new_pashub_link) + + @staticmethod + def _new_or_updated_pashub_link( + new_pashub_link: str, old_deal: HubspotDealData + ) -> bool: + if not old_deal.pashub_link: + return True + return old_deal.pashub_link != new_pashub_link + + @staticmethod + def _coordination_completed( + new_deal: Dict[str, str], old_deal: HubspotDealData + ) -> bool: + new_status: str = new_deal.get("coordination_status", "") + return ( + new_status != "" + and new_status in HubspotDealDiffer.COORDINATION_COMPLETE + and new_status != old_deal.coordination_status + ) + + @staticmethod + def _design_completed(new_deal: Dict[str, str], old_deal: HubspotDealData) -> bool: + new_status: str = new_deal.get("design_status", "") + return ( + new_status != "" + and new_status == HubspotDealDiffer.RETROFIT_DESIGN_COMPLETE + and new_status != old_deal.design_status + ) + + @staticmethod + def _lodgement_completed( + new_deal: Dict[str, str], old_deal: HubspotDealData + ) -> bool: + new_status: str = new_deal.get("lodgement_status", "") + return ( + new_status != "" + and new_status in HubspotDealDiffer.LODGEMENT_COMPLETE + and new_status != old_deal.lodgement_status + ) diff --git a/etl/hubspot/hubspot_trigger_orchestrator_trigger_request.py b/etl/hubspot/hubspot_trigger_orchestrator_trigger_request.py new file mode 100644 index 00000000..1adfa07c --- /dev/null +++ b/etl/hubspot/hubspot_trigger_orchestrator_trigger_request.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class HubspotTriggerOrchestratorTriggerRequest(BaseModel): + hubspot_deal_id: str diff --git a/etl/hubspot/scripts/onboarding/new_organisation.py b/etl/hubspot/scripts/onboarding/new_organisation.py index f8c6ba7a..0785949a 100644 --- a/etl/hubspot/scripts/onboarding/new_organisation.py +++ b/etl/hubspot/scripts/onboarding/new_organisation.py @@ -22,7 +22,7 @@ companies_to_add_or_ensure_it_exists = [ for company in companies_to_add_or_ensure_it_exists: company_info: CompanyData = hubspot.get_company_information(company.value) - dbRead.upsert_company(company_info) + dbRead.upsert_organisation(company_info) dbRead = HubspotDataToDb() diff --git a/etl/hubspot/scripts/scraper/bulk_load.py b/etl/hubspot/scripts/scraper/bulk_load.py new file mode 100644 index 00000000..91aa89e2 --- /dev/null +++ b/etl/hubspot/scripts/scraper/bulk_load.py @@ -0,0 +1,51 @@ +from etl.hubspot.hubspotClient import HubspotClient, Companies, Pipeline +from etl.hubspot.scripts.scraper.main import handler +from tqdm import tqdm +import json + + +PIPELINE_ID = Pipeline.OPERATIONS_SOCIAL_HOUSING.value + +companies = list( + [ + # Companies.THE_GUINESS_PARTNERSHIP, + # Companies.SOUTHERN_HOUSING_GROUP, + Companies.CALICO_HOMES, + ] +) + + +def bulk_load(companies: list[Companies] | None = None) -> None: + """ + Load all deals from the given companies (defaults to all Companies enum values) + into the database, filtered to the Operations/Social Housing pipeline. + """ + hubspot = HubspotClient() + targets = companies or list(Companies) + + for company in tqdm(targets, desc="Companies", unit="co", leave=False): + company_id = company.value + deal_ids = hubspot.get_deal_ids_from_company(company_id) + + processed = 0 + with tqdm( + deal_ids, desc=company.name, unit="deal", leave=True, position=0 + ) as deal_bar: + for deal_id in deal_bar: + deal_data = hubspot.from_deal_id_get_info(deal_id) + if deal_data.get("pipeline") != PIPELINE_ID: + deal_bar.set_postfix({"status": "skip", "deal": deal_id}) + continue + deal_bar.set_postfix({"status": "uploading", "deal": deal_id}) + handler( + {"Records": [{"body": json.dumps({"hubspot_deal_id": deal_id})}]}, + context=None, + ) + processed += 1 + deal_bar.set_postfix({"status": "done", "deal": deal_id}) + + tqdm.write(f"[{company.name}] {processed}/{len(deal_ids)} deals in pipeline") + + +if __name__ == "__main__": + bulk_load(companies) diff --git a/etl/hubspot/scripts/scraper/main.py b/etl/hubspot/scripts/scraper/main.py index 48864b22..d754cbb1 100644 --- a/etl/hubspot/scripts/scraper/main.py +++ b/etl/hubspot/scripts/scraper/main.py @@ -1,40 +1,113 @@ -""" -1) [completed]Get hubspot deal properties from one deal -2) Put it in some class -3) [completed] Load the db and check if upsert it into the table -4) [completed]Getting working on a AWS lambda -5) [completed] subtask and tasks history -6) [TODO]The new sexy deal properties, move it over -""" +import json +import boto3 +from typing import Any, Dict, Optional +from backend.app.config import get_settings from etl.hubspot.hubspotClient import HubspotClient -from etl.hubspot.hubspotDataTodB import HubspotDataToDb -from typing import Any +from etl.hubspot.hubspotDataTodB import CompanyData, HubspotDataToDb +from etl.hubspot.hubspot_deal_differ import HubspotDealDiffer +from etl.hubspot.hubspot_trigger_orchestrator_trigger_request import ( + HubspotTriggerOrchestratorTriggerRequest, +) +from backend.utils.subtasks import task_handler +from backend.app.db.models.hubspot_deal_data import HubspotDealData +from utils.logger import setup_logger + +logger = setup_logger() -# @subtask_handler() TODO: Do this without subtask_handler but task_handler() that creates task_id and subtask_id -def handler(body: dict[str, Any], context: Any, local: bool = False) -> None: - if local is True: - body = { - "hubspot_deal_id": "254427203793", - } +@task_handler() +def handler(body: dict[str, Any], context: Any) -> None: + db_client = HubspotDataToDb() + hubspot_client = HubspotClient() - hubspot_deal_id = body.get("hubspot_deal_id", "") + sqs_client = boto3.client("sqs") + PASHUB_TRIGGER_QUEUE_URL = get_settings().PASHUB_TO_ARA_SQS_URL - if hubspot_deal_id == "": - raise RuntimeError( - "Missing Hubspot Deal ID in SQS body request, 'hubspot_deal_id'" - ) + payload = HubspotTriggerOrchestratorTriggerRequest.model_validate(body) + hubspot_deal_id: str = payload.hubspot_deal_id - hubspot: HubspotClient = HubspotClient() - dbloader: HubspotDataToDb = HubspotDataToDb() + db_deal: Optional[HubspotDealData] = db_client.find_deal_with_deal_id( + hubspot_deal_id + ) - deal = dbloader.find_deal_with_deal_id(hubspot_deal_id) + hubspot_deal: Dict[str, str] + company: Optional[str] + listing: Optional[dict[str, str]] - if deal: - dbloader.update_deal_with_checks(deal, hubspot) + hubspot_deal, company, listing = hubspot_client.get_deal_and_company_and_listing( + hubspot_deal_id + ) + + deal_changed = False + if not db_deal: + # New hubspot deal, no diffing to do + logger.info(f"New HubSpot deal of ID {hubspot_deal_id}. Loading to database...") + if company: + company_data: CompanyData = hubspot_client.get_company_information(company) + db_client: HubspotDataToDb = HubspotDataToDb() + db_client.upsert_organisation(company_data) + + db_client.upsert_deal(hubspot_deal, company, listing, hubspot_client) else: - deal, company, listing = hubspot.get_deal_info_for_db(hubspot_deal_id) - dbloader.upsert_deal(deal, company, listing, hubspot) + # Deal already in db, check whether anything has changed + logger.info( + f"HubSpot deal {hubspot_deal_id} already in database. Checking for changes..." + ) + if HubspotDealDiffer.check_for_db_update_trigger( + new_deal=hubspot_deal, + new_company=company, + new_listing=listing, + old_deal=db_deal, + ): + logger.info( + f"Deal {hubspot_deal_id} has been changed, updating database..." + ) + db_client.upsert_deal( + deal_data=hubspot_deal, + company=company, + listing=listing, + hubspot_client=hubspot_client, + ) + deal_changed = True - print("Finsihed running") + if not deal_changed: + logger.info(f"No changes to deal {hubspot_deal_id}") + return + + # ============================== + # Orchestration of other lambdas + # ============================== + if HubspotDealDiffer.check_for_pashub_trigger( + new_deal=hubspot_deal, old_deal=db_deal + ): + logger.info( + f"Triggering Pas Hub file fetcher for HubSpot deal ID {hubspot_deal_id}" + ) + message_body: Dict[str, Optional[str]] = { + "pashub_link": hubspot_deal["pashub_link"], + "address": None, # potentially available from Listing, leave as None for now + "sharepoint_link": hubspot_deal["sharepoint_link"], + "uprn": hubspot_deal["national_uprn"], + "landlord_property_id": hubspot_deal["owner_property_id"], + "deal_stage": hubspot_deal["deal_stage"], + } + + response = sqs_client.send_message( + QueueUrl=PASHUB_TRIGGER_QUEUE_URL, MessageBody=json.dumps(message_body) + ) + + logger.info( + f"Sent message to Pashub To Ara queue. MessageId: {response['MessageId']}" + ) + else: + logger.info( + f"Not Triggering PasHub file fetcher for HubSpot deal ID {hubspot_deal_id}" + ) + + print("done") + + +if __name__ == "__main__": + handler({"hubspot_deal_id": "371470706915"}, "") + print("beep") diff --git a/etl/hubspot/tests/test_hubspot_client_integration.py b/etl/hubspot/tests/test_hubspot_client_integration.py index a3d8ae54..0f4b425c 100644 --- a/etl/hubspot/tests/test_hubspot_client_integration.py +++ b/etl/hubspot/tests/test_hubspot_client_integration.py @@ -71,7 +71,7 @@ class TestHubspotClientIntegration: def test_get_deal_info_for_db(self, client: HubspotClient): deal_id: str = "263490768079" - deal, company, listing = client.get_deal_info_for_db(deal_id) + deal, company, listing = client.get_deal_and_company_and_listing(deal_id) assert "dealname" in deal assert "dealstage" in deal diff --git a/etl/hubspot/tests/test_hubspot_deal_differ.py b/etl/hubspot/tests/test_hubspot_deal_differ.py new file mode 100644 index 00000000..69f7668b --- /dev/null +++ b/etl/hubspot/tests/test_hubspot_deal_differ.py @@ -0,0 +1,401 @@ +from datetime import datetime, timezone +from typing import Any, Dict +import uuid + +import pytest + +from backend.app.db.models.hubspot_deal_data import HubspotDealData +from etl.hubspot.hubspot_deal_differ import HubspotDealDiffer + + +BASE_TIME = datetime(2025, 12, 1, 12, 0, 0) + + +def make_old_deal(**overrides: Any) -> HubspotDealData: + return HubspotDealData( + id=overrides.get("id", uuid.uuid4()), + deal_id="1", + created_at=BASE_TIME, + updated_at=BASE_TIME, + **{k: v for k, v in overrides.items() if k != "id"}, + ) + + +def make_new_deal(deal_id: uuid.UUID, **overrides: Any) -> Dict[str, str]: + return { + "id": str(deal_id), + "deal_id": "1", + "created_at": BASE_TIME.isoformat(), + "updated_at": datetime(2025, 12, 1, 12, 30, 0).isoformat(), + **overrides, + } + + +# ==================== +# PASHUB TRIGGER TESTS +# ==================== + + +@pytest.mark.parametrize( + "new_overrides,expected", + [ + ({"outcome_notes": "test note"}, False), + ], +) +def test_pashub_trigger__outcome_note_added__returns_false( + new_overrides: Dict[str, str], + expected: bool, +) -> None: + deal_id = uuid.uuid4() + old_deal = make_old_deal(id=deal_id) + new_deal = make_new_deal(deal_id, **new_overrides) + + assert ( + HubspotDealDiffer.check_for_pashub_trigger( + new_deal=new_deal, + old_deal=old_deal, + ) + == expected + ) + + +@pytest.mark.parametrize( + "old_overrides,new_overrides,expected", + [ + ( + {"pashub_link": "www.google.co.uk"}, + {"pashub_link": "www.bbc.co.uk"}, + True, + ), + ], +) +def test_pashub_trigger__pashub_link_changed__returns_true( + old_overrides: Dict[str, str], + new_overrides: Dict[str, str], + expected: bool, +) -> None: + deal_id = uuid.uuid4() + old_deal = make_old_deal(id=deal_id, **old_overrides) + new_deal = make_new_deal(deal_id, **new_overrides) + + assert ( + HubspotDealDiffer.check_for_pashub_trigger( + new_deal=new_deal, + old_deal=old_deal, + ) + == expected + ) + + +@pytest.mark.parametrize( + "coordination_status,expected", + [ + ("v1 ioe/mtp complete", True), + ("v2 ioe/mtp complete", True), + ], +) +def test_pashub_trigger__coordination_completed_and_pashub_link_set__returns_true( + coordination_status: str, + expected: bool, +) -> None: + deal_id = uuid.uuid4() + + old_deal = make_old_deal( + id=deal_id, + pashub_link="www.google.co.uk", + coordination_status="random", + ) + + new_deal = make_new_deal( + deal_id, + pashub_link="www.google.co.uk", + coordination_status=coordination_status, + ) + + assert ( + HubspotDealDiffer.check_for_pashub_trigger( + new_deal=new_deal, + old_deal=old_deal, + ) + == expected + ) + + +def test_pashub_trigger__coordination_completed_and_pashub_link_not_set__returns_false() -> ( + None +): + deal_id = uuid.uuid4() + + old_deal = make_old_deal( + id=deal_id, + coordination_status="random", + ) + + new_deal = make_new_deal( + deal_id, + coordination_status="v2 ioe/mtp complete", + ) + + assert ( + HubspotDealDiffer.check_for_pashub_trigger( + new_deal=new_deal, + old_deal=old_deal, + ) + is False + ) + + +def test_pashub_trigger__design_completed_and_pashub_link_set__returns_true() -> None: + deal_id = uuid.uuid4() + + old_deal = make_old_deal( + id=deal_id, + pashub_link="www.google.co.uk", + ) + + new_deal = make_new_deal( + deal_id, + pashub_link="www.google.co.uk", + design_status="uploaded", + ) + + assert ( + HubspotDealDiffer.check_for_pashub_trigger( + new_deal=new_deal, + old_deal=old_deal, + ) + is True + ) + + +def test_pashub_trigger__design_completed_and_pashub_link_not_set__returns_false() -> ( + None +): + deal_id = uuid.uuid4() + + old_deal = make_old_deal(id=deal_id) + + new_deal = make_new_deal( + deal_id, + design_status="uploaded", + ) + + assert ( + HubspotDealDiffer.check_for_pashub_trigger( + new_deal=new_deal, + old_deal=old_deal, + ) + is False + ) + + +@pytest.mark.parametrize( + "lodgement_status,expected", + [ + ("lodgement complete", True), + ("measures lodged", True), + ], +) +def test_pashub_trigger__lodgement_completed_and_pashub_link_set__returns_true( + lodgement_status: str, + expected: bool, +) -> None: + deal_id = uuid.uuid4() + + old_deal = make_old_deal( + id=deal_id, + pashub_link="www.google.co.uk", + ) + + new_deal = make_new_deal( + deal_id, + pashub_link="www.google.co.uk", + lodgement_status=lodgement_status, + ) + + assert ( + HubspotDealDiffer.check_for_pashub_trigger( + new_deal=new_deal, + old_deal=old_deal, + ) + == expected + ) + + +def test_pashub_trigger__lodgement_completed_and_pashub_link_not_set__returns_false() -> ( + None +): + deal_id = uuid.uuid4() + + old_deal = make_old_deal(id=deal_id) + + new_deal = make_new_deal( + deal_id, + design_status="lodgement complete", + ) + + assert ( + HubspotDealDiffer.check_for_pashub_trigger( + new_deal=new_deal, + old_deal=old_deal, + ) + is False + ) + + +def test_pashub_trigger__coordination_design_lodgement_not_completed_and_pashub_link_set__returns_false() -> ( + None +): + deal_id = uuid.uuid4() + + old_deal = make_old_deal( + id=deal_id, + pashub_link="www.google.co.uk", + ) + + new_deal = make_new_deal( + deal_id, + pashub_link="www.google.co.uk", + coordination_status="not uploaded", + design_status="not uploaded", + lodgement_status="not uploaded", + ) + + assert ( + HubspotDealDiffer.check_for_pashub_trigger( + new_deal=new_deal, + old_deal=old_deal, + ) + is False + ) + + +# ======================= +# DB UPDATE TRIGGER TESTS +# ======================= + + +def test_db_update_trigger__no_changes__returns_false() -> None: + deal_id = uuid.uuid4() + + old_deal = make_old_deal( + id=deal_id, + dealname="Test Deal", + dealstage="stage_1", + outcome="won", + ) + + new_deal = make_new_deal( + deal_id, + hs_object_id="1", + dealname="Test Deal", + dealstage="stage_1", + outcome="won", + ) + + result = HubspotDealDiffer.check_for_db_update_trigger( + new_deal=new_deal, + new_company=None, + new_listing=None, + old_deal=old_deal, + ) + + assert result is False + + +def test_db_update_trigger__dealname_changed__returns_true() -> None: + deal_id = uuid.uuid4() + + old_deal = make_old_deal( + id=deal_id, + dealname="Old Name", + ) + + new_deal = make_new_deal( + deal_id, + hs_object_id="1", + dealname="New Name", + ) + + result = HubspotDealDiffer.check_for_db_update_trigger( + new_deal=new_deal, + new_company=None, + new_listing=None, + old_deal=old_deal, + ) + + assert result is True + + +def test_db_update_trigger__company_changed__returns_true() -> None: + deal_id = uuid.uuid4() + + old_deal = make_old_deal( + id=deal_id, + company_id="old_company", + ) + + new_deal = make_new_deal( + deal_id, + hs_object_id="1", + ) + + new_company = "new_company" + + result = HubspotDealDiffer.check_for_db_update_trigger( + new_deal=new_deal, + new_company=new_company, + new_listing=None, + old_deal=old_deal, + ) + + assert result is True + + +def test_db_update_trigger__missing_hubspot_timezone__returns_false() -> None: + deal_id = uuid.uuid4() + + old_deal = make_old_deal( + id=deal_id, + design_completion_date=datetime(2025, 11, 3, 0, 0, tzinfo=timezone.utc), + ) + + new_deal = make_new_deal( + deal_id, + hs_object_id="1", + design_completion_date=datetime(2025, 11, 3, 0, 0).isoformat(), + ) + + result = HubspotDealDiffer.check_for_db_update_trigger( + new_deal=new_deal, + new_company=None, + new_listing=None, + old_deal=old_deal, + ) + + assert result is False + + +def test_db_update_trigger__listing_changed__returns_true() -> None: + deal_id = uuid.uuid4() + + old_deal = make_old_deal( + id=deal_id, + listing_id="abc", + ) + + new_deal = make_new_deal( + deal_id, + hs_object_id="1", + ) + + new_listing = {"listing_id": "xyz"} + + result = HubspotDealDiffer.check_for_db_update_trigger( + new_deal=new_deal, + new_company=None, + new_listing=new_listing, + old_deal=old_deal, + ) + + assert result is True diff --git a/etl/hubspot/utils.py b/etl/hubspot/utils.py new file mode 100644 index 00000000..b7331f94 --- /dev/null +++ b/etl/hubspot/utils.py @@ -0,0 +1,16 @@ +from datetime import datetime, timezone +from typing import Optional + + +def parse_hs_date(value: Optional[str]) -> Optional[datetime]: + if not value: + return None + try: + dt = datetime.fromisoformat(value.replace("Z", "+00:00")) + + if dt.tzinfo is None: + return dt.replace(tzinfo=timezone.utc) + + return dt.astimezone(timezone.utc) + except ValueError: + return None diff --git a/infrastructure/terraform/lambda/_template/variables.tf b/infrastructure/terraform/lambda/_template/variables.tf index ae588840..0a3092ee 100644 --- a/infrastructure/terraform/lambda/_template/variables.tf +++ b/infrastructure/terraform/lambda/_template/variables.tf @@ -19,7 +19,7 @@ variable "image_digest" { variable "maximum_concurrency" { type = number - default = null + default = 2 description = "Maximum number of concurrent Lambda invocations from SQS (2-1000). null = no limit." } diff --git a/infrastructure/terraform/lambda/ecmk_to_ara/main.tf b/infrastructure/terraform/lambda/ecmk_to_ara/main.tf new file mode 100644 index 00000000..357c2f87 --- /dev/null +++ b/infrastructure/terraform/lambda/ecmk_to_ara/main.tf @@ -0,0 +1,27 @@ +data "terraform_remote_state" "shared" { + backend = "s3" + config = { + bucket = "assessment-model-terraform-state" + key = "env:/${var.stage}/terraform.tfstate" + region = "eu-west-2" + } +} + +module "lambda" { + source = "../../modules/lambda_with_sqs" + + name = "ecmk_to_ara" #"address2uprn" for example + stage = var.stage + + image_uri = local.image_uri + + # Optional: Set maximum_concurrency to limit concurrent SQS-triggered invocations (2-1000) + maximum_concurrency = var.maximum_concurrency + + batch_size = var.batch_size + + environment = { + STAGE = var.stage + LOG_LEVEL = "info" + } +} diff --git a/infrastructure/terraform/lambda/ecmk_to_ara/provider.tf b/infrastructure/terraform/lambda/ecmk_to_ara/provider.tf new file mode 100644 index 00000000..87a94150 --- /dev/null +++ b/infrastructure/terraform/lambda/ecmk_to_ara/provider.tf @@ -0,0 +1,16 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } + + backend "s3" { + bucket = "ecmk-to-ara-terraform-state" + key = "terraform.tfstate" + region = "eu-west-2" + } + + required_version = ">= 1.2.0" +} \ No newline at end of file diff --git a/infrastructure/terraform/lambda/ecmk_to_ara/variables.tf b/infrastructure/terraform/lambda/ecmk_to_ara/variables.tf new file mode 100644 index 00000000..984e3908 --- /dev/null +++ b/infrastructure/terraform/lambda/ecmk_to_ara/variables.tf @@ -0,0 +1,37 @@ +variable "lambda_name" { + type = string + description = "Logical name of the lambda (e.g. address2uprn)" +} + +variable "stage" { + description = "Deployment stage (e.g. dev, prod)" + type = string +} +variable "ecr_repo_url" { + type = string + description = "ECR repository URL (no tag, no digest)" +} + +variable "image_digest" { + type = string + description = "Image digest (sha256:...)" +} + +variable "maximum_concurrency" { + type = number + default = 2 + description = "Maximum number of concurrent Lambda invocations from SQS (2-1000). null = no limit." +} + +variable "batch_size" { + type = number + default = 1 +} + +locals { + image_uri = "${var.ecr_repo_url}@${var.image_digest}" +} + +output "resolved_image_uri" { + value = local.image_uri +} diff --git a/infrastructure/terraform/lambda/hubspot_deal_etl/main.tf b/infrastructure/terraform/lambda/hubspot_deal_etl/main.tf index 051c7154..e8762337 100644 --- a/infrastructure/terraform/lambda/hubspot_deal_etl/main.tf +++ b/infrastructure/terraform/lambda/hubspot_deal_etl/main.tf @@ -7,6 +7,14 @@ data "terraform_remote_state" "shared" { } } +data "terraform_remote_state" "pashub_to_ara" { + backend = "s3" + config = { + bucket = "pashub-to-ara-terraform-state" + key = "env:/${var.stage}/terraform.tfstate" + region = "eu-west-2" + } +} data "aws_secretsmanager_secret_version" "db_credentials" { secret_id = "${var.stage}/assessment_model/db_credentials" @@ -35,10 +43,16 @@ module "hubspot_deal_etl" { LOG_LEVEL = "info" DB_USERNAME = local.db_credentials.db_assessment_model_username DB_PASSWORD = local.db_credentials.db_assessment_model_password + DB_HOST = var.db_host + DB_NAME = var.db_name + DB_PORT = var.db_port + HUBSPOT_API_KEY = var.hubspot_api_key + + PASHUB_TO_ARA_SQS_URL = data.terraform_remote_state.pashub_to_ara.outputs.pashub_to_ara_queue_url } } resource "aws_iam_role_policy_attachment" "lambda_s3_policy" { - role = module.lambda.role_name + role = module.hubspot_deal_etl.role_name policy_arn = data.terraform_remote_state.shared.outputs.hubspot_etl_s3_read_and_write_arn } \ No newline at end of file diff --git a/infrastructure/terraform/lambda/hubspot_deal_etl/variables.tf b/infrastructure/terraform/lambda/hubspot_deal_etl/variables.tf index 2e7da609..84f0e567 100644 --- a/infrastructure/terraform/lambda/hubspot_deal_etl/variables.tf +++ b/infrastructure/terraform/lambda/hubspot_deal_etl/variables.tf @@ -19,13 +19,13 @@ variable "image_digest" { variable "maximum_concurrency" { type = number - default = null + default = 2 description = "Maximum number of concurrent Lambda invocations from SQS (2-1000). null = no limit." } variable "batch_size" { type = number - default = 1 + default = 5 } locals { @@ -41,6 +41,11 @@ variable "db_host" { type = string } +variable "hubspot_api_key" { + type = string +} + + variable "db_name" { type = string } diff --git a/infrastructure/terraform/lambda/pashub_to_ara/outputs.tf b/infrastructure/terraform/lambda/pashub_to_ara/outputs.tf new file mode 100644 index 00000000..d44b8763 --- /dev/null +++ b/infrastructure/terraform/lambda/pashub_to_ara/outputs.tf @@ -0,0 +1,4 @@ +output "pashub_to_ara_queue_url" { + value = module.lambda.queue_url + description = "URL of the PasHub to Ara SQS queue" +} diff --git a/infrastructure/terraform/shared/main.tf b/infrastructure/terraform/shared/main.tf index 9d272eb6..47866c92 100644 --- a/infrastructure/terraform/shared/main.tf +++ b/infrastructure/terraform/shared/main.tf @@ -538,6 +538,20 @@ module "pashub_to_ara_registry" { stage = var.stage } +################################################ +# ECMK to Ara – Lambda +################################################ +module "ecmk_to_ara_state_bucket" { + source = "../modules/tf_state_bucket" + bucket_name = "ecmk-to-ara-terraform-state" +} + +module "ecmk_to_ara_registry" { + source = "../modules/container_registry" + name = "ecmk_to_ara" + stage = var.stage +} + ################################################ # Engine – Lambda ECR ################################################ diff --git a/pytest.ini b/pytest.ini index db7afaf5..4a5327c1 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,6 +3,6 @@ pythonpath = . log_cli = true log_cli_level = INFO addopts = --cov-report term-missing --cov=etl/epc --cov=recommendations --cov=backend --cov=etl/epc_clean --cov=etl/spatial -testpaths = recommendations/tests backend/tests etl/epc/tests etl/epc_clean/tests etl/spatial/tests backend/condition/tests backend/address2UPRN/tests backend/onboarders/tests backend/categorisation/tests backend/export/tests etl/hubspot/tests +testpaths = recommendations/tests backend/tests etl/epc/tests etl/epc_clean/tests etl/spatial/tests backend/condition/tests backend/address2UPRN/tests backend/onboarders/tests backend/categorisation/tests backend/export/tests etl/hubspot/tests backend/hubspot_trigger_orchestrator/tests datatypes/epc/schema/tests markers = integration: mark a test as an integration test diff --git a/recommendations/HeatingRecommender.py b/recommendations/HeatingRecommender.py index 5a7a0e03..6aa5c93a 100644 --- a/recommendations/HeatingRecommender.py +++ b/recommendations/HeatingRecommender.py @@ -140,6 +140,9 @@ class HeatingRecommender: # All heat systems are in here so we identify whether two of these are true # MainHeatAttributes.HEAT_SYSTEMS + if "sap05" in self.property.main_heating["clean_description"].lower(): + return False + n_trues = 0 for heat_system in MainHeatAttributes.HEAT_SYSTEMS: if self.property.main_heating[f"has_{heat_system.replace(' ', '_')}"]: @@ -318,6 +321,9 @@ class HeatingRecommender: :param measures: A list of measures for the recommendations """ + if "sap05" in self.property.main_heating["clean_description"].lower(): + return + measures = MEASURE_MAP["heating"] if measures is None else measures # if we have a non-invasive ashp recommendation, we get the configuration directly from the property instance diff --git a/recommendations/LightingRecommendations.py b/recommendations/LightingRecommendations.py index 61b1f66a..91db19c9 100644 --- a/recommendations/LightingRecommendations.py +++ b/recommendations/LightingRecommendations.py @@ -100,6 +100,9 @@ class LightingRecommendations: :return: """ + if "sap05" in self.property.lighting["clean_description"].lower(): + return + if self.property.lighting["low_energy_proportion"] >= 1: return diff --git a/recommendations/SecondaryHeating.py b/recommendations/SecondaryHeating.py index c2250e1e..5b7f4ff9 100644 --- a/recommendations/SecondaryHeating.py +++ b/recommendations/SecondaryHeating.py @@ -18,7 +18,9 @@ class SecondaryHeating: def recommend(self, phase: int): # Reset self.recommendation = [] - if self.property.epc_record.secondheat_description in ["None", None]: + if self.property.epc_record.secondheat_description in ["None", None] or ( + "sap05" in self.property.epc_record.secondheat_description.lower() + ): # No secondary heating system, so no recommendation to remove it return diff --git a/recommendations/WallRecommendations.py b/recommendations/WallRecommendations.py index a696e878..6baa85aa 100644 --- a/recommendations/WallRecommendations.py +++ b/recommendations/WallRecommendations.py @@ -169,7 +169,7 @@ class WallRecommendations(Definitions): if ( (insulation_thickness in ["average", "above average"]) or self.property.walls["is_filled_cavity"] - or self.property.walls["clean_description"] is None + or self.property.walls["clean_description"] in [None, "Sap05:walls"] ) and ("cavity_extract_and_refill" not in measures ): return diff --git a/recommendations/WindowsRecommendations.py b/recommendations/WindowsRecommendations.py index ff75e72d..54445eb1 100644 --- a/recommendations/WindowsRecommendations.py +++ b/recommendations/WindowsRecommendations.py @@ -45,7 +45,8 @@ class WindowsRecommendations: measures = MEASURE_MAP["windows"] if measures is None else measures # If we have no windows recs, leave - if not any(x in measures for x in MEASURE_MAP["windows"]): + if not any(x in measures for x in MEASURE_MAP["windows"]) or "sap05" in self.property.windows[ + "clean_description"].lower(): return if self.property.windows["glazing_type"] in ["triple", "high performance"]: diff --git a/sfr/principal_pitch/2_export_data.py b/sfr/principal_pitch/2_export_data.py index c89560cb..7c80f4dc 100644 --- a/sfr/principal_pitch/2_export_data.py +++ b/sfr/principal_pitch/2_export_data.py @@ -26,13 +26,13 @@ from backend.app.db.functions.materials_functions import get_materials from collections import defaultdict from sqlalchemy import func -PORTFOLIO_ID = 639 -SCENARIOS = [1157] +PORTFOLIO_ID = 632 +SCENARIOS = [1144] scenario_names = { - 1157: "EPC C - no EWI solid floor", + 1144: "EPC C", } -project_name = "Instagroup Sample" +project_name = "Calico Project" def get_data(portfolio_id, scenario_ids): diff --git a/utils/s3.py b/utils/s3.py index 242e0db5..930e2e15 100644 --- a/utils/s3.py +++ b/utils/s3.py @@ -330,7 +330,7 @@ def read_csv_from_s3(bucket_name: str, filepath: str) -> list[dict[str, str]]: body = s3_object["Body"].read() # Use StringIO to create a file-like object from the string - csv_data = StringIO(body.decode("utf-8")) + csv_data = StringIO(body.decode("utf-8-sig")) # Use csv library to read it into a list of dictionaries reader = csv.DictReader(csv_data)