From 316e45848b6140aaf57e6f10939ef6a1009e6501 Mon Sep 17 00:00:00 2001 From: Conrad S Date: Fri, 28 Apr 2017 11:29:02 -0400 Subject: [PATCH 1/4] Add constants file mapping ECM database name to slide index and slide name --- bpeng/reports/constants.py | 175 +++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 bpeng/reports/constants.py diff --git a/bpeng/reports/constants.py b/bpeng/reports/constants.py new file mode 100644 index 0000000..d5ed48b --- /dev/null +++ b/bpeng/reports/constants.py @@ -0,0 +1,175 @@ + +ECM_TO_SLIDE = { + 'Weatherstripping (Exterior Doors)': { + 'slide_index': 5, + 'slide_name': 'Install Weather-stripping', + }, + 'Weatherstripping (Windows)': { + 'slide_index': 5, + 'slide_name': 'Install Weather-stripping', + }, + 'Weatherstripping (A/C Units)': { + 'slide_index': 5, + 'slide_name': 'Install Weather-stripping', + }, + 'Window Replacement': { + 'slide_index': 6, + 'slide_name': 'Replace Windows', + }, + 'Insulation (Roof)': { + 'slide_index': 7, + 'slide_name': 'Install Roof Insulation', + }, + 'Cool Roof ': { + 'slide_index': 8, + 'slide_name': 'Install Cool Roof', + }, + 'Boiler Control(Indoor Feedback)': { + 'slide_index': 9, + 'slide_name': 'Install Boiler Control (Indoor Feedback)', + }, + 'Boiler Control(Outdoor Reset)': { + 'slide_index': 10, + 'slide_name': 'Install Boiler Control (Outdoor Reset)', + }, + 'Smart Thermostat w/ Indoor Sensor': { + 'slide_index': 11, + 'slide_name': 'Install Smart Thermostat with Indoor Sensors', + }, + 'Building Management System/ Energy Management system': { + 'slide_index': 12, + 'slide_name': 'Integrate EMS/BMS Systems', + }, + 'Oil to Gas Conversion': { + 'slide_index': 13, + 'slide_name': 'Convert from Oil to Gas', + }, + 'Boiler Replacement': { + 'slide_index': 14, + 'slide_name': 'Replace Boiler', + }, + 'Burner Replacement': { + 'slide_index': 15, + 'slide_name': 'Replace Burner & Controls', + }, + 'Modulating Burner Controls': { + 'slide_index': 15, + 'slide_name': 'Replace Burner & Controls', + }, + 'Sealed Combustion/Power Burner Installation': { + 'slide_index': 15, + 'slide_name': 'Replace Burner & Controls', + }, + 'Pipe Insulation': { + 'slide_index': 16, + 'slide_name': 'Install Pipe Insulation', + }, + 'Smart Pumps': { + 'slide_index': 17, + 'slide_name': 'Install Smart Pumps', + }, + 'Variable Frequency Drive Pumps': { + 'slide_index': 17, + 'slide_name': 'Install Smart Pumps', + }, + 'TRVs (Steam, 1 pipe)': { + 'slide_index': 18, + 'slide_name': 'Install Thermostatic Radiator Valves (TRVs)', + }, + 'TRVs (Steam, 2 pipe)': { + 'slide_index': 18, + 'slide_name': 'Install Thermostatic Radiator Valves (TRVs)', + }, + 'TRVs (Hydronic)': { + 'slide_index': 18, + 'slide_name': 'Install Thermostatic Radiator Valves (TRVs)', + }, + 'Baseboard Replacement': { + 'slide_index': 19, + 'slide_name': 'Clean/Replace Baseboards', + }, + 'Hydronic Conversion': { + 'slide_index': 20, + 'slide_name': 'Convert from Steam to Hydronic', + }, + 'Master Venting': { + 'slide_index': 21, + 'slide_name': 'Install Master Venting', + }, + 'Steam Boiler Setting Optimization': { + 'slide_index': 22, + 'slide_name': 'Optimize Boiler\'s Pressure', + }, + 'Air Vent Installation/Replacement': { + 'slide_index': 23, + 'slide_name': 'Install Air Vents', + }, + 'Steam Trap Installation/Replacement (1 pipe)': { + 'slide_index': 24, + 'slide_name': 'Install Steam Traps', + }, + 'Steam Trap Installation/Replacement (2 pipe)': { + 'slide_index': 24, + 'slide_name': 'Install Steam Traps', + }, + 'Condensate Removal System Upgrade': { + 'slide_index': 25, + 'slide_name': 'Install Effective Condensate Removal', + }, + 'Vented Condensate Pumps': { + 'slide_index': 25, + 'slide_name': 'Install Effective Condensate Removal', + }, + 'Steam Boiler Anode Bars': { + 'slide_index': 26, + 'slide_name': 'Install Anode Bars in Boiler Tank', + }, + 'Rooftop Unit Replacement': { + 'slide_index': 27, + 'slide_name': 'Replace Roof Top Units', + }, + 'DHW Temperature Controls': { + 'slide_index': 28, + 'slide_name': 'Improve DHW Temperature Control', + }, + 'Tankless Water Heater': { + 'slide_index': 29, + 'slide_name': 'Install Tankless Water Heater', + }, + 'Tanked Water Heater': { + 'slide_index': 30, + 'slide_name': 'Install Tanked Water Heater', + }, + 'Solar Water Heater': { + 'slide_index': 31, + 'slide_name': 'Install Solar Water Heaters', + }, + 'Low-Flow Water Fixtures': { + 'slide_index': 32, + 'slide_name': 'Install Low-Flow Water Fixtures', + }, + 'Automatic Water Meter Reader': { + 'slide_index': 33, + 'slide_name': 'Install Automated Meter Reader', + }, + 'LED Lighting Install': { + 'slide_index': 34, + 'slide_name': 'Install LEDs and Lighting Controls', + }, + 'Lighting Controls (Motion Sensors/Timer)': { + 'slide_index': 34, + 'slide_name': 'Install LEDs and Lighting Controls', + }, + 'Solar Panels': { + 'slide_index': 35, + 'slide_name': 'Install Solar Panels', + }, + 'Replace Unit Appliances w/ Energy Star Models': { + 'slide_index': 36, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Replace Laundry Room Appliances w/ Energy Star Models': { + 'slide_index': 36, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, +} -- GitLab From 97d8c659213775ed64fb13f32a426d670ef37a75 Mon Sep 17 00:00:00 2001 From: Conrad S Date: Fri, 28 Apr 2017 11:33:26 -0400 Subject: [PATCH 2/4] Update gitignore to ignore pptx files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 32e5c2f..1464721 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ build .coverage coverage* htmlcov + +# pptx files +*.pptx -- GitLab From 2217955df5919a9feaa06aa0bfd00cd16d8e84ee Mon Sep 17 00:00:00 2001 From: Conrad S Date: Fri, 28 Apr 2017 14:41:08 -0400 Subject: [PATCH 3/4] Update _generate_report to take in database input rather than presentation-style strings --- bpeng/reports/cbra_diag.py | 50 +++++++++--- bpeng/reports/constants.py | 125 +++++++++++++++++++---------- bpeng/tests/data/test_reports.xlsx | Bin 14704 -> 11111 bytes bpeng/tests/test_reports.py | 47 ++++++----- 4 files changed, 144 insertions(+), 78 deletions(-) diff --git a/bpeng/reports/cbra_diag.py b/bpeng/reports/cbra_diag.py index d5e5691..f330372 100644 --- a/bpeng/reports/cbra_diag.py +++ b/bpeng/reports/cbra_diag.py @@ -22,6 +22,7 @@ from pptx.enum.text import ( ) from pptx.util import Inches, Pt +from .constants import ECM_TO_SLIDE, TEMPLATE_TO_DATABASE class CbraDiagnostic: """ @@ -71,9 +72,40 @@ class CbraDiagnostic: heat_loss_val = heat_loss_data['heat_loss_val'] ecm_list = ecm_data['ecm_list'] - ecm_delete_list = ecm_data['ecm_delete_list'] ecm_heat_save = ecm_data['ecm_heat_save'] ecm_elec_save = ecm_data['ecm_elec_save'] + # Convert database strings to presentation strings + # Also calculate new heat or elec save in cases where + # database ecm's are combined to a single presentation ecm + new_ecm_list = [] + new_ecm_heat_save = [] + new_ecm_elec_save = [] + for name, heat, elec in zip(ecm_list, ecm_heat_save, ecm_elec_save): + presentation_name = ECM_TO_SLIDE[name]['slide_name'] + if presentation_name in new_ecm_list: + combine_index = new_ecm_list.index(presentation_name) + # Right now we just add together heat and elec. + # In the future we might want to calculate the combined value for + # heat and elec differently based on the ECM + new_ecm_heat_save[combine_index] += heat + new_ecm_elec_save[combine_index] += elec + else: + new_ecm_list.append(presentation_name) + new_ecm_heat_save.append(heat) + new_ecm_elec_save.append(elec) + ecm_list = new_ecm_list + ecm_heat_save = new_ecm_heat_save + ecm_elec_save = new_ecm_elec_save + + # Create a delete list by adding everything not in the ecm_list + ecm_delete_list = set() + for slide in ECM_TO_SLIDE.values(): + if slide['slide_name'] not in ecm_list: + ecm_delete_list.add(slide['slide_index']) + + ecm_delete_list = list(ecm_delete_list) + # Put delete list in reverse order so we delete from the back + ecm_delete_list.sort(reverse=True) # Define presentation to edit as the finished template diag_rep = Presentation(template_file) @@ -328,14 +360,11 @@ class CbraDiagnostic: ecm_table.table.cell(ecm_count+1, 2).text = str(annual_energy_percent) + '\N{PERCENT SIGN}' # Slides 5-end: removing slides based on recommendations - deleted_slide_counter = 0 - NUM_NON_ECM_SLIDES = 4 for ecm_index in ecm_delete_list: diag_rep.slides.delete_slide( diag_rep, - ecm_index + NUM_NON_ECM_SLIDES - deleted_slide_counter, + ecm_index, ) - deleted_slide_counter += 1 return project_address, diag_rep @@ -410,22 +439,19 @@ class CbraDiagnostic: } ecm_list = [] - ecm_delete_list = [] ecm_heat_save = [] ecm_elec_save = [] for j in range(len(input_values)): if input_values[j] == 'Y': - ecm_list.append(input_list[j]) + # Convert to database string + ecm_list.append( + TEMPLATE_TO_DATABASE[input_list[j]] + ) ecm_heat_save.append(input_values[j+1]) ecm_elec_save.append(input_values[j+2]) - if input_values[j] == 'N': - ecm_index = int((j-20)/3) - ecm_delete_list.append(ecm_index) - ecm_data = { 'ecm_list': ecm_list, - 'ecm_delete_list': ecm_delete_list, 'ecm_heat_save': ecm_heat_save, 'ecm_elec_save': ecm_elec_save, } diff --git a/bpeng/reports/constants.py b/bpeng/reports/constants.py index d5ed48b..43c0e08 100644 --- a/bpeng/reports/constants.py +++ b/bpeng/reports/constants.py @@ -1,175 +1,212 @@ - ECM_TO_SLIDE = { 'Weatherstripping (Exterior Doors)': { - 'slide_index': 5, + 'slide_index': 4, 'slide_name': 'Install Weather-stripping', }, 'Weatherstripping (Windows)': { - 'slide_index': 5, + 'slide_index': 4, 'slide_name': 'Install Weather-stripping', }, 'Weatherstripping (A/C Units)': { - 'slide_index': 5, + 'slide_index': 4, 'slide_name': 'Install Weather-stripping', }, 'Window Replacement': { - 'slide_index': 6, + 'slide_index': 5, 'slide_name': 'Replace Windows', }, 'Insulation (Roof)': { - 'slide_index': 7, + 'slide_index': 6, 'slide_name': 'Install Roof Insulation', }, 'Cool Roof ': { - 'slide_index': 8, + 'slide_index': 7, 'slide_name': 'Install Cool Roof', }, 'Boiler Control(Indoor Feedback)': { - 'slide_index': 9, + 'slide_index': 8, 'slide_name': 'Install Boiler Control (Indoor Feedback)', }, 'Boiler Control(Outdoor Reset)': { - 'slide_index': 10, + 'slide_index': 9, 'slide_name': 'Install Boiler Control (Outdoor Reset)', }, 'Smart Thermostat w/ Indoor Sensor': { - 'slide_index': 11, + 'slide_index': 10, 'slide_name': 'Install Smart Thermostat with Indoor Sensors', }, 'Building Management System/ Energy Management system': { - 'slide_index': 12, + 'slide_index': 11, 'slide_name': 'Integrate EMS/BMS Systems', }, 'Oil to Gas Conversion': { - 'slide_index': 13, + 'slide_index': 12, 'slide_name': 'Convert from Oil to Gas', }, 'Boiler Replacement': { - 'slide_index': 14, + 'slide_index': 13, 'slide_name': 'Replace Boiler', }, 'Burner Replacement': { - 'slide_index': 15, + 'slide_index': 14, 'slide_name': 'Replace Burner & Controls', }, 'Modulating Burner Controls': { - 'slide_index': 15, + 'slide_index': 14, 'slide_name': 'Replace Burner & Controls', }, 'Sealed Combustion/Power Burner Installation': { - 'slide_index': 15, + 'slide_index': 14, 'slide_name': 'Replace Burner & Controls', }, 'Pipe Insulation': { - 'slide_index': 16, + 'slide_index': 15, 'slide_name': 'Install Pipe Insulation', }, 'Smart Pumps': { - 'slide_index': 17, + 'slide_index': 16, 'slide_name': 'Install Smart Pumps', }, 'Variable Frequency Drive Pumps': { - 'slide_index': 17, + 'slide_index': 16, 'slide_name': 'Install Smart Pumps', }, 'TRVs (Steam, 1 pipe)': { - 'slide_index': 18, + 'slide_index': 17, 'slide_name': 'Install Thermostatic Radiator Valves (TRVs)', }, 'TRVs (Steam, 2 pipe)': { - 'slide_index': 18, + 'slide_index': 17, 'slide_name': 'Install Thermostatic Radiator Valves (TRVs)', }, 'TRVs (Hydronic)': { - 'slide_index': 18, + 'slide_index': 17, 'slide_name': 'Install Thermostatic Radiator Valves (TRVs)', }, 'Baseboard Replacement': { - 'slide_index': 19, + 'slide_index': 18, 'slide_name': 'Clean/Replace Baseboards', }, 'Hydronic Conversion': { - 'slide_index': 20, + 'slide_index': 19, 'slide_name': 'Convert from Steam to Hydronic', }, 'Master Venting': { - 'slide_index': 21, + 'slide_index': 20, 'slide_name': 'Install Master Venting', }, 'Steam Boiler Setting Optimization': { - 'slide_index': 22, + 'slide_index': 21, 'slide_name': 'Optimize Boiler\'s Pressure', }, 'Air Vent Installation/Replacement': { - 'slide_index': 23, + 'slide_index': 22, 'slide_name': 'Install Air Vents', }, 'Steam Trap Installation/Replacement (1 pipe)': { - 'slide_index': 24, + 'slide_index': 23, 'slide_name': 'Install Steam Traps', }, 'Steam Trap Installation/Replacement (2 pipe)': { - 'slide_index': 24, + 'slide_index': 23, 'slide_name': 'Install Steam Traps', }, 'Condensate Removal System Upgrade': { - 'slide_index': 25, + 'slide_index': 24, 'slide_name': 'Install Effective Condensate Removal', }, 'Vented Condensate Pumps': { - 'slide_index': 25, + 'slide_index': 24, 'slide_name': 'Install Effective Condensate Removal', }, 'Steam Boiler Anode Bars': { - 'slide_index': 26, + 'slide_index': 25, 'slide_name': 'Install Anode Bars in Boiler Tank', }, 'Rooftop Unit Replacement': { - 'slide_index': 27, + 'slide_index': 26, 'slide_name': 'Replace Roof Top Units', }, 'DHW Temperature Controls': { - 'slide_index': 28, + 'slide_index': 27, 'slide_name': 'Improve DHW Temperature Control', }, 'Tankless Water Heater': { - 'slide_index': 29, + 'slide_index': 28, 'slide_name': 'Install Tankless Water Heater', }, 'Tanked Water Heater': { - 'slide_index': 30, + 'slide_index': 29, 'slide_name': 'Install Tanked Water Heater', }, 'Solar Water Heater': { - 'slide_index': 31, + 'slide_index': 30, 'slide_name': 'Install Solar Water Heaters', }, 'Low-Flow Water Fixtures': { - 'slide_index': 32, + 'slide_index': 31, 'slide_name': 'Install Low-Flow Water Fixtures', }, 'Automatic Water Meter Reader': { - 'slide_index': 33, + 'slide_index': 32, 'slide_name': 'Install Automated Meter Reader', }, 'LED Lighting Install': { - 'slide_index': 34, + 'slide_index': 33, 'slide_name': 'Install LEDs and Lighting Controls', }, 'Lighting Controls (Motion Sensors/Timer)': { - 'slide_index': 34, + 'slide_index': 33, 'slide_name': 'Install LEDs and Lighting Controls', }, 'Solar Panels': { - 'slide_index': 35, + 'slide_index': 34, 'slide_name': 'Install Solar Panels', }, 'Replace Unit Appliances w/ Energy Star Models': { - 'slide_index': 36, + 'slide_index': 35, 'slide_name': 'Replace Appliances with Energy Star Appliances', }, 'Replace Laundry Room Appliances w/ Energy Star Models': { - 'slide_index': 36, + 'slide_index': 35, 'slide_name': 'Replace Appliances with Energy Star Appliances', }, } + +# Convert template ECM names to the names that are in the database +# Template ECM to database ECM is a 1 to n mapping so we just take the first one + +TEMPLATE_TO_DATABASE = { + 'Install Weather-stripping': 'Weatherstripping (Exterior Doors)', + 'Replace Windows': 'Window Replacement', + 'Install Roof Insulation': 'Insulation (Roof)', + 'Install Cool Roof': 'Cool Roof ', + 'Install Boiler Control (Indoor Feedback)': 'Boiler Control(Indoor Feedback)', + 'Install Boiler Control (Outdoor Reset)': 'Boiler Control(Outdoor Reset)', + 'Install Smart Thermostat with Indoor Sensors': 'Smart Thermostat w/ Indoor Sensor', + 'Integrate EMS/BMS Systems': 'Building Management System/ Energy Management system', + 'Convert from Oil to Gas': 'Oil to Gas Conversion', + 'Replace Boiler': 'Boiler Replacement', + 'Replace Burner & Controls': 'Burner Replacement', + 'Install Pipe Insulation': 'Pipe Insulation', + 'Install Smart Pumps': 'Smart Pumps', + 'Install Thermostatic Radiator Valves (TRVs)': 'TRVs (Steam, 1 pipe)', + 'Clean/Replace Baseboards': 'Baseboard Replacement', + 'Convert from Steam to Hydronic': 'Hydronic Conversion', + 'Install Master Venting': 'Master Venting', + 'Optimize Boiler\'s Pressure': 'Steam Boiler Setting Optimization', + 'Install Air Vents': 'Air Vent Installation/Replacement', + 'Install Steam Traps': 'Steam Trap Installation/Replacement (1 pipe)', + 'Install Effective Condensate Removal': 'Condensate Removal System Upgrade', + 'Install Anode Bars in Boiler Tank': 'Steam Boiler Anode Bars', + 'Replace Roof Top Units': 'Rooftop Unit Replacement', + 'Improve DHW Temperature Control': 'DHW Temperature Controls', + 'Install Tankless Water Heater': 'Tankless Water Heater', + 'Install Tanked Water Heater': 'Tanked Water Heater', + 'Install Solar Water Heaters': 'Solar Water Heater', + 'Install Low-Flow Water Fixtures': 'Low-Flow Water Fixtures', + 'Install Automated Meter Reader': 'Automatic Water Meter Reader', + 'Install LEDs and Lighting Controls ': 'LED Lighting Install', + 'Install Solar Panels': 'Solar Panels', + 'Replace Appliances with Energy Star Appliances': 'Replace Unit Appliances w/ Energy Star Models', +} diff --git a/bpeng/tests/data/test_reports.xlsx b/bpeng/tests/data/test_reports.xlsx index e425486b87c7241958ae5f72c2e050d04a6b218f..086495765ff121ba502d3fb8b9ec124b04237ba7 100644 GIT binary patch literal 11111 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 Date: Tue, 2 May 2017 17:17:12 -0400 Subject: [PATCH 4/4] Update constants name and cbra_multi_rep arg changes --- bpeng/reports/cbra_diag.py | 18 +++++++++++++++--- bpeng/reports/constants.py | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/bpeng/reports/cbra_diag.py b/bpeng/reports/cbra_diag.py index f330372..fddea4b 100644 --- a/bpeng/reports/cbra_diag.py +++ b/bpeng/reports/cbra_diag.py @@ -22,7 +22,7 @@ from pptx.enum.text import ( ) from pptx.util import Inches, Pt -from .constants import ECM_TO_SLIDE, TEMPLATE_TO_DATABASE +from .constants import ECM_TO_SLIDE, RETROFIT_TO_CBRA_RETROFIT class CbraDiagnostic: """ @@ -445,7 +445,7 @@ class CbraDiagnostic: if input_values[j] == 'Y': # Convert to database string ecm_list.append( - TEMPLATE_TO_DATABASE[input_list[j]] + RETROFIT_TO_CBRA_RETROFIT[input_list[j]] ) ecm_heat_save.append(input_values[j+1]) ecm_elec_save.append(input_values[j+2]) @@ -479,7 +479,19 @@ class CbraDiagnostic: file_name = repgen_list[i] file_input = pd.ExcelFile(file_name) sheet_input = file_input.parse("Inputs", header=None) - proj_loc, diag_rep = CbraDiagnostic._generate_report(template_file, sheet_input) + ( + project_summary_data, + util_break_data, + heat_loss_data, + ecm_data + ) = CbraDiagnostic.parse_arguments(sheet_input) + proj_loc, diag_rep = CbraDiagnostic._generate_report( + template_file, + project_summary_data, + util_break_data, + heat_loss_data, + ecm_data, + ) diag_rep.save('EEDR {}.pptx'.format(proj_loc)) print('Reports generated!') else: diff --git a/bpeng/reports/constants.py b/bpeng/reports/constants.py index 43c0e08..8e23267 100644 --- a/bpeng/reports/constants.py +++ b/bpeng/reports/constants.py @@ -176,7 +176,7 @@ ECM_TO_SLIDE = { # Convert template ECM names to the names that are in the database # Template ECM to database ECM is a 1 to n mapping so we just take the first one -TEMPLATE_TO_DATABASE = { +RETROFIT_TO_CBRA_RETROFIT = { 'Install Weather-stripping': 'Weatherstripping (Exterior Doors)', 'Replace Windows': 'Window Replacement', 'Install Roof Insulation': 'Insulation (Roof)', -- GitLab