mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
545 lines
30 KiB
Python
545 lines
30 KiB
Python
from recommendations.Costs import Costs
|
||
from unittest.mock import Mock
|
||
import datetime
|
||
import pytest
|
||
|
||
|
||
class TestCosts:
|
||
|
||
def test_cavity_wall_insulation(self):
|
||
mock_property = Mock()
|
||
mock_property.data = {
|
||
"county": "Northamptonshire"
|
||
}
|
||
|
||
costs = Costs(mock_property)
|
||
|
||
cwi_material = {
|
||
"description": "cwi",
|
||
"depth": 75,
|
||
"thermal_conductivity": 0.037,
|
||
"prime_cost": 5.17,
|
||
"material_cost": 5.62,
|
||
"labour_cost": 1.125,
|
||
"labour_hours_per_unit": 0.065,
|
||
}
|
||
|
||
cwi_results = costs.cavity_wall_insulation(
|
||
wall_area=95.9104281347967,
|
||
material=cwi_material,
|
||
)
|
||
|
||
assert cwi_results == {
|
||
'total': 1065.0661223512907, 'subtotal': 887.5551019594088, 'vat': 177.51102039188177,
|
||
'contingency': 63.396792997100626, 'preliminaries': 63.396792997100626, 'material': 539.0166061175574,
|
||
'profit': 126.79358599420125, 'labour_hours': 6.234177828761786, 'labour_cost': 94.95132385344874,
|
||
'labour_days': 0.38963611429761164
|
||
}
|
||
|
||
def test_loft_insulation(self):
|
||
mock_property = Mock()
|
||
mock_property.data = {
|
||
"county": "Northamptonshire"
|
||
}
|
||
|
||
costs = Costs(mock_property)
|
||
loft_material = {
|
||
"description": "Crown Loft Roll 44 glass fibre roll",
|
||
"depth": 270,
|
||
"thermal_conductivity": 0.044,
|
||
"prime_cost": None,
|
||
"material_cost": 5.91938,
|
||
"labour_cost": 1.96,
|
||
"labour_hours_per_unit": 0.11
|
||
}
|
||
|
||
loft_results = costs.loft_insulation(
|
||
floor_area=33.5,
|
||
material=loft_material,
|
||
)
|
||
|
||
assert loft_results == {
|
||
'total': 639.4133610000001, 'subtotal': 532.8444675000001, 'vat': 106.56889350000002,
|
||
'contingency': 71.045929, 'preliminaries': 35.5229645, 'material': 297.448845, 'profit': 71.045929,
|
||
'labour_hours': 3.685, 'labour_cost': 57.7808, 'labour_days': 0.460625
|
||
}
|
||
|
||
def test_internal_wall_insulation(self):
|
||
mock_property = Mock()
|
||
mock_property.data = {
|
||
"county": "Northamptonshire"
|
||
}
|
||
|
||
costs = Costs(mock_property)
|
||
iwi_non_insulation_materials = [
|
||
{'type': 'iwi_wall_demolition',
|
||
'description': 'Solid & Dry Lined walls: Hack of wall finishes with chipping hammer; plaster to walls.',
|
||
'depth': 0.0, 'depth_unit': 0.0, 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.0,
|
||
'thermal_conductivity_unit': 0.0, 'prime_material_cost': 0.0, 'material_cost': 0.0, 'labour_cost': 10.27,
|
||
'labour_hours_per_unit': 0.33, 'plant_cost': 1.28, 'total_cost': 11.55, 'link': 'SPONs', 'Notes': 0.0},
|
||
{'type': 'iwi_wall_demolition',
|
||
'description': 'Stud walls: Remove wall linings including battening behind; plasterboard and skim',
|
||
'depth': 0.0, 'depth_unit': 0.0, 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.0,
|
||
'thermal_conductivity_unit': 0.0, 'prime_material_cost': 0.0, 'material_cost': 0.0, 'labour_cost': 6.23,
|
||
'labour_hours_per_unit': 0.2, 'plant_cost': 1.25, 'total_cost': 7.48, 'link': 'SPONs', 'Notes': 0.0},
|
||
{'type': 'iwi_wall_demolition',
|
||
'description': 'Lathe and Plaster walls: Remove wall linings including battening behind; wood lath and '
|
||
'plaster',
|
||
'depth': 0.0, 'depth_unit': 0.0, 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.0,
|
||
'thermal_conductivity_unit': 0.0, 'prime_material_cost': 0.0, 'material_cost': 0.0, 'labour_cost': 6.85,
|
||
'labour_hours_per_unit': 0.22, 'plant_cost': 2.09, 'total_cost': 8.94, 'link': 'SPONs', 'Notes': 0.0},
|
||
{'Notes': "",
|
||
'cost_unit': "",
|
||
'depth': "",
|
||
'depth_unit': "",
|
||
'description': 'Visqueen High Performance Vapour Barrier',
|
||
'labour_cost': 0.48,
|
||
'labour_hours_per_unit': 0.02,
|
||
'link': 'SPONs',
|
||
'material_cost': 1.21,
|
||
'plant_cost': 0,
|
||
'prime_material_cost': 0.58,
|
||
'thermal_conductivity': "",
|
||
'thermal_conductivity_unit': "",
|
||
'total_cost': 1.69,
|
||
'type': 'iwi_vapour_barrier'},
|
||
{'Notes': "",
|
||
'cost_unit': "",
|
||
'depth': "",
|
||
'depth_unit': "",
|
||
'description': 'Plaster; one coat Thistle board finish or other equal; steel trowelled; 3 mm thick work '
|
||
'to walls or ceilings; one coat; to plasterboard base; over 600mm wide',
|
||
'labour_cost': 6.58,
|
||
'labour_hours_per_unit': 0.25,
|
||
'link': "",
|
||
'material_cost': 0.06,
|
||
'plant_cost': 0,
|
||
'prime_material_cost': 0.0,
|
||
'thermal_conductivity': "",
|
||
'thermal_conductivity_unit': "",
|
||
'total_cost': 6.64,
|
||
'type': 'iwi_redecoration'},
|
||
{'Notes': "",
|
||
'cost_unit': "",
|
||
'depth': "",
|
||
'depth_unit': "",
|
||
'description': 'Two coats emulsion paint on plaster, over 40mm girth; 3.5m - '
|
||
'5m high',
|
||
'labour_cost': 0.0,
|
||
'labour_hours_per_unit': 0.21,
|
||
'link': "",
|
||
'material_cost': 0.41,
|
||
'plant_cost': 0,
|
||
'prime_material_cost': "",
|
||
'thermal_conductivity': "",
|
||
'thermal_conductivity_unit': "",
|
||
'total_cost': 4.34,
|
||
'type': 'iwi_redecoration'},
|
||
{'Notes': "",
|
||
'cost_unit': "",
|
||
'depth': "",
|
||
'depth_unit': "",
|
||
'description': 'Fitting existing softwood skirting or architrave to new '
|
||
'frames; 150mm high',
|
||
'labour_cost': 4.87,
|
||
'labour_hours_per_unit': 0.01,
|
||
'link': "",
|
||
'material_cost': 4.86,
|
||
'plant_cost': 0,
|
||
'prime_material_cost': "",
|
||
'thermal_conductivity': "",
|
||
'thermal_conductivity_unit': "",
|
||
'total_cost': 4.88,
|
||
'type': 'iwi_redecoration'}
|
||
]
|
||
|
||
iwi_material = {
|
||
"type": "internal_wall_insulation",
|
||
"description": "Ecotherm Eco-Versal PIR Insulation Board",
|
||
"depth": 150,
|
||
"depth_unit": "mm",
|
||
"cost_unit": "gbp_per_m2",
|
||
"thermal_conductivity": 0.022,
|
||
"thermal_conductivity_unit": "watt_per_meter_kelvin",
|
||
"prime_material_cost": "",
|
||
"material_cost": 11.68,
|
||
"labour_cost": 3.12,
|
||
"labour_hours_per_unit": 0.18,
|
||
"plant_cost": "",
|
||
"total_cost": 14.8,
|
||
"link": "SPONs"
|
||
}
|
||
|
||
iwi_results = costs.internal_wall_insulation(
|
||
wall_area=95.9104281347967,
|
||
material=iwi_material,
|
||
non_insulation_materials=iwi_non_insulation_materials
|
||
)
|
||
|
||
assert iwi_results == {
|
||
'total': 6880.2304726777775, 'subtotal': 5733.525393898148, 'vat': 1146.7050787796295,
|
||
'contingency': 764.470052519753, 'preliminaries': 382.2350262598765, 'material': 1747.488000615996,
|
||
'profit': 764.470052519753, 'labour_hours': 88.23759388401297, 'labour_days': 2.757424808875405,
|
||
'labour_cost': 1927.1602026551818
|
||
}
|
||
|
||
def test_suspended_floor_insulation(self):
|
||
mock_property = Mock()
|
||
mock_property.data = {
|
||
"county": "Northamptonshire"
|
||
}
|
||
|
||
costs = Costs(mock_property)
|
||
|
||
sus_floor_material = {
|
||
'type': 'suspended_floor_insulation', 'description': 'Thermafleece CosyWool Roll',
|
||
'depth': 140.0,
|
||
'depth_unit': 'mm', 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.039,
|
||
'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'prime_material_cost': 0,
|
||
'material_cost': 11.68, 'labour_cost': 1.78, 'labour_hours_per_unit': 0.1,
|
||
'plant_cost': 0,
|
||
'total_cost': 13.46, 'link': 'SPONs',
|
||
'Notes': 'Spons did not contain labour costs so we use values for similar insulations. '
|
||
'We use the '
|
||
'same values as in Crown loft roll 44, since it is also an insulation roll'
|
||
}
|
||
|
||
sus_floor_non_insulation_materials = [
|
||
{'type': 'suspended_floor_demolition', 'description': 'Removal of carpet and underfelt', 'depth': 0,
|
||
'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 3.32, 'labour_hours_per_unit': 0.11,
|
||
'plant_cost': 0, 'total_cost': 3.32, 'link': 'SPONs',
|
||
'Notes': 'We ignore the plant cost that is in SPONs because we assume the carpet is not scrapped and '
|
||
'therefore there is no need for a skip'},
|
||
{'type': 'suspended_floor_demolition',
|
||
'description': 'Remove boarding; withdraw nails; set aside for reuse; ground level', 'depth': 0,
|
||
'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 9.34, 'labour_hours_per_unit': 0.3,
|
||
'plant_cost': 0, 'total_cost': 9.34, 'link': 'SPONs', 'Notes': 0},
|
||
{'type': 'suspended_floor_vapour_barrier', 'description': 'Visqueen High Performance Vapour Barrier',
|
||
'depth': 0, 'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0,
|
||
'thermal_conductivity_unit': 0, 'prime_material_cost': 0.58, 'material_cost': 1.21, 'labour_cost': 0.48,
|
||
'labour_hours_per_unit': 0.02, 'plant_cost': 0, 'total_cost': 1.69, 'link': 'SPONs', 'Notes': 0},
|
||
{'type': 'suspended_floor_redecoration', 'description': 'refix floorboards previously set aside',
|
||
'depth': 0, 'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0,
|
||
'thermal_conductivity_unit': 0, 'prime_material_cost': 0, 'material_cost': 1.54, 'labour_cost': 24.98,
|
||
'labour_hours_per_unit': 0.74, 'plant_cost': 0, 'total_cost': 26.52, 'link': 'SPONs', 'Notes': 0},
|
||
{'type': 'suspended_floor_redecoration', 'description': 'Fitting carpet', 'depth': 0, 'depth_unit': 0,
|
||
'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 6.59, 'labour_hours_per_unit': 0.37,
|
||
'plant_cost': 0, 'total_cost': 6.59, 'link': 'SPONs',
|
||
'Notes': 'SPONs does not have data on re-fitting the carpet so we use the data in Fitted carpeting; '
|
||
'Gradus woven polypropylene tufted loop\n\n as a baseline. We assume re-use of carpets, '
|
||
'therefore we need just labour rates'}]
|
||
|
||
sus_floor_results = costs.suspended_floor_insulation(
|
||
insulation_floor_area=33.5,
|
||
material=sus_floor_material,
|
||
non_insulation_materials=sus_floor_non_insulation_materials
|
||
)
|
||
|
||
assert sus_floor_results == {
|
||
'total': 3337.07436, 'subtotal': 2780.8953, 'vat': 556.17906, 'contingency': 370.78604,
|
||
'preliminaries': 185.39302, 'material': 483.405, 'profit': 370.78604, 'labour_hours': 54.940000000000005,
|
||
'labour_days': 2.289166666666667, 'labour_cost': 1370.5252
|
||
}
|
||
|
||
def test_solid_floor_insulation(self):
|
||
mock_property = Mock()
|
||
mock_property.data = {
|
||
"county": "Northamptonshire"
|
||
}
|
||
|
||
costs = Costs(mock_property)
|
||
sol_floor_material = {
|
||
'type': 'solid_floor_insulation', 'description': 'Kay-Cel Expanded Polystyrene Board',
|
||
'depth': 100.0, 'depth_unit': 'mm', 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.033,
|
||
'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'prime_material_cost': 0,
|
||
'material_cost': 12.02, 'labour_cost': 4.4, 'labour_hours_per_unit': 0.19, 'plant_cost': 0,
|
||
'total_cost': 16.42, 'link': 'SPONs', 'Notes': 0
|
||
}
|
||
|
||
sol_floor_non_insulation_materials = [
|
||
{'type': 'solid_floor_demolition', 'description': 'Removal of carpet and underfelt', 'depth': 0,
|
||
'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 3.32, 'labour_hours_per_unit': 0.11,
|
||
'plant_cost': 0, 'total_cost': 3.32, 'link': 'SPONs',
|
||
'Notes': 'We ignore the plant cost that is in SPONs because we assume the carpet is not scrapped and '
|
||
'therefore there is no need for a skip'},
|
||
{'type': 'solid_floor_preparation',
|
||
'description': 'clean surface of concrete to receive new damp-proof membrane', 'depth': 0,
|
||
'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 4.36, 'labour_hours_per_unit': 0.14,
|
||
'plant_cost': 0, 'total_cost': 4.36, 'link': 0, 'Notes': 0}, {
|
||
'type': 'solid_floor_preparation',
|
||
'description': 'Clean out crack to '
|
||
'form a 20mm×20mm '
|
||
'groove and fill with '
|
||
'cement: mortar mixed '
|
||
'with bonding agent',
|
||
'depth': 0, 'depth_unit': 0,
|
||
'cost_unit': 0,
|
||
'thermal_conductivity': 0,
|
||
'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0,
|
||
'material_cost': 6.91,
|
||
'labour_cost': 18.99,
|
||
'labour_hours_per_unit': 0.61,
|
||
'plant_cost': 0.16,
|
||
'total_cost': 26.06, 'link': 0,
|
||
'Notes': 'This step is the '
|
||
'assessment and repair of '
|
||
'any damage to the concrete '
|
||
'floor such as filling '
|
||
'cracks or levelling uneven '
|
||
'areas'},
|
||
{'type': 'solid_floor_vapour_barrier', 'description': 'Visqueen High Performance Vapour Barrier',
|
||
'depth': 0, 'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0,
|
||
'thermal_conductivity_unit': 0, 'prime_material_cost': 0.58, 'material_cost': 1.21, 'labour_cost': 0.48,
|
||
'labour_hours_per_unit': 0.02, 'plant_cost': 0, 'total_cost': 1.69, 'link': 'SPONs', 'Notes': 0},
|
||
{'type': 'solid_floor_redecoration',
|
||
'description': 'Screeded beds; protection to compressible formwork exceeding 600mm wide', 'depth': 0,
|
||
'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 9.6, 'material_cost': 9.89, 'labour_cost': 2.67, 'labour_hours_per_unit': 0.15,
|
||
'plant_cost': 0, 'total_cost': 12.56, 'link': 'SPONs',
|
||
'Notes': 'This is the screed layer, placed on top of the insulation'},
|
||
{'type': 'solid_floor_redecoration', 'description': 'Fitting carpet', 'depth': 0, 'depth_unit': 0,
|
||
'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 6.59, 'labour_hours_per_unit': 0.37,
|
||
'plant_cost': 0, 'total_cost': 6.59, 'link': 'SPONs',
|
||
'Notes': 'SPONs does not have data on re-fitting the carpet so we use the data in Fitted carpeting; '
|
||
'Gradus woven polypropylene tufted loop\n\n as a baseline. We assume re-use of carpets, '
|
||
'therefore we need just labour rates'},
|
||
{'type': 'solid_floor_redecoration',
|
||
'description': 'Fitting existing softwood skirting or architrave to new frames; 150mm high', 'depth': 0,
|
||
'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 0.01, 'labour_cost': 4.87, 'labour_hours_per_unit': 0.12,
|
||
'plant_cost': 0, 'total_cost': 4.88, 'link': 'SPONs', 'Notes': 0}
|
||
]
|
||
|
||
sol_floor_results = costs.solid_floor_insulation(
|
||
insulation_floor_area=33.5,
|
||
material=sol_floor_material,
|
||
non_insulation_materials=sol_floor_non_insulation_materials
|
||
)
|
||
|
||
assert sol_floor_results == {
|
||
'total': 4245.023520000001, 'subtotal': 3537.5196, 'vat': 707.5039200000001, 'contingency': 471.66928,
|
||
'preliminaries': 235.83464, 'material': 1006.3399999999999, 'profit': 471.66928, 'labour_hours': 57.285,
|
||
'labour_days': 2.386875, 'labour_cost': 1346.6464
|
||
}
|
||
|
||
def test_external_wall_insulation(self):
|
||
mock_property = Mock()
|
||
mock_property.data = {
|
||
"county": "Northamptonshire",
|
||
"property-type": "House",
|
||
"built-form": 'End-Terrace'
|
||
}
|
||
|
||
costs = Costs(mock_property)
|
||
|
||
ewi_material = {
|
||
'type': 'external_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board',
|
||
'depth': 150.0, 'depth_unit': 'mm', 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.022,
|
||
'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'prime_material_cost': 23.53,
|
||
'material_cost': 34.62, 'labour_cost': 33.06, 'labour_hours_per_unit': 1.4, 'plant_cost': 0,
|
||
'total_cost': 67.68, 'link': 'SPONs', 'Notes': 0
|
||
}
|
||
ewi_non_insulation_materials = [
|
||
{'type': 'ewi_wall_demolition',
|
||
'description': 'Solid & Dry Lined walls: Hack of wall finishes with chipping '
|
||
'hammer; plaster to walls.',
|
||
'depth': 0, 'depth_unit': 0, 'cost_unit': 'gbp_per_m2',
|
||
'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 10.27,
|
||
'labour_hours_per_unit': 0.33, 'plant_cost': 1.28, 'total_cost': 11.55,
|
||
'link': 'SPONs', 'Notes': 0}, {'type': 'ewi_wall_demolition',
|
||
'description': 'Stud walls: Remove wall linings '
|
||
'including battening behind; '
|
||
'plasterboard and skim',
|
||
'depth': 0, 'depth_unit': 0,
|
||
'cost_unit': 'gbp_per_m2',
|
||
'thermal_conductivity': 0,
|
||
'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 0,
|
||
'labour_cost': 6.23, 'labour_hours_per_unit': 0.2,
|
||
'plant_cost': 1.25, 'total_cost': 7.48,
|
||
'link': 'SPONs', 'Notes': 0},
|
||
{'type': 'ewi_wall_demolition',
|
||
'description': 'Lathe and Plaster walls: Remove wall linings including battening '
|
||
'behind; wood lath and plaster',
|
||
'depth': 0, 'depth_unit': 0, 'cost_unit': 'gbp_per_m2',
|
||
'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 6.85,
|
||
'labour_hours_per_unit': 0.22, 'plant_cost': 2.09, 'total_cost': 8.94,
|
||
'link': 'SPONs', 'Notes': 0}, {'type': 'ewi_wall_preparation',
|
||
'description': 'Clean and prepare surfaces, '
|
||
'one coat Keim dilution, '
|
||
'one coat primer and two coats '
|
||
'of Keim Ecosil paint; Brick or '
|
||
'block walls; over 300 mm girth',
|
||
'depth': 0, 'depth_unit': 0, 'cost_unit': 0,
|
||
'thermal_conductivity': 0,
|
||
'thermal_conductivity_unit': 0,
|
||
'prime_material_cost': 0, 'material_cost': 7.3,
|
||
'labour_cost': 5.62, 'labour_hours_per_unit': 0.3,
|
||
'plant_cost': 0, 'total_cost': 12.92,
|
||
'link': 'SPONs',
|
||
'Notes': 'This work covers the preparation and '
|
||
'priming of the wall before insulating'},
|
||
{'type': 'ewi_wall_redecoration',
|
||
'description': 'EPS insulation fixed with adhesive to SFS structure (measured '
|
||
'separately) with horizontal PVC intermediate track and vertical '
|
||
'T-spines; with glassfibre mesh reinforcement embedded in Sto '
|
||
'Armat Classic Basecoat Render and Stolit K 1.5 Decorative '
|
||
'Topcoat Render (white)',
|
||
'depth': 0, 'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0,
|
||
'thermal_conductivity_unit': 0, 'prime_material_cost': 0, 'material_cost': 0,
|
||
'labour_cost': 0, 'labour_hours_per_unit': 0, 'plant_cost': 0,
|
||
'total_cost': 69.94, 'link': 'SPONs',
|
||
'Notes': 'This material in SPONs is for 70mm EPS insulation, which comes in at a '
|
||
'cost of 99.17 per meter square. This includes the cost of insulation. '
|
||
'To get the costing for just the works and not the insulation, '
|
||
'we subtract the cost of EPS insulation, using Ravathem 75mm insulation '
|
||
'as an example, which costs £29.23 per meter square, giving us the cost '
|
||
'of the remaining works without insulation. This material gives us a '
|
||
'cost for basecoat, mesh application and a render finish'}]
|
||
|
||
ewi_results = costs.external_wall_insulation(
|
||
wall_area=95.9104281347967,
|
||
material=ewi_material,
|
||
non_insulation_materials=ewi_non_insulation_materials
|
||
)
|
||
|
||
assert ewi_results == {
|
||
'total': 15047.078622131372, 'subtotal': 12539.232185109477, 'vat': 2507.8464370218953,
|
||
'contingency': 808.9827216199662, 'preliminaries': 2022.4568040499155, 'material': 4020.565147410677,
|
||
'profit': 1617.9654432399325, 'labour_hours': 187.02533486285358, 'labour_days': 5.8445417144641745,
|
||
'labour_cost': 3921.5600094613983
|
||
}
|
||
|
||
def test_flat_roof_insulation(self):
|
||
mock_property = Mock()
|
||
mock_property.data = {
|
||
"county": "Northamptonshire"
|
||
}
|
||
|
||
costs = Costs(mock_property)
|
||
flat_roof_material = {'id': 1225, 'type': 'flat_roof_insulation',
|
||
'description': 'Kingspan Thermaroof TR21 zero OPD '
|
||
'urethene insulation board',
|
||
'depth': 100.0, 'depth_unit': 'mm', 'cost': None,
|
||
'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.04,
|
||
'r_value_unit': 'square_meter_kelvin_per_watt',
|
||
'thermal_conductivity': 0.025,
|
||
'thermal_conductivity_unit': 'watt_per_meter_kelvin',
|
||
'link': 'SPONs',
|
||
'created_at': "now", 'is_active': True,
|
||
'prime_material_cost': None, 'material_cost': 50.95,
|
||
'labour_cost': 10.66, 'labour_hours_per_unit': 0.48,
|
||
'plant_cost': 0.0, 'total_cost': 61.61,
|
||
'notes': "SPONs didn't have a labour hours so we use "
|
||
"0.48 which is similar to other materials"}
|
||
|
||
flat_roof_non_insulation_materials = [
|
||
{'id': 17, 'type': 'mechanical_ventilation', 'description': 'Mechanical Extract Ventilation', 'depth': None,
|
||
'depth_unit': None, 'cost': 500, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': None, 'r_value_unit': None,
|
||
'thermal_conductivity': None, 'thermal_conductivity_unit': None, 'link': None,
|
||
'created_at': datetime.datetime(2023, 10, 18, 16, 39, 9, 827188), 'is_active': True,
|
||
'prime_material_cost': None,
|
||
'material_cost': None, 'labour_cost': None, 'labour_hours_per_unit': None, 'plant_cost': None,
|
||
'total_cost': None,
|
||
'notes': None},
|
||
{'id': 1221, 'type': 'flat_roof_preparation',
|
||
'description': 'clean surface to receive new damp-proof membrane',
|
||
'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': None,
|
||
'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None,
|
||
'thermal_conductivity_unit': None,
|
||
'link': 'SPONs', 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True,
|
||
'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 4.36, 'labour_hours_per_unit': 0.14,
|
||
'plant_cost': 0.0, 'total_cost': 4.36,
|
||
'notes': 'This data is based on concrete however forms a decent baseline for a Bituminous Felt flat roof'},
|
||
{'id': 1223, 'type': 'flat_roof_preparation',
|
||
'description': 'One coat primer; on wood surfaces before fixing; General surfaces; over 300 mm girth',
|
||
'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': None,
|
||
'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None,
|
||
'thermal_conductivity_unit': None,
|
||
'link': 'SPONs', 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True,
|
||
'prime_material_cost': None, 'material_cost': 2.49, 'labour_cost': 1.5, 'labour_hours_per_unit': 0.08,
|
||
'plant_cost': 0.0, 'total_cost': 3.99, 'notes': 'SPONs data gives us a baseline for a wood surface'},
|
||
{'id': 1224, 'type': 'flat_roof_vapour_barrier', 'description': 'Visqueen High Performance Vapour Barrier',
|
||
'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': None,
|
||
'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None,
|
||
'thermal_conductivity_unit': None,
|
||
'link': 'SPONs', 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True,
|
||
'prime_material_cost': 0.58, 'material_cost': 1.21, 'labour_cost': 0.48, 'labour_hours_per_unit': 0.02,
|
||
'plant_cost': 0.0, 'total_cost': 1.69, 'notes': None},
|
||
{'id': 1234, 'type': 'flat_roof_waterproofing',
|
||
'description': '20 mm thick two coat coverings; felt isolating membrane; to concrete (or '
|
||
'timber) base; flat or to falls or slopes not exceeding 10° from horizontal',
|
||
'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': None,
|
||
'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None,
|
||
'thermal_conductivity_unit': None, 'link': 'SPONs',
|
||
'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True,
|
||
'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 0.0,
|
||
'labour_hours_per_unit': 0.5, 'plant_cost': 0.0, 'total_cost': 31.13, 'notes': None}
|
||
]
|
||
|
||
flat_roof_floor_results = costs.flat_roof_insulation(
|
||
floor_area=33.5,
|
||
material=flat_roof_material,
|
||
non_insulation_materials=flat_roof_non_insulation_materials
|
||
)
|
||
|
||
assert flat_roof_floor_results == {'total': 5325.327767999999, 'subtotal': 4437.773139999999,
|
||
'vat': 887.5546279999999, 'contingency': 459.07998,
|
||
'preliminaries': 306.05332, 'material': 1830.775, 'profit': 612.10664,
|
||
'labour_hours': 24.79, 'labour_days': 1.549375, 'labour_cost': 186.9032}
|
||
|
||
assert costs.labour_adjustment_factor == 0.88
|
||
|
||
# Mock property instance for regional tests
|
||
@pytest.fixture(params=[
|
||
("Northamptonshire", "East Midlands", 7927.44),
|
||
("Greater London Authority", "Inner London", 10475.0),
|
||
("Adur", "South East England", 8333.32),
|
||
("Bournemouth", "South West England", 8452),
|
||
("Basildon", "East of England", 7895.44),
|
||
("Birmingham", "West Midlands", 7706.2),
|
||
("County Durham", "North East England", 8113.96),
|
||
("Allerdale", "North West England", 6481.68),
|
||
("York", "Yorkshire and the Humber", 8243.6),
|
||
("Cardiff", "Wales", 7595.32),
|
||
("Glasgow City", "Scotland", 7871.88),
|
||
("Belfast", "Northern Ireland", 8504.36)
|
||
])
|
||
def mock_property_with_region(self, request):
|
||
county, region, expected_cost = request.param
|
||
mock_property = Mock()
|
||
mock_property.data = {"county": county}
|
||
return mock_property, region, expected_cost
|
||
|
||
# Test for different wattages
|
||
@pytest.mark.parametrize("wattage, expected_cost", [
|
||
(3000, 5945.58),
|
||
(4000, 7927.44),
|
||
(5000, 9909.3),
|
||
(6000, 11891.16),
|
||
])
|
||
def test_solar_pv_different_wattages(self, wattage, expected_cost):
|
||
mock_property = Mock()
|
||
mock_property.data = {"county": "Mansfield"}
|
||
costs = Costs(mock_property)
|
||
result = costs.solar_pv(wattage)
|
||
assert result['total'] == pytest.approx(expected_cost, rel=0.01)
|
||
|
||
def test_solar_pv_regional_variation(self, mock_property_with_region):
|
||
# Test for regional cost variations
|
||
property_instance, expected_region, expected_cost = mock_property_with_region
|
||
costs = Costs(property_instance)
|
||
|
||
assert costs.region == expected_region
|
||
|
||
result = costs.solar_pv(4000) # Testing with a fixed wattage of 4000
|
||
assert result['total'] == pytest.approx(expected_cost, rel=0.01)
|