Merge pull request #89 from Hestia-Homes/feautre/walthamforest_etl

Feautre/walthamforest etl
This commit is contained in:
Jun-te Kim 2025-09-24 16:03:02 +01:00 committed by GitHub
commit a5499fb784
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 1640 additions and 600 deletions

View file

@ -4,6 +4,7 @@ on:
schedule:
- cron: '0 7 * * 1' # Every Monday at 07:00 UTC
- cron: '0 7 29 * *' # On the 29th of every month at 07:00 UTC
- cron: '0 7 26 * *' # On the 26th of every month at 07:00 UTC
workflow_dispatch:
jobs:

View file

@ -0,0 +1,60 @@
"""added more enums
Revision ID: 4c67501b7451
Revises: ac8dba8cef50
Create Date: 2025-09-23 10:22:20.648664
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "4c67501b7451"
down_revision: Union[str, None] = "ac8dba8cef50"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
ENUM_NAME = "reporttype"
# Values that were already present BEFORE this migration
OLD_VALUES = (
"QUIDOS_PRESITE_NOTE",
"CHARTED_SURVEYOR_REPORT",
"ENERGY_PERFORMANCE_REPORT",
"U_VALUE_CALCULATOR_REPORT",
"OVERWRITING_U_VALUE_DECLARATION_FORM",
"OSMOSIS_CONDITION_PAS_2035_REPORT",
"DOMNA_CONDITION_PAS_2035_REPORT",
)
# Values we are ADDING in this migration
NEW_VALUES = (
"DECENT_HOMES_RAW_DATA",
"DECENT_HOMES_SUMMARY",
"DECENT_HOMES_PROPERTY_META",
)
def upgrade() -> None:
for v in NEW_VALUES:
op.execute(f"ALTER TYPE {ENUM_NAME} ADD VALUE IF NOT EXISTS '{v}'")
def downgrade() -> None:
# 1) Create a replacement type with ONLY the old values
old_vals = ", ".join(f"'{v}'" for v in OLD_VALUES)
op.execute(f"CREATE TYPE {ENUM_NAME}_old AS ENUM ({old_vals})")
# 2) Move columns to the temporary type
op.execute(
f"ALTER TABLE documents ALTER COLUMN document_type TYPE {ENUM_NAME}_old "
f"USING document_type::text::{ENUM_NAME}_old"
)
op.execute(
f"ALTER TABLE uploaded_files ALTER COLUMN doc_type TYPE {ENUM_NAME}_old "
f"USING doc_type::text::{ENUM_NAME}_old"
)
# 3) Drop original type and rename the temp back
op.execute(f"DROP TYPE {ENUM_NAME}")
op.execute(f"ALTER TYPE {ENUM_NAME}_old RENAME TO {ENUM_NAME}")

View file

@ -0,0 +1,38 @@
"""added more report type
Revision ID: ac8dba8cef50
Revises: a8cc4a5fccb6
Create Date: 2025-09-23 10:14:54.461633
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'ac8dba8cef50'
down_revision: Union[str, None] = 'a8cc4a5fccb6'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('uploaded_files', 'id',
existing_type=sa.UUID(),
server_default=None,
existing_nullable=False)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('uploaded_files', 'id',
existing_type=sa.UUID(),
server_default=sa.text('gen_random_uuid()'),
existing_nullable=False)
# ### end Alembic commands ###

View file

@ -8,7 +8,6 @@ from etl.fileReader.sitenotes import (
SiteNotesExtractor,
WarmHomesConditionReport
)
from uuid import UUID
import json
from typing import Any

View file

@ -6,8 +6,16 @@ import copy
from collections import defaultdict
from typing import List, Dict, Any, Union, Optional
import boto3
import datetime
from urllib.parse import urlparse
from decent_homes_pilot import decent_homes_calc
import uuid
from datetime import datetime, timezone, time, date
from decimal import Decimal
from sqlmodel import select
from sqlalchemy import update
from etl.models.topLevel import uploaded_files, ReportType
from etl.db.db import get_db_session, init_db
def process_complex(sheet_name, group_key="ADDRESS"):
df = pd.read_excel("../../../../../home/Downloads/data.xlsx", sheet_name=sheet_name)
@ -107,13 +115,14 @@ def combine_records_for_flats(assets: dict, simple: list) -> dict:
for record in assets:
# Make sure record is a dict
record.update({"BLOCK_INFO": block_info})
# record.update({"BLOCK_INFO": block_info})
for ele_desc in block_info["elements"]:
if ele_desc not in record["elements"]:
record["elements"].update({ele_desc:block_info["elements"][ele_desc]})
return assets
def _json_default(o):
from datetime import date, datetime, time
from decimal import Decimal
# datetimes → ISO 8601 strings
if isinstance(o, (datetime, date, time)):
return o.isoformat()
@ -145,6 +154,11 @@ def uprn_to_address():
mapping = df.set_index('Address')['UPRN'].to_dict()
return mapping
def stories_to_address():
df = pd.read_excel("../../../../../home/Downloads/data.xlsx", sheet_name="All Energy Breakdown ")
mapping = df.set_index('Address')['Storeys'].to_dict()
return mapping
def parse_s3_uri(uri: str):
"""
Parse an S3 URI or HTTPS S3 URL into bucket and key.
@ -171,7 +185,7 @@ def parse_s3_uri(uri: str):
return bucket, key
def upload_json_to_s3(json_obj, dest_uri: str) -> str:
def upload_json_to_s3(json_obj, dest_uri: str, location="decent_homes/raw_data") -> str:
"""
Upload a JSON-serializable object to S3 at the given s3:// or https S3 URL.
Returns the public-style HTTPS S3 URL (still private if bucket is private).
@ -180,8 +194,8 @@ def upload_json_to_s3(json_obj, dest_uri: str) -> str:
base_folder = os.path.dirname(pdf_key) # e.g. ".../report"
# Build jsonified folder + timestamp filename
timestamp = datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S")
json_key = f"{base_folder}/walthamforest_raw_data/jsonified/{timestamp}.json"
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
json_key = f"{base_folder}/{location}/jsonified/{timestamp}.json"
# Same region/creds you used for download
aws_access_key = "AKIAU5A36PPNJMZZ3KRW"
@ -212,14 +226,54 @@ def upload_json_to_s3(json_obj, dest_uri: str) -> str:
def generate_file_uri(UPRN):
timestamp = datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S")
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
file_uri = f"https://retrofit-energy-assessments-dev.s3.eu-west-2.amazonaws.com/documents/{UPRN}/"
return file_uri
def create_or_update_uploaded_file_entry(
db_session,
uprn: str,
doc_type: ReportType,
json_uri: str,
s3_file_uri: str
):
"""
Create or update an entry in uploaded_files.
- If a record with the same (uprn, doc_type) exists, update it.
- Otherwise, insert a new record.
Commits, refreshes, and returns the ORM object.
"""
existing = (
db_session.query(uploaded_files)
.filter(uploaded_files.uprn == str(uprn), uploaded_files.doc_type == doc_type)
.one_or_none()
)
if existing:
# Update existing record
existing.s3_json_uri = json_uri
existing.s3_json_upload_timestamp = datetime.now(timezone.utc)
existing.s3_file_uri = s3_file_uri
obj = existing
else:
# Insert new record
obj = uploaded_files(
doc_type=doc_type,
s3_json_uri=json_uri,
s3_json_upload_timestamp=datetime.now(timezone.utc),
s3_file_uri=s3_file_uri,
uprn=str(uprn),
)
db_session.add(obj)
db_session.commit()
db_session.refresh(obj)
return obj
def handler(event, context):
uprn_mapping = uprn_to_address()
flats_to_stories = stories_to_address()
# read data for houses only
assets = process_complex("Houses Asset Data")
@ -232,9 +286,41 @@ def handler(event, context):
house.update({"UPRN": uprn_mapping[pseudo_name.upper()]})
#upload to s3
for house in houses:
print(house["UPRN"])
json_uri = upload_json_to_s3(house, generate_file_uri(house["UPRN"]))
for i,house in enumerate(houses):
uprn = house["UPRN"]
print(uprn)
json_uri = upload_json_to_s3(house, generate_file_uri(house["UPRN"]), location="decent_homes/raw_data")
# Save JSON locally
filename = f"{uprn}.json"
filepath = os.path.join("output", filename) # saves inside an "output" folder
os.makedirs("output", exist_ok=True) # make sure folder exists
with open(filepath, "w", encoding="utf-8") as f:
json.dump(house, f, indent=2, ensure_ascii=False, default=_json_default)
property_decent_home, decent_home_meta = decent_homes_calc(filepath)
json_uri_1 = upload_json_to_s3(property_decent_home, generate_file_uri(uprn), location="decent_homes/property_decent_home")
with get_db_session() as session:
create_or_update_uploaded_file_entry(
db_session=session,
uprn=uprn,
doc_type=ReportType.DECENT_HOMES_SUMMARY,
json_uri=json_uri_1,
s3_file_uri=json_uri,
)
json_uri_1 = upload_json_to_s3(decent_home_meta, generate_file_uri(uprn), location="decent_homes/decent_homes_meta")
with get_db_session() as session:
create_or_update_uploaded_file_entry(
db_session=session,
uprn=uprn,
doc_type=ReportType.DECENT_HOMES_PROPERTY_META,
json_uri=json_uri_1,
s3_file_uri=json_uri,
)
# read data for flats
assets = process_complex("Chingford Rd 236-256 Properties")
@ -245,18 +331,50 @@ def handler(event, context):
pseudo_name = house["ADDRESS"].split(",")[0]
if pseudo_name.lower() in (k.lower() for k in uprn_mapping.keys()):
print(uprn_mapping[pseudo_name.upper()])
house.update({"UPRN": uprn_mapping[pseudo_name.upper()]})
house["property_info"].update({"FLAT LEVEL": flats_to_stories[pseudo_name.upper()]})
for house in flats:
print(house["UPRN"])
for i,house in enumerate(flats):
uprn = house["UPRN"]
print(uprn)
json_uri = upload_json_to_s3(house, generate_file_uri(house["UPRN"]))
# with open("flat.json", "w") as f:
# json.dump(houses[0], f, indent=2, ensure_ascii=False, default=_json_default)
# Save JSON locally
filename = f"{house['UPRN']}.json"
filepath = os.path.join("output", filename) # saves inside an "output" folder
os.makedirs("output", exist_ok=True) # make sure folder exists
with open(filepath, "w", encoding="utf-8") as f:
json.dump(house, f, indent=2, ensure_ascii=False, default=_json_default)
property_decent_home, decent_home_meta = decent_homes_calc(filepath)
json_uri_1 = upload_json_to_s3(property_decent_home, generate_file_uri(uprn), location="decent_homes/property_decent_home")
with get_db_session() as session:
create_or_update_uploaded_file_entry(
db_session=session,
uprn=uprn,
doc_type=ReportType.DECENT_HOMES_SUMMARY,
json_uri=json_uri_1,
s3_file_uri=json_uri,
)
json_uri_1 = upload_json_to_s3(decent_home_meta, generate_file_uri(uprn), location="decent_homes/decent_homes_meta")
with get_db_session() as session:
create_or_update_uploaded_file_entry(
db_session=session,
uprn=uprn,
doc_type=ReportType.DECENT_HOMES_PROPERTY_META,
json_uri=json_uri_1,
s3_file_uri=json_uri,
)
# Keep track of saved file path
# run a script that upload to s3 -> uprn -> jsonified -> walthamforest -> uri
# To Do:
# [Jun-te] Spec of quesation that we have for waltham forest
# [Khalim] A document that has our mapping and our understanding of our data

View file

@ -0,0 +1,811 @@
import json
import os
import pandas as pd
from datetime import datetime
from docutils.nodes import table
def years_between(d1, d2):
# precise year difference (accounts for months/days)
return (d1.year - d2.year) - ((d1.month, d1.day) < (d2.month, d2.day))
def get_element(elements, label):
"""Safely get an element dict by display label (your JSON keys)."""
return elements.get(label)
def append_result(decent_homes_meta, criteria, variable, sub_variable, result, install_date=None, expiry_date=None):
decent_homes_meta.append({
"criteria": criteria,
"variable": variable,
"sub_variable": sub_variable,
"result": result,
"hhsrs_rank": None,
"hhsrs_score": None,
"install_date": install_date,
"expiry_date": expiry_date,
})
def decent_homes_calc(one_property):
# Read in static json, which is transformed by Jun-te's script
folder = "../../../../../home/Downloads/"
fn = one_property
# filenames = ["flat 1.json", "house 1.json"]
houses_waltham_forest_data = pd.read_excel(
os.path.join(folder, "data.xlsx"),
sheet_name="Houses Asset Data"
)
flats_waltham_forest_data = pd.read_excel(
os.path.join(folder, "data.xlsx"),
sheet_name="CHINGFORD ROAD 236-254 Asset Bl"
)
# Standardised variables which will form the enums in the db
HHSRS_VARIABLES = [
"damp_and_mould_growth",
"excess_cold",
"excess_heat",
"asbestos_and_mm_fibres",
"biocides",
"carbon_monoxide_and_fuel_combustion_products",
"lead",
"radiation",
"uncombusted_fuel_gas",
"volatile_organic_compounds",
"crowding_and_space",
"entry_by_intruders",
"lighting",
"noise",
"domestic_hygiene_pests_and_refuse",
"food_safety",
"personal_hygiene_sanitation_and_drainage",
"water_supply",
"falls_associated_with_baths",
"falls_on_level_surfaces",
"falls_on_stairs_and_steps",
"falls_between_levels",
"electrical_hazards",
"fire",
"flames_hot_surfaces_and_materials",
"collision_and_entrapment",
"explosions",
"ergonomics",
"structural_collapse_and_falling_elements"
]
ELEMENT_CODE_TO_DESCRIPTION = {
# One-to-one
"HHSRSDAMP": "damp_and_mould_growth",
"HHSRSCOLD": "excess_cold",
"HHSRSHEAT": "excess_heat",
"HHSRSASB": "asbestos_and_mm_fibres",
"HHSRSBIOC": "biocides",
"HHSRSLEAD": "lead",
"HHSRSRADIA": "radiation",
"HHSRSFUEL": "uncombusted_fuel_gas",
"HHSRSORGAN": "volatile_organic_compounds",
"HHSRSCROWD": "crowding_and_space",
"HHSRSENTRY": "entry_by_intruders",
"HHSRSLIGHT": "lighting",
"HHSRSNOISE": "noise",
"HHSRSDOMES": "domestic_hygiene_pests_and_refuse",
"HHSRSFOOD": "food_safety",
"HHSRSPERS": "personal_hygiene_sanitation_and_drainage",
"HHSRSWATER": "water_supply",
"HHSRSFBATH": "falls_associated_with_baths",
"HHSRSFLEVE": "falls_on_level_surfaces",
"HHSRSFSTAI": "falls_on_stairs_and_steps",
"HHSRSFBETW": "falls_between_levels",
"HHSRSELEC": "electrical_hazards",
"HHSRSFIRE": "fire",
"HHSRSFLAME": "flames_hot_surfaces_and_materials",
"HHSRSEXPLO": "explosions",
"HHSRSPOSI": "ergonomics",
"HHSRSSTRUC": "structural_collapse_and_falling_elements",
# One-to-many expansions
"HHSRSCO": "carbon_monoxide",
"HHSRSSO2": "sulphur_dioxide_and_smoke",
"HHSRSNO2": "nitrogen_dioxide",
"HHSRSENTRP": "collision_and_entrapment",
"HHSRSCLOW": "collision_hazards_and_low_headroom",
}
CRITERION_B_VARIABLES = [
"external_walls_structure", "lintels", "brickwork_spalling", "wall_finish", "roof_structure", "roof_finish",
"chimneys", "windows", "external_doors", "kitchens", "bathrooms", "central_heating_boiler",
"central_heating_distribution_system", "heating_other", "electrical_systems",
]
CRITERION_C_VARIABLES = [
"kitchen_less_than_20_years_old", "kitchen_adequate_space_and_layout", "bathroom_less_than_30_years_old",
"bathroom_wc_appropriately_located", "adequate_external_noise_insulation", "adequate_common_entrance_areas",
]
# Criterion C explicit age limits (different from component lifespans used elsewhere)
CRITERION_C_AGE_LIMITS = {
"kitchen_years_max": 20,
"bathroom_years_max": 30,
}
# Field labels as they appear in your JSON (based on your code)
LABEL_KITCHEN = "Adequacy of Kitchen and Type in Property"
LABEL_BATHROOM = "Adequacy of Bathroom Location in Property"
LABEL_NOISE = "Adequacy of Noise Insulation in Property"
LABEL_COMMON_CIRC = "Circulation Space in Common Area" # flats only
STANDARD_HHSRS_MAPPING = {
"pass": ["TYPRISK"],
"fail": ["MODRISK","SLIGHTRISK"],
"no_data": ["TOBEASSESS"],
}
# Criterion A - mapping of HHSRS variables to Waltham forest element codes
HHSRS_MAPPING = {
"damp_and_mould_growth": {"HHSRSDAMP": STANDARD_HHSRS_MAPPING},
"excess_cold": {"HHSRSCOLD": STANDARD_HHSRS_MAPPING},
"excess_heat": {"HHSRSHEAT": STANDARD_HHSRS_MAPPING},
"asbestos_and_mm_fibres": {"HHSRSASB": STANDARD_HHSRS_MAPPING},
"biocides": {"HHSRSBIOC": STANDARD_HHSRS_MAPPING},
"carbon_monoxide_and_fuel_combustion_products": {
"HHSRSCO": STANDARD_HHSRS_MAPPING,
"HHSRSSO2": STANDARD_HHSRS_MAPPING,
"HHSRSNO2": STANDARD_HHSRS_MAPPING
},
"lead": {"HHSRSLEAD": STANDARD_HHSRS_MAPPING},
"radiation": {"HHSRSRADIA": STANDARD_HHSRS_MAPPING},
"uncombusted_fuel_gas": {"HHSRSFUEL": STANDARD_HHSRS_MAPPING},
"volatile_organic_compounds": {"HHSRSORGAN": STANDARD_HHSRS_MAPPING},
"crowding_and_space": {"HHSRSCROWD": STANDARD_HHSRS_MAPPING},
"entry_by_intruders": {"HHSRSENTRY": STANDARD_HHSRS_MAPPING},
"lighting": {"HHSRSLIGHT": STANDARD_HHSRS_MAPPING},
"noise": {"HHSRSNOISE": STANDARD_HHSRS_MAPPING},
"domestic_hygiene_pests_and_refuse": {"HHSRSDOMES": STANDARD_HHSRS_MAPPING},
"food_safety": {"HHSRSFOOD": STANDARD_HHSRS_MAPPING},
"personal_hygiene_sanitation_and_drainage": {"HHSRSPERS": STANDARD_HHSRS_MAPPING},
"water_supply": {"HHSRSWATER": STANDARD_HHSRS_MAPPING},
"falls_associated_with_baths": {"HHSRSFBATH": STANDARD_HHSRS_MAPPING},
"falls_on_level_surfaces": {"HHSRSFLEVE": STANDARD_HHSRS_MAPPING},
"falls_on_stairs_and_steps": {"HHSRSFSTAI": STANDARD_HHSRS_MAPPING},
"falls_between_levels": {"HHSRSFBETW": STANDARD_HHSRS_MAPPING},
"electrical_hazards": {"HHSRSELEC": STANDARD_HHSRS_MAPPING},
"fire": {"HHSRSFIRE": STANDARD_HHSRS_MAPPING},
"flames_hot_surfaces_and_materials": {"HHSRSFLAME": STANDARD_HHSRS_MAPPING},
"collision_and_entrapment": {"HHSRSENTRP": STANDARD_HHSRS_MAPPING, "HHSRSCLOW": STANDARD_HHSRS_MAPPING},
"explosions": {"HHSRSEXPLO": STANDARD_HHSRS_MAPPING},
"ergonomics": {"HHSRSPOSI": STANDARD_HHSRS_MAPPING},
"structural_collapse_and_falling_elements": {"HHSRSSTRUC": STANDARD_HHSRS_MAPPING}
}
# print(houses_waltham_forest_data[
# houses_waltham_forest_data["ELEMENT CODE"] == "INTBTHADEQ"
# ][["ATTRIBUTE CODE", "ATTRIBUTE CODE DESCRIPTION"]].drop_duplicates())
# print(flats_waltham_forest_data[
# flats_waltham_forest_data["ELEMENT CODE"] == "INTBTHADEQ"
# ][["ATTRIBUTE CODE", "ATTRIBUTE CODE DESCRIPTION"]].drop_duplicates())
# Criterion B
B_COMPONENT_LABELS = {
# Key components
"wall_structure": [
"Wall Structure in External Area",
],
"lintels": [
"Lintels in External Area",
],
"brickwork_spalling": [
"Wall Spalling in External Area",
],
"wall_finish": [
"Wall Finish 1 in External Area",
"Wall Finish 2 in External Area",
"External Decorations in External Area",
"Brickwork Pointing in External Area",
],
"roof_structure": [
"Roof Structure 1 in External Area",
"Roof Structure 2 in External Area",
"Roof Structure 3 in External Area",
"Garage Roof in External Area",
"Garage and Store Roofs in External Area",
"Store Roof in External Area",
"Fascia / Soffit / Bargeboard in External Area",
"Gutters in External Area",
"Downpipes in External Area",
"Internal Downpipes in External Area"
],
"roof_finish": [
"Roof Covering 1 in External Area",
"Roof Covering 2 in External Area",
"Roof Covering 3 in External Area",
],
"chimneys": [
"Chimneys in External Area",
],
"windows": [
"Windows in Property",
"Windows 1 in External Area",
"Windows 2 in External Area",
"Garage and Store Windows in External Area",
"Garage Windows in External Area",
"Store Windows in External Area",
],
"external_doors": [
"Type and Location of Front Door in Property",
"Front Door Fire Rating in Property",
"Patio and French Doors 1 in External Area",
"Back and Side Doors 1 in External Area",
"Back and Side Doors 2 in External Area",
"Garage and Store Doors in External Area",
"Garage Door in External Area",
"Store Door in External Area",
],
"central_heating_boiler": [
# "Heating Improvement Required in Property",
"Boiler Fuel in Property",
"Type of Water Heating in Property",
],
"heating_other": [
# "Heating Distribution System in Property"
"Boiler Fuel in Property",
"Type of Water Heating in Property",
],
"electrical_systems": [
"Electrics Required in Property",
],
# Other components
"kitchen": [
"Adequacy of Kitchen and Type in Property",
],
"bathroom": [
"Adequacy of Bathroom Location in Property",
],
"central_heating_distribution_system": [
"Heating Distribution System in Property",
],
}
KEY_COMPONENTS = {
"wall_structure", "lintels", "brickwork_spalling", "wall_finish",
"roof_structure", "roof_finish", "chimneys", "windows",
"external_doors", "central_heating_boiler", "heating_other",
"electrical_systems",
}
OTHER_COMPONENTS = {
"kitchen", "bathroom", "central_heating_distribution_system",
}
# Criterion C
COMPONENT_LIFESPANS = {
# Key components
"wall_structure": {
"house": 80, "flat_below_6_storeys": 80, "flat_above_6_storeys": 80
},
"lintels": {
"house": 60, "flat_below_6_storeys": 60, "flat_above_6_storeys": 60
},
"brickwork_spalling": {
"house": 30, "flat_below_6_storeys": 30, "flat_above_6_storeys": 30
},
"wall_finish": {
"house": 60, "flat_below_6_storeys": 60, "flat_above_6_storeys": 30
},
"roof_structure": {
"house": 50, "flat_below_6_storeys": 30, "flat_above_6_storeys": 30
},
"roof_finish": {
"house": 50, "flat_below_6_storeys": 30, "flat_above_6_storeys": 30
},
"chimneys": {
"house": 50, "flat_below_6_storeys": 50, "flat_above_6_storeys": None # N/A
},
"windows": {
"house": 40, "flat_below_6_storeys": 30, "flat_above_6_storeys": 30
},
"external_doors": {
"house": 40, "flat_below_6_storeys": 30, "flat_above_6_storeys": 30
},
"central_heating_boiler": {
"house": 15, "flat_below_6_storeys": 15, "flat_above_6_storeys": 15
},
"heating_other": {
"house": 30, "flat_below_6_storeys": 30, "flat_above_6_storeys": 30
},
"electrical_systems": {
"house": 30, "flat_below_6_storeys": 30, "flat_above_6_storeys": 30
},
# Other components
"kitchen": {
"house": 30, "flat_below_6_storeys": 30, "flat_above_6_storeys": 30
},
"bathroom": {
"house": 40, "flat_below_6_storeys": 40, "flat_above_6_storeys": 40
},
"central_heating_distribution_system": {
"house": 40, "flat_below_6_storeys": 40, "flat_above_6_storeys": 40
},
}
# Database design
# creation_date, uprn, variable, result (pass/fail/nodata), hhsrs_score (optional, numeric), hhsrs_rank (A-J),
# install_date (for components which expire, e.g. kitchen), remaining_life (for components which expire, e.g. kitchen),
# TODO: Add the criterion
decent_homes_meta = []
# Use to capture criterion A, B, C and D. Should be:
# {"uprn": int, "creation_date": datetime, "criterion_a": bool, "criterion_b": bool, "criterion_c": bool,
# "criterion_d": bool, "decent_homes": bool"}
property_decent_homes = []
with open(os.path.join(fn), "rb") as f:
data = json.load(f)
today = pd.Timestamp.today().normalize()
property_info = data["property_info"]
if property_info["PROP TYPE"] in ["HOU"]:
property_type = "house"
elif property_info["PROP TYPE"] == "FLA":
if property_info["FLAT LEVEL"] < 6:
property_type = "flat_below_6_storeys"
else:
property_type = "flat_above_6_storeys"
else:
raise NotImplementedError("Unknown property type")
# ---------------- Criterion A ----------------
# Critrion A: pass/fail
# If fail, why?
for hhsrs_variable, mapping in HHSRS_MAPPING.items():
element_code = list(mapping.keys())[0]
# Find the data in the JSON within data["elements"]
check_pass = []
for k, v in data["elements"].items():
if v["ELEMENT CODE"] == element_code:
# We check the attribute code
# Check if pass
if v["ATTRIBUTE CODE"] in mapping[element_code]["pass"]:
result = "pass"
elif v["ATTRIBUTE CODE"] in mapping[element_code]["fail"]:
result = "fail"
elif v["ATTRIBUTE CODE"] in mapping[element_code]["no_data"]:
result = "no_data"
else:
raise ValueError(f"Unknown attribute code: '{v[element_code]}")
check_pass.append(result)
append_result(
decent_homes_meta,
criteria="A",
variable=hhsrs_variable,
sub_variable=ELEMENT_CODE_TO_DESCRIPTION[element_code],
result=result,
install_date=None,
expiry_date=None,
)
# We check if we have a pass, fail or no_data
# if all([x == "pass" for x in check_pass]):
# hhsrs_result = "pass"
# elif any([x == "fail" for x in check_pass]):
# hhsrs_result = "fail"
# elif any([x == "no_data" for x in check_pass]):
# hhsrs_result = "no_data"
# else:
# raise NotImplementedError("Mixed results not implemented")
# ---------------- Criterion B ----------------
# Check each of the components
# ---------------- Criterion B ----------------
property_boiler = get_element(data["elements"], "Boiler Fuel in Property")
for component, labels in B_COMPONENT_LABELS.items():
for label in labels:
label_data = get_element(data["elements"], label)
# Handle no-data or not-applicable
if label_data["ATTRIBUTE CODE"] in ["UNKNOWN", "NONE", "UNKNOWNG", "UNKNOWNS", "UNKNOWNMAT"] and pd.isnull(label_data["INSTALL DATE"]):
continue
# Special skip conditions for heating
no_boiler_condition = (
property_boiler["ATTRIBUTE CODE"] in ["NONENOCH"]
and component == "central_heating_boiler"
)
other_heating_condition = (
label_data["ATTRIBUTE CODE"] in ["NONENOCH"]
and component == "heating_other"
)
if no_boiler_condition or other_heating_condition:
# append_result(
# decent_homes_meta,
# criteria="B",
# variable=component,
# sub_variable=label,
# result="pass",
# install_date=None,
# expiry_date=None,
# )
continue
# Normal case: evaluate install date + lifetime + remaining life
install_date = pd.to_datetime(label_data["INSTALL DATE"])
if pd.isnull(install_date):
append_result(
decent_homes_meta,
criteria="B",
variable=component,
sub_variable=label,
result="no_data",
install_date=str(install_date),
expiry_date=None,
)
continue
component_lifetime = COMPONENT_LIFESPANS[component][property_type]
is_old = years_between(today.to_pydatetime(), install_date.to_pydatetime()) > component_lifetime
if pd.isnull(label_data["REMAINING LIFE"]):
append_result(
decent_homes_meta,
criteria="B",
variable=component,
sub_variable=label,
result="no_data",
install_date=str(install_date),
expiry_date=None,
)
continue
has_failed = label_data["REMAINING LIFE"] < 0
expiry_date = today.to_pydatetime() + pd.DateOffset(years=label_data["REMAINING LIFE"])
component_result = "fail" if is_old and has_failed else "pass"
# Push into decent_homes_meta
append_result(
decent_homes_meta,
criteria="B",
variable=component,
sub_variable=label,
result=component_result,
install_date=str(install_date),
expiry_date=str(expiry_date),
)
# ---------------- Criterion C ----------------
# Guard: property type string already set earlier
is_flat = (property_info["PROP TYPE"] == "FLA")
# 1) Kitchen age ≤ 20 years
kitchen = get_element(data["elements"], LABEL_KITCHEN)
if kitchen:
kit_install_raw = kitchen["INSTALL DATE"]
kit_install = pd.to_datetime(kit_install_raw)
kit_age_years = years_between(today.to_pydatetime(), kit_install.to_pydatetime())
kitchen_age_result = "pass" if kit_age_years <= CRITERION_C_AGE_LIMITS["kitchen_years_max"] else "fail"
# For transparency, store next renewal as install + 20 years (criterion C perspective)
kit_next_due = today.to_pydatetime() + pd.DateOffset(years=kitchen["REMAINING LIFE"])
else:
raise NotImplementedError("Kitchen data missing - pls check")
append_result(
decent_homes_meta,
criteria="C",
variable="kitchen_less_than_20_years_old",
sub_variable="kitchen_less_than_20_years_old",
result=kitchen_age_result,
install_date=str(kit_install),
expiry_date=str(kit_next_due)
)
# 2) Kitchen adequate space/layout
# Prefer explicit codes if you have them, fall back to text in ATTRIBUTE CODE DESCRIPTION
if kitchen:
kit_attr_desc = kitchen["ATTRIBUTE CODE"]
if kit_attr_desc == "STDKITADQ":
kitchen_adequacy_result = "pass"
else:
raise NotImplementedError("No other observed codes yet")
else:
raise NotImplementedError("Kitchen data missing - pls check")
append_result(
decent_homes_meta,
criteria="C",
variable="kitchen_adequate_space_and_layout",
sub_variable="kitchen_adequate_space_and_layout",
result=kitchen_adequacy_result,
)
# 3) Bathroom age ≤ 30 years
bath = get_element(data["elements"], LABEL_BATHROOM)
if bath:
bth_install_raw = bath["INSTALL DATE"]
bth_install = pd.to_datetime(bth_install_raw)
bth_age_years = years_between(today.to_pydatetime(), bth_install.to_pydatetime())
bathroom_age_result = "pass" if bth_age_years <= CRITERION_C_AGE_LIMITS["bathroom_years_max"] else "fail"
bth_next_due = today.to_pydatetime() + pd.DateOffset(years=bath["REMAINING LIFE"])
else:
raise NotImplementedError("Bathroom data missing - pls check")
append_result(
decent_homes_meta,
criteria="C",
variable="bathroom_less_than_30_years_old",
sub_variable="bathroom_less_than_30_years_old",
result=bathroom_age_result,
install_date=str(bth_install),
expiry_date=bth_next_due
)
# 4) Bathroom/WC appropriately located
if bath:
bth_attr_code = bath["ATTRIBUTE CODE"]
if bth_attr_code in {"STDBTHADQ", "ADPBTHADQ"}:
bathroom_location_result = "pass"
elif bth_attr_code in {"STDBTHINAD"}:
bathroom_location_result = "fail"
else:
raise NotImplementedError(f"No other observed codes yet {bth_attr_code}")
else:
raise NotImplementedError("Bathroom data missing - pls check")
append_result(
decent_homes_meta,
criteria="C",
variable="bathroom_wc_appropriately_located",
sub_variable="bathroom_wc_appropriately_located",
result=bathroom_location_result
)
# 5) Adequate external noise insulation
noise = get_element(data["elements"], LABEL_NOISE)
if noise:
noise_code = noise["ATTRIBUTE CODE"]
if noise_code in {"ADEQUATE"}:
noise_result = "pass"
else:
raise NotImplementedError("No other observed codes yet")
else:
raise NotImplementedError("Noise insulation data missing - pls check")
append_result(
decent_homes_meta,
criteria="C",
variable="adequate_external_noise_insulation",
sub_variable="adequate_external_noise_insulation",
result=noise_result
)
# 6) Adequate common entrance areas (flats only)
if is_flat:
common = get_element(data["elements"], LABEL_COMMON_CIRC)
if common:
circ_desc = common["ATTRIBUTE CODE DESCRIPTION"]
if circ_desc in {"Adequate Circulation Space in Common Area"}:
common_areas_result = "pass"
else:
raise NotImplementedError(f"New description on common area {circ_desc}")
else:
common_areas_result = "no_data"
append_result(
decent_homes_meta=decent_homes_meta,
criteria="C",
variable="adequate_common_entrance_areas",
sub_variable="adequate_common_entrance_areas",
result=common_areas_result,
)
# ---------------- Criterion D ----------------
# heating system type
heating = get_element(data["elements"], "Heating Improvement Required in Property")
if heating:
heat_type_code = heating["ATTRIBUTE CODE"]
if heat_type_code in {"NOTAPPLIC"}:
heating_type_result = "pass"
elif heat_type_code in {"WETINSFULL"}:
heating_type_result = "fail"
else:
raise NotImplementedError("No other observed codes yet")
else:
raise NotImplementedError("Heating element missing in dataset")
append_result(
decent_homes_meta,
criteria="D",
variable="efficient_heating_system_type",
sub_variable="efficient_heating_system_type",
result=heating_type_result
)
# heating distribution
heating_dist = get_element(data["elements"], "Heating Distribution System in Property")
if heating_dist:
dist_code = heating_dist["ATTRIBUTE CODE"]
if dist_code == "UNKNOWN":
# For the observed case, there was no heating and wet heating needed to be installed in full so the value
# was unknown
heating_dist_result = "no_data"
elif dist_code in {"RADIATORS", "ELECWARMAR"}:
# Found one with heating distribution - check with Khalim if this is pass
heating_dist_result = "pass"
else:
print(f"heating_dist {heating_dist}")
print(f"dist-code {dist_code}")
raise NotImplementedError("No other observed codes yet")
else:
raise NotImplementedError("Heating distribution element missing in dataset")
append_result(
decent_homes_meta,
criteria="D",
variable="efficient_heating_distribution",
sub_variable="efficient_heating_distribution",
result=heating_dist_result
)
# insulation
loft = get_element(data["elements"], "Size in mm of Loft Insulation Thickness in Property")
wall = get_element(data["elements"], "Wall Insulation Improvement in External Area")
# To determine how much loft insulation is required
# Loft insulation check (example threshold: ≥ 270mm = pass)
if loft:
# We have a specific code, where further loft insulation is needed - It appears the heating type check has
# already been completed in this dataset and so we just need to check the code
loft_code = loft["ATTRIBUTE CODE"]
if loft_code == "LOFTINSRQD":
loft_result = "fail"
elif loft_code.isnumeric():
loft_result = "pass"
elif loft_code == "UNKNOWN":
loft_result = None
else:
raise NotImplementedError(f"Unknown loft insulation code - pls check {loft_code}")
else:
raise NotImplementedError("Loft insulation data missing - pls check")
if loft_result:
append_result(
decent_homes_meta,
criteria="D",
variable="loft_insulation_sufficient",
sub_variable="loft_insulation_sufficient",
result=loft_result
)
# Wall insulation check
if wall:
wall_code = wall["ATTRIBUTE CODE"]
if wall_code in {"NONE"}: # Means no insulation improvement required
wall_result = "pass"
elif wall_code in {"UNKNOWN"}:
wall_result = "no_data"
elif wall_code in {"SOLID"}:
wall_result = "fail"
else:
raise NotImplementedError(f"No other observed codes yet {wall_code}")
else:
raise NotImplementedError("Wall insulation data missing - pls check")
append_result(
decent_homes_meta,
criteria="D",
variable="wall_insulation_sufficient",
sub_variable="wall_insulation_sufficient",
result=wall_result
)
# ---------------- Criterion A overall ----------------
a_vars = set(HHSRS_MAPPING.keys())
latest_a_results = {r["variable"]: r["result"] for r in decent_homes_meta if r["variable"] in a_vars}
if any(v == "fail" for v in latest_a_results.values()):
criterion_a_result = "fail"
elif all(v == "pass" for v in latest_a_results.values()):
criterion_a_result = "pass"
else:
criterion_a_result = "no_data"
# ---------------- Criterion B overall ----------------
component_results = {}
for component in B_COMPONENT_LABELS.keys():
comp_rows = [r for r in decent_homes_meta if
r["criteria"] == "B" and r["variable"] == component and r["sub_variable"] is not None]
comp_sub_results = [r["result"] for r in comp_rows]
if not comp_sub_results: # no rows at all
comp_result = "no_data"
elif any(r == "fail" for r in comp_sub_results):
comp_result = "fail"
elif all(r == "pass" for r in comp_sub_results):
comp_result = "pass"
else:
comp_result = "no_data"
component_results[component] = comp_result
key_fails = [c for c, r in component_results.items() if c in KEY_COMPONENTS and r == "fail"]
other_fails = [c for c, r in component_results.items() if c in OTHER_COMPONENTS and r == "fail"]
if key_fails:
criterion_b_result = "fail"
elif len(other_fails) >= 2:
criterion_b_result = "fail"
elif any(r == "no_data" for r in component_results.values()):
criterion_b_result = "no_data"
else:
criterion_b_result = "pass"
# ---------------- Criterion C overall ----------------
criterion_c_vars = [
"kitchen_less_than_20_years_old",
"kitchen_adequate_space_and_layout",
"bathroom_less_than_30_years_old",
"bathroom_wc_appropriately_located",
"adequate_external_noise_insulation",
]
if is_flat:
criterion_c_vars.append("adequate_common_entrance_areas")
latest_c_results = {r["variable"]: r["result"] for r in decent_homes_meta if r["variable"] in criterion_c_vars}
count_fails = sum(1 for v in latest_c_results.values() if v == "fail")
# optionally count no_data too if you want strict interpretation
criterion_c_result = "fail" if count_fails >= 3 else "pass"
# ---------------- Criterion D overall ----------------
# Needs to have both efficient geating and distribution so all should pass
criterion_d_vars = [
"efficient_heating_system_type",
"efficient_heating_distribution",
"loft_insulation_sufficient",
"wall_insulation_sufficient",
]
latest_d_results = {r["variable"]: r["result"] for r in decent_homes_meta if r["variable"] in criterion_d_vars}
if any(v == "fail" for v in latest_d_results.values()):
criterion_d_result = "fail"
elif all(v == "pass" for v in latest_d_results.values()):
criterion_d_result = "pass"
else:
criterion_d_result = "no_data"
# ---------------- Append to property_decent_homes ----------------
check_pass = [
criterion_a_result,
criterion_b_result,
criterion_c_result,
criterion_d_result
]
decent_homes_result = "no_data"
if all(v == "pass" for v in check_pass):
decent_homes_result = "pass"
elif any(v == "fail" for v in check_pass):
decent_homes_result = "fail"
elif any(v=="no_data" for v in check_pass):
decent_homes_result = "no_data"
property_decent_homes.append({
"uprn": data.get("UPRN"), # TODO: Need UPRN
"creation_date": datetime.now().date().isoformat(),
"criterion_a": criterion_a_result,
"criterion_b": criterion_b_result,
"criterion_c": criterion_c_result,
"criterion_d": criterion_d_result,
"decent_homes": decent_homes_result,
})
return property_decent_homes[0], decent_homes_meta,

View file

@ -1,386 +1,386 @@
# SQLModel mapping for ConditionReportModel using BaseModel
from sqlmodel import SQLModel, Field, Relationship, Column, JSON
from typing import Optional, List
import uuid
from datetime import datetime
from etl.models.topLevel import BaseModel, Documents
# # SQLModel mapping for ConditionReportModel using BaseModel
# from sqlmodel import SQLModel, Field, Relationship, Column, JSON
# from typing import Optional, List
# import uuid
# from datetime import datetime
# from etl.models.topLevel import BaseModel, Documents
class AssessorDetails(BaseModel, table=True):
assessor_name_and_id: str
elmhurst_id: str
# class AssessorDetails(BaseModel, table=True):
# assessor_name_and_id: str
# elmhurst_id: str
class InspectionAndProject(BaseModel, table=True):
inspection_date: str
# class InspectionAndProject(BaseModel, table=True):
# inspection_date: str
class TheProperty(BaseModel, table=True):
house_type: str
on_which_floor_is_the_flat_located: str
is_there_a_corridor: bool
is_it_heated: bool
it_there_a_balcony: bool
classification_type: str
orientation_front_elevation: str
orientation_in_degrees_front_elevation: str
exposure_zone: str
main_wall_construction: str
# class TheProperty(BaseModel, table=True):
# house_type: str
# on_which_floor_is_the_flat_located: str
# is_there_a_corridor: bool
# is_it_heated: bool
# it_there_a_balcony: bool
# classification_type: str
# orientation_front_elevation: str
# orientation_in_degrees_front_elevation: str
# exposure_zone: str
# main_wall_construction: str
class ElevationInfo(BaseModel, table=True):
elevation_type: str
cavity_wall_depth: str
is_insulation_present: bool
insulation_type: str
# class ElevationInfo(BaseModel, table=True):
# elevation_type: str
# cavity_wall_depth: str
# is_insulation_present: bool
# insulation_type: str
main_elevation: Optional["MainElevation"] = Relationship(back_populates="elevation_info")
# main_elevation: Optional["MainElevation"] = Relationship(back_populates="elevation_info")
elevation_id: Optional[uuid.UUID] = Field(foreign_key="elevation.id")
elevation_table: Optional["Elevation"] = Relationship(back_populates="info")
# elevation_id: Optional[uuid.UUID] = Field(foreign_key="elevation.id")
# elevation_table: Optional["Elevation"] = Relationship(back_populates="info")
class MainElevation(BaseModel, table=True):
elevation_info_id: uuid.UUID = Field(foreign_key="elevationinfo.id")
# class MainElevation(BaseModel, table=True):
# elevation_info_id: uuid.UUID = Field(foreign_key="elevationinfo.id")
#SQLAlcemy things
elevation_info: ElevationInfo = Relationship(back_populates="main_elevation")
# #SQLAlcemy things
# elevation_info: ElevationInfo = Relationship(back_populates="main_elevation")
class Elevation(BaseModel, table=True):
protected_conservatory_or_aonb: bool
material_type: str
visible_signs_of_existing_wall_insulation: str
ground_level_bridge_the_dpc: bool
# class Elevation(BaseModel, table=True):
# protected_conservatory_or_aonb: bool
# material_type: str
# visible_signs_of_existing_wall_insulation: str
# ground_level_bridge_the_dpc: bool
info: List["ElevationInfo"] = Relationship(back_populates="elevation_table")
# info: List["ElevationInfo"] = Relationship(back_populates="elevation_table")
class GeneralInformation(BaseModel, table=True):
assessor_detail_id: uuid.UUID = Field(foreign_key="assessordetails.id")
inspection_and_project_id: uuid.UUID = Field(foreign_key="inspectionandproject.id")
the_property_id: uuid.UUID = Field(foreign_key="theproperty.id")
main_elevation_id: uuid.UUID = Field(foreign_key="mainelevation.id")
elevations_id: uuid.UUID = Field(foreign_key="elevation.id")
# class GeneralInformation(BaseModel, table=True):
# assessor_detail_id: uuid.UUID = Field(foreign_key="assessordetails.id")
# inspection_and_project_id: uuid.UUID = Field(foreign_key="inspectionandproject.id")
# the_property_id: uuid.UUID = Field(foreign_key="theproperty.id")
# main_elevation_id: uuid.UUID = Field(foreign_key="mainelevation.id")
# elevations_id: uuid.UUID = Field(foreign_key="elevation.id")
assessor_details: AssessorDetails = Relationship()
inspection_and_project: InspectionAndProject = Relationship()
the_property: TheProperty = Relationship()
main_elevation: MainElevation = Relationship()
elevations: Elevation = Relationship()
# assessor_details: AssessorDetails = Relationship()
# inspection_and_project: InspectionAndProject = Relationship()
# the_property: TheProperty = Relationship()
# main_elevation: MainElevation = Relationship()
# elevations: Elevation = Relationship()
class PropertyAccess(BaseModel, table=True):
are_there_any_road_restriction_in_the_locality: bool
is_on_street_parking_available: bool
are_there_any_overhead_wires_or_cables: bool
is_the_access_gated: bool
is_there_restricted_space_for_contractors_to_access_the_wall_area: bool
is_there_restricted_space_for_contractors_to_access_the_roof_area: bool
more_than_1_5_meters_in_width_to_fence_or__along_the_full_gable_elevation: bool
is_access_to_the_rear_provided_by_use_of_a_ginnel: bool
is_access_to_the_rear_provided_by_use_of_a_secured_alleyway: bool
# class PropertyAccess(BaseModel, table=True):
# are_there_any_road_restriction_in_the_locality: bool
# is_on_street_parking_available: bool
# are_there_any_overhead_wires_or_cables: bool
# is_the_access_gated: bool
# is_there_restricted_space_for_contractors_to_access_the_wall_area: bool
# is_there_restricted_space_for_contractors_to_access_the_roof_area: bool
# more_than_1_5_meters_in_width_to_fence_or__along_the_full_gable_elevation: bool
# is_access_to_the_rear_provided_by_use_of_a_ginnel: bool
# is_access_to_the_rear_provided_by_use_of_a_secured_alleyway: bool
class ExternalElevation(BaseModel, table=True):
structural_defects_of_elevation: str
does_any_structural_defect_need_resolving_before_retrofit: bool
any_signs_of_water_penetration_caused_by_failed_rainwater_goods_or_pipework: bool
are_there_any_visible_signs_of_movement: bool
are_there_any_visible_signs_of_cracking_to_the_existing_external_finish: bool
# class ExternalElevation(BaseModel, table=True):
# structural_defects_of_elevation: str
# does_any_structural_defect_need_resolving_before_retrofit: bool
# any_signs_of_water_penetration_caused_by_failed_rainwater_goods_or_pipework: bool
# are_there_any_visible_signs_of_movement: bool
# are_there_any_visible_signs_of_cracking_to_the_existing_external_finish: bool
class ExternalElevationFront(BaseModel, table=True):
external_elevation_id: uuid.UUID = Field(foreign_key="externalelevation.id")
external_elevation: ExternalElevation = Relationship()
# class ExternalElevationFront(BaseModel, table=True):
# external_elevation_id: uuid.UUID = Field(foreign_key="externalelevation.id")
# external_elevation: ExternalElevation = Relationship()
class ExternalElevationRear(BaseModel, table=True):
do_all_answers_for_the_front_elevation_apply_to_this_wall: bool
external_elevation_id: Optional[uuid.UUID] = Field(foreign_key="externalelevation.id")
external_elevation: Optional[ExternalElevation] = Relationship()
# class ExternalElevationRear(BaseModel, table=True):
# do_all_answers_for_the_front_elevation_apply_to_this_wall: bool
# external_elevation_id: Optional[uuid.UUID] = Field(foreign_key="externalelevation.id")
# external_elevation: Optional[ExternalElevation] = Relationship()
class ExternalElevationGableOne(BaseModel, table=True):
do_all_answers_for_the_front_elevation_apply_to_this_wall: bool
external_elevation_id: Optional[uuid.UUID] = Field(foreign_key="externalelevation.id")
external_elevation: Optional[ExternalElevation] = Relationship()
# class ExternalElevationGableOne(BaseModel, table=True):
# do_all_answers_for_the_front_elevation_apply_to_this_wall: bool
# external_elevation_id: Optional[uuid.UUID] = Field(foreign_key="externalelevation.id")
# external_elevation: Optional[ExternalElevation] = Relationship()
class ExternalElevationGableTwo(BaseModel, table=True):
is_there_a_fourth_external_elevation: bool
external_elevation_id: Optional[uuid.UUID] = Field(foreign_key="externalelevation.id")
# class ExternalElevationGableTwo(BaseModel, table=True):
# is_there_a_fourth_external_elevation: bool
# external_elevation_id: Optional[uuid.UUID] = Field(foreign_key="externalelevation.id")
class ConservatoryOrOutbuilding(BaseModel, table=True):
is_there_a_conservatory: bool
is_there_a_cellar_present: bool
is_there_an_outbuilding: bool
# class ConservatoryOrOutbuilding(BaseModel, table=True):
# is_there_a_conservatory: bool
# is_there_a_cellar_present: bool
# is_there_an_outbuilding: bool
class AccessAndElevations(BaseModel, table=True):
property_access_id: uuid.UUID = Field(foreign_key="propertyaccess.id")
external_elevation_front_id: uuid.UUID = Field(foreign_key="externalelevationfront.id")
external_elevation_back_id: uuid.UUID = Field(foreign_key="externalelevationrear.id")
external_elevation_gable_one_id: uuid.UUID = Field(foreign_key="externalelevationgableone.id")
external_elevation_gable_two_id: uuid.UUID = Field(foreign_key="externalelevationgabletwo.id")
conservatory_or_out_building_id: uuid.UUID = Field(foreign_key="conservatoryoroutbuilding.id")
# class AccessAndElevations(BaseModel, table=True):
# property_access_id: uuid.UUID = Field(foreign_key="propertyaccess.id")
# external_elevation_front_id: uuid.UUID = Field(foreign_key="externalelevationfront.id")
# external_elevation_back_id: uuid.UUID = Field(foreign_key="externalelevationrear.id")
# external_elevation_gable_one_id: uuid.UUID = Field(foreign_key="externalelevationgableone.id")
# external_elevation_gable_two_id: uuid.UUID = Field(foreign_key="externalelevationgabletwo.id")
# conservatory_or_out_building_id: uuid.UUID = Field(foreign_key="conservatoryoroutbuilding.id")
property_access: PropertyAccess = Relationship()
external_elevation_front: ExternalElevationFront = Relationship()
external_elevation_back: ExternalElevationRear = Relationship()
external_elevation_gable_one: ExternalElevationGableOne = Relationship()
external_elevation_gable_two: ExternalElevationGableTwo = Relationship()
conservatory_or_out_building: ConservatoryOrOutbuilding = Relationship()
# property_access: PropertyAccess = Relationship()
# external_elevation_front: ExternalElevationFront = Relationship()
# external_elevation_back: ExternalElevationRear = Relationship()
# external_elevation_gable_one: ExternalElevationGableOne = Relationship()
# external_elevation_gable_two: ExternalElevationGableTwo = Relationship()
# conservatory_or_out_building: ConservatoryOrOutbuilding = Relationship()
class VentilationInfo(BaseModel, table=True):
is_there_a_ventilation_system_present_in_the_room: bool
any_damp_mould_or_excessive_condensation_within_the_room: bool
are_there_sufficient_undercuts_on_the_closed_door: str
is_there_any_open_flue_heating_appliances_within_the_room: bool
# class VentilationInfo(BaseModel, table=True):
# is_there_a_ventilation_system_present_in_the_room: bool
# any_damp_mould_or_excessive_condensation_within_the_room: bool
# are_there_sufficient_undercuts_on_the_closed_door: str
# is_there_any_open_flue_heating_appliances_within_the_room: bool
class WindowsInfo(BaseModel, table=True):
does_the_room_have_any_windows: bool
condition_of_the_windows: Optional[str] = None
do_the_windows_have_trickle_vents: Optional[bool] = None
are_the_windows_openable: Optional[bool] = None
input_trickle_vent_product_code_or_measurement: Optional[str] = None
# class WindowsInfo(BaseModel, table=True):
# does_the_room_have_any_windows: bool
# condition_of_the_windows: Optional[str] = None
# do_the_windows_have_trickle_vents: Optional[bool] = None
# are_the_windows_openable: Optional[bool] = None
# input_trickle_vent_product_code_or_measurement: Optional[str] = None
class RoomInfo(BaseModel, table=True):
overall_condition_of_the_room: str
does_the_room_have_any_defects: str
are_there_any_sloped_ceiling_areas: Optional[bool] = None
# class RoomInfo(BaseModel, table=True):
# overall_condition_of_the_room: str
# does_the_room_have_any_defects: str
# are_there_any_sloped_ceiling_areas: Optional[bool] = None
windows_info_id: uuid.UUID = Field(foreign_key="windowsinfo.id")
ventilation_info_id: uuid.UUID = Field(foreign_key="ventilationinfo.id")
# windows_info_id: uuid.UUID = Field(foreign_key="windowsinfo.id")
# ventilation_info_id: uuid.UUID = Field(foreign_key="ventilationinfo.id")
windows_info: WindowsInfo = Relationship()
ventilation_info: VentilationInfo = Relationship()
# windows_info: WindowsInfo = Relationship()
# ventilation_info: VentilationInfo = Relationship()
class Hallway(BaseModel, table=True):
is_there_a_hallway: bool
room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
room_info: Optional[RoomInfo] = Relationship()
# class Hallway(BaseModel, table=True):
# is_there_a_hallway: bool
# room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
# room_info: Optional[RoomInfo] = Relationship()
class LivingRoom(BaseModel, table=True):
room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
room_info: Optional[RoomInfo] = Relationship()
# class LivingRoom(BaseModel, table=True):
# room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
# room_info: Optional[RoomInfo] = Relationship()
class DiningRoom(BaseModel, table=True):
is_there_a_dining_room: bool
room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
room_info: Optional[RoomInfo] = Relationship()
# class DiningRoom(BaseModel, table=True):
# is_there_a_dining_room: bool
# room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
# room_info: Optional[RoomInfo] = Relationship()
class Kitchen(BaseModel, table=True):
room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
room_info: Optional[RoomInfo] = Relationship()
is_there_a_cooker_hood_present_in_the_room: bool
# class Kitchen(BaseModel, table=True):
# room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
# room_info: Optional[RoomInfo] = Relationship()
# is_there_a_cooker_hood_present_in_the_room: bool
class Utility(BaseModel, table=True):
is_there_a_utility_room: bool
room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
room_info: Optional[RoomInfo] = Relationship()
# class Utility(BaseModel, table=True):
# is_there_a_utility_room: bool
# room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
# room_info: Optional[RoomInfo] = Relationship()
class WC(BaseModel, table=True):
is_there_a_seperated_wc: bool
room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
room_info: Optional[RoomInfo] = Relationship()
# class WC(BaseModel, table=True):
# is_there_a_seperated_wc: bool
# room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
# room_info: Optional[RoomInfo] = Relationship()
class Landing(BaseModel, table=True):
is_there_a_landing: bool
room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
room_info: Optional[RoomInfo] = Relationship()
# class Landing(BaseModel, table=True):
# is_there_a_landing: bool
# room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
# room_info: Optional[RoomInfo] = Relationship()
class LoftSpace(BaseModel, table=True):
is_the_main_loft_space_accessible: str
is_there_more_than_one_loft_space: bool
# class LoftSpace(BaseModel, table=True):
# is_the_main_loft_space_accessible: str
# is_there_more_than_one_loft_space: bool
class RoomInRoof(BaseModel, table=True):
is_there_a_room_in_roof: bool
room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
room_info: Optional[RoomInfo] = Relationship()
# class RoomInRoof(BaseModel, table=True):
# is_there_a_room_in_roof: bool
# room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
# room_info: Optional[RoomInfo] = Relationship()
class Bedroom(BaseModel, table=True):
double_or_single_bedroom: str
room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
room_info: Optional[RoomInfo] = Relationship()
rooms_id: uuid.UUID = Field(foreign_key="rooms.id")
rooms: Optional["Rooms"] = Relationship(back_populates="bedrooms")
# class Bedroom(BaseModel, table=True):
# double_or_single_bedroom: str
# room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
# room_info: Optional[RoomInfo] = Relationship()
# rooms_id: uuid.UUID = Field(foreign_key="rooms.id")
# rooms: Optional["Rooms"] = Relationship(back_populates="bedrooms")
class Bathroom(BaseModel, table=True):
is_this_an_ensuite_bathroom: bool
room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
room_info: Optional[RoomInfo] = Relationship()
# class Bathroom(BaseModel, table=True):
# is_this_an_ensuite_bathroom: bool
# room_info_id: Optional[uuid.UUID] = Field(foreign_key="roominfo.id")
# room_info: Optional[RoomInfo] = Relationship()
rooms_id: uuid.UUID = Field(foreign_key="rooms.id")
rooms: Optional["Rooms"] = Relationship(back_populates="bathrooms")
# rooms_id: uuid.UUID = Field(foreign_key="rooms.id")
# rooms: Optional["Rooms"] = Relationship(back_populates="bathrooms")
class Rooms(BaseModel, table=True):
hallway_id: uuid.UUID = Field(foreign_key="hallway.id")
hallway: Hallway = Relationship()
# class Rooms(BaseModel, table=True):
# hallway_id: uuid.UUID = Field(foreign_key="hallway.id")
# hallway: Hallway = Relationship()
living_room_id: uuid.UUID = Field(foreign_key="livingroom.id")
living_room: LivingRoom = Relationship()
# living_room_id: uuid.UUID = Field(foreign_key="livingroom.id")
# living_room: LivingRoom = Relationship()
dining_room_id: uuid.UUID = Field(foreign_key="diningroom.id")
dining_room: DiningRoom = Relationship()
# dining_room_id: uuid.UUID = Field(foreign_key="diningroom.id")
# dining_room: DiningRoom = Relationship()
kitchen_id: uuid.UUID = Field(foreign_key="kitchen.id")
kitchen: Kitchen = Relationship()
# kitchen_id: uuid.UUID = Field(foreign_key="kitchen.id")
# kitchen: Kitchen = Relationship()
utility_id: uuid.UUID = Field(foreign_key="utility.id")
utility: Utility = Relationship()
# utility_id: uuid.UUID = Field(foreign_key="utility.id")
# utility: Utility = Relationship()
wash_chamber_id: uuid.UUID = Field(foreign_key="wc.id")
wash_chamber: WC = Relationship()
# wash_chamber_id: uuid.UUID = Field(foreign_key="wc.id")
# wash_chamber: WC = Relationship()
landing_id: uuid.UUID = Field(foreign_key="landing.id")
landing: Landing = Relationship()
# landing_id: uuid.UUID = Field(foreign_key="landing.id")
# landing: Landing = Relationship()
loft_space_id: uuid.UUID = Field(foreign_key="loftspace.id")
loft_space: LoftSpace = Relationship()
# loft_space_id: uuid.UUID = Field(foreign_key="loftspace.id")
# loft_space: LoftSpace = Relationship()
room_in_roof_id: uuid.UUID = Field(foreign_key="roominroof.id")
room_in_roof: RoomInRoof = Relationship()
# room_in_roof_id: uuid.UUID = Field(foreign_key="roominroof.id")
# room_in_roof: RoomInRoof = Relationship()
bedrooms: List[Bedroom] = Relationship(back_populates="rooms")
bathrooms: List[Bathroom] = Relationship(back_populates="rooms")
# bedrooms: List[Bedroom] = Relationship(back_populates="rooms")
# bathrooms: List[Bathroom] = Relationship(back_populates="rooms")
class GeneralConditionHeatingSystem(BaseModel, table=True):
is_the_heating_system_in_working_order: bool
does_the_occupant_have_a_smart_meter: bool
are_there_any_smart_monitoring_devices: bool
are_the_gas_and_electricity_meters_accessible: bool
dual_or_single_electric_meter: str
# class GeneralConditionHeatingSystem(BaseModel, table=True):
# is_the_heating_system_in_working_order: bool
# does_the_occupant_have_a_smart_meter: bool
# are_there_any_smart_monitoring_devices: bool
# are_the_gas_and_electricity_meters_accessible: bool
# dual_or_single_electric_meter: str
class MainHeatingOne(BaseModel, table=True):
as_defined_by: str
fuel: str
type: str
# class MainHeatingOne(BaseModel, table=True):
# as_defined_by: str
# fuel: str
# type: str
class MainHeatingTwo(BaseModel, table=True):
is_there_a_main_heating_two: bool
# class MainHeatingTwo(BaseModel, table=True):
# is_there_a_main_heating_two: bool
class SecondaryHeating(BaseModel, table=True):
is_there_a_secondary_heating: bool
fuel: str
electric_heating_type: str
gas_heating_type: str
# class SecondaryHeating(BaseModel, table=True):
# is_there_a_secondary_heating: bool
# fuel: str
# electric_heating_type: str
# gas_heating_type: str
class HeatingByRoom(BaseModel, table=True):
rooms_heated_by_main_system_one: List[str] = Field(sa_column=Column(JSON))
rooms_heated_by_main_system_two: List[str] = Field(sa_column=Column(JSON))
rooms_heated_by_secondary_heating: List[str] = Field(sa_column=Column(JSON))
are_there_any_partially_heated_rooms: bool
partially_heated_rooms: Optional[List[str]] = Field(sa_column=Column(JSON))
are_there_any_unheated_rooms: bool
unheated_rooms: List[str] = Field(sa_column=Column(JSON))
# class HeatingByRoom(BaseModel, table=True):
# rooms_heated_by_main_system_one: List[str] = Field(sa_column=Column(JSON))
# rooms_heated_by_main_system_two: List[str] = Field(sa_column=Column(JSON))
# rooms_heated_by_secondary_heating: List[str] = Field(sa_column=Column(JSON))
# are_there_any_partially_heated_rooms: bool
# partially_heated_rooms: Optional[List[str]] = Field(sa_column=Column(JSON))
# are_there_any_unheated_rooms: bool
# unheated_rooms: List[str] = Field(sa_column=Column(JSON))
class Renewables(BaseModel, table=True):
is_there_any_renewable_energy_system_in_place: bool
suitable_roof_orientation_for_solar_pv_water: str
is_there_a_water_tank: bool
type: str
size: str
tank_location: str
is_the_tank_insulated: bool
type_of_insulation: str
thickness_of_insulation_in_mm: int
# class Renewables(BaseModel, table=True):
# is_there_any_renewable_energy_system_in_place: bool
# suitable_roof_orientation_for_solar_pv_water: str
# is_there_a_water_tank: bool
# type: str
# size: str
# tank_location: str
# is_the_tank_insulated: bool
# type_of_insulation: str
# thickness_of_insulation_in_mm: int
class HeatingSystem(BaseModel, table=True):
general_condition_id: uuid.UUID = Field(foreign_key="generalconditionheatingsystem.id")
general_condition: GeneralConditionHeatingSystem = Relationship()
# class HeatingSystem(BaseModel, table=True):
# general_condition_id: uuid.UUID = Field(foreign_key="generalconditionheatingsystem.id")
# general_condition: GeneralConditionHeatingSystem = Relationship()
main_heating_one_id: uuid.UUID = Field(foreign_key="mainheatingone.id")
main_heating_one: MainHeatingOne = Relationship()
# main_heating_one_id: uuid.UUID = Field(foreign_key="mainheatingone.id")
# main_heating_one: MainHeatingOne = Relationship()
main_heating_two_id: uuid.UUID = Field(foreign_key="mainheatingtwo.id")
main_heating_two: MainHeatingTwo = Relationship()
# main_heating_two_id: uuid.UUID = Field(foreign_key="mainheatingtwo.id")
# main_heating_two: MainHeatingTwo = Relationship()
secondary_heating_id: uuid.UUID = Field(foreign_key="secondaryheating.id")
secondary_heating: SecondaryHeating = Relationship()
# secondary_heating_id: uuid.UUID = Field(foreign_key="secondaryheating.id")
# secondary_heating: SecondaryHeating = Relationship()
heating_by_room_id: uuid.UUID = Field(foreign_key="heatingbyroom.id")
heating_by_room: HeatingByRoom = Relationship()
# heating_by_room_id: uuid.UUID = Field(foreign_key="heatingbyroom.id")
# heating_by_room: HeatingByRoom = Relationship()
renewables_id: uuid.UUID = Field(foreign_key="renewables.id")
renewables: Renewables = Relationship()
# renewables_id: uuid.UUID = Field(foreign_key="renewables.id")
# renewables: Renewables = Relationship()
class Occupant(BaseModel, table=True):
name: str
have_evidence_of_12_months_of_fuel_bill_data: bool
total_number_of_occupants: int
no_of_adult_occupants: int
no_of_child_occupants: int
no_of_occupant_of_a_pensionable_age: int
are_there_any_vulnerable_people: bool
is_there_anyone_with_a_disability: bool
status_of_occupant: str
landlord_wrote_that_the_tenent_agrees_assessment_been_supplied: bool
# class Occupant(BaseModel, table=True):
# name: str
# have_evidence_of_12_months_of_fuel_bill_data: bool
# total_number_of_occupants: int
# no_of_adult_occupants: int
# no_of_child_occupants: int
# no_of_occupant_of_a_pensionable_age: int
# are_there_any_vulnerable_people: bool
# is_there_anyone_with_a_disability: bool
# status_of_occupant: str
# landlord_wrote_that_the_tenent_agrees_assessment_been_supplied: bool
class EnergyUse(BaseModel, table=True):
property_tenure: str
who_is_the_electricity_payer: str
# class EnergyUse(BaseModel, table=True):
# property_tenure: str
# who_is_the_electricity_payer: str
class HeatingFromConditionReport(BaseModel, table=True):
room_stat_in_temperature_in_celsius: Optional[str] = None
room_stat_location: Optional[str] = None
is_the_heating_pattern_known: Optional[str] = None
# class HeatingFromConditionReport(BaseModel, table=True):
# room_stat_in_temperature_in_celsius: Optional[str] = None
# room_stat_location: Optional[str] = None
# is_the_heating_pattern_known: Optional[str] = None
class ShowerAndBath(BaseModel, table=True):
shower_type: str
do_you_know_the_no_of_showers_per_day_per_week: bool
please_input_no_of_showers_and_specify_a_day_or_a_week: str
do_you_know_the_number_of_baths_per_day_or_per_week: str
# class ShowerAndBath(BaseModel, table=True):
# shower_type: str
# do_you_know_the_no_of_showers_per_day_per_week: bool
# please_input_no_of_showers_and_specify_a_day_or_a_week: str
# do_you_know_the_number_of_baths_per_day_or_per_week: str
class FridgeAndFreezers(BaseModel, table=True):
no_of_stand_alone_seperate_fridges: int
no_of_stand_alone_seperate_freezers: int
no_of_stand_alone_or_integrated_fridge_freezers: int
# class FridgeAndFreezers(BaseModel, table=True):
# no_of_stand_alone_seperate_fridges: int
# no_of_stand_alone_seperate_freezers: int
# no_of_stand_alone_or_integrated_fridge_freezers: int
class Cooker(BaseModel,table=True):
range_fuel: str
normal_large_range: str
cooker_type: str
# class Cooker(BaseModel,table=True):
# range_fuel: str
# normal_large_range: str
# cooker_type: str
class TumbleDryer(BaseModel, table=True):
percentage_of_annual_use: int
space_for_outdoor_drying: bool
# class TumbleDryer(BaseModel, table=True):
# percentage_of_annual_use: int
# space_for_outdoor_drying: bool
class OccupantAssessment(BaseModel, table=True):
occupant_id: uuid.UUID = Field(foreign_key="occupant.id")
occupant: Occupant = Relationship()
# class OccupantAssessment(BaseModel, table=True):
# occupant_id: uuid.UUID = Field(foreign_key="occupant.id")
# occupant: Occupant = Relationship()
energy_use_id: uuid.UUID = Field(foreign_key="energyuse.id")
energy_use: EnergyUse = Relationship()
# energy_use_id: uuid.UUID = Field(foreign_key="energyuse.id")
# energy_use: EnergyUse = Relationship()
heating_id: uuid.UUID = Field(foreign_key="heatingfromconditionreport.id")
heating: HeatingFromConditionReport = Relationship()
# heating_id: uuid.UUID = Field(foreign_key="heatingfromconditionreport.id")
# heating: HeatingFromConditionReport = Relationship()
shower_and_bath_id: uuid.UUID = Field(foreign_key="showerandbath.id")
shower_and_bath: ShowerAndBath = Relationship()
# shower_and_bath_id: uuid.UUID = Field(foreign_key="showerandbath.id")
# shower_and_bath: ShowerAndBath = Relationship()
# appliances: Optional[Appliances]
# appliances_id
# # appliances: Optional[Appliances]
# # appliances_id
fridge_and_freezers_id: uuid.UUID = Field(foreign_key="fridgeandfreezers.id")
fridge_and_freezers: FridgeAndFreezers = Relationship()
# fridge_and_freezers_id: uuid.UUID = Field(foreign_key="fridgeandfreezers.id")
# fridge_and_freezers: FridgeAndFreezers = Relationship()
cooker_id: uuid.UUID = Field(foreign_key="cooker.id")
cooker: Cooker = Relationship()
# cooker_id: uuid.UUID = Field(foreign_key="cooker.id")
# cooker: Cooker = Relationship()
tumble_dryer_id: uuid.UUID = Field(foreign_key="tumbledryer.id")
tumble_dryer: TumbleDryer = Relationship()
# tumble_dryer_id: uuid.UUID = Field(foreign_key="tumbledryer.id")
# tumble_dryer: TumbleDryer = Relationship()
class ConditionReportModel(BaseModel, table=True):
project_site_name: str
property_reference_code: str
property_address: str
postcode: str
# class ConditionReportModel(BaseModel, table=True):
# project_site_name: str
# property_reference_code: str
# property_address: str
# postcode: str
general_information_id: uuid.UUID = Field(foreign_key="generalinformation.id")
general_information: GeneralInformation = Relationship()
# general_information_id: uuid.UUID = Field(foreign_key="generalinformation.id")
# general_information: GeneralInformation = Relationship()
access_and_elevations_id: uuid.UUID = Field(foreign_key="accessandelevations.id")
access_and_elevations: AccessAndElevations = Relationship()
# access_and_elevations_id: uuid.UUID = Field(foreign_key="accessandelevations.id")
# access_and_elevations: AccessAndElevations = Relationship()
rooms_id: uuid.UUID = Field(foreign_key="rooms.id")
rooms: Rooms = Relationship()
# rooms_id: uuid.UUID = Field(foreign_key="rooms.id")
# rooms: Rooms = Relationship()
heating_system_id: uuid.UUID = Field(foreign_key="heatingsystem.id")
heating_system: HeatingSystem = Relationship()
# heating_system_id: uuid.UUID = Field(foreign_key="heatingsystem.id")
# heating_system: HeatingSystem = Relationship()
occupancy_assessment_id: uuid.UUID = Field(foreign_key="occupantassessment.id")
occupancy_assessment: OccupantAssessment = Relationship()
# occupancy_assessment_id: uuid.UUID = Field(foreign_key="occupantassessment.id")
# occupancy_assessment: OccupantAssessment = Relationship()

View file

@ -1,335 +1,335 @@
from sqlmodel import Field, SQLModel, Relationship
import uuid
from typing import Optional, List
from datetime import datetime
from pydantic import EmailStr
from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import UUID
from etl.models.topLevel import BaseModel, Documents
# from sqlmodel import Field, SQLModel, Relationship
# import uuid
# from typing import Optional, List
# from datetime import datetime
# from pydantic import EmailStr
# from sqlalchemy import Column
# from sqlalchemy.dialects.postgresql import UUID
# # from etl.models.topLevel import BaseModel, Documents
class PreSiteNote(BaseModel, table=True):
summary_info_id: uuid.UUID = Field(
foreign_key="presitenotessummaryinfo.id",
nullable=False
)
# class PreSiteNote(BaseModel, table=True):
# summary_info_id: uuid.UUID = Field(
# foreign_key="presitenotessummaryinfo.id",
# nullable=False
# )
summary_info: Optional["PreSiteNotesSummaryInfo"] = Relationship(back_populates="pre_site_notes")
# summary_info: Optional["PreSiteNotesSummaryInfo"] = Relationship(back_populates="pre_site_notes")
# Assessor Info
assessor_id: uuid.UUID = Field(
foreign_key="assessorinfo.id",
nullable=False
)
# # Assessor Info
# assessor_id: uuid.UUID = Field(
# foreign_key="assessorinfo.id",
# nullable=False
# )
assessor: Optional["AssessorInfo"] = Relationship(back_populates="pre_site_notes")
# assessor: Optional["AssessorInfo"] = Relationship(back_populates="pre_site_notes")
pre_site_note_description_id: uuid.UUID = Field(
foreign_key="propertydescription.id",
nullable=True
)
# pre_site_note_description_id: uuid.UUID = Field(
# foreign_key="propertydescription.id",
# nullable=True
# )
pre_site_note_description: Optional["PropertyDescription"] = Relationship(back_populates="pre_site_notes")
# pre_site_note_description: Optional["PropertyDescription"] = Relationship(back_populates="pre_site_notes")
class Dimension(BaseModel, table=True):
floor_area_m2: float
room_height_m: float
loss_perimeter_m: float
party_wall_length_m: float
property_detail_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
property_detail: Optional["PropertyDetail"] = Relationship(back_populates="dimensions")
# class Dimension(BaseModel, table=True):
# floor_area_m2: float
# room_height_m: float
# loss_perimeter_m: float
# party_wall_length_m: float
# property_detail_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
# property_detail: Optional["PropertyDetail"] = Relationship(back_populates="dimensions")
class Walls(BaseModel, table=True):
construction: str
insulation: str
insulation_thickness_mm: str
wall_thickness_measured: bool
wall_thickness_mm: Optional[int]
u_value_known: bool
u_value_w_m2_k: Optional[float]
dry_lining: bool
alternative_wall_present: bool
# class Walls(BaseModel, table=True):
# construction: str
# insulation: str
# insulation_thickness_mm: str
# wall_thickness_measured: bool
# wall_thickness_mm: Optional[int]
# u_value_known: bool
# u_value_w_m2_k: Optional[float]
# dry_lining: bool
# alternative_wall_present: bool
class Roofs(BaseModel, table=True):
construction: str
insulation_type: str
insulation_thickness: str
u_value_known: bool
# class Roofs(BaseModel, table=True):
# construction: str
# insulation_type: str
# insulation_thickness: str
# u_value_known: bool
class Floors(BaseModel, table=True):
floor_type: str
ground_floor_construction: str
ground_floor_insulation_type: Optional[str] = ""
floor_insulation_thickness_mm: Optional[float] = -1
u_value_known: bool
# class Floors(BaseModel, table=True):
# floor_type: str
# ground_floor_construction: str
# ground_floor_insulation_type: Optional[str] = ""
# floor_insulation_thickness_mm: Optional[float] = -1
# u_value_known: bool
class Windows(BaseModel, table=True):
glazing_type: str
area_m2: float
roof_window: bool
orientation: str
u_value_w_m2_k: int
g_value: int
property_detail_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
property_detail: Optional["PropertyDetail"] = Relationship(back_populates="windows")
# class Windows(BaseModel, table=True):
# glazing_type: str
# area_m2: float
# roof_window: bool
# orientation: str
# u_value_w_m2_k: int
# g_value: int
# property_detail_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
# property_detail: Optional["PropertyDetail"] = Relationship(back_populates="windows")
class PropertyDetail(BaseModel, table=True):
age_band: str
wall_id: Optional[uuid.UUID] = Field(default=None, foreign_key="walls.id")
roof_id: Optional[uuid.UUID] = Field(default=None, foreign_key="roofs.id")
floor_id: Optional[uuid.UUID] = Field(default=None, foreign_key="floors.id")
# class PropertyDetail(BaseModel, table=True):
# age_band: str
# wall_id: Optional[uuid.UUID] = Field(default=None, foreign_key="walls.id")
# roof_id: Optional[uuid.UUID] = Field(default=None, foreign_key="roofs.id")
# floor_id: Optional[uuid.UUID] = Field(default=None, foreign_key="floors.id")
# Relationships
dimensions: List[Dimension] = Relationship(back_populates="property_detail")
windows: List[Windows] = Relationship(back_populates="property_detail")
# # Relationships
# dimensions: List[Dimension] = Relationship(back_populates="property_detail")
# windows: List[Windows] = Relationship(back_populates="property_detail")
class Door(BaseModel, table=True):
no_of_doors: int
no_of_insulated_doors: int
u_value_w_m2_k: Optional[str]
# class Door(BaseModel, table=True):
# no_of_doors: int
# no_of_insulated_doors: int
# u_value_w_m2_k: Optional[str]
property_description: Optional["PropertyDescription"] = Relationship(back_populates="door")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="door")
class VentilationAndCooling(BaseModel, table=True):
no_of_open_fireplaces: int
ventilation_type: str
space_cooling_system_present: bool
# class VentilationAndCooling(BaseModel, table=True):
# no_of_open_fireplaces: int
# ventilation_type: str
# space_cooling_system_present: bool
property_description: Optional["PropertyDescription"] = Relationship(back_populates="ventilation_and_cooling")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="ventilation_and_cooling")
class Lighting(BaseModel, table=True):
total_no_of_light_fittings: int
total_no_of_lel_fittings: int
# class Lighting(BaseModel, table=True):
# total_no_of_light_fittings: int
# total_no_of_lel_fittings: int
property_description: Optional["PropertyDescription"] = Relationship(back_populates="lighting")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="lighting")
class HeatingSystemControls(BaseModel, table=True):
control_type: str
flue_type: str
fan_assisted_flue: bool
heat_emitter_type: str
electricity_meter_type: Optional[str] = ""
mains_gas_available: Optional[bool] = False
# class HeatingSystemControls(BaseModel, table=True):
# control_type: str
# flue_type: str
# fan_assisted_flue: bool
# heat_emitter_type: str
# electricity_meter_type: Optional[str] = ""
# mains_gas_available: Optional[bool] = False
class HeatingFromPreSiteNotes(BaseModel, table=True):
type: str
heating_source: str
efficiency_source: str
heating_fuel: str
brand_name: str
model_name: str
model_qualifer: str
sap_2009_table: Optional[str] = ""
percentage_of_heated_floor_area_served: Optional[str] = ""
controls_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingsystemcontrols.id")
# class HeatingFromPreSiteNotes(BaseModel, table=True):
# type: str
# heating_source: str
# efficiency_source: str
# heating_fuel: str
# brand_name: str
# model_name: str
# model_qualifer: str
# sap_2009_table: Optional[str] = ""
# percentage_of_heated_floor_area_served: Optional[str] = ""
# controls_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingsystemcontrols.id")
property_description: Optional["PropertyDescription"] = Relationship(
back_populates="main_heating", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating_id]"}
)
property_description2: Optional["PropertyDescription"] = Relationship(
back_populates="main_heating2", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating2_id]"}
)
# property_description: Optional["PropertyDescription"] = Relationship(
# back_populates="main_heating", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating_id]"}
# )
# property_description2: Optional["PropertyDescription"] = Relationship(
# back_populates="main_heating2", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating2_id]"}
# )
class HeatingType(BaseModel, table=True):
heating_type: str
fuel_type: str
# class HeatingType(BaseModel, table=True):
# heating_type: str
# fuel_type: str
property_description: Optional["PropertyDescription"] = Relationship(back_populates="secondary_heating_type")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="secondary_heating_type")
class WaterHeating(BaseModel, table=True):
heating_type: str
fuel_type: str
# class WaterHeating(BaseModel, table=True):
# heating_type: str
# fuel_type: str
property_description: Optional["PropertyDescription"] = Relationship(back_populates="water_heating")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="water_heating")
class HotWaterCylinder(BaseModel, table=True):
volume: str
insulation_type: str
insulation_thickness: str
thermostat: bool
# class HotWaterCylinder(BaseModel, table=True):
# volume: str
# insulation_type: str
# insulation_thickness: str
# thermostat: bool
property_description: Optional["PropertyDescription"] = Relationship(back_populates="hot_water_cylinder")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="hot_water_cylinder")
class SolarWaterHeating(BaseModel, table=True):
solar_water_heating_details_known: bool
# class SolarWaterHeating(BaseModel, table=True):
# solar_water_heating_details_known: bool
property_description: Optional["PropertyDescription"] = Relationship(back_populates="solar_water_heating")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="solar_water_heating")
class ShowerAndBaths(BaseModel, table=True):
no_of_rooms_with_baths_and_or_shower: int
no_of_rooms_with_mixer_shower_and_no_baths: int
no_of_rooms_with_mixer_shower_and_baths: int
# class ShowerAndBaths(BaseModel, table=True):
# no_of_rooms_with_baths_and_or_shower: int
# no_of_rooms_with_mixer_shower_and_no_baths: int
# no_of_rooms_with_mixer_shower_and_baths: int
property_description: Optional["PropertyDescription"] = Relationship(back_populates="shower_and_baths")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="shower_and_baths")
class FlueGasHeatRecoverySystem(BaseModel, table=True):
fghrs_present: bool
# class FlueGasHeatRecoverySystem(BaseModel, table=True):
# fghrs_present: bool
property_description: Optional["PropertyDescription"] = Relationship(back_populates="flue_gas_heat_recovery_system")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="flue_gas_heat_recovery_system")
class PhotovoltaicPanel(BaseModel, table=True):
pvs_are_connected_to_dwelling_electricity_meter: bool
percentage_of_external_roof_area_with_pvs: str
# class PhotovoltaicPanel(BaseModel, table=True):
# pvs_are_connected_to_dwelling_electricity_meter: bool
# percentage_of_external_roof_area_with_pvs: str
property_description: Optional["PropertyDescription"] = Relationship(back_populates="photovoltaic_panel")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="photovoltaic_panel")
class WindTurbine(BaseModel, table=True):
wind_turbine: bool
# class WindTurbine(BaseModel, table=True):
# wind_turbine: bool
property_description: Optional["PropertyDescription"] = Relationship(back_populates="wind_turbine")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="wind_turbine")
class OtherDetails(BaseModel, table=True):
electricity_meter_type: str
main_gas_avalible: bool
# class OtherDetails(BaseModel, table=True):
# electricity_meter_type: str
# main_gas_avalible: bool
property_description: Optional["PropertyDescription"] = Relationship(back_populates="other_details")
# property_description: Optional["PropertyDescription"] = Relationship(back_populates="other_details")
class PropertyDescription(BaseModel, table=True):
built_form: str
detachment_or_position: str
no_of_main_property: int
no_of_extension_1: Optional[int] = 0
no_of_extension_2: Optional[int] = 0
no_of_extension_3: Optional[int] = 0
no_of_extension_4: Optional[int] = 0
no_of_habitable_rooms: int
no_of_heated_rooms: int
heated_basement: bool
conservatory_type: str
percentage_of_draught_proofed: int
terrain_type: str
conservatory: bool
# class PropertyDescription(BaseModel, table=True):
# built_form: str
# detachment_or_position: str
# no_of_main_property: int
# no_of_extension_1: Optional[int] = 0
# no_of_extension_2: Optional[int] = 0
# no_of_extension_3: Optional[int] = 0
# no_of_extension_4: Optional[int] = 0
# no_of_habitable_rooms: int
# no_of_heated_rooms: int
# heated_basement: bool
# conservatory_type: str
# percentage_of_draught_proofed: int
# terrain_type: str
# conservatory: bool
main_property_id: uuid.UUID = Field(foreign_key="propertydetail.id")
ex1_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
ex2_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
ex3_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
ex4_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
# main_property_id: uuid.UUID = Field(foreign_key="propertydetail.id")
# ex1_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
# ex2_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
# ex3_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
# ex4_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id")
door_id: Optional[uuid.UUID] = Field(default=None, foreign_key="door.id")
ventilation_and_cooling_id: Optional[uuid.UUID] = Field(default=None, foreign_key="ventilationandcooling.id")
lighting_id: Optional[uuid.UUID] = Field(default=None, foreign_key="lighting.id")
water_heating_id: Optional[uuid.UUID] = Field(default=None, foreign_key="waterheating.id")
hot_water_cylinder_id: Optional[uuid.UUID] = Field(default=None, foreign_key="hotwatercylinder.id")
solar_water_heating_id: Optional[uuid.UUID] = Field(default=None, foreign_key="solarwaterheating.id")
shower_and_baths_id: Optional[uuid.UUID] = Field(default=None, foreign_key="showerandbaths.id")
flue_gas_heat_recovery_system_id: Optional[uuid.UUID] = Field(default=None, foreign_key="fluegasheatrecoverysystem.id")
photovoltaic_panel_id: Optional[uuid.UUID] = Field(default=None, foreign_key="photovoltaicpanel.id")
wind_turbine_id: Optional[uuid.UUID] = Field(default=None, foreign_key="windturbine.id")
other_details_id: Optional[uuid.UUID] = Field(default=None, foreign_key="otherdetails.id")
main_heating_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingfrompresitenotes.id")
main_heating2_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingfrompresitenotes.id")
secondary_heating_type_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingtype.id")
# door_id: Optional[uuid.UUID] = Field(default=None, foreign_key="door.id")
# ventilation_and_cooling_id: Optional[uuid.UUID] = Field(default=None, foreign_key="ventilationandcooling.id")
# lighting_id: Optional[uuid.UUID] = Field(default=None, foreign_key="lighting.id")
# water_heating_id: Optional[uuid.UUID] = Field(default=None, foreign_key="waterheating.id")
# hot_water_cylinder_id: Optional[uuid.UUID] = Field(default=None, foreign_key="hotwatercylinder.id")
# solar_water_heating_id: Optional[uuid.UUID] = Field(default=None, foreign_key="solarwaterheating.id")
# shower_and_baths_id: Optional[uuid.UUID] = Field(default=None, foreign_key="showerandbaths.id")
# flue_gas_heat_recovery_system_id: Optional[uuid.UUID] = Field(default=None, foreign_key="fluegasheatrecoverysystem.id")
# photovoltaic_panel_id: Optional[uuid.UUID] = Field(default=None, foreign_key="photovoltaicpanel.id")
# wind_turbine_id: Optional[uuid.UUID] = Field(default=None, foreign_key="windturbine.id")
# other_details_id: Optional[uuid.UUID] = Field(default=None, foreign_key="otherdetails.id")
# main_heating_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingfrompresitenotes.id")
# main_heating2_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingfrompresitenotes.id")
# secondary_heating_type_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingtype.id")
# Relationships
main_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_property_id]"})
ex1_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex1_property_id]"})
ex2_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex2_property_id]"})
ex3_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex3_property_id]"})
ex4_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex4_property_id]"})
# # Relationships
# main_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_property_id]"})
# ex1_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex1_property_id]"})
# ex2_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex2_property_id]"})
# ex3_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex3_property_id]"})
# ex4_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex4_property_id]"})
# Related Models
door: Optional["Door"] = Relationship(back_populates="property_description")
ventilation_and_cooling: Optional["VentilationAndCooling"] = Relationship(back_populates="property_description")
lighting: Optional["Lighting"] = Relationship(back_populates="property_description")
water_heating: Optional["WaterHeating"] = Relationship(back_populates="property_description")
hot_water_cylinder: Optional["HotWaterCylinder"] = Relationship(back_populates="property_description")
solar_water_heating: Optional["SolarWaterHeating"] = Relationship(back_populates="property_description")
shower_and_baths: Optional["ShowerAndBaths"] = Relationship(back_populates="property_description")
flue_gas_heat_recovery_system: Optional["FlueGasHeatRecoverySystem"] = Relationship(back_populates="property_description")
photovoltaic_panel: Optional["PhotovoltaicPanel"] = Relationship(back_populates="property_description")
wind_turbine: Optional["WindTurbine"] = Relationship(back_populates="property_description")
other_details: Optional["OtherDetails"] = Relationship(back_populates="property_description")
main_heating: Optional["HeatingFromPreSiteNotes"] = Relationship(back_populates="property_description", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating_id]"})
main_heating2: Optional["HeatingFromPreSiteNotes"] = Relationship(back_populates="property_description", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating2_id]"})
secondary_heating_type: Optional["HeatingType"] = Relationship(back_populates="property_description")
# # Related Models
# door: Optional["Door"] = Relationship(back_populates="property_description")
# ventilation_and_cooling: Optional["VentilationAndCooling"] = Relationship(back_populates="property_description")
# lighting: Optional["Lighting"] = Relationship(back_populates="property_description")
# water_heating: Optional["WaterHeating"] = Relationship(back_populates="property_description")
# hot_water_cylinder: Optional["HotWaterCylinder"] = Relationship(back_populates="property_description")
# solar_water_heating: Optional["SolarWaterHeating"] = Relationship(back_populates="property_description")
# shower_and_baths: Optional["ShowerAndBaths"] = Relationship(back_populates="property_description")
# flue_gas_heat_recovery_system: Optional["FlueGasHeatRecoverySystem"] = Relationship(back_populates="property_description")
# photovoltaic_panel: Optional["PhotovoltaicPanel"] = Relationship(back_populates="property_description")
# wind_turbine: Optional["WindTurbine"] = Relationship(back_populates="property_description")
# other_details: Optional["OtherDetails"] = Relationship(back_populates="property_description")
# main_heating: Optional["HeatingFromPreSiteNotes"] = Relationship(back_populates="property_description", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating_id]"})
# main_heating2: Optional["HeatingFromPreSiteNotes"] = Relationship(back_populates="property_description", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating2_id]"})
# secondary_heating_type: Optional["HeatingType"] = Relationship(back_populates="property_description")
pre_site_notes: Optional["PreSiteNote"] = Relationship(back_populates="pre_site_note_description")
# pre_site_notes: Optional["PreSiteNote"] = Relationship(back_populates="pre_site_note_description")
class AssessorInfo(BaseModel, table=True):
accreditation_number: str
name: str
phone_number: Optional[str] = None
email_address: Optional[EmailStr] = None
# class AssessorInfo(BaseModel, table=True):
# accreditation_number: str
# name: str
# phone_number: Optional[str] = None
# email_address: Optional[EmailStr] = None
company_id: Optional[uuid.UUID] = Field(default=None, foreign_key="companyinfo.id")
company: Optional["CompanyInfo"] = Relationship(back_populates="assessors")
pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="assessor")
documents: List["Documents"] = Relationship(back_populates="author")
class PreSiteNotesSummaryInfo(BaseModel, table=True):
reference_number: str
epc_language: str
uprn: Optional[str] = ""
postcode: str
region: str
address: str
town: str
county: Optional[str] = ""
property_tenure: str
transaction_type: str
inspection_date: datetime
current_sap: str
potential_sap: str
current_ei: str
potential_ei: str
current_annual_emissions: str
current_annual_emission_including_0925_multiplayer: str
current_annual_energy_costs: str
pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="summary_info")
class CompanyInfo(BaseModel, table=True):
address: str
trading_name: str
post_code: str
fax_number: Optional[str] = None
related_party_disclosure: Optional[str] = None
assessors: List[AssessorInfo] = Relationship(back_populates="company")
class Insulation(BaseModel, table=True):
type: str
PreSiteNote.update_forward_refs()
AssessorInfo.update_forward_refs()
# company_id: Optional[uuid.UUID] = Field(default=None, foreign_key="companyinfo.id")
# company: Optional["CompanyInfo"] = Relationship(back_populates="assessors")
# pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="assessor")
# documents: List["Documents"] = Relationship(back_populates="author")
# class PreSiteNotesSummaryInfo(BaseModel, table=True):
# reference_number: str
# epc_language: str
# uprn: Optional[str] = ""
# postcode: str
# region: str
# address: str
# town: str
# county: Optional[str] = ""
# property_tenure: str
# transaction_type: str
# inspection_date: datetime
# current_sap: str
# potential_sap: str
# current_ei: str
# potential_ei: str
# current_annual_emissions: str
# current_annual_emission_including_0925_multiplayer: str
# current_annual_energy_costs: str
# pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="summary_info")
# class CompanyInfo(BaseModel, table=True):
# address: str
# trading_name: str
# post_code: str
# fax_number: Optional[str] = None
# related_party_disclosure: Optional[str] = None
# assessors: List[AssessorInfo] = Relationship(back_populates="company")
# class Insulation(BaseModel, table=True):
# type: str
# PreSiteNote.update_forward_refs()
# AssessorInfo.update_forward_refs()

View file

@ -1,4 +1,4 @@
from sqlmodel import Field, SQLModel, Relationship, Column, text
from sqlmodel import Field, SQLModel, Relationship, text
import uuid
from typing import Optional, List
from datetime import datetime
@ -10,41 +10,37 @@ from etl.fileReader.reportType import ReportType
from sqlalchemy import DateTime
from sqlalchemy.dialects.postgresql import JSON
from sqlalchemy import Text
from enum import Enum
from sqlalchemy import Column
class BaseModel(SQLModel):
# Put primary_key=True in Column; don't pass primary_key to Field
id: uuid.UUID = Field(
sa_column=Column(
UUID(as_uuid=True),
primary_key=True,
nullable=False,
server_default=text("gen_random_uuid()"), # requires pgcrypto extension
)
)
# Generate a fresh Column per table (no shared Column instance)
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
class Buildings(BaseModel, table=True):
address: str
postcode: str
UPRN: str
landlord_id: str
domna_id: str
# class Buildings(BaseModel, table=True):
# address: str
# postcode: str
# UPRN: str
# landlord_id: str
# domna_id: str
documents: List["Documents"] = Relationship(back_populates="building")
# documents: List["Documents"] = Relationship(back_populates="building")
class Documents(BaseModel, table=True):
assessor_id: uuid.UUID = Field(
foreign_key="assessorinfo.id",
nullable=False
)
author: Optional["AssessorInfo"] = Relationship(back_populates="documents")
created_at: datetime
document_type: ReportType
# class Documents(BaseModel, table=True):
# assessor_id: uuid.UUID = Field(
# foreign_key="assessorinfo.id",
# nullable=False
# )
# author: Optional["AssessorInfo"] = Relationship(back_populates="documents")
# created_at: datetime
# document_type: ReportType
building_id: uuid.UUID = Field(foreign_key="buildings.id", nullable=False)
building: Optional["Buildings"] = Relationship(back_populates="documents")
# building_id: uuid.UUID = Field(foreign_key="buildings.id", nullable=False)
# building: Optional["Buildings"] = Relationship(back_populates="documents")
target_table: str
target_id: uuid.UUID
# target_table: str
# target_id: uuid.UUID
class ReportType(str, Enum):
QUIDOS_PRESITE_NOTE = "QUIDOS_PRESITE_NOTE"
@ -54,6 +50,10 @@ class ReportType(str, Enum):
OVERWRITING_U_VALUE_DECLARATION_FORM = "OVERWRITING_U_VALUE_DECLARATION_FORM"
OSMOSIS_CONDITION_PAS_2035_REPORT = "OSMOSIS_CONDITION_PAS_2035_REPORT"
DOMNA_CONDITION_PAS_2035_REPORT = "DOMNA_CONDITION_PAS_2035_REPORT"
# Decent Homes Things
DECENT_HOMES_RAW_DATA = "DECENT_HOMES_RAW_DATA"
DECENT_HOMES_SUMMARY = "DECENT_HOMES_SUMMARY"
DECENT_HOMES_PROPERTY_META = "DECENT_HOMES_PROPERTY_META"
class uploaded_files(BaseModel, table=True):
__tablename__ = "uploaded_files"

View file

@ -1,4 +1,4 @@
#poetry run alembic revision --autogenerate -m "json_uri is a string"
#poetry run alembic revision --autogenerate -m "added more enums"
poetry run alembic upgrade head

16
poetry.lock generated
View file

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand.
[[package]]
name = "alembic"
@ -479,6 +479,18 @@ idna = ["idna (>=3.7)"]
trio = ["trio (>=0.23)"]
wmi = ["wmi (>=1.5.1)"]
[[package]]
name = "docutils"
version = "0.22.2"
description = "Docutils -- Python Documentation Utilities"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "docutils-0.22.2-py3-none-any.whl", hash = "sha256:b0e98d679283fc3bb0ead8a5da7f501baa632654e7056e9c5846842213d674d8"},
{file = "docutils-0.22.2.tar.gz", hash = "sha256:9fdb771707c8784c8f2728b67cb2c691305933d68137ef95a75db5f4dfbc213d"},
]
[[package]]
name = "email-validator"
version = "2.2.0"
@ -2097,4 +2109,4 @@ files = [
[metadata]
lock-version = "2.1"
python-versions = ">=3.12"
content-hash = "dfda98ea4e00851a83a2f67c231b59476d407a1e38006610722c64842976e736"
content-hash = "326b28eb13e9454717ce89fe4100053066e927e5698895690f71c14cfe0c1d6c"

View file

@ -24,6 +24,7 @@ dependencies = [
"hubspot-api-client (>=12.0.0,<13.0.0)",
"boto3 (>=1.39.6,<2.0.0)",
"psycopg2-binary (>=2.9.10,<3.0.0)",
"docutils (>=0.22.2,<0.23.0)",
]
[tool.poetry]