Added windows costs to costs etl process

This commit is contained in:
Khalim Conn-Kowlessar 2023-12-20 14:38:31 +00:00
parent 6388d45638
commit 3426629418
8 changed files with 135 additions and 7 deletions

2
.idea/Model.iml generated
View file

@ -7,7 +7,7 @@
<sourceFolder url="file://$MODULE_DIR$/open_uprn" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/recommendations" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="Python 3.10 (model_data)" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.10 (backend)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyNamespacePackagesService">

2
.idea/misc.xml generated
View file

@ -3,7 +3,7 @@
<component name="Black">
<option name="sdkName" value="Python 3.10 (backend)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (model_data)" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (backend)" project-jdk-type="Python SDK" />
<component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" />
</component>

View file

@ -75,6 +75,7 @@ def app():
ewi_costs = pd.read_excel(DATA_DIRECTORY, sheet_name="external_wall_insulation", header=0)
lel_costs = pd.read_excel(DATA_DIRECTORY, sheet_name="low_energy_lighting", header=0)
flat_roof_costs = pd.read_excel(DATA_DIRECTORY, sheet_name="flat_roof_insulation", header=0)
window_costs = pd.read_excel(DATA_DIRECTORY, sheet_name="window_glazing", header=0)
# Form a single table to be uploaded
costs = pd.concat(

View file

@ -52,7 +52,7 @@ class WindowAttributes(Definitions):
raise ValueError('Invalid description')
def process(self) -> Dict[str, Union[str, bool]]:
result: Dict[str, Union[str, bool]] = {
result: Dict[str, Union[str, bool, None]] = {
"has_glazing": False,
"glazing_coverage": None,
"glazing_type": None,
@ -83,4 +83,8 @@ class WindowAttributes(Definitions):
if not result["glazing_coverage"]:
result["glazing_coverage"] = "full"
# We reset some values if the glazing is single
if result["glazing_type"] == "single":
result["has_glazing"] = False
return result

View file

@ -30,7 +30,8 @@ windows_cases = [
'glazing_type': 'triple', 'no_data': False},
{'original_description': 'Gwydrau triphlyg rhannol', 'has_glazing': True, 'glazing_coverage': 'partial',
'glazing_type': 'triple', 'no_data': False},
{'original_description': 'Single glazed', 'has_glazing': True, 'glazing_coverage': 'full', 'glazing_type': 'single',
{'original_description': 'Single glazed', 'has_glazing': False, 'glazing_coverage': None,
'glazing_type': 'single',
'no_data': False},
{'original_description': 'Some double glazing', 'has_glazing': True, 'glazing_coverage': 'partial',
'glazing_type': 'double', 'no_data': False},
@ -46,7 +47,8 @@ windows_cases = [
'glazing_type': 'double', 'no_data': False},
{'original_description': 'Gwydrau dwbl gan mwyaf', 'has_glazing': True, 'glazing_coverage': 'most',
'glazing_type': 'double', 'no_data': False},
{'original_description': 'Gwydrau sengl', 'has_glazing': True, 'glazing_coverage': 'full', 'glazing_type': 'single',
{'original_description': 'Gwydrau sengl', 'has_glazing': False, 'glazing_coverage': None,
'glazing_type': 'single',
'no_data': False},
{'original_description': 'Ffenestri perfformiad uchel', 'has_glazing': True, 'glazing_coverage': 'full',
'glazing_type': 'high performance', 'no_data': False},

View file

@ -64,6 +64,12 @@ class Costs:
VAT_RATE = 0.2
PROFIT_MARGIN = 0.2
# Based on this greenmatch article, on average, a Sash window is around 50% more expensive than a casement window.
# Therefore, for a conservative cost estimate, and allowance for a more premium window type, we inflate the material
# cost of the windows to allow for a sash window type
# https://www.greenmatch.co.uk/windows/double-glazing/cost
SASH_WINDOW_INFLATION_FACTOR = 1.5
def __init__(self, property_instance):
"""
Initializes the Costs class with a property instance.
@ -719,3 +725,65 @@ class Costs:
"labour_days": labour_days,
"labour_cost": labour_costs
}
def window_glazing(self, number_of_windows, material):
"""
We characterise the jobs to be done for window glazing as the following:
1) Initial Assessment and Measurements: Before removing the existing window, it's essential to assess the
condition of the window frame and opening. Precise measurements are taken to ensure the new double glazed
windows fit perfectly.
2) Remove the Existing Window: This involves carefully dismantling and removing the old single glazed window. It
requires skill to avoid damaging the surrounding wall and the window frame (if it's to be reused).
3) Dispose of the Existing Window: The old window, especially if it's a single glazed unit, needs to be
disposed of responsibly. Glass and other materials should be recycled where possible.
4) Surface Preparation: The window opening might need some preparation, especially if there's damage or if
adjustments are needed to accommodate the new window. This can include repairing or replacing parts of the
window frame, sealing gaps, and ensuring the opening is level and square.
5) Install the Window Frame (if new frames are used): In many cases, double glazed windows come with their
frames. These need to be installed securely into the window opening. This process involves aligning, leveling,
and fixing the frame in place.
6) Install the Window Sill: If a new window sill is required, it is installed at this stage. It needs to be
correctly aligned with the frame and securely attached.
7) Install the Double Glazed Glass Units: The glass units are carefully inserted into the frame. This step
requires precision to ensure a snug fit without causing stress on the glass, which could lead to cracking or
breaking.
8) Sealing and Weatherproofing: After the glass units are in place, it's crucial to seal around the frame and
between the glass and frame to ensure there are no drafts and that the installation is weather-tight. This
typically involves applying silicone sealant or other appropriate sealing materials.
9) Finishing Touches: This includes any cosmetic work, such as trimming, painting, or staining the frame and
sill to match the rest of the property. It might also involve cleaning up any mess created during the
installation.
10) Inspection and Testing: Finally, the new windows should be inspected to ensure they open, close, and lock
correctly. This is also a good time to check for any gaps or issues with the sealing.
For this cost estimation process, we factor in initial assement into the preliminaries
:param number_of_windows:
:return:
"""
number_of_windows = 7
windows_cost = 345
material_cost = windows_cost * number_of_windows * self.SASH_WINDOW_INFLATION_FACTOR
subtotal = material_cost
contingency_cost = subtotal * 0.2
preliminaries_cost = subtotal * 0.2
profit_cost = subtotal * 0.2
subtotal_before_vat = subtotal + contingency_cost + preliminaries_cost + profit_cost
vat_cost = subtotal_before_vat * 0.2
total_cost = subtotal_before_vat + vat_cost

View file

@ -9,7 +9,7 @@ class WindowsRecommendations:
self.property = property_instance
self.costs = Costs(self.property)
self.recommendations = []
self.recommendation = []
self.glazing_materials = [
material for material in materials if material["type"] == "window_glazing"
@ -29,5 +29,28 @@ class WindowsRecommendations:
:return:
"""
if not self.property.number_of_windows:
number_of_windows = self.property.number_of_windows
if not number_of_windows:
raise ValueError("Number of windows not specified")
if self.property.windows["has_glazing"] & self.property.windows["glazing_coverage"] == "full":
return
# We then price the job based on the number of windows that there are
cost_result = {}
description = None
self.recommendation = [
{
"parts": [],
"type": "window_glazing",
"description": description,
"starting_u_value": None,
"new_u_value": None,
"sap_points": None,
**cost_result
}
]

View file

@ -0,0 +1,30 @@
from recommendations.WindowsRecommendations import WindowsRecommendations
from backend.Property import Property
from unittest.mock import Mock
class TestWindowRecommendations:
def test_fully_single_glazed(self):
"""
For this property, we expect all windows to be single glazed and should recommend full double glazing
:return:
"""
property_1 = Property(
id=1,
postcode='1',
address1='1',
epc_client=Mock(),
data={
"county": "Wychavon"
}
)
property_1.windows = {
'original_description': 'Single glazed', 'has_glazing': False, 'glazing_coverage': 'full',
'glazing_type': 'single',
'no_data': False
}
property_1.number_of_windows = 5
recommender = WindowsRecommendations(property_instance=property_1, materials=[])