mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
244 lines
9.5 KiB
Python
244 lines
9.5 KiB
Python
import enum
|
|
import pytz
|
|
import datetime
|
|
from sqlalchemy import Column, Integer, Text, Boolean, Float, DateTime, Enum, ForeignKey, CheckConstraint
|
|
from sqlalchemy.ext.declarative import declarative_base
|
|
from backend.app.db.models.users import UserModel # noqa
|
|
from backend.app.db.models.materials import MaterialType
|
|
|
|
Base = declarative_base()
|
|
|
|
|
|
class PortfolioStatus(enum.Enum):
|
|
SCOPING = "scoping"
|
|
ASSESSMENT = "assessment"
|
|
SURVEY = "survey"
|
|
TENDERING = "tendering"
|
|
PROJECT_UNDERWAY = "project underway"
|
|
COMPLETION_ON_TRACK = "completion; status: on track"
|
|
COMPLETION_DELAYED = "completion; status: delayed"
|
|
COMPLETION_AT_RISK = "completion; status: at risk"
|
|
COMPLETED = "completion; status: completed"
|
|
NEEDS_REVIEW = "needs review"
|
|
|
|
|
|
class PortfolioGoal(enum.Enum):
|
|
VALUATION_IMPROVEMENT = "Valuation Improvement"
|
|
INCREASING_EPC = "Increasing EPC"
|
|
REDUCING_CO2_EMISSIONS = "Reducing CO2 emissions"
|
|
ENERGY_SAVINGS = "Energy Savings"
|
|
NONE = "None"
|
|
|
|
|
|
class Portfolio(Base):
|
|
__tablename__ = 'portfolio'
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
name = Column(Text, nullable=False)
|
|
budget = Column(Float)
|
|
status = Column(Enum(PortfolioStatus, values_callable=lambda x: [e.value for e in x]), nullable=False)
|
|
goal = Column(Enum(PortfolioGoal, values_callable=lambda x: [e.value for e in x]), nullable=False)
|
|
cost = Column(Float)
|
|
number_of_properties = Column(Integer)
|
|
co2_equivalent_savings = Column(Float) # Unit is always tonnes so we don't need to store the unit
|
|
energy_savings = Column(Float) # Unit is always kWh so we don't need to store the unit
|
|
energy_cost_savings = Column(Float) # Unit is always £ so we don't need to store the unit for the moment
|
|
property_valuation_increase = Column(Float) # Unit is always £ so we don't need to store the unit for the moment
|
|
rental_yield_increase = Column(Float) # Unit is always £ so we don't need to store the unit for the moment
|
|
total_work_hours = Column(Float)
|
|
labour_days = Column(Float)
|
|
created_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
|
updated_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
|
# Aggregations for summary
|
|
epc_breakdown_pre_retrofit = Column(Text)
|
|
epc_breakdown_post_retrofit = Column(Text)
|
|
n_units_to_retrofit = Column(Integer)
|
|
co2_per_unit_pre_retrofit = Column(Text)
|
|
co2_per_unit_post_retrofit = Column(Text)
|
|
energy_bill_per_unit_pre_retrofit = Column(Text)
|
|
energy_bill_per_unit_post_retrofit = Column(Text)
|
|
energy_consumption_per_unit_pre_retrofit = Column(Text)
|
|
energy_consumption_per_unit_post_retrofit = Column(Text)
|
|
valuation_improvement_per_unit = Column(Text)
|
|
cost_per_unit = Column(Text)
|
|
cost_per_co2_saved = Column(Text)
|
|
cost_per_sap_point = Column(Text)
|
|
valuation_return_on_investment = Column(Text)
|
|
|
|
|
|
class PropertyCreationStatus(enum.Enum):
|
|
LOADING = "LOADING"
|
|
READY = "READY"
|
|
ERROR = "ERROR"
|
|
|
|
|
|
class Epc(enum.Enum):
|
|
A = "A"
|
|
B = "B"
|
|
C = "C"
|
|
D = "D"
|
|
E = "E"
|
|
F = "F"
|
|
G = "G"
|
|
|
|
|
|
class PropertyModel(Base):
|
|
__tablename__ = 'property'
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
portfolio_id = Column(Integer, ForeignKey('portfolio.id'), nullable=False)
|
|
creation_status = Column(Enum(PropertyCreationStatus), nullable=False)
|
|
uprn = Column(Integer)
|
|
landlord_property_id = Column(Text)
|
|
building_reference_number = Column(Integer)
|
|
status = Column(Enum(PortfolioStatus, values_callable=lambda x: [e.value for e in x]), nullable=False)
|
|
address = Column(Text)
|
|
postcode = Column(Text)
|
|
has_pre_condition_report = Column(Boolean)
|
|
has_recommendations = Column(Boolean)
|
|
created_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
|
updated_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
|
property_type = Column(Text)
|
|
built_form = Column(Text)
|
|
local_authority = Column(Text)
|
|
constituency = Column(Text)
|
|
number_of_rooms = Column(Integer)
|
|
year_built = Column(Text)
|
|
tenure = Column(Text)
|
|
current_epc_rating = Column(Enum(Epc))
|
|
current_sap_points = Column(Float)
|
|
current_valuation = Column(Float)
|
|
|
|
|
|
class FeatureRating(enum.Enum):
|
|
VERY_GOOD = 5
|
|
GOOD = 4
|
|
AVERAGE = 3
|
|
POOR = 2
|
|
VERY_POOR = 1
|
|
NA = None
|
|
|
|
|
|
rating_lookup = {
|
|
"Very Good": FeatureRating.VERY_GOOD,
|
|
"Good": FeatureRating.GOOD,
|
|
"Average": FeatureRating.AVERAGE,
|
|
"Poor": FeatureRating.POOR,
|
|
"Very Poor": FeatureRating.VERY_POOR,
|
|
"N/A": FeatureRating.NA
|
|
}
|
|
|
|
|
|
def get_feature_rating_from_string(rating_str: str):
|
|
return rating_lookup.get(rating_str, FeatureRating.NA)
|
|
|
|
|
|
class PropertyDetailsEpcModel(Base):
|
|
__tablename__ = 'property_details_epc'
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
property_id = Column(Integer, ForeignKey('property.id'), nullable=False)
|
|
portfolio_id = Column(Integer, ForeignKey('portfolio.id'), nullable=False)
|
|
full_address = Column(Text)
|
|
total_floor_area = Column(Float)
|
|
walls = Column(Text)
|
|
walls_rating = Column(Integer, CheckConstraint('walls_rating>=1 AND walls_rating<=5'))
|
|
roof = Column(Text)
|
|
roof_rating = Column(Integer, CheckConstraint('roof_rating>=1 AND roof_rating<=5'))
|
|
floor = Column(Text)
|
|
floor_rating = Column(Integer, CheckConstraint('floor_rating>=1 AND floor_rating<=5'))
|
|
windows = Column(Text)
|
|
windows_rating = Column(Integer, CheckConstraint('windows_rating>=1 AND windows_rating<=5'))
|
|
heating = Column(Text)
|
|
heating_rating = Column(Integer, CheckConstraint('heating_rating>=1 AND heating_rating<=5'))
|
|
heating_controls = Column(Text)
|
|
heating_controls_rating = Column(
|
|
Integer, CheckConstraint('heating_controls_rating>=1 AND heating_controls_rating<=5')
|
|
)
|
|
hot_water = Column(Text)
|
|
hot_water_rating = Column(Integer, CheckConstraint('hot_water_rating>=1 AND hot_water_rating<=5'))
|
|
lighting = Column(Text)
|
|
lighting_rating = Column(Integer, CheckConstraint('lighting_rating>=1 AND lighting_rating<=5'))
|
|
mainfuel = Column(Text)
|
|
ventilation = Column(Text)
|
|
solar_pv = Column(Text)
|
|
solar_hot_water = Column(Text)
|
|
wind_turbine = Column(Text)
|
|
floor_height = Column(Float)
|
|
number_heated_rooms = Column(Integer)
|
|
heat_loss_corridor = Column(Boolean)
|
|
unheated_corridor_length = Column(Float)
|
|
number_of_open_fireplaces = Column(Integer)
|
|
number_of_extensions = Column(Integer)
|
|
number_of_storeys = Column(Integer)
|
|
mains_gas = Column(Boolean)
|
|
energy_tariff = Column(Text)
|
|
primary_energy_consumption = Column(Float)
|
|
co2_emissions = Column(Float)
|
|
current_energy_demand = Column(Float)
|
|
current_energy_demand_heating_hotwater = Column(Float)
|
|
estimated = Column(Boolean, default=False)
|
|
sap_05_overwritten = Column(Boolean, default=False)
|
|
# Include estimates for energy bills, across the different types of energy
|
|
heating_cost_current = Column(Float)
|
|
hot_water_cost_current = Column(Float)
|
|
lighting_cost_current = Column(Float)
|
|
appliances_cost_current = Column(Float)
|
|
gas_standing_charge = Column(Float)
|
|
electricity_standing_charge = Column(Float)
|
|
|
|
|
|
class PropertyDetailsSpatial(Base):
|
|
__tablename__ = "property_details_spatial"
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
uprn = Column(Integer, nullable=False)
|
|
x_coordinate = Column(Float)
|
|
y_coordinate = Column(Float)
|
|
latitude = Column(Float)
|
|
longitude = Column(Float)
|
|
conservation_status = Column(Boolean)
|
|
is_listed_building = Column(Boolean)
|
|
is_heritage_building = Column(Boolean)
|
|
|
|
|
|
class PropertyDetailsMeter(Base):
|
|
__tablename__ = 'property_details_meter'
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
uprn = Column(Integer, nullable=False)
|
|
energy_supplier = Column(Text)
|
|
gas_supplier = Column(Text)
|
|
meter_reading_total = Column(Float)
|
|
meter_reading_electricity = Column(Float)
|
|
meter_reading_gas = Column(Float)
|
|
|
|
|
|
class PropertyTargetsModel(Base):
|
|
__tablename__ = 'property_targets'
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
property_id = Column(Integer, ForeignKey('property.id'), nullable=False)
|
|
portfolio_id = Column(Integer, ForeignKey('portfolio.id'), nullable=False)
|
|
created_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
|
epc = Column(Enum(Epc))
|
|
heat_demand = Column(Text)
|
|
|
|
|
|
class PortfolioUsers(Base):
|
|
__tablename__ = "portfolioUsers"
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
user_id = Column(Integer, ForeignKey('user.id'), nullable=False)
|
|
portfolioId = Column(Integer, ForeignKey('portfolio.id'), nullable=False)
|
|
role = Column(Text, nullable=False)
|
|
created_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
|
updated_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
|
|
|
|
|
class PropertyInstalledMeasures(Base):
|
|
"""
|
|
This model keeps a record of the installed measures for each property, at the UPRN level
|
|
"""
|
|
__tablename__ = 'property_installed_measures'
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
uprn = Column(Integer, nullable=False)
|
|
measure_type = Column(
|
|
Enum(MaterialType, values_callable=lambda x: [e.value for e in x], create_constraint=False),
|
|
nullable=False
|
|
)
|
|
created_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
|
installed_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|