Model/recommendations/tests/test_recommendations.py

843 lines
55 KiB
Python

import datetime
import pandas as pd
from pandas import Timestamp
import numpy as np
from numpy import nan
from unittest.mock import Mock
from recommendations.Recommendations import Recommendations
def test__filter_phase_adjustment():
eg1 = [
{'recommendation_id': '0_phase=0', 'phase': 0, 'sap_adjustment': 1.7},
{'recommendation_id': '1_phase=0', 'phase': 0, 'sap_adjustment': 1.7},
{'recommendation_id': '2_phase=0', 'phase': 0, 'sap_adjustment': 1.7}
]
res1 = Recommendations._filter_phase_adjustment(eg1)
assert res1 == [{'recommendation_id': '0_phase=0', 'phase': 0, 'sap_adjustment': 1.7}]
eg2 = [
{'recommendation_id': '0_phase=0', 'phase': 0, 'sap_adjustment': 1},
{'recommendation_id': '1_phase=0', 'phase': 1, 'sap_adjustment': 2},
{'recommendation_id': '2_phase=0', 'phase': 2, 'sap_adjustment': 3}
]
res2 = Recommendations._filter_phase_adjustment(eg2)
assert res2 == [
{'recommendation_id': '0_phase=0', 'phase': 0, 'sap_adjustment': 1},
{'recommendation_id': '1_phase=0', 'phase': 1, 'sap_adjustment': 2},
{'recommendation_id': '2_phase=0', 'phase': 2, 'sap_adjustment': 3}
]
eg3 = [
{'recommendation_id': 'third', 'phase': 3, 'sap_adjustment': 1},
{'recommendation_id': 'first', 'phase': 1, 'sap_adjustment': 2},
{'recommendation_id': 'second', 'phase': 2, 'sap_adjustment': 3}
]
res3 = Recommendations._filter_phase_adjustment(eg3)
assert res3 == [
{'recommendation_id': 'first', 'phase': 1, 'sap_adjustment': 2},
{'recommendation_id': 'second', 'phase': 2, 'sap_adjustment': 3},
{'recommendation_id': 'third', 'phase': 3, 'sap_adjustment': 1},
]
eg4 = [
{'recommendation_id': 'third_0', 'phase': 3, 'sap_adjustment': 1},
{'recommendation_id': 'third_1', 'phase': 3, 'sap_adjustment': 2},
{'recommendation_id': 'first_0', 'phase': 1, 'sap_adjustment': 2},
{'recommendation_id': 'first_1', 'phase': 1, 'sap_adjustment': 2},
{'recommendation_id': 'first_2', 'phase': 1, 'sap_adjustment': 100},
{'recommendation_id': 'second', 'phase': 2, 'sap_adjustment': 3}
]
res4 = Recommendations._filter_phase_adjustment(eg4)
assert res4 == [
{'recommendation_id': 'first_2', 'phase': 1, 'sap_adjustment': 100},
{'recommendation_id': 'second', 'phase': 2, 'sap_adjustment': 3},
{'recommendation_id': 'third_1', 'phase': 3, 'sap_adjustment': 2},
]
def test_calculate_recommendation_impact():
all_predictions = {
"sap_change_predictions": pd.DataFrame(
[
{'id': '614626+0_phase=0', 'predictions': 66.7, 'property_id': '614626',
'recommendation_id': '0_phase=0',
'phase': 0},
{'id': '614626+1_phase=0', 'predictions': 66.7, 'property_id': '614626',
'recommendation_id': '1_phase=0',
'phase': 0},
{'id': '614626+2_phase=0', 'predictions': 66.7, 'property_id': '614626',
'recommendation_id': '2_phase=0',
'phase': 0},
{'id': '614626+3_phase=1', 'predictions': 65.3, 'property_id': '614626',
'recommendation_id': '3_phase=1',
'phase': 1},
{'id': '614626+4_phase=2', 'predictions': 66.3, 'property_id': '614626',
'recommendation_id': '4_phase=2',
'phase': 2},
{'id': '614626+5_phase=3', 'predictions': 67.3, 'property_id': '614626',
'recommendation_id': '5_phase=3',
'phase': 3},
{'id': '614626+6_phase=3', 'predictions': 68.1, 'property_id': '614626',
'recommendation_id': '6_phase=3',
'phase': 3},
{'id': '614626+7_phase=3', 'predictions': 70.1, 'property_id': '614626',
'recommendation_id': '7_phase=3',
'phase': 3},
{'id': '614626+8_phase=4', 'predictions': 67.3, 'property_id': '614626',
'recommendation_id': '8_phase=4',
'phase': 4},
{'id': '614626+9_phase=5', 'predictions': 85.3, 'property_id': '614626',
'recommendation_id': '9_phase=5',
'phase': 5}, {'id': '614626+10_phase=5', 'predictions': 85.3, 'property_id': '614626',
'recommendation_id': '10_phase=5', 'phase': 5},
{'id': '614626+11_phase=5', 'predictions': 85.3, 'property_id': '614626',
'recommendation_id': '11_phase=5', 'phase': 5},
{'id': '614626+12_phase=5', 'predictions': 85.5, 'property_id': '614626',
'recommendation_id': '12_phase=5', 'phase': 5},
{'id': '614626+13_phase=5', 'predictions': 85.5, 'property_id': '614626',
'recommendation_id': '13_phase=5', 'phase': 5},
{'id': '614626+14_phase=5', 'predictions': 85.5, 'property_id': '614626',
'recommendation_id': '14_phase=5', 'phase': 5},
{'id': '614626+15_phase=5', 'predictions': 85.5, 'property_id': '614626',
'recommendation_id': '15_phase=5', 'phase': 5},
{'id': '614626+16_phase=5', 'predictions': 85.5, 'property_id': '614626',
'recommendation_id': '16_phase=5', 'phase': 5},
{'id': '614626+17_phase=5', 'predictions': 85.5, 'property_id': '614626',
'recommendation_id': '17_phase=5', 'phase': 5},
{'id': '614626+18_phase=5', 'predictions': 85.5, 'property_id': '614626',
'recommendation_id': '18_phase=5', 'phase': 5},
{'id': '614626+19_phase=5', 'predictions': 86.4, 'property_id': '614626',
'recommendation_id': '19_phase=5', 'phase': 5},
{'id': '614626+20_phase=5', 'predictions': 86.4, 'property_id': '614626',
'recommendation_id': '20_phase=5', 'phase': 5},
{'id': '614626+21_phase=5', 'predictions': 86.4, 'property_id': '614626',
'recommendation_id': '21_phase=5', 'phase': 5},
{'id': '614626+22_phase=5', 'predictions': 86.4, 'property_id': '614626',
'recommendation_id': '22_phase=5', 'phase': 5},
{'id': '614626+23_phase=5', 'predictions': 86.4, 'property_id': '614626',
'recommendation_id': '23_phase=5', 'phase': 5},
{'id': '614626+24_phase=5', 'predictions': 86.4, 'property_id': '614626',
'recommendation_id': '24_phase=5', 'phase': 5},
{'id': '614626+25_phase=5', 'predictions': 86.7, 'property_id': '614626',
'recommendation_id': '25_phase=5', 'phase': 5},
{'id': '614626+26_phase=5', 'predictions': 86.7, 'property_id': '614626',
'recommendation_id': '26_phase=5', 'phase': 5},
{'id': '614626+27_phase=5', 'predictions': 86.7, 'property_id': '614626',
'recommendation_id': '27_phase=5', 'phase': 5},
{'id': '614626+28_phase=5', 'predictions': 86.7, 'property_id': '614626',
'recommendation_id': '28_phase=5', 'phase': 5},
{'id': '614626+29_phase=5', 'predictions': 83.8, 'property_id': '614626',
'recommendation_id': '29_phase=5', 'phase': 5},
{'id': '614626+30_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '30_phase=5', 'phase': 5},
{'id': '614626+31_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '31_phase=5', 'phase': 5},
{'id': '614626+32_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '32_phase=5', 'phase': 5},
{'id': '614626+33_phase=5', 'predictions': 86.4, 'property_id': '614626',
'recommendation_id': '33_phase=5', 'phase': 5},
{'id': '614626+34_phase=5', 'predictions': 86.4, 'property_id': '614626',
'recommendation_id': '34_phase=5', 'phase': 5},
{'id': '614626+35_phase=5', 'predictions': 86.4, 'property_id': '614626',
'recommendation_id': '35_phase=5', 'phase': 5},
{'id': '614626+36_phase=5', 'predictions': 86.4, 'property_id': '614626',
'recommendation_id': '36_phase=5', 'phase': 5},
{'id': '614626+37_phase=5', 'predictions': 81.2, 'property_id': '614626',
'recommendation_id': '37_phase=5', 'phase': 5},
{'id': '614626+38_phase=5', 'predictions': 81.2, 'property_id': '614626',
'recommendation_id': '38_phase=5', 'phase': 5},
{'id': '614626+39_phase=5', 'predictions': 81.2, 'property_id': '614626',
'recommendation_id': '39_phase=5', 'phase': 5},
{'id': '614626+40_phase=5', 'predictions': 83.4, 'property_id': '614626',
'recommendation_id': '40_phase=5', 'phase': 5},
{'id': '614626+41_phase=5', 'predictions': 83.4, 'property_id': '614626',
'recommendation_id': '41_phase=5', 'phase': 5},
{'id': '614626+42_phase=5', 'predictions': 83.4, 'property_id': '614626',
'recommendation_id': '42_phase=5', 'phase': 5},
{'id': '614626+43_phase=5', 'predictions': 83.4, 'property_id': '614626',
'recommendation_id': '43_phase=5', 'phase': 5},
{'id': '614626+44_phase=5', 'predictions': 85.5, 'property_id': '614626',
'recommendation_id': '44_phase=5', 'phase': 5},
{'id': '614626+45_phase=5', 'predictions': 85.5, 'property_id': '614626',
'recommendation_id': '45_phase=5', 'phase': 5},
{'id': '614626+46_phase=5', 'predictions': 85.5, 'property_id': '614626',
'recommendation_id': '46_phase=5', 'phase': 5},
{'id': '614626+47_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '47_phase=5', 'phase': 5},
{'id': '614626+48_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '48_phase=5', 'phase': 5},
{'id': '614626+49_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '49_phase=5', 'phase': 5},
{'id': '614626+50_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '50_phase=5', 'phase': 5},
{'id': '614626+51_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '51_phase=5', 'phase': 5},
{'id': '614626+52_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '52_phase=5', 'phase': 5},
{'id': '614626+53_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '53_phase=5', 'phase': 5},
{'id': '614626+54_phase=5', 'predictions': 85.4, 'property_id': '614626',
'recommendation_id': '54_phase=5', 'phase': 5},
{'id': '614626+55_phase=5', 'predictions': 79.4, 'property_id': '614626',
'recommendation_id': '55_phase=5', 'phase': 5},
{'id': '614626+56_phase=5', 'predictions': 81.2, 'property_id': '614626',
'recommendation_id': '56_phase=5', 'phase': 5},
{'id': '614626+57_phase=5', 'predictions': 81.2, 'property_id': '614626',
'recommendation_id': '57_phase=5', 'phase': 5}]
),
"heat_demand_predictions": pd.DataFrame(
[
{'id': '614626+0_phase=0', 'predictions': 256.6, 'property_id': '614626',
'recommendation_id': '0_phase=0',
'phase': 0},
{'id': '614626+1_phase=0', 'predictions': 256.6, 'property_id': '614626',
'recommendation_id': '1_phase=0',
'phase': 0},
{'id': '614626+2_phase=0', 'predictions': 256.6, 'property_id': '614626',
'recommendation_id': '2_phase=0',
'phase': 0},
{'id': '614626+3_phase=1', 'predictions': 263.1, 'property_id': '614626',
'recommendation_id': '3_phase=1',
'phase': 1},
{'id': '614626+4_phase=2', 'predictions': 259.0, 'property_id': '614626',
'recommendation_id': '4_phase=2',
'phase': 2},
{'id': '614626+5_phase=3', 'predictions': 250.5, 'property_id': '614626',
'recommendation_id': '5_phase=3',
'phase': 3},
{'id': '614626+6_phase=3', 'predictions': 245.7, 'property_id': '614626',
'recommendation_id': '6_phase=3',
'phase': 3},
{'id': '614626+7_phase=3', 'predictions': 199.7, 'property_id': '614626',
'recommendation_id': '7_phase=3',
'phase': 3},
{'id': '614626+8_phase=4', 'predictions': 250.5, 'property_id': '614626',
'recommendation_id': '8_phase=4',
'phase': 4},
{'id': '614626+9_phase=5', 'predictions': 139.5, 'property_id': '614626',
'recommendation_id': '9_phase=5',
'phase': 5}, {'id': '614626+10_phase=5', 'predictions': 139.5, 'property_id': '614626',
'recommendation_id': '10_phase=5', 'phase': 5},
{'id': '614626+11_phase=5', 'predictions': 139.5, 'property_id': '614626',
'recommendation_id': '11_phase=5', 'phase': 5},
{'id': '614626+12_phase=5', 'predictions': 133.6, 'property_id': '614626',
'recommendation_id': '12_phase=5', 'phase': 5},
{'id': '614626+13_phase=5', 'predictions': 133.6, 'property_id': '614626',
'recommendation_id': '13_phase=5', 'phase': 5},
{'id': '614626+14_phase=5', 'predictions': 133.6, 'property_id': '614626',
'recommendation_id': '14_phase=5', 'phase': 5},
{'id': '614626+15_phase=5', 'predictions': 133.6, 'property_id': '614626',
'recommendation_id': '15_phase=5', 'phase': 5},
{'id': '614626+16_phase=5', 'predictions': 133.6, 'property_id': '614626',
'recommendation_id': '16_phase=5', 'phase': 5},
{'id': '614626+17_phase=5', 'predictions': 133.6, 'property_id': '614626',
'recommendation_id': '17_phase=5', 'phase': 5},
{'id': '614626+18_phase=5', 'predictions': 133.6, 'property_id': '614626',
'recommendation_id': '18_phase=5', 'phase': 5},
{'id': '614626+19_phase=5', 'predictions': 114.3, 'property_id': '614626',
'recommendation_id': '19_phase=5', 'phase': 5},
{'id': '614626+20_phase=5', 'predictions': 114.3, 'property_id': '614626',
'recommendation_id': '20_phase=5', 'phase': 5},
{'id': '614626+21_phase=5', 'predictions': 114.3, 'property_id': '614626',
'recommendation_id': '21_phase=5', 'phase': 5},
{'id': '614626+22_phase=5', 'predictions': 114.3, 'property_id': '614626',
'recommendation_id': '22_phase=5', 'phase': 5},
{'id': '614626+23_phase=5', 'predictions': 114.3, 'property_id': '614626',
'recommendation_id': '23_phase=5', 'phase': 5},
{'id': '614626+24_phase=5', 'predictions': 114.3, 'property_id': '614626',
'recommendation_id': '24_phase=5', 'phase': 5},
{'id': '614626+25_phase=5', 'predictions': 102.5, 'property_id': '614626',
'recommendation_id': '25_phase=5', 'phase': 5},
{'id': '614626+26_phase=5', 'predictions': 102.5, 'property_id': '614626',
'recommendation_id': '26_phase=5', 'phase': 5},
{'id': '614626+27_phase=5', 'predictions': 102.5, 'property_id': '614626',
'recommendation_id': '27_phase=5', 'phase': 5},
{'id': '614626+28_phase=5', 'predictions': 102.5, 'property_id': '614626',
'recommendation_id': '28_phase=5', 'phase': 5},
{'id': '614626+29_phase=5', 'predictions': 82.5, 'property_id': '614626',
'recommendation_id': '29_phase=5', 'phase': 5},
{'id': '614626+30_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '30_phase=5', 'phase': 5},
{'id': '614626+31_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '31_phase=5', 'phase': 5},
{'id': '614626+32_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '32_phase=5', 'phase': 5},
{'id': '614626+33_phase=5', 'predictions': 114.3, 'property_id': '614626',
'recommendation_id': '33_phase=5', 'phase': 5},
{'id': '614626+34_phase=5', 'predictions': 114.3, 'property_id': '614626',
'recommendation_id': '34_phase=5', 'phase': 5},
{'id': '614626+35_phase=5', 'predictions': 114.3, 'property_id': '614626',
'recommendation_id': '35_phase=5', 'phase': 5},
{'id': '614626+36_phase=5', 'predictions': 114.3, 'property_id': '614626',
'recommendation_id': '36_phase=5', 'phase': 5},
{'id': '614626+37_phase=5', 'predictions': 169.2, 'property_id': '614626',
'recommendation_id': '37_phase=5', 'phase': 5},
{'id': '614626+38_phase=5', 'predictions': 169.2, 'property_id': '614626',
'recommendation_id': '38_phase=5', 'phase': 5},
{'id': '614626+39_phase=5', 'predictions': 169.2, 'property_id': '614626',
'recommendation_id': '39_phase=5', 'phase': 5},
{'id': '614626+40_phase=5', 'predictions': 155.1, 'property_id': '614626',
'recommendation_id': '40_phase=5', 'phase': 5},
{'id': '614626+41_phase=5', 'predictions': 155.1, 'property_id': '614626',
'recommendation_id': '41_phase=5', 'phase': 5},
{'id': '614626+42_phase=5', 'predictions': 155.1, 'property_id': '614626',
'recommendation_id': '42_phase=5', 'phase': 5},
{'id': '614626+43_phase=5', 'predictions': 155.1, 'property_id': '614626',
'recommendation_id': '43_phase=5', 'phase': 5},
{'id': '614626+44_phase=5', 'predictions': 133.6, 'property_id': '614626',
'recommendation_id': '44_phase=5', 'phase': 5},
{'id': '614626+45_phase=5', 'predictions': 133.6, 'property_id': '614626',
'recommendation_id': '45_phase=5', 'phase': 5},
{'id': '614626+46_phase=5', 'predictions': 133.6, 'property_id': '614626',
'recommendation_id': '46_phase=5', 'phase': 5},
{'id': '614626+47_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '47_phase=5', 'phase': 5},
{'id': '614626+48_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '48_phase=5', 'phase': 5},
{'id': '614626+49_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '49_phase=5', 'phase': 5},
{'id': '614626+50_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '50_phase=5', 'phase': 5},
{'id': '614626+51_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '51_phase=5', 'phase': 5},
{'id': '614626+52_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '52_phase=5', 'phase': 5},
{'id': '614626+53_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '53_phase=5', 'phase': 5},
{'id': '614626+54_phase=5', 'predictions': 130.0, 'property_id': '614626',
'recommendation_id': '54_phase=5', 'phase': 5},
{'id': '614626+55_phase=5', 'predictions': 182.6, 'property_id': '614626',
'recommendation_id': '55_phase=5', 'phase': 5},
{'id': '614626+56_phase=5', 'predictions': 169.2, 'property_id': '614626',
'recommendation_id': '56_phase=5', 'phase': 5},
{'id': '614626+57_phase=5', 'predictions': 169.2, 'property_id': '614626',
'recommendation_id': '57_phase=5', 'phase': 5}
]
),
"carbon_change_predictions": pd.DataFrame(
[
{'id': '614626+0_phase=0', 'predictions': 2.2, 'property_id': '614626',
'recommendation_id': '0_phase=0',
'phase': 0},
{'id': '614626+1_phase=0', 'predictions': 2.2, 'property_id': '614626',
'recommendation_id': '1_phase=0',
'phase': 0},
{'id': '614626+2_phase=0', 'predictions': 2.2, 'property_id': '614626',
'recommendation_id': '2_phase=0',
'phase': 0},
{'id': '614626+3_phase=1', 'predictions': 2.2, 'property_id': '614626',
'recommendation_id': '3_phase=1',
'phase': 1},
{'id': '614626+4_phase=2', 'predictions': 2.2, 'property_id': '614626',
'recommendation_id': '4_phase=2',
'phase': 2},
{'id': '614626+5_phase=3', 'predictions': 2.1, 'property_id': '614626',
'recommendation_id': '5_phase=3',
'phase': 3},
{'id': '614626+6_phase=3', 'predictions': 2.1, 'property_id': '614626',
'recommendation_id': '6_phase=3',
'phase': 3},
{'id': '614626+7_phase=3', 'predictions': 1.4, 'property_id': '614626',
'recommendation_id': '7_phase=3',
'phase': 3},
{'id': '614626+8_phase=4', 'predictions': 2.1, 'property_id': '614626',
'recommendation_id': '8_phase=4',
'phase': 4},
{'id': '614626+9_phase=5', 'predictions': 1.3, 'property_id': '614626',
'recommendation_id': '9_phase=5',
'phase': 5},
{'id': '614626+10_phase=5', 'predictions': 1.3, 'property_id': '614626',
'recommendation_id': '10_phase=5',
'phase': 5},
{'id': '614626+11_phase=5', 'predictions': 1.3, 'property_id': '614626',
'recommendation_id': '11_phase=5',
'phase': 5},
{'id': '614626+12_phase=5', 'predictions': 1.2, 'property_id': '614626',
'recommendation_id': '12_phase=5',
'phase': 5},
{'id': '614626+13_phase=5', 'predictions': 1.2, 'property_id': '614626',
'recommendation_id': '13_phase=5',
'phase': 5},
{'id': '614626+14_phase=5', 'predictions': 1.2, 'property_id': '614626',
'recommendation_id': '14_phase=5',
'phase': 5},
{'id': '614626+15_phase=5', 'predictions': 1.2, 'property_id': '614626',
'recommendation_id': '15_phase=5',
'phase': 5},
{'id': '614626+16_phase=5', 'predictions': 1.2, 'property_id': '614626',
'recommendation_id': '16_phase=5',
'phase': 5},
{'id': '614626+17_phase=5', 'predictions': 1.2, 'property_id': '614626',
'recommendation_id': '17_phase=5',
'phase': 5},
{'id': '614626+18_phase=5', 'predictions': 1.2, 'property_id': '614626',
'recommendation_id': '18_phase=5',
'phase': 5},
{'id': '614626+19_phase=5', 'predictions': 1.0, 'property_id': '614626',
'recommendation_id': '19_phase=5',
'phase': 5},
{'id': '614626+20_phase=5', 'predictions': 1.0, 'property_id': '614626',
'recommendation_id': '20_phase=5',
'phase': 5},
{'id': '614626+21_phase=5', 'predictions': 1.0, 'property_id': '614626',
'recommendation_id': '21_phase=5',
'phase': 5},
{'id': '614626+22_phase=5', 'predictions': 1.0, 'property_id': '614626',
'recommendation_id': '22_phase=5',
'phase': 5},
{'id': '614626+23_phase=5', 'predictions': 1.0, 'property_id': '614626',
'recommendation_id': '23_phase=5',
'phase': 5},
{'id': '614626+24_phase=5', 'predictions': 1.0, 'property_id': '614626',
'recommendation_id': '24_phase=5',
'phase': 5},
{'id': '614626+25_phase=5', 'predictions': 0.9, 'property_id': '614626',
'recommendation_id': '25_phase=5',
'phase': 5},
{'id': '614626+26_phase=5', 'predictions': 0.9, 'property_id': '614626',
'recommendation_id': '26_phase=5',
'phase': 5},
{'id': '614626+27_phase=5', 'predictions': 0.9, 'property_id': '614626',
'recommendation_id': '27_phase=5',
'phase': 5},
{'id': '614626+28_phase=5', 'predictions': 0.9, 'property_id': '614626',
'recommendation_id': '28_phase=5',
'phase': 5},
{'id': '614626+29_phase=5', 'predictions': 0.8, 'property_id': '614626',
'recommendation_id': '29_phase=5',
'phase': 5},
{'id': '614626+30_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '30_phase=5',
'phase': 5},
{'id': '614626+31_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '31_phase=5',
'phase': 5},
{'id': '614626+32_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '32_phase=5',
'phase': 5},
{'id': '614626+33_phase=5', 'predictions': 1.0, 'property_id': '614626',
'recommendation_id': '33_phase=5',
'phase': 5},
{'id': '614626+34_phase=5', 'predictions': 1.0, 'property_id': '614626',
'recommendation_id': '34_phase=5',
'phase': 5},
{'id': '614626+35_phase=5', 'predictions': 1.0, 'property_id': '614626',
'recommendation_id': '35_phase=5',
'phase': 5},
{'id': '614626+36_phase=5', 'predictions': 1.0, 'property_id': '614626',
'recommendation_id': '36_phase=5',
'phase': 5},
{'id': '614626+37_phase=5', 'predictions': 1.5, 'property_id': '614626',
'recommendation_id': '37_phase=5',
'phase': 5},
{'id': '614626+38_phase=5', 'predictions': 1.5, 'property_id': '614626',
'recommendation_id': '38_phase=5',
'phase': 5},
{'id': '614626+39_phase=5', 'predictions': 1.5, 'property_id': '614626',
'recommendation_id': '39_phase=5',
'phase': 5},
{'id': '614626+40_phase=5', 'predictions': 1.4, 'property_id': '614626',
'recommendation_id': '40_phase=5',
'phase': 5},
{'id': '614626+41_phase=5', 'predictions': 1.4, 'property_id': '614626',
'recommendation_id': '41_phase=5',
'phase': 5},
{'id': '614626+42_phase=5', 'predictions': 1.4, 'property_id': '614626',
'recommendation_id': '42_phase=5',
'phase': 5},
{'id': '614626+43_phase=5', 'predictions': 1.4, 'property_id': '614626',
'recommendation_id': '43_phase=5',
'phase': 5},
{'id': '614626+44_phase=5', 'predictions': 1.2, 'property_id': '614626',
'recommendation_id': '44_phase=5',
'phase': 5},
{'id': '614626+45_phase=5', 'predictions': 1.2, 'property_id': '614626',
'recommendation_id': '45_phase=5',
'phase': 5},
{'id': '614626+46_phase=5', 'predictions': 1.2, 'property_id': '614626',
'recommendation_id': '46_phase=5',
'phase': 5},
{'id': '614626+47_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '47_phase=5',
'phase': 5},
{'id': '614626+48_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '48_phase=5',
'phase': 5},
{'id': '614626+49_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '49_phase=5',
'phase': 5},
{'id': '614626+50_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '50_phase=5',
'phase': 5},
{'id': '614626+51_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '51_phase=5',
'phase': 5},
{'id': '614626+52_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '52_phase=5',
'phase': 5},
{'id': '614626+53_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '53_phase=5',
'phase': 5},
{'id': '614626+54_phase=5', 'predictions': 1.1, 'property_id': '614626',
'recommendation_id': '54_phase=5',
'phase': 5},
{'id': '614626+55_phase=5', 'predictions': 1.6, 'property_id': '614626',
'recommendation_id': '55_phase=5',
'phase': 5},
{'id': '614626+56_phase=5', 'predictions': 1.5, 'property_id': '614626',
'recommendation_id': '56_phase=5',
'phase': 5},
{'id': '614626+57_phase=5', 'predictions': 1.5, 'property_id': '614626',
'recommendation_id': '57_phase=5',
'phase': 5}
]
),
"hotwater_kwh_predictions": pd.DataFrame([]),
"heating_kwh_predictions": pd.DataFrame([]),
}
# Mock the property - we need id and some of the data
p = Mock(
id=614626,
data={
"current-energy-efficiency": 65,
"co2-emissions-current": 2.4,
"energy-consumption-current": 284,
"roof-energy-eff": "Good",
"lighting-energy-eff": "Good"
},
roof={
'original_description': 'Pitched, 250 mm loft insulation',
'clean_description': 'Pitched, 250 mm loft insulation', 'thermal_transmittance': None,
'thermal_transmittance_unit': None, 'is_pitched': True, 'is_roof_room': False, 'is_loft': True,
'is_flat': False, 'is_thatched': False, 'is_at_rafters': False, 'is_assumed': False,
'has_dwelling_above': False, 'is_valid': True, 'insulation_thickness': '250'
},
lighting={
'original_description': 'Low energy lighting in 50% of fixed outlets',
'clean_description': 'Low energy lighting in 50% of fixed outlets', 'low_energy_proportion': 0.5
}
)
recommendations = {
614626: [
[
{
'phase': 0, 'parts': [
{'id': 3362, 'type': 'loft_insulation', 'description': 'Fibre loft insulation', 'depth': 300.0,
'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.022727273,
'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044,
'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'Warm Front',
'created_at': Timestamp('2025-08-15 16:31:52.995292'), 'is_active': True,
'prime_material_cost': None,
'material_cost': 0.0, 'labour_cost': 0.0, 'labour_hours_per_unit': 0.0, 'plant_cost': 0.0,
'total_cost': 21.0, 'notes': None, 'is_installer_quote': True, 'innovation_rate': 0.0,
'size': None,
'size_unit': None, 'includes_scaffolding': False, 'includes_battery': False, 'battery_size': None,
'quantity': 54.125488565924286, 'quantity_unit': 'm2', 'total': 1029.0, 'contingency': 102.9,
'contingency_rate': 0.1, 'labour_hours': 8, 'labour_days': 1}], 'type': 'loft_insulation',
'measure_type': 'loft_insulation',
'description': 'Install 300mm of Fibre loft insulation in your loft',
'starting_u_value': np.float64(0.17), 'new_u_value': np.float64(0.14), 'sap_points': 0,
'already_installed': False, 'simulation_config': {'roof_insulation_thickness_ending': '300',
'roof_thermal_transmittance_ending': np.float64(
0.14),
'roof_energy_eff_ending': 'Very Good'},
'description_simulation': {'roof-description': 'Pitched, 300mm loft insulation',
'roof-energy-eff': 'Very Good'}, 'total': 1029.0, 'contingency': 102.9,
'contingency_rate': 0.1, 'labour_hours': 8, 'labour_days': 1, 'survey': False,
'innovation_rate': 0.0,
'recommendation_id': '0_phase=0', 'efficiency': np.float64(6052.801176470587),
'co2_equivalent_savings': np.float64(0.19999999999999973),
'heat_demand': np.float64(27.399999999999977)},
],
[
{
'phase': 1, 'parts': [{'id': 3337, 'type': 'mechanical_ventilation',
'description': 'Decentralised mechanical extract ventilation', 'depth': 0.0,
'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_unit',
'r_value_per_mm': nan, 'r_value_unit': 'square_meter_kelvin_per_watt',
'thermal_conductivity': None, 'thermal_conductivity_unit': None,
'link': 'CRG',
'created_at': datetime.datetime(2025, 8, 15, 16, 31, 52, 995292),
'is_active': True, 'prime_material_cost': None, 'material_cost': 0.0,
'labour_cost': 0.0, 'labour_hours_per_unit': 0.0, 'plant_cost': 0.0,
'total_cost': 280.0, 'notes': None, 'is_installer_quote': True,
'innovation_rate': 0.0, 'size': None, 'size_unit': None,
'includes_scaffolding': False, 'includes_battery': False,
'battery_size': None,
'total': 560.0, 'quantity': 2, 'quantity_unit': 'part'}],
'type': 'mechanical_ventilation', 'measure_type': 'mechanical_ventilation',
'description': 'Install 2 Decentralised mechanical extract ventilation units',
'starting_u_value': None,
'new_u_value': None, 'already_installed': False, 'sap_points': np.float64(-1.4000000000000057),
'heat_demand': np.float64(-6.5), 'kwh_savings': 0, 'co2_equivalent_savings': np.float64(0.0),
'energy_cost_savings': 0, 'total': 560.0, 'labour_hours': 8, 'labour_days': 1.0,
'simulation_config': {'mechanical_ventilation_ending': 'mechanical, extract only'},
'description_simulation': {'mechanical-ventilation': 'mechanical, extract only'},
'innovation_rate': 0.0,
'recommendation_id': '3_phase=1', 'efficiency': 0}
],
[
{'phase': 2, 'parts': [], 'type': 'low_energy_lighting', 'measure_type': 'low_energy_lighting',
'description': 'Install low energy lighting in 3 outlets', 'starting_u_value': None,
'new_u_value': None,
'already_installed': False, 'sap_points': 1, 'kwh_savings': 164.25,
'energy_cost_savings': 45.480824999999996, 'co2_equivalent_savings': np.float64(0.0),
'description_simulation': {'lighting-energy-eff': 'Very Good',
'lighting-description': 'Low energy lighting in all fixed outlets',
'low-energy-lighting': 100}, 'total': 10.5, 'contingency': 2.73,
'contingency_rate': 0.26, 'labour_hours': 1, 'labour_days': 0.125, 'survey': True,
'innovation_rate': 0.0,
'recommendation_id': '4_phase=2', 'efficiency': 10.5, 'heat_demand': np.float64(4.100000000000023)}
],
[
{'type': 'heating', 'measure_type': 'roomstat_programmer_trvs', 'phase': 3, 'parts': [],
'description': 'Upgrade heating controls to Room thermostat, programmer and TRVs', 'total': 70,
'contingency': 7.0, 'contingency_rate': 0.1, 'subtotal': 58.333333333333336, 'vat': 11.666666666666664,
'labour_hours': 0.5, 'labour_days': 1, 'starting_u_value': None, 'new_u_value': None,
'sap_points': np.float64(1.0), 'already_installed': False,
'simulation_config': {'trvs_ending': 'trvs', 'mainheatc_energy_eff_ending': 'Good'},
'description_simulation': {'mainheatcont-description': 'Programmer, room thermostat and TRVS',
'mainheatc-energy-eff': 'Good'}, 'innovation_rate': 0.0,
'recommendation_id': '5_phase=3', 'efficiency': 70,
'co2_equivalent_savings': np.float64(0.10000000000000009), 'heat_demand': np.float64(8.5)},
{'type': 'heating', 'phase': 3, 'measure_type': 'time_temperature_zone_control', 'parts': [],
'description': 'Upgrade heating controls to Smart Thermostats, room sensors and smart radiator '
'valves (time & temperature zone control)',
'total': 604.5840000000001, 'contingency': 60.45840000000001, 'contingency_rate': 0.1,
'subtotal': 571.32,
'vat': 33.264, 'labour_hours': 3.08, 'labour_days': np.float64(1.0), 'starting_u_value': None,
'new_u_value': None, 'sap_points': np.float64(1.8), 'already_installed': False,
'simulation_config': {'thermostatic_control_ending': 'time and temperature zone control',
'switch_system_ending': None, 'mainheatc_energy_eff_ending': 'Very Good'},
'description_simulation': {'mainheatcont-description': 'Time and temperature zone control',
'mainheatc-energy-eff': 'Very Good'}, 'innovation_rate': 0.0,
'recommendation_id': '6_phase=3', 'efficiency': 604.5840000000001,
'co2_equivalent_savings': np.float64(0.10000000000000009),
'heat_demand': np.float64(13.300000000000011)},
{'phase': 3, 'parts': [], 'type': 'heating', 'measure_type': 'air_source_heat_pump',
'description': 'Install a 5KW air source heat pump, and upgrade heating controls to Smart '
'Thermostats, room sensors and smart radiator valves (time & temperature zone '
'control). Ensure you have a single tariff',
'starting_u_value': None, 'new_u_value': None, 'sap_points': np.float64(3.8),
'already_installed': False,
'simulation_config': {'mainheat_energy_eff_ending': 'Good', 'hot_water_energy_eff_ending': 'Average',
'has_boiler_ending': False, 'has_air_source_heat_pump_ending': True,
'has_electric_ending': True, 'has_mains_gas_ending': False,
'fuel_type_ending': 'electricity',
'thermostatic_control_ending': 'time and temperature zone control',
'switch_system_ending': None, 'mainheatc_energy_eff_ending': 'Very Good'},
'description_simulation': {'mainheat-description': 'Air source heat pump, radiators, electric',
'mainheat-energy-eff': 'Good', 'hot-water-energy-eff': 'Average',
'hotwater-description': 'From main system',
'main-fuel': 'electricity (not community)',
'mainheatcont-description': 'Time and temperature zone control',
'mainheatc-energy-eff': 'Very Good'}, 'total': 17144.924,
'contingency': 4195.5434000000005, 'contingency_rate': 0.35, 'vat': 33.264, 'labour_hours': 83.08,
'labour_days': np.float64(11.0), 'innovation_rate': 0, 'recommendation_id': '7_phase=3',
'efficiency': 17144.924, 'co2_equivalent_savings': np.float64(0.8000000000000003),
'heat_demand': np.float64(59.30000000000001)}
],
[
{'phase': 4, 'parts': [], 'type': 'secondary_heating', 'measure_type': 'secondary_heating',
'description': 'Remove the secondary heating system', 'starting_u_value': None, 'new_u_value': None,
'sap_points': np.float64(0.0), 'already_installed': False, 'total': 60.0, 'contingency': 6.0,
'contingency_rate': 0.1, 'subtotal': 50.0, 'vat': 10.0, 'labour_hours': 6.0,
'labour_days': np.float64(1.0), 'simulation_config': {'secondheat_description_ending': 'None'},
'description_simulation': {'secondheat-description': 'None'}, 'innovation_rate': 0.0,
'recommendation_id': '8_phase=4', 'efficiency': 60.0, 'co2_equivalent_savings': np.float64(0.0),
'heat_demand': np.float64(0.0)}
],
[
{
'phase': 5, 'parts': [
{'id': 3516, 'type': 'solar_pv', 'description': 'Trina Vertex S3 445W solar panels', 'depth': 0.0,
'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan,
'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None,
'thermal_conductivity_unit': None, 'link': 'Coactivation',
'created_at': datetime.datetime(2025, 8, 15, 16, 31, 52, 995292), 'is_active': True,
'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 0.0,
'labour_hours_per_unit': 0.0,
'plant_cost': 0.0, 'total_cost': 5892.21, 'notes': '445W panels', 'is_installer_quote': True,
'innovation_rate': 0.0, 'size': 5.34, 'size_unit': 'kWp', 'includes_scaffolding': True,
'includes_battery': False, 'battery_size': None, 'panel_size': 445}], 'type': 'solar_pv',
'measure_type': 'solar_pv',
'description': 'Trina Vertex S3 445W solar panels - 5.34 kWp '
'system',
'starting_u_value': None, 'new_u_value': None,
'sap_points': np.float64(16.0), 'already_installed': False,
'total': 5892.21, 'subtotal': 5892.21, 'contingency': 883.8315,
'contingency_rate': 0.15, 'vat': 0, 'labour_hours': 48,
'labour_days': 2, 'has_battery': False,
'simulation_config': {'photo_supply_ending': np.float64(80.0)},
'initial_ac_kwh_per_year': np.float64(4844.465553999999),
'description_simulation': {'photo-supply': np.float64(80.0)},
'innovation_rate': 0.0, 'recommendation_id': '29_phase=5',
'efficiency': np.float64(368.263125)
}
]
]
}
representative_recommendations = {
614626: [
{
'phase': 0, 'parts': [
{'id': 3362, 'type': 'loft_insulation', 'description': 'Fibre loft insulation', 'depth': 300.0,
'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.022727273,
'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044,
'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'Warm Front',
'created_at': Timestamp('2025-08-15 16:31:52.995292'), 'is_active': True,
'prime_material_cost': None,
'material_cost': 0.0, 'labour_cost': 0.0, 'labour_hours_per_unit': 0.0, 'plant_cost': 0.0,
'total_cost': 21.0, 'notes': None, 'is_installer_quote': True, 'innovation_rate': 0.0,
'size': None,
'size_unit': None, 'includes_scaffolding': False, 'includes_battery': False, 'battery_size': None,
'quantity': 54.125488565924286, 'quantity_unit': 'm2', 'total': 1029.0, 'contingency': 102.9,
'contingency_rate': 0.1, 'labour_hours': 8, 'labour_days': 1}], 'type': 'loft_insulation',
'measure_type': 'loft_insulation',
'description': 'Install 300mm of Fibre loft insulation in your loft',
'starting_u_value': np.float64(0.17), 'new_u_value': np.float64(0.14), 'sap_points': 0,
'already_installed': False, 'simulation_config': {'roof_insulation_thickness_ending': '300',
'roof_thermal_transmittance_ending': np.float64(
0.14),
'roof_energy_eff_ending': 'Very Good'},
'description_simulation': {'roof-description': 'Pitched, 300mm loft insulation',
'roof-energy-eff': 'Very Good'}, 'total': 1029.0, 'contingency': 102.9,
'contingency_rate': 0.1, 'labour_hours': 8, 'labour_days': 1, 'survey': False,
'innovation_rate': 0.0,
'recommendation_id': '0_phase=0', 'efficiency': np.float64(6052.801176470587),
'co2_equivalent_savings': np.float64(0.19999999999999973),
'heat_demand': np.float64(27.399999999999977)
},
{
'phase': 1, 'parts': [
{'id': 3337, 'type': 'mechanical_ventilation',
'description': 'Decentralised mechanical extract ventilation', 'depth': 0.0, 'depth_unit': None,
'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': nan,
'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None,
'thermal_conductivity_unit': None, 'link': 'CRG',
'created_at': datetime.datetime(2025, 8, 15, 16, 31, 52, 995292), 'is_active': True,
'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 0.0, 'labour_hours_per_unit': 0.0,
'plant_cost': 0.0, 'total_cost': 280.0, 'notes': None, 'is_installer_quote': True,
'innovation_rate': 0.0,
'size': None, 'size_unit': None, 'includes_scaffolding': False, 'includes_battery': False,
'battery_size': None, 'total': 560.0, 'quantity': 2, 'quantity_unit': 'part'}],
'type': 'mechanical_ventilation',
'measure_type': 'mechanical_ventilation',
'description': 'Install 2 Decentralised mechanical '
'extract ventilation units',
'starting_u_value': None, 'new_u_value': None,
'already_installed': False,
'sap_points': np.float64(-1.4000000000000057),
'heat_demand': np.float64(-6.5), 'kwh_savings': 0,
'co2_equivalent_savings': np.float64(0.0),
'energy_cost_savings': 0, 'total': 560.0,
'labour_hours': 8, 'labour_days': 1.0,
'simulation_config': {
'mechanical_ventilation_ending': 'mechanical, '
'extract only'},
'description_simulation': {
'mechanical-ventilation': 'mechanical, '
'extract only'},
'innovation_rate': 0.0,
'recommendation_id': '3_phase=1', 'efficiency': 0},
{
'phase': 2, 'parts': [], 'type': 'low_energy_lighting', 'measure_type': 'low_energy_lighting',
'description': 'Install low energy lighting in 3 outlets', 'starting_u_value': None,
'new_u_value': None, 'already_installed': False, 'sap_points': 1, 'kwh_savings': 164.25,
'energy_cost_savings': 45.480824999999996, 'co2_equivalent_savings': np.float64(0.0),
'description_simulation': {'lighting-energy-eff': 'Very Good',
'lighting-description': 'Low energy lighting in all fixed outlets',
'low-energy-lighting': 100}, 'total': 10.5, 'contingency': 2.73,
'contingency_rate': 0.26, 'labour_hours': 1, 'labour_days': 0.125, 'survey': True,
'innovation_rate': 0.0, 'recommendation_id': '4_phase=2', 'efficiency': 10.5,
'heat_demand': np.float64(4.100000000000023)
},
{
'type': 'heating', 'measure_type': 'roomstat_programmer_trvs', 'phase': 3, 'parts': [],
'description': 'Upgrade heating controls to Room thermostat, programmer and TRVs', 'total': 70,
'contingency': 7.0, 'contingency_rate': 0.1, 'subtotal': 58.333333333333336,
'vat': 11.666666666666664, 'labour_hours': 0.5, 'labour_days': 1, 'starting_u_value': None,
'new_u_value': None, 'sap_points': np.float64(1.0), 'already_installed': False,
'simulation_config': {'trvs_ending': 'trvs', 'mainheatc_energy_eff_ending': 'Good'},
'description_simulation': {'mainheatcont-description': 'Programmer, room thermostat and TRVS',
'mainheatc-energy-eff': 'Good'}, 'innovation_rate': 0.0,
'recommendation_id': '5_phase=3', 'efficiency': 70,
'co2_equivalent_savings': np.float64(0.10000000000000009), 'heat_demand': np.float64(8.5)
},
{
'phase': 4, 'parts': [], 'type': 'secondary_heating', 'measure_type': 'secondary_heating',
'description': 'Remove the secondary heating system', 'starting_u_value': None, 'new_u_value': None,
'sap_points': np.float64(0.0), 'already_installed': False, 'total': 60.0, 'contingency': 6.0,
'contingency_rate': 0.1, 'subtotal': 50.0, 'vat': 10.0, 'labour_hours': 6.0,
'labour_days': np.float64(1.0), 'simulation_config': {'secondheat_description_ending': 'None'},
'description_simulation': {'secondheat-description': 'None'}, 'innovation_rate': 0.0,
'recommendation_id': '8_phase=4', 'efficiency': 60.0, 'co2_equivalent_savings': np.float64(0.0),
'heat_demand': np.float64(0.0)},
{
'phase': 5, 'parts': [
{'id': 3516, 'type': 'solar_pv', 'description': 'Trina Vertex S3 445W solar panels', 'depth': 0.0,
'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan,
'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None,
'thermal_conductivity_unit': None, 'link': 'Coactivation',
'created_at': datetime.datetime(2025, 8, 15, 16, 31, 52, 995292), 'is_active': True,
'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 0.0,
'labour_hours_per_unit': 0.0,
'plant_cost': 0.0, 'total_cost': 5892.21, 'notes': '445W panels', 'is_installer_quote': True,
'innovation_rate': 0.0, 'size': 5.34, 'size_unit': 'kWp', 'includes_scaffolding': True,
'includes_battery': False, 'battery_size': None, 'panel_size': 445}], 'type': 'solar_pv',
'measure_type': 'solar_pv',
'description': 'Trina Vertex S3 445W solar panels - 5.34 kWp '
'system',
'starting_u_value': None, 'new_u_value': None,
'sap_points': np.float64(16.0), 'already_installed': False,
'total': 5892.21, 'subtotal': 5892.21, 'contingency': 883.8315,
'contingency_rate': 0.15, 'vat': 0, 'labour_hours': 48,
'labour_days': 2, 'has_battery': False,
'simulation_config': {'photo_supply_ending': np.float64(80.0)},
'initial_ac_kwh_per_year': np.float64(4844.465553999999),
'description_simulation': {'photo-supply': np.float64(80.0)},
'innovation_rate': 0.0, 'recommendation_id': '29_phase=5',
'efficiency': np.float64(368.263125)
}
]
}
recommendations_with_impact, impact_summary, adjustments = (
Recommendations.calculate_recommendation_impact(
property_instance=p,
all_predictions=all_predictions,
recommendations=recommendations,
representative_recommendations=representative_recommendations,
debug=True
)
)
# We expect an adjustment to be made for loft insulation, reducing the impact by
# 1.7
assert adjustments == [{'recommendation_id': '0_phase=0', 'phase': 0, 'sap_adjustment': np.float64(1.7)}]
# We expect that adjustment to flow through to the final recommendation so that the solar recommendation has
# a 1.7 sap point reduction in impact
assert float(impact_summary[-1]["sap"]) == 82.1
assert float(impact_summary[-1]["sap_prediction"]) == 83.8
assert impact_summary[-1] == {
'phase': 5, 'representative': True, 'recommendation_id': '29_phase=5', 'measure_type': 'solar_pv',
'sap': np.float64(82.1), 'carbon': np.float64(0.8), 'heat_demand': np.float64(82.5),
'sap_prediction': np.float64(83.8)
}