diff --git a/bpeng/reports/__init__.py b/bpeng/reports/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7bf47562d20e347208fffbfb4bac3c53f8942114 --- /dev/null +++ b/bpeng/reports/__init__.py @@ -0,0 +1 @@ +from .cbra_diag import CbraDiagnostic diff --git a/bpeng/reports/cbra_diag.py b/bpeng/reports/cbra_diag.py new file mode 100644 index 0000000000000000000000000000000000000000..bf65401a2ee9a3901ca21b4a167eed39f1ddffd2 --- /dev/null +++ b/bpeng/reports/cbra_diag.py @@ -0,0 +1,499 @@ +""" + Code is designed to take template for CBRA EEDR and fill in engineering data, charts, graphs + for easy presentation creation. +""" + +from datetime import datetime + +import os +import pandas as pd +from pptx import Presentation +from pptx.chart.data import ChartData +from pptx.dml.color import RGBColor +from pptx.enum.chart import ( + XL_CHART_TYPE, + XL_LEGEND_POSITION, + XL_TICK_LABEL_POSITION +) +from pptx.enum.text import ( # pylint: disable=no-name-in-module + MSO_AUTO_SIZE, + MSO_ANCHOR, + PP_ALIGN +) +from pptx.util import Inches, Pt + +from .constants import ECM_TO_SLIDE, RETROFIT_TO_CBRA_RETROFIT + + +class CbraDiagnostic: + """ + Download template file from https://s3.amazonaws.com/bp-dev-assets/EEDR_CBRA.pptx + """ + + @staticmethod + def _generate_report( # pylint: disable=too-many-locals,too-many-branches,too-many-statements + template_file, + project_summary_data, + util_break_data, + heat_loss_data, + ecm_data, + ): + """ + Generate powerpoint file + + Args: + template_file (str): pptx template stored on S3 and Drive + project_summary_data (dict): python dictionary of client name, project address, + annual utility savings percentage, annual dollar + savings, CO2 reduction, jobs created, and blocmaps + comparison + util_break_data (dict): python dictionary of utility breakdown [$] by end usage + heat_loss_data (dict): python dictionary of building components and heat loss + values + ecm_data (dict): python dictionary of selected retrofits and calculated percent + savings on utility bill [$] by fuel + + + Returns: + str: the address of the project, street name and building number only + object: presentation file of the diagnostic report + + """ + # Put all of the arguments into variables + client_name = project_summary_data['client_name'] + project_address = project_summary_data['project_address'] + annual_utility_percent = project_summary_data['annual_utility_percent'] + annual_dollar_save = project_summary_data['annual_dollar_save'] + co2_tonnage = project_summary_data['co2_tonnage'] + jobs_created = project_summary_data['jobs_created'] + blocmaps_est = project_summary_data['blocmaps_est'] + + util_break_sh = util_break_data['util_break_sh'] + util_break_dhw = util_break_data['util_break_dhw'] + util_break_light = util_break_data['util_break_light'] + util_break_plug = util_break_data['util_break_plug'] + util_break_h2o = util_break_data['util_break_h2o'] + + heat_loss_cat = heat_loss_data['heat_loss_cat'] + heat_loss_val = heat_loss_data['heat_loss_val'] + + ecm_list = ecm_data['ecm_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 = [] + for slide in ECM_TO_SLIDE.values(): + if slide['slide_name'] not in ecm_list and slide['slide_index'] not in ecm_delete_list: + ecm_delete_list.append(slide['slide_index']) + + # 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) + + # Slide 1: cover slide + # cover_slide_layout = diag_rep.slide_layouts[0] + cover_slide = diag_rep.slides[0] + cover_title = cover_slide.shapes.title + cover_subtitle = cover_slide.placeholders[1] + + cover_title.text = "{} - {}".format(client_name, project_address) + cover_subtitle.text = "Energy Efficiency Diagnostic Report" + cover_report_date = cover_subtitle.text_frame.add_paragraph() + cover_report_date.text = str(datetime.now().strftime('%B %Y')) + + # Slide 2: Executive Summary + Table of Contents + # shapes[0] = slide #, 1 = executive summary text box, 2 = title , 3 = table of contents + exec_summary_slide = diag_rep.slides[1] + exec_results_box = exec_summary_slide.shapes[1] + exec_results_box.text_frame.auto_size = MSO_AUTO_SIZE.SHAPE_TO_FIT_TEXT + exec_results_box.text_frame.alignment = PP_ALIGN.LEFT + exec_title_run = exec_results_box.text_frame.paragraphs[0].add_run() + exec_title_run.text = "Executive Summary:" + exec_title_run.alignment = PP_ALIGN.LEFT + exec_title_run.font.name = 'Arial' + exec_title_run.font.size = Pt(20) + exec_title_run.font.underline = True + + exec_results_box.text_frame.add_paragraph() + exec_results_box.text_frame.add_paragraph() + + crnyc_intro_run = exec_results_box.text_frame.paragraphs[2].add_run() + exec_results_box.text_frame.paragraphs[2].alignment = PP_ALIGN.LEFT + + crnyc_intro_run.text = 'Community Retrofit NYC’s engineering feasibility study provides ' \ + 'a cost effective and energy efficient solution for {}. ' \ + 'Implementation of the recommended ECMs would ' \ + 'result in the following:'.format(project_address) + + crnyc_intro_run.font.name = 'Arial' + crnyc_intro_run.font.size = Pt(14) + exec_results_box.text_frame.add_paragraph() + + exec_energy_savings_line = exec_results_box.text_frame.add_paragraph() + exec_energy_savings_run = exec_energy_savings_line.add_run() + + exec_energy_savings_run.text = '{}\N{PERCENT SIGN} decrease in utility ' \ + 'costs'.format(annual_utility_percent) + + exec_results_box.text_frame.paragraphs[4].alignment = PP_ALIGN.LEFT + exec_energy_savings_run.font.name = 'Arial' + exec_energy_savings_run.font.size = Pt(14) + exec_energy_savings_run.font.bold = True + exec_results_box.text_frame.add_paragraph() + + exec_dollar_savings_line = exec_results_box.text_frame.add_paragraph() + exec_dollar_savings_run = exec_dollar_savings_line.add_run() + + exec_dollar_savings_run.text = '\N{DOLLAR SIGN}{} estimated annual' \ + 'savings'.format(annual_dollar_save) + + exec_results_box.text_frame.paragraphs[6].alignment = PP_ALIGN.LEFT + exec_dollar_savings_run.font.name = 'Arial' + exec_dollar_savings_run.font.size = Pt(14) + exec_dollar_savings_run.font.bold = True + exec_results_box.text_frame.add_paragraph() + + exec_emissions_line = exec_results_box.text_frame.add_paragraph() + exec_emissions_run = exec_emissions_line.add_run() + exec_emissions_run.text = "{} less tonnes of CO\u2082 generated".format(co2_tonnage) + exec_results_box.text_frame.paragraphs[8].alignment = PP_ALIGN.LEFT + exec_emissions_run.font.name = 'Arial' + exec_emissions_run.font.size = Pt(14) + exec_emissions_run.font.bold = True + exec_results_box.text_frame.add_paragraph() + + exec_job_add_line = exec_results_box.text_frame.add_paragraph() + exec_job_add_run = exec_job_add_line.add_run() + exec_job_add_run.text = "{} jobs created".format(jobs_created) + exec_results_box.text_frame.paragraphs[10].alignment = PP_ALIGN.LEFT + exec_job_add_run.font.name = 'Arial' + exec_job_add_run.font.size = Pt(14) + exec_job_add_run.font.bold = True + exec_results_box.text_frame.add_paragraph() + + # Slide 3: Energy Analysis, includes heatload pie chart + image from + # BlocMaps (might be static) + efficiency % from BlocMaps + + heat_load_slide = diag_rep.slides[2] + energy_analyze_box = heat_load_slide.shapes[3] + + # BlocMaps Efficiency Comparison + eab_run = energy_analyze_box.text_frame.paragraphs[0].add_run() + eab_run.text = 'Your building is ' + blocmap_comp = energy_analyze_box.text_frame.paragraphs[0].add_run() + blocmap_comp.text = '{}\N{PERCENT SIGN} '.format(blocmaps_est) + eab_run_end = energy_analyze_box.text_frame.paragraphs[0].add_run() + eab_run_end.text = 'less efficient than buildings of similar age,' \ + ' size, and type.' + eab_run.font.name = 'Arial' + eab_run.font.size = Pt(14) + eab_run.font.color.rgb = RGBColor(0xff, 0xff, 0xff) + blocmap_comp.font.name = 'Arial' + blocmap_comp.font.bold = True + blocmap_comp.font.size = Pt(14) + blocmap_comp.font.color.rgb = RGBColor(0xff, 0xff, 0xff) + eab_run_end.font.name = 'Arial' + eab_run_end.font.size = Pt(14) + eab_run_end.font.color.rgb = RGBColor(0xff, 0xff, 0xff) + + total_heat_loss = sum(heat_loss_val) + heat_loss_break = heat_loss_val.divide(total_heat_loss) + + heat_loss_data = pd.concat([heat_loss_cat, heat_loss_break], axis=1) + heat_loss_data = heat_loss_data[heat_loss_data[1] > 0.01] + + heat_loss_cat = heat_loss_data[0] + heat_loss_break = heat_loss_data[1] + + heat_load_pie_data = ChartData() + heat_load_pie_data.categories = heat_loss_cat + heat_load_pie_data.add_series('Heat Load Breakdown', heat_loss_break) + + # Set chart location and formatting + chart_horz_pos, chart_vert_pos = Inches(4.2), Inches(1.3) + chart_width, chart_height = Inches(5.27), Inches(3.47) + heat_load_pie = heat_load_slide.shapes.add_chart( + XL_CHART_TYPE.PIE, + chart_horz_pos, + chart_vert_pos, + chart_width, + chart_height, + heat_load_pie_data + ).chart + + heat_load_pie.has_legend = True + heat_load_pie.legend.include_in_layout = False + + heat_load_pie.plots[0].has_data_labels = True + heat_loss_labels = heat_load_pie.plots[0].data_labels + heat_loss_labels.number_format = '0%' + heat_loss_labels.font.size = Pt(22) + heat_loss_labels.font.bold = True + + # Slide 4: Potential Savings, includes utility expenses stacked bar charts for + # pre+post-retrofit, ECM table w/ names, savings for elec and gas, and total savings + + # Slide 4: Utility breakdown + utility_projection_slide = diag_rep.slides[3] + + # Define stacked bar chart data + util_cat = ["Existing", "Projected"] + + util_break_data = ChartData() + util_break_data.categories = util_cat + + if util_break_sh.iloc[0] != 0: + util_break_data.add_series("Space Heating", util_break_sh) + if util_break_dhw.iloc[0] != 0: + util_break_data.add_series("DHW", util_break_dhw) + if util_break_light.iloc[0] != 0: + util_break_data.add_series("Lighting", util_break_light) + if util_break_plug.iloc[0] != 0: + util_break_data.add_series("Plug Load", util_break_plug) + if util_break_h2o.iloc[0] != 0: + util_break_data.add_series("Water", util_break_h2o) + + # Set chart location and formatting + chart_horz_pos, chart_vert_pos = Inches(0.05), Inches(2.4) + chart_width, chart_height = Inches(3.5), Inches(2.5) + util_break_chart = utility_projection_slide.shapes.add_chart( + XL_CHART_TYPE.COLUMN_STACKED, + chart_horz_pos, + chart_vert_pos, + chart_width, + chart_height, + util_break_data + ).chart + + # Formatting for chart + # Legend + util_break_chart.has_legend = True + util_break_chart.legend.include_in_layout = False + util_break_chart.legend.position = XL_LEGEND_POSITION.LEFT + util_break_chart.legend.font.size = Pt(10) + util_break_chart.legend.font.bold = True + util_break_chart.legend.font.color.rgb = RGBColor(0xff, 0xff, 0xff) + + # Data Labels + util_break_chart.plots[0].has_data_labels = True + util_labels = util_break_chart.plots[0].data_labels + util_labels.number_format = '$0' + util_labels.font.size = Pt(10) + util_labels.font.bold = True + util_labels.font.color.rgb = RGBColor(0xff, 0xff, 0xff) + + # Axis + util_break_chart.has_axis = True + util_break_chart.has_axis_title = True + util_break_chart.category_axis.tick_labels.font.size = Pt(10) + util_break_chart.category_axis.tick_labels.font.bold = True + util_break_chart.category_axis.tick_labels.font.color.rgb = RGBColor(0xff, 0xff, 0xff) + util_break_chart.value_axis.has_major_gridlines = False + util_break_chart.value_axis.tick_label_position = XL_TICK_LABEL_POSITION.NONE + + # Create ECM summary table + + ecm_count = len(ecm_list) + ecm_table_head = [ + 'Energy Conservation Measure', + "Heating Savings*", + "Electricity Savings*", + "Water Savings*" + ] + ecm_table = utility_projection_slide.shapes.add_table( + ecm_count + 2, + len(ecm_table_head), + Inches(3.62), + Inches(0.08), + Inches(6.12), + Inches(4.9) + ) + ecm_table.last_row = True + ecm_table.horz_banding = False + ecm_table.table.columns[0].width = Inches(3.12) + ecm_table.table.columns[1].width = Inches(1) + ecm_table.table.columns[2].width = Inches(1) + ecm_table.table.columns[3].width = Inches(1) + + for j in range(len(ecm_table_head)): # pylint: disable=consider-using-enumerate + ecm_table.table.cell(0, j).text = ecm_table_head[j] + ecm_table.table.cell(0, j).text_frame.paragraphs[0].font.size = Pt(12) + ecm_table.table.cell(0, j).text_frame.paragraphs[0].font.bold = True + + for k, j in zip(ecm_list, range(len(ecm_list))): + ecm_table.table.cell(j+1, 0).text_frame.paragraphs[0].font.size = Pt(10) + ecm_table.table.cell(j+1, 0).text = k + ecm_table.table.cell(j+1, 1).text_frame.paragraphs[0].font.size = Pt(10) + ecm_table.table.cell(j+1, 1).text = str(int(ecm_heat_save[j])) + '\N{PERCENT SIGN}' + ecm_table.table.cell(j+1, 1).text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER + ecm_table.table.cell(j+1, 1).text_frame.vertical_anchor = MSO_ANCHOR.MIDDLE + ecm_table.table.cell(j+1, 2).text_frame.paragraphs[0].font.size = Pt(10) + ecm_table.table.cell(j+1, 2).text = str(int(ecm_elec_save[j])) + '\N{PERCENT SIGN}' + ecm_table.table.cell(j+1, 2).text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER + ecm_table.table.cell(j+1, 2).text_frame.vertical_anchor = MSO_ANCHOR.MIDDLE + + ecm_table.table.cell(ecm_count+1, 0).text_frame.paragraphs[0].font.size = Pt(12) + ecm_table.table.cell(ecm_count+1, 0).text_frame.paragraphs[0].font.bold = True + ecm_table.table.cell(ecm_count+1, 0).text = 'Total' + + ecm_table.table.cell(ecm_count+1, 2).text_frame.paragraphs[0].font.size = Pt(12) + ecm_table.table.cell(ecm_count+1, 2).text_frame.paragraphs[0].font.bold = True + ecm_table.table.cell(ecm_count+1, 2).text = str(annual_utility_percent) + '\N{PERCENT SIGN}' + + # Slides 5-end: removing slides based on recommendations + for ecm_index in ecm_delete_list: + diag_rep.slides.delete_slide( + diag_rep, + ecm_index, + ) + + return project_address, diag_rep + + @staticmethod + def generate_report(): + """ + Generate report with finding file in current working directory + """ + template_file = input('Enter Template Name (with .pptx extension): ') + file_name = input('Enter File Name (with .xlsx extension): ') + file_input = pd.ExcelFile(file_name) + sheet_input = file_input.parse("Inputs", header=None) + ( + project_summary_data, + util_break_data, + heat_loss_data, + ecm_data + ) = CbraDiagnostic.parse_arguments(sheet_input) + project_address, diag_rep = CbraDiagnostic._generate_report( + template_file, + project_summary_data, + util_break_data, + heat_loss_data, + ecm_data, + ) + diag_rep.save('EEDR {}.pptx'.format(project_address)) + + @staticmethod + def parse_arguments(sheet_input): + """ + Parse an excel file to generate arguments for the _generate_report function + + Args: + sheet_input (dataframe): The sheet where the data is located + + Returns: + dict: A dictionary containing all of the project summary data + dict: A dictionary containing all of the utility breakdown data + dict: A dictionary containing all of the heat loss data + dict: A dictionary containing all of the ECM data + """ + + project_summary_data = { + 'client_name': sheet_input.loc[1, 1], + 'project_address': sheet_input.loc[2, 1], + 'annual_utility_percent': int(sheet_input.loc[3, 1]), + 'annual_dollar_save': int(sheet_input.loc[4, 1]), + 'co2_tonnage': int(sheet_input.loc[5, 1]), + 'jobs_created': int(sheet_input.loc[6, 1]), + 'blocmaps_est': sheet_input.loc[7, 1], + } + + util_break_data = { + 'util_break_sh': sheet_input.loc[16:17, 1], + 'util_break_dhw': sheet_input.loc[16:17, 2], + 'util_break_light': sheet_input.loc[16:17, 3], + 'util_break_plug': sheet_input.loc[16:17, 4], + 'util_break_h2o': sheet_input.loc[16:17, 5], + } + + input_list = sheet_input[0] + input_values = sheet_input[1] + + heat_loss_data = { + 'heat_loss_cat': input_list[9:15], + 'heat_loss_val': input_values[9:15], + } + + ecm_list = [] + ecm_heat_save = [] + ecm_elec_save = [] + for j in range(len(input_values)): # pylint: disable=C0200 + if input_values[j] == 'Y': + # Convert to database string + ecm_list.append( + RETROFIT_TO_CBRA_RETROFIT[input_list[j]] + ) + ecm_heat_save.append(input_values[j+1]) + ecm_elec_save.append(input_values[j+2]) + + ecm_data = { + 'ecm_list': ecm_list, + 'ecm_heat_save': ecm_heat_save, + 'ecm_elec_save': ecm_elec_save, + } + + return project_summary_data, util_break_data, heat_loss_data, ecm_data + + @staticmethod + def cbra_multi_rep(): + """ + + Generate report for every excel file in a given directory + """ + target_dir = input('Enter directory where pptx template is located: ') + cbra_pptx = os.path.join(target_dir, "EEDR_CBRA_Template_Final.pptx") + + if os.path.exists(cbra_pptx): + repgen_list = [] + + for xlsx_item in [xl for xl in os.listdir(os.getcwd()) if xl.endswith(".xlsx")]: + + repgen_list.append(xlsx_item) + + for i in range(len(repgen_list)): # pylint: disable=C0200 + template_file = cbra_pptx + file_name = repgen_list[i] + file_input = pd.ExcelFile(file_name) + sheet_input = file_input.parse("Inputs", header=None) + ( + 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: + raise Exception('Template not found. Please download pptx template to directory') diff --git a/bpeng/reports/constants.py b/bpeng/reports/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..6e1dcdbc7f6de194b06257f37648764c381e05a0 --- /dev/null +++ b/bpeng/reports/constants.py @@ -0,0 +1,334 @@ +ECM_TO_SLIDE = { + 'Weatherstripping (Exterior Doors)': { + 'slide_index': 4, + 'slide_name': 'Install Weather-stripping', + }, + 'Weatherstripping (Windows)': { + 'slide_index': 4, + 'slide_name': 'Install Weather-stripping', + }, + 'Weatherstripping (A/C Units)': { + 'slide_index': 4, + 'slide_name': 'Install Weather-stripping', + }, + 'Window Replacement': { + 'slide_index': 5, + 'slide_name': 'Replace Windows', + }, + 'Insulation (Roof)': { + 'slide_index': 6, + 'slide_name': 'Install Roof Insulation', + }, + 'Insulation (Wall)': { + 'slide_index': 6, + 'slide_name': 'Install Roof Insulation', + }, + 'Cool Roof': { + 'slide_index': 7, + 'slide_name': 'Install Cool Roof', + }, + 'Boiler Control (Indoor Feedback)': { + 'slide_index': 8, + 'slide_name': 'Install Boiler Control (Indoor Feedback)', + }, + 'Boiler Control (Outdoor Reset)': { + 'slide_index': 9, + 'slide_name': 'Install Boiler Control (Outdoor Reset)', + }, + 'Smart Thermostat w/ Indoor Sensor': { + 'slide_index': 10, + 'slide_name': 'Install Smart Thermostat with Indoor Sensors', + }, + 'Building Management System/ Energy Management system': { + 'slide_index': 11, + 'slide_name': 'Integrate EMS/BMS Systems', + }, + 'Oil to Gas Conversion': { + 'slide_index': 12, + 'slide_name': 'Convert from Oil to Gas', + }, + 'Boiler Replacement': { + 'slide_index': 13, + 'slide_name': 'Replace Boiler', + }, + 'Burner Replacement': { + 'slide_index': 14, + 'slide_name': 'Replace Burner & Controls', + }, + 'Modulating Burner Controls': { + 'slide_index': 14, + 'slide_name': 'Replace Burner & Controls', + }, + 'Sealed Combustion/Power Burner Installation': { + 'slide_index': 14, + 'slide_name': 'Replace Burner & Controls', + }, + 'Pipe Insulation': { + 'slide_index': 15, + 'slide_name': 'Install Pipe Insulation', + }, + 'Smart Pumps': { + 'slide_index': 16, + 'slide_name': 'Install Smart Pumps', + }, + 'Variable Frequency Drive Pumps': { + 'slide_index': 16, + 'slide_name': 'Install Smart Pumps', + }, + 'TRVs (Steam, 1 pipe)': { + 'slide_index': 17, + 'slide_name': 'Install Thermostatic Radiator Valves (TRVs)', + }, + 'TRVs (Steam, 2 pipe)': { + 'slide_index': 17, + 'slide_name': 'Install Thermostatic Radiator Valves (TRVs)', + }, + 'TRVs (Hydronic)': { + 'slide_index': 17, + 'slide_name': 'Install Thermostatic Radiator Valves (TRVs)', + }, + 'Baseboard Replacement': { + 'slide_index': 18, + 'slide_name': 'Clean/Replace Baseboards', + }, + 'Hydronic Conversion': { + 'slide_index': 19, + 'slide_name': 'Convert from Steam to Hydronic', + }, + 'Master Venting': { + 'slide_index': 20, + 'slide_name': 'Install Master Venting', + }, + 'Steam Boiler Setting Optimization': { + 'slide_index': 21, + 'slide_name': 'Optimize Boiler\'s Pressure', + }, + 'Air Vent Installation/Replacement': { + 'slide_index': 22, + 'slide_name': 'Install Air Vents', + }, + 'Steam Trap Installation/Replacement (1 pipe)': { + 'slide_index': 23, + 'slide_name': 'Install Steam Traps', + }, + 'Steam Trap Installation/Replacement (2 pipe)': { + 'slide_index': 23, + 'slide_name': 'Install Steam Traps', + }, + 'Condensate Removal System Upgrade': { + 'slide_index': 24, + 'slide_name': 'Install Effective Condensate Removal', + }, + 'Vented Condensate Pumps': { + 'slide_index': 24, + 'slide_name': 'Install Effective Condensate Removal', + }, + 'Steam Boiler Anode Bars': { + 'slide_index': 25, + 'slide_name': 'Install Anode Bars in Boiler Tank', + }, + 'Rooftop Unit Replacement': { + 'slide_index': 26, + 'slide_name': 'Replace Roof Top Units', + }, + 'DHW Temperature Controls': { + 'slide_index': 27, + 'slide_name': 'Improve DHW Temperature Control', + }, + 'Tankless Water Heater': { + 'slide_index': 28, + 'slide_name': 'Install Tankless Water Heater', + }, + 'Tanked Water Heater': { + 'slide_index': 29, + 'slide_name': 'Install Tanked Water Heater', + }, + 'Solar Water Heater': { + 'slide_index': 30, + 'slide_name': 'Install Solar Water Heaters', + }, + 'Low-Flow Water Fixtures': { + 'slide_index': 31, + 'slide_name': 'Install Low-Flow Water Fixtures', + }, + 'Automatic Water Meter Reader': { + 'slide_index': 32, + 'slide_name': 'Install Automated Meter Reader', + }, + 'LED Lighting Install': { + 'slide_index': 33, + 'slide_name': 'Install LEDs and Lighting Controls', + }, + 'Lighting Controls (Motion Sensors/Timer)': { + 'slide_index': 33, + 'slide_name': 'Install LEDs and Lighting Controls', + }, + 'Solar Panels': { + 'slide_index': 34, + 'slide_name': 'Install Solar Panels', + }, + 'Replace Unit Appliances w/ Energy Star Models': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Replace Laundry Room Appliances w/ Energy Star Models': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + # These do not have a slide, putting them with the last slide + # They will be dealt with later + 'DHW/Space Heating System Separation': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Master Metering to Submetering Conversion': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'High Efficiency Furnace Installation': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Duct Sealing/Insulation': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Temperature Control Optimization: Impose Min/Max Temp Rule': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'HVAC Filter Replacement': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Portable Fan Installation': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Exhaust Fan Replacement': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Wall-mounted HEPA filter': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Mold Damage Repair': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Carpet/Floor-Tile Replacement': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Painted Surface Repair (Re-paint or paint removal)': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Ceiling Tile Replacement': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Heat Recovery Ventilation System Installation': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Energy Recovery Ventilation System Installation': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Dedicated Outdoor Air System Installation': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Adjustable Grille/Register Installation': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Cascading Air Filter System': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Blower Fan Replacement': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Zone Damper Controls': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Boiler Blowdown Smart Controls': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Modulating Zone Valves': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Drip Leg Installation': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Radiant Barriers for Wall/Enclosed Radiators': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Orifice Plates': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Smart Boiler Feed Pump': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Vacuum Pumps': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Flow Balancing Valve Installation': { + 'slide_index': 35, + 'slide_name': 'Replace Appliances with Energy Star Appliances', + }, + 'Roof or Attic Sheathing': { + '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 + +RETROFIT_TO_CBRA_RETROFIT = { + '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', +}