mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Added windows costs to costs etl process
This commit is contained in:
parent
6388d45638
commit
3426629418
8 changed files with 135 additions and 7 deletions
2
.idea/Model.iml
generated
2
.idea/Model.iml
generated
|
|
@ -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
2
.idea/misc.xml
generated
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
|
|
|
|||
30
recommendations/tests/test_window_recommendations.py
Normal file
30
recommendations/tests/test_window_recommendations.py
Normal 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=[])
|
||||
Loading…
Add table
Reference in a new issue