From 2b626a8aa5e9430b1b3c7a3185afca6d42180aa7 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Tue, 9 May 2017 11:34:07 -0400 Subject: [PATCH 01/30] nothing --- bpfin/utilbills/bill_lib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bpfin/utilbills/bill_lib.py b/bpfin/utilbills/bill_lib.py index 51b1d52..59c9642 100644 --- a/bpfin/utilbills/bill_lib.py +++ b/bpfin/utilbills/bill_lib.py @@ -4,6 +4,7 @@ import pandas as pd import copy from bpfin.lib.other import month_shift + def add_list(obj_list, number): """Add a number to each value in a list. -- GitLab From aa6866ace70391d8ec420a33cb39ef646545449f Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Tue, 9 May 2017 16:14:44 -0400 Subject: [PATCH 02/30] Know nowhere to go... --- bpfin/tests/testdata/sample_data.py | 2 +- bpfin/utilbills/bill.py | 76 +++++++++++++++++++++++++++++ bpfin/utilbills/bill_lib.py | 1 - 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 bpfin/utilbills/bill.py diff --git a/bpfin/tests/testdata/sample_data.py b/bpfin/tests/testdata/sample_data.py index 5be9768..7815cdc 100644 --- a/bpfin/tests/testdata/sample_data.py +++ b/bpfin/tests/testdata/sample_data.py @@ -145,7 +145,7 @@ bill_overview_post = { 2018: 26538.724712295312, 2019: 27188.618071137025, 2020: 27825.148026928808, - 2021: 28415.31151022465, +9 2021: 28415.31151022465, 2022: 28952.82377820487, 2023: 29480.94391241407, 2024: 30021.058259530582, diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py new file mode 100644 index 0000000..b6556d1 --- /dev/null +++ b/bpfin/utilbills/bill.py @@ -0,0 +1,76 @@ +from datetime import date +from bpfin.utilbills.bill_month_normalize_rough import bill_month_normalize_rough +from bpfin.tests.testdata import sample_data as db +from bpfin.financials.financial_lib import organize_bill_overview + +utility_type_list = ['electricity', 'gas', 'oil', 'water'] +# Bill class should be created one by one, alongside the bill fill in. +# Bill class may take in validated monthly data and annual data, and it changes the manual_input? +# Bill table will be created in later time when bill_overview calculate button pressed + + +class Bill(): + """ + """ + def __init__(self, algo_flag, raw_bill): + """ + """ + + self.algo_flag = algo_flag + self.raw_bill_dict = raw_bill + # self.type = raw_bill['utility_type'] + # self.raw_date_from = raw_bill['date_from'] + # self.raw_date_to = raw_bill['date_to'] + # self.raw_usage = raw_bill['usage'] + # self.raw_charge = raw_bill['charge'] + self.monthly_normalized_bill_rough = None + self.monthly_normailzed_bill_reg = None + self.is_manual_input = None + # data validation for raw_bill. either none or >=12 months + if self.raw_bill_dict is not None: + if validate_12_month_coverage(date_from, date_to) is True: + self.is_manual_input = False + self.monthly_normalized_bill_rough = bill_month_normalize_rough(self.raw_bill_dict) + + + + # def month_normailize_rough(self): + # self.monthly_normalized_bill = bill_month_normalize_rough(self.raw_bill_dict) + # return self.monthly_normalized_bill + + def prior_proj(self): + if self.algo_flag == 'rough': + pass + if self.algo_flag == 'dissa': + pass + + def post_proj(self): + if self.algo_flag == 'rough': + pass + if self.algo_flag == 'dissa': + pass + + +class Bill_Table(): + """ + """ + def __init__(self, raw_bill_dict, raw_bill_overview, analysis_date): + """ + """ + self.raw_bill_dict = raw_bill_dict + self.manual_input_dict = {} + self.raw_annual_bill_dict = raw_bill_overview + + for utility_type in utility_type_list: + self.manual_input_dict[utility_type] = not raw_bill_overview[utility_type][1] + self.raw_annual_bill_dict[utility_type] = raw_bill_overview[utility_type][0] + + self.annual_bill_dict = organize_bill_overview(raw_bill_overview, analysis_date) + +# **** ugly test **** +# e_bill = Bill('rough', db.raw_bill, False) +# print(e_bill.month_normailize_rough()) + + +bill_table = Bill_Table(db.prior_month_bill, db.bill_overview, db.analysis_date) +print(bill_table.annual_bill_dict) diff --git a/bpfin/utilbills/bill_lib.py b/bpfin/utilbills/bill_lib.py index 59c9642..69d21ca 100644 --- a/bpfin/utilbills/bill_lib.py +++ b/bpfin/utilbills/bill_lib.py @@ -119,7 +119,6 @@ def form_year_calendar(date_start, date_end): return year_list - def form_year_month(target_terms): """Return list of month,year of datetimes in a list. -- GitLab From 1af4cec392dbbae9e5366e24d5afa7e0b86c6c68 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Tue, 9 May 2017 17:05:28 -0400 Subject: [PATCH 03/30] Better mindset --- bpfin/utilbills/bill.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index b6556d1..93c6ced 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -14,6 +14,10 @@ class Bill(): """ def __init__(self, algo_flag, raw_bill): """ + initialize bill. validate input data (either none or >=12 month), generate rough_month_normalization, usage and price + Also, we should do rough_projection with normalized data + maybe we don't need algo_flag + and we should have annual_bill as an attribute """ self.algo_flag = algo_flag @@ -25,22 +29,33 @@ class Bill(): # self.raw_charge = raw_bill['charge'] self.monthly_normalized_bill_rough = None self.monthly_normailzed_bill_reg = None - self.is_manual_input = None + self.is_manual_input = True # data validation for raw_bill. either none or >=12 months if self.raw_bill_dict is not None: if validate_12_month_coverage(date_from, date_to) is True: self.is_manual_input = False self.monthly_normalized_bill_rough = bill_month_normalize_rough(self.raw_bill_dict) - - + self.is_manual_input = False # def month_normailize_rough(self): # self.monthly_normalized_bill = bill_month_normalize_rough(self.raw_bill_dict) # return self.monthly_normalized_bill - def prior_proj(self): + def put_annual_bill(self, raw_annual_bill): + if self.is_manual_input is True: + # average out annual bill + pass + else: + # annualize bill + pass + # after this step, bill should have annual bill ready, with proper manual_flag, and prior_month_proj or none_month_bill + pass + + def get_prior_proj_rough(self): if self.algo_flag == 'rough': pass + + def get_prior_proj_reg(self): if self.algo_flag == 'dissa': pass -- GitLab From 86d4196050a24d9fcd31e400abc1d039ff7680fc Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 10 May 2017 15:35:41 -0400 Subject: [PATCH 04/30] Add average_non_zero_list func --- bpfin/lib/other.py | 18 ++++++++ bpfin/tests/test_lib/test_other.py | 16 ++++++++ bpfin/tests/testdata/sample_data.py | 2 +- bpfin/utilbills/bill.py | 64 +++++++++++++++++++++-------- 4 files changed, 83 insertions(+), 17 deletions(-) diff --git a/bpfin/lib/other.py b/bpfin/lib/other.py index 7c080a7..d1d0068 100644 --- a/bpfin/lib/other.py +++ b/bpfin/lib/other.py @@ -347,6 +347,24 @@ def average(list1): return sum(list1) / len(list1) +def average_nonzero_list(list1): + """ + determine average value for all non-zero elements + Args: + list1 (list): list of float values + Return: + float: average value + """ + # if len(list1) == 0: + # return 0 + # else: + new_list = [] + for element in list1: + if element != 0: + new_list.append(element) + return (sum(new_list)/len(new_list) if len(new_list) else 0) + + def cal_cagr(start_value, end_value, n): """ Calculate compound anual growth rate Args: diff --git a/bpfin/tests/test_lib/test_other.py b/bpfin/tests/test_lib/test_other.py index 0fdbb95..fad00f2 100644 --- a/bpfin/tests/test_lib/test_other.py +++ b/bpfin/tests/test_lib/test_other.py @@ -1,6 +1,7 @@ from bpfin.lib.other import add_year_dictionary from bpfin.lib.other import subtract_year_dictionary from bpfin.lib.other import divide_dscr_dict +from bpfin.lib.other import average_nonzero_list from bpfin.tests.testdata import sample_data as db @@ -27,3 +28,18 @@ def test_divide_dscr_dict(): output = {2011: 0.0, 2012: 2.5, 2013: 0.3333333333333333, 2014: 2.0, 2015: None} result = divide_dscr_dict(year_1_input, year_2_input) assert result == output + + +def test_average_nonzero_list(): + input_list1 = [] + output_list1 = 0 + input_list2 = [1, 0, 2, 0, 4, 5, 6] + output_list2 = 3.6 + input_list3 = [0] + output_list3 = 0 + input_list4 = [0, 0, 1] + output_list4 = 1 + assert output_list1 == average_nonzero_list(input_list1) + assert output_list2 == average_nonzero_list(input_list2) + assert output_list3 == average_nonzero_list(input_list3) + assert output_list4 == average_nonzero_list(input_list4) diff --git a/bpfin/tests/testdata/sample_data.py b/bpfin/tests/testdata/sample_data.py index 7815cdc..5be9768 100644 --- a/bpfin/tests/testdata/sample_data.py +++ b/bpfin/tests/testdata/sample_data.py @@ -145,7 +145,7 @@ bill_overview_post = { 2018: 26538.724712295312, 2019: 27188.618071137025, 2020: 27825.148026928808, -9 2021: 28415.31151022465, + 2021: 28415.31151022465, 2022: 28952.82377820487, 2023: 29480.94391241407, 2024: 30021.058259530582, diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 93c6ced..cf032ac 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -1,7 +1,11 @@ from datetime import date +import copy from bpfin.utilbills.bill_month_normalize_rough import bill_month_normalize_rough from bpfin.tests.testdata import sample_data as db from bpfin.financials.financial_lib import organize_bill_overview +from bpfin.utilbills.bill_prior_proj_rough import bill_prior_proj_rough +from bpfin.utilbills.bill_lib import annualizing_projection +from bpfin.utilbills.bill_lib import form_bill_calendar utility_type_list = ['electricity', 'gas', 'oil', 'water'] # Bill class should be created one by one, alongside the bill fill in. @@ -9,10 +13,31 @@ utility_type_list = ['electricity', 'gas', 'oil', 'water'] # Bill table will be created in later time when bill_overview calculate button pressed +def validate_raw_bill(raw_bill): + """ + To Do: validate date >= 12months + """ + if raw_bill is not None: + # validate date >= 12 months + return True + else: + return False + +def estimate_annual_bill(raw_annual_bill, analysis_date): + """ + Args: + raw_annual_bill (dictionary): {year: float}. annual bill allowing a few years missing + Return: + dictionary: {year: float}. annual_bill with all years filled with average charge + """ + proforma_year = form_bill_year(analysis_date['proforma_start'], + analysis_date['proforma_duration']) + + class Bill(): """ """ - def __init__(self, algo_flag, raw_bill): + def __init__(self, raw_bill, analysis_date): """ initialize bill. validate input data (either none or >=12 month), generate rough_month_normalization, usage and price Also, we should do rough_projection with normalized data @@ -20,8 +45,10 @@ class Bill(): and we should have annual_bill as an attribute """ - self.algo_flag = algo_flag + # self.algo_flag = algo_flag self.raw_bill_dict = raw_bill + self.is_manual_input = True + self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] # self.type = raw_bill['utility_type'] # self.raw_date_from = raw_bill['date_from'] # self.raw_date_to = raw_bill['date_to'] @@ -29,21 +56,25 @@ class Bill(): # self.raw_charge = raw_bill['charge'] self.monthly_normalized_bill_rough = None self.monthly_normailzed_bill_reg = None - self.is_manual_input = True + self.annual_bill = None + # data validation for raw_bill. either none or >=12 months - if self.raw_bill_dict is not None: - if validate_12_month_coverage(date_from, date_to) is True: - self.is_manual_input = False - self.monthly_normalized_bill_rough = bill_month_normalize_rough(self.raw_bill_dict) - self.is_manual_input = False + if validate_raw_bill(raw_bill) is True: + self.monthly_normalized_bill_rough = bill_month_normalize_rough(copy.deepcopy(self.raw_bill_dict)) + self.is_manual_input = False + + self.prior_bill_rough = bill_prior_proj_rough( + self.monthly_normalized_bill_rough, + self.raw_bill_dict, + analysis_date, + db.inflation_coeff_dict + ) - # def month_normailize_rough(self): - # self.monthly_normalized_bill = bill_month_normalize_rough(self.raw_bill_dict) - # return self.monthly_normalized_bill + self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) def put_annual_bill(self, raw_annual_bill): if self.is_manual_input is True: - # average out annual bill + cal_average_annual_charge pass else: # annualize bill @@ -82,10 +113,11 @@ class Bill_Table(): self.annual_bill_dict = organize_bill_overview(raw_bill_overview, analysis_date) + # **** ugly test **** -# e_bill = Bill('rough', db.raw_bill, False) -# print(e_bill.month_normailize_rough()) +e_bill = Bill('rough', db.raw_bill, db.analysis_date) +print(e_bill.annual_bill) -bill_table = Bill_Table(db.prior_month_bill, db.bill_overview, db.analysis_date) -print(bill_table.annual_bill_dict) +# bill_table = Bill_Table(db.prior_month_bill, db.bill_overview, db.analysis_date) +# print(bill_table.annual_bill_dict) -- GitLab From 858e88824134cf9a86ab09313fd88a5257289d10 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 10 May 2017 15:52:49 -0400 Subject: [PATCH 05/30] Add put_annual_bill in Bill --- bpfin/utilbills/bill.py | 42 ++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index cf032ac..13ed361 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -6,6 +6,8 @@ from bpfin.financials.financial_lib import organize_bill_overview from bpfin.utilbills.bill_prior_proj_rough import bill_prior_proj_rough from bpfin.utilbills.bill_lib import annualizing_projection from bpfin.utilbills.bill_lib import form_bill_calendar +from bpfin.utilbills.bill_lib import form_bill_year +from bpfin.lib.other import average_nonzero_list utility_type_list = ['electricity', 'gas', 'oil', 'water'] # Bill class should be created one by one, alongside the bill fill in. @@ -23,6 +25,7 @@ def validate_raw_bill(raw_bill): else: return False + def estimate_annual_bill(raw_annual_bill, analysis_date): """ Args: @@ -31,7 +34,14 @@ def estimate_annual_bill(raw_annual_bill, analysis_date): dictionary: {year: float}. annual_bill with all years filled with average charge """ proforma_year = form_bill_year(analysis_date['proforma_start'], - analysis_date['proforma_duration']) + analysis_date['proforma_duration']) + average_charge = average_nonzero_list(raw_annual_bill.values()) + print('\n oil charge =', average_charge) + annual_bill_dict = copy.deepcopy(raw_annual_bill) + for year in proforma_year: + if year not in annual_bill_dict: + annual_bill_dict[year] = average_charge + return annual_bill_dict class Bill(): @@ -46,6 +56,7 @@ class Bill(): """ # self.algo_flag = algo_flag + self.analysis_date = analysis_date self.raw_bill_dict = raw_bill self.is_manual_input = True self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] @@ -63,24 +74,19 @@ class Bill(): self.monthly_normalized_bill_rough = bill_month_normalize_rough(copy.deepcopy(self.raw_bill_dict)) self.is_manual_input = False - self.prior_bill_rough = bill_prior_proj_rough( - self.monthly_normalized_bill_rough, - self.raw_bill_dict, - analysis_date, - db.inflation_coeff_dict - ) + self.prior_bill_rough = bill_prior_proj_rough( + self.monthly_normalized_bill_rough, + self.raw_bill_dict, + analysis_date, + db.inflation_coeff_dict + ) - self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) + self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) def put_annual_bill(self, raw_annual_bill): if self.is_manual_input is True: - cal_average_annual_charge - pass - else: - # annualize bill - pass - # after this step, bill should have annual bill ready, with proper manual_flag, and prior_month_proj or none_month_bill - pass + self.annual_bill = estimate_annual_bill(raw_annual_bill, self.analysis_date) + return self.annual_bill def get_prior_proj_rough(self): if self.algo_flag == 'rough': @@ -115,8 +121,10 @@ class Bill_Table(): # **** ugly test **** -e_bill = Bill('rough', db.raw_bill, db.analysis_date) -print(e_bill.annual_bill) +e_bill = Bill(db.raw_bill, db.analysis_date) +print('\n annual_bill_rough', e_bill.annual_bill) +o_bill = Bill(None, db.analysis_date) +print('\n annual_bill_oil', o_bill.put_annual_bill(db.bill_overview['oil'][0])) # bill_table = Bill_Table(db.prior_month_bill, db.bill_overview, db.analysis_date) -- GitLab From 073a42d7939ae107a9df61b4968c2c67df670f4f Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 10 May 2017 17:44:54 -0400 Subject: [PATCH 06/30] Create feature_data as new sample_data for tests. Add put_annual_bill for both Bill and Bill_Table Add rewrite_month_bill for both Bill and Bill_Table --- bpfin/financials/saving.py | 8 +- bpfin/tests/testdata/feature_data.py | 59 +++++++++++++ bpfin/tests/testdata/sample_data.py | 8 +- bpfin/utilbills/bill.py | 126 +++++++++++++++++++-------- 4 files changed, 157 insertions(+), 44 deletions(-) create mode 100644 bpfin/tests/testdata/feature_data.py diff --git a/bpfin/financials/saving.py b/bpfin/financials/saving.py index 0a90322..c76ad61 100644 --- a/bpfin/financials/saving.py +++ b/bpfin/financials/saving.py @@ -240,10 +240,10 @@ class Saving_Overview(): electricity_bill = {2014: 100, 2015:200, ...} """ self.manual_input_dict = { - 'electricity': not bill_overview['electricity'][1], - 'gas': not bill_overview['gas'][1], - 'oil': not bill_overview['oil'][1], - 'water': not bill_overview['water'][1]} + 'electricity': bill_overview['electricity'][1], + 'gas': bill_overview['gas'][1], + 'oil': bill_overview['oil'][1], + 'water': bill_overview['water'][1]} self.prior_annual_bill_table = prior_annual_bill_table self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] self.commissioning_date = commissioning_date diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py new file mode 100644 index 0000000..d3e8f77 --- /dev/null +++ b/bpfin/tests/testdata/feature_data.py @@ -0,0 +1,59 @@ +import datetime + + +# pro-forma projection time base +analysis_date = { + 'proforma_start': datetime.date(2012, 1, 15), + 'proforma_duration': 25} + + +# electricity raw bill +raw_bill = {} +raw_bill['utility_type'] = 'electricity' +raw_bill['date_from'] = [ + datetime.date(2014, 4, 17), datetime.date(2014, 5, 16), datetime.date(2014, 6, 17), + datetime.date(2014, 7, 17), datetime.date(2014, 8, 15), datetime.date(2014, 9, 16), + datetime.date(2014, 10, 16), datetime.date(2014, 11, 14), datetime.date(2014, 12, 17), + datetime.date(2015, 1, 16), datetime.date(2015, 2, 18), datetime.date(2015, 3, 19), + datetime.date(2015, 4, 17), datetime.date(2015, 5, 18), datetime.date(2015, 6, 17), + datetime.date(2015, 7, 17), datetime.date(2015, 8, 17), datetime.date(2015, 9, 16), + datetime.date(2015, 10, 16), datetime.date(2015, 11, 16), datetime.date(2015, 12, 17), + datetime.date(2016, 1, 19), datetime.date(2016, 2, 18), datetime.date(2016, 3, 18)] +raw_bill['date_to'] = [ + datetime.date(2014, 5, 16), datetime.date(2014, 6, 17), datetime.date(2014, 7, 17), + datetime.date(2014, 8, 15), datetime.date(2014, 9, 16), datetime.date(2014, 10, 16), + datetime.date(2014, 11, 14), datetime.date(2014, 12, 17), datetime.date(2015, 1, 16), + datetime.date(2015, 2, 18), datetime.date(2015, 3, 19), datetime.date(2015, 4, 17), + datetime.date(2015, 5, 18), datetime.date(2015, 6, 17), datetime.date(2015, 7, 17), + datetime.date(2015, 8, 17), datetime.date(2015, 9, 16), datetime.date(2015, 10, 16), + datetime.date(2015, 11, 16), datetime.date(2015, 12, 17), datetime.date(2016, 1, 19), + datetime.date(2016, 2, 18), datetime.date(2016, 3, 18), datetime.date(2016, 4, 18)] +raw_bill['charge'] = [ + 2601.6289696969693, 2642.1740261508003, 3437.551946607342, 2930.5070588449826, + 2514.1242835094554, 2804.4448749999997, 3237.7359972191325, 3289.4155027808674, + 2796.5700000000002, 2463.5113047830923, 2370.0007392051775, 2479.081440860215] +raw_bill['usage'] = [ + 0.094265231101663627, 0.10175446346513953, 0.12248981352003527, 0.11229655777596657, + 0.092170964664193167, 0.094173672324921492, 0.095187070309577237, 0.097177529967719486, + 0.095676831493476874, 0.091583582602530869, 0.089122740090923164, 0.091516956021057888] + + +raw_bill_table = { + 'electricity': raw_bill, + 'gas': None, + 'oil': None, + 'water': None, +} + + +# raw annual_bill +annual_bill_gas = {2014: 0, 2015: 1020, 2016: 1220, 2017: 1520} +annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} +annual_bill_water = {2015: 0, 2016: 0, 2017: 0} + +raw_annual_bill_table = { + 'electricity': None, # True == Mannual Input + 'gas': annual_bill_gas, + 'oil': annual_bill_oil, + 'water': annual_bill_water +} diff --git a/bpfin/tests/testdata/sample_data.py b/bpfin/tests/testdata/sample_data.py index 5be9768..392ff56 100644 --- a/bpfin/tests/testdata/sample_data.py +++ b/bpfin/tests/testdata/sample_data.py @@ -95,10 +95,10 @@ annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} annual_bill_water = {2015: 0, 2016: 0, 2017: 0} bill_overview = { - 'electricity': [annual_bill_electricity, True], # True == scraper - 'gas': [annual_bill_gas, False], - 'oil': [annual_bill_oil, False], - 'water': [annual_bill_water, False] + 'electricity': [annual_bill_electricity, False], # True == Mannual Input + 'gas': [annual_bill_gas, True], + 'oil': [annual_bill_oil, True], + 'water': [annual_bill_water, True] } bill_overview_organized = { diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 13ed361..5f37e7b 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -1,7 +1,8 @@ from datetime import date import copy from bpfin.utilbills.bill_month_normalize_rough import bill_month_normalize_rough -from bpfin.tests.testdata import sample_data as db +from bpfin.tests.testdata import feature_data as db +from bpfin.utilbills.inflation_sample_data import inflation_coeff_dict from bpfin.financials.financial_lib import organize_bill_overview from bpfin.utilbills.bill_prior_proj_rough import bill_prior_proj_rough from bpfin.utilbills.bill_lib import annualizing_projection @@ -36,7 +37,7 @@ def estimate_annual_bill(raw_annual_bill, analysis_date): proforma_year = form_bill_year(analysis_date['proforma_start'], analysis_date['proforma_duration']) average_charge = average_nonzero_list(raw_annual_bill.values()) - print('\n oil charge =', average_charge) + # print('\n oil charge =', average_charge) annual_bill_dict = copy.deepcopy(raw_annual_bill) for year in proforma_year: if year not in annual_bill_dict: @@ -54,78 +55,131 @@ class Bill(): maybe we don't need algo_flag and we should have annual_bill as an attribute """ - - # self.algo_flag = algo_flag self.analysis_date = analysis_date + self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] self.raw_bill_dict = raw_bill self.is_manual_input = True - self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] - # self.type = raw_bill['utility_type'] - # self.raw_date_from = raw_bill['date_from'] - # self.raw_date_to = raw_bill['date_to'] - # self.raw_usage = raw_bill['usage'] - # self.raw_charge = raw_bill['charge'] + self.monthly_normalized_bill_rough = None + self.prior_bill_rough = None self.monthly_normailzed_bill_reg = None + self.prior_bill_reg = None self.annual_bill = None - # data validation for raw_bill. either none or >=12 months if validate_raw_bill(raw_bill) is True: self.monthly_normalized_bill_rough = bill_month_normalize_rough(copy.deepcopy(self.raw_bill_dict)) self.is_manual_input = False - self.prior_bill_rough = bill_prior_proj_rough( self.monthly_normalized_bill_rough, self.raw_bill_dict, analysis_date, - db.inflation_coeff_dict + inflation_coeff_dict ) + self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) + + def rewrite_month_bill(self, raw_bill, analysis_date): + if validate_raw_bill(raw_bill) is True: + self.analysis_date = analysis_date + self.proforma_date = form_bill_calendar( + analysis_date['proforma_start'], + analysis_date['proforma_duration'])[1] + self.raw_bill_dict = raw_bill + + self.monthly_normalized_bill_rough = None + self.prior_bill_rough = None + self.monthly_normailzed_bill_reg = None + self.prior_bill_reg = None + self.annual_bill = None + self.monthly_normalized_bill_rough = bill_month_normalize_rough(copy.deepcopy(self.raw_bill_dict)) + self.is_manual_input = False + self.prior_bill_rough = bill_prior_proj_rough( + self.monthly_normalized_bill_rough, + self.raw_bill_dict, + analysis_date, + inflation_coeff_dict + ) self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) def put_annual_bill(self, raw_annual_bill): if self.is_manual_input is True: self.annual_bill = estimate_annual_bill(raw_annual_bill, self.analysis_date) - return self.annual_bill def get_prior_proj_rough(self): - if self.algo_flag == 'rough': - pass + return self.prior_bill_rough def get_prior_proj_reg(self): - if self.algo_flag == 'dissa': - pass + return self.prior_bill_reg - def post_proj(self): - if self.algo_flag == 'rough': - pass - if self.algo_flag == 'dissa': - pass + def get_annual_bill(self): + return self.annual_bill class Bill_Table(): """ """ - def __init__(self, raw_bill_dict, raw_bill_overview, analysis_date): + def __init__(self, raw_bill_table, analysis_date): """ """ - self.raw_bill_dict = raw_bill_dict + self.analysis_date = analysis_date + self.raw_bill_table = raw_bill_table self.manual_input_dict = {} - self.raw_annual_bill_dict = raw_bill_overview + self.raw_annual_bill_table = None + self.utility_bill_dict = {} # dict of object for utility_type in utility_type_list: - self.manual_input_dict[utility_type] = not raw_bill_overview[utility_type][1] - self.raw_annual_bill_dict[utility_type] = raw_bill_overview[utility_type][0] + self.utility_bill_dict[utility_type] = Bill(raw_bill_table[utility_type], analysis_date) + self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) - self.annual_bill_dict = organize_bill_overview(raw_bill_overview, analysis_date) + # self.annual_bill_dict = organize_bill_overview(raw_bill_overview, analysis_date) + def put_annual_bill_table(self, raw_annual_bill_table): + self.raw_annual_bill_table = raw_annual_bill_table + for utility_type in utility_type_list: + self.utility_bill_dict[utility_type].put_annual_bill(raw_annual_bill_table[utility_type]) -# **** ugly test **** -e_bill = Bill(db.raw_bill, db.analysis_date) -print('\n annual_bill_rough', e_bill.annual_bill) -o_bill = Bill(None, db.analysis_date) -print('\n annual_bill_oil', o_bill.put_annual_bill(db.bill_overview['oil'][0])) + def rewrite_month_bill_utility(self, raw_bill, analysis_date, target_type): + if target_type not in utility_type_list: + return False + self.utility_bill_dict[target_type].rewrite_month_bill(raw_bill, analysis_date) + + def get_bill_overview(self): + """ + should be deleted or replaced when future calling func got updated + """ + bill_overview = {} + for utility_type in utility_type_list: + bill_overview[utility_type] = [ + (self.raw_annual_bill_table[utility_type] if + self.utility_bill_dict[utility_type].is_manual_input is True + else self.utility_bill_dict[utility_type].get_annual_bill()), + self.utility_bill_dict[utility_type].is_manual_input + ] + return bill_overview + + def get_annual_bill_table(self): + """ + Annual bill table, for 4 utility types, with annualized projected charge, or average annual charge + sample_data.bill_overview_organized + """ + annual_bill_table = {} + for utility_type in utility_type_list: + annual_bill_table[utility_type] = self.utility_bill_dict[utility_type].get_annual_bill() + return annual_bill_table -# bill_table = Bill_Table(db.prior_month_bill, db.bill_overview, db.analysis_date) -# print(bill_table.annual_bill_dict) +# **** ugly test **** +# e_bill = Bill(db.raw_bill_dict['electricity'], db.analysis_date) +# print('\n annual_bill_rough', e_bill.annual_bill) +# o_bill = Bill(db.raw_bill_dict['oil'], db.analysis_date) +# o_bill.put_annual_bill(db.raw_annual_bill_dict['oil']) +# print('\n annual_bill_oil', o_bill.get_annual_bill()) + + +bill_table = Bill_Table(db.raw_bill_table, db.analysis_date) +# print(bill_table.utility_bill_dict['oil'].get_annual_bill()) +# print('manual_input =', bill_table.manual_input_dict) +bill_table.put_annual_bill_table(db.raw_annual_bill_table) +# print(bill_table.utility_bill_dict['oil'].get_annual_bill()) +# print(bill_table.get_bill_overview()) +print(bill_table.get_annual_bill_table()) -- GitLab From be576e90712369b13ddef2a1f38c5862feca00b0 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 10 May 2017 17:50:09 -0400 Subject: [PATCH 07/30] Change is_manual_input to proper way --- bpfin/financials/financial_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bpfin/financials/financial_lib.py b/bpfin/financials/financial_lib.py index 97cde72..67dda03 100644 --- a/bpfin/financials/financial_lib.py +++ b/bpfin/financials/financial_lib.py @@ -31,7 +31,7 @@ def organize_bill_overview(bill_overview, analysis_date): average_bill_dict = {} for i in range(4): - if bill_dict[i + 1][1] is False: + if bill_dict[i + 1][1] is True: sum_bill = 0 year_number = 0 for year in bill_dict[i + 1][0]: -- GitLab From 92125730586d07d5d29887f25265254841000c7f Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 10 May 2017 18:05:08 -0400 Subject: [PATCH 08/30] Clean comments --- bpfin/utilbills/bill.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 5f37e7b..90a0b06 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -1,19 +1,16 @@ from datetime import date import copy -from bpfin.utilbills.bill_month_normalize_rough import bill_month_normalize_rough -from bpfin.tests.testdata import feature_data as db -from bpfin.utilbills.inflation_sample_data import inflation_coeff_dict -from bpfin.financials.financial_lib import organize_bill_overview -from bpfin.utilbills.bill_prior_proj_rough import bill_prior_proj_rough from bpfin.utilbills.bill_lib import annualizing_projection from bpfin.utilbills.bill_lib import form_bill_calendar from bpfin.utilbills.bill_lib import form_bill_year from bpfin.lib.other import average_nonzero_list +from bpfin.utilbills.bill_month_normalize_rough import bill_month_normalize_rough +from bpfin.utilbills.bill_prior_proj_rough import bill_prior_proj_rough +from bpfin.utilbills.inflation_sample_data import inflation_coeff_dict +# from bpfin.tests.testdata import feature_data as db + utility_type_list = ['electricity', 'gas', 'oil', 'water'] -# Bill class should be created one by one, alongside the bill fill in. -# Bill class may take in validated monthly data and annual data, and it changes the manual_input? -# Bill table will be created in later time when bill_overview calculate button pressed def validate_raw_bill(raw_bill): @@ -146,6 +143,7 @@ class Bill_Table(): def get_bill_overview(self): """ should be deleted or replaced when future calling func got updated + because all functions would need to use either the bill or the yes/no flag. """ bill_overview = {} for utility_type in utility_type_list: @@ -176,10 +174,10 @@ class Bill_Table(): # print('\n annual_bill_oil', o_bill.get_annual_bill()) -bill_table = Bill_Table(db.raw_bill_table, db.analysis_date) +# bill_table = Bill_Table(db.raw_bill_table, db.analysis_date) # print(bill_table.utility_bill_dict['oil'].get_annual_bill()) # print('manual_input =', bill_table.manual_input_dict) -bill_table.put_annual_bill_table(db.raw_annual_bill_table) +# bill_table.put_annual_bill_table(db.raw_annual_bill_table) # print(bill_table.utility_bill_dict['oil'].get_annual_bill()) # print(bill_table.get_bill_overview()) -print(bill_table.get_annual_bill_table()) +# print(bill_table.get_annual_bill_table()) -- GitLab From b41125df4122efaf3d0ca8516faea8b14900ddf3 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 10 May 2017 18:34:50 -0400 Subject: [PATCH 09/30] Add raw_bill validation --- bpfin/lib/other.py | 4 +++- bpfin/utilbills/bill.py | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/bpfin/lib/other.py b/bpfin/lib/other.py index d1d0068..d08cf0f 100644 --- a/bpfin/lib/other.py +++ b/bpfin/lib/other.py @@ -86,7 +86,9 @@ def form_date_calendar(date_start, date_end): date_start (datetime): date xx/yy/abcd date_end (datetime): date xx/yy/abcd Returns: - list: all calendar dates, first date is first day of calendar + list: list of 2 lists, for all calendar dates + output_list[0]: list of date, each element is first date of a month + output_list[1]: list of date, each element is end date of a month """ bdstart = [] bdend = [] diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 90a0b06..8f07779 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -4,10 +4,11 @@ from bpfin.utilbills.bill_lib import annualizing_projection from bpfin.utilbills.bill_lib import form_bill_calendar from bpfin.utilbills.bill_lib import form_bill_year from bpfin.lib.other import average_nonzero_list +from bpfin.lib.other import form_date_calendar from bpfin.utilbills.bill_month_normalize_rough import bill_month_normalize_rough from bpfin.utilbills.bill_prior_proj_rough import bill_prior_proj_rough from bpfin.utilbills.inflation_sample_data import inflation_coeff_dict -# from bpfin.tests.testdata import feature_data as db +from bpfin.tests.testdata import feature_data as db utility_type_list = ['electricity', 'gas', 'oil', 'water'] @@ -15,11 +16,16 @@ utility_type_list = ['electricity', 'gas', 'oil', 'water'] def validate_raw_bill(raw_bill): """ - To Do: validate date >= 12months """ if raw_bill is not None: # validate date >= 12 months - return True + month_list = form_date_calendar( + raw_bill['date_from'][0], + raw_bill['date_to'][-1])[1] + if len(month_list) >= 12: + return True + else: + return False else: return False @@ -30,12 +36,18 @@ def estimate_annual_bill(raw_annual_bill, analysis_date): raw_annual_bill (dictionary): {year: float}. annual bill allowing a few years missing Return: dictionary: {year: float}. annual_bill with all years filled with average charge + + To Do: validate data == 0 and data == None, difference inputs cause different results """ proforma_year = form_bill_year(analysis_date['proforma_start'], analysis_date['proforma_duration']) - average_charge = average_nonzero_list(raw_annual_bill.values()) - # print('\n oil charge =', average_charge) - annual_bill_dict = copy.deepcopy(raw_annual_bill) + if raw_annual_bill is None: + average_charge = 0 + annual_bill_dict = {} + else: + average_charge = average_nonzero_list(raw_annual_bill.values()) + annual_bill_dict = copy.deepcopy(raw_annual_bill) + for year in proforma_year: if year not in annual_bill_dict: annual_bill_dict[year] = average_charge @@ -148,9 +160,11 @@ class Bill_Table(): bill_overview = {} for utility_type in utility_type_list: bill_overview[utility_type] = [ - (self.raw_annual_bill_table[utility_type] if - self.utility_bill_dict[utility_type].is_manual_input is True - else self.utility_bill_dict[utility_type].get_annual_bill()), + ( + self.raw_annual_bill_table[utility_type] if + self.utility_bill_dict[utility_type].is_manual_input is True + else self.utility_bill_dict[utility_type].get_annual_bill() + ), self.utility_bill_dict[utility_type].is_manual_input ] return bill_overview @@ -174,10 +188,8 @@ class Bill_Table(): # print('\n annual_bill_oil', o_bill.get_annual_bill()) -# bill_table = Bill_Table(db.raw_bill_table, db.analysis_date) -# print(bill_table.utility_bill_dict['oil'].get_annual_bill()) -# print('manual_input =', bill_table.manual_input_dict) -# bill_table.put_annual_bill_table(db.raw_annual_bill_table) +bill_table = Bill_Table(db.raw_bill_table, db.analysis_date) +bill_table.put_annual_bill_table(db.raw_annual_bill_table) # print(bill_table.utility_bill_dict['oil'].get_annual_bill()) # print(bill_table.get_bill_overview()) -# print(bill_table.get_annual_bill_table()) +print(bill_table.get_annual_bill_table()) -- GitLab From b7df65ab15475287af6965e13c81ace0ca44adb8 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 11 May 2017 11:05:36 -0400 Subject: [PATCH 10/30] Add put_month_bill() in Bill Remove rewrite_month_bill from Bill Modify bill_month_norm_rough func --- .../test_month_normalize_rough.py | 4 +- bpfin/utilbills/bill.py | 69 +++++++------------ bpfin/utilbills/bill_month_normalize_rough.py | 56 ++++++++++++++- 3 files changed, 81 insertions(+), 48 deletions(-) diff --git a/bpfin/tests/test_utilbills/test_month_normalize_rough.py b/bpfin/tests/test_utilbills/test_month_normalize_rough.py index 04a6cbc..74e0318 100644 --- a/bpfin/tests/test_utilbills/test_month_normalize_rough.py +++ b/bpfin/tests/test_utilbills/test_month_normalize_rough.py @@ -7,7 +7,7 @@ def test_month_normalization_rough(): input_dict = db.raw_bill output_dict = db.monthly_normalized_bill - result_dict = bill_month_normalize_rough(input_dict) + result_dict = bill_month_normalize_rough('electricity', input_dict) assert result_dict == output_dict @@ -15,5 +15,5 @@ def test_month_normalization_rough(): def test_month_normalization_rough_oil(): input_dict = db.raw_bill_oil output_dict = db.monthly_normalized_bill_oil - result_dict = bill_month_normalize_rough(input_dict) + result_dict = bill_month_normalize_rough('oil', input_dict) assert result_dict == output_dict diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 8f07779..33db4c3 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -57,55 +57,37 @@ def estimate_annual_bill(raw_annual_bill, analysis_date): class Bill(): """ """ - def __init__(self, raw_bill, analysis_date): + monthly_normalized_bill_rough = None + prior_bill_rough = None + monthly_normailzed_bill_reg = None + prior_bill_reg = None + annual_bill = None + utility_type = None + + def __init__(self, utility_type, analysis_date): """ - initialize bill. validate input data (either none or >=12 month), generate rough_month_normalization, usage and price Also, we should do rough_projection with normalized data maybe we don't need algo_flag and we should have annual_bill as an attribute """ + self.utility_type = utility_type self.analysis_date = analysis_date self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] - self.raw_bill_dict = raw_bill + # self.raw_bill_dict = raw_bill self.is_manual_input = True - self.monthly_normalized_bill_rough = None - self.prior_bill_rough = None - self.monthly_normailzed_bill_reg = None - self.prior_bill_reg = None - self.annual_bill = None - - if validate_raw_bill(raw_bill) is True: - self.monthly_normalized_bill_rough = bill_month_normalize_rough(copy.deepcopy(self.raw_bill_dict)) - self.is_manual_input = False - self.prior_bill_rough = bill_prior_proj_rough( - self.monthly_normalized_bill_rough, - self.raw_bill_dict, - analysis_date, - inflation_coeff_dict - ) - self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) - - def rewrite_month_bill(self, raw_bill, analysis_date): + def put_month_bill(self, raw_bill): + """ + initialize bill. validate input data (either none or >=12 month) + generate rough_month_normalization, usage and price + """ if validate_raw_bill(raw_bill) is True: - self.analysis_date = analysis_date - self.proforma_date = form_bill_calendar( - analysis_date['proforma_start'], - analysis_date['proforma_duration'])[1] - self.raw_bill_dict = raw_bill - - self.monthly_normalized_bill_rough = None - self.prior_bill_rough = None - self.monthly_normailzed_bill_reg = None - self.prior_bill_reg = None - self.annual_bill = None - - self.monthly_normalized_bill_rough = bill_month_normalize_rough(copy.deepcopy(self.raw_bill_dict)) + self.monthly_normalized_bill_rough = bill_month_normalize_rough(self.utility_type, copy.deepcopy(raw_bill)) self.is_manual_input = False self.prior_bill_rough = bill_prior_proj_rough( - self.monthly_normalized_bill_rough, - self.raw_bill_dict, - analysis_date, + copy.deepcopy(self.monthly_normalized_bill_rough), + copy.deepcopy(raw_bill), + self.analysis_date, inflation_coeff_dict ) self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) @@ -137,11 +119,11 @@ class Bill_Table(): self.utility_bill_dict = {} # dict of object for utility_type in utility_type_list: - self.utility_bill_dict[utility_type] = Bill(raw_bill_table[utility_type], analysis_date) + self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) + if raw_bill_table[utility_type] is not None: + self.utility_bill_dict[utility_type].put_month_bill(raw_bill_table[utility_type]) self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) - # self.annual_bill_dict = organize_bill_overview(raw_bill_overview, analysis_date) - def put_annual_bill_table(self, raw_annual_bill_table): self.raw_annual_bill_table = raw_annual_bill_table for utility_type in utility_type_list: @@ -181,7 +163,8 @@ class Bill_Table(): # **** ugly test **** -# e_bill = Bill(db.raw_bill_dict['electricity'], db.analysis_date) +# e_bill = Bill('electricity', db.analysis_date) +# e_bill.put_month_bill(db.raw_bill_table['electricity']) # print('\n annual_bill_rough', e_bill.annual_bill) # o_bill = Bill(db.raw_bill_dict['oil'], db.analysis_date) # o_bill.put_annual_bill(db.raw_annual_bill_dict['oil']) @@ -190,6 +173,6 @@ class Bill_Table(): bill_table = Bill_Table(db.raw_bill_table, db.analysis_date) bill_table.put_annual_bill_table(db.raw_annual_bill_table) -# print(bill_table.utility_bill_dict['oil'].get_annual_bill()) -# print(bill_table.get_bill_overview()) +print(bill_table.utility_bill_dict['oil'].get_annual_bill()) +print(bill_table.get_bill_overview()) print(bill_table.get_annual_bill_table()) diff --git a/bpfin/utilbills/bill_month_normalize_rough.py b/bpfin/utilbills/bill_month_normalize_rough.py index 23909a9..2d443d7 100644 --- a/bpfin/utilbills/bill_month_normalize_rough.py +++ b/bpfin/utilbills/bill_month_normalize_rough.py @@ -3,7 +3,7 @@ import bpfin.lib.other as lib import bpfin.utilbills.bill_lib as bl -def bill_month_normalize_rough(raw_bill): +def bill_month_normalize_rough(utility_type, raw_bill): """ take raw bill as input, normalize the date from random dates to beg/end of a month math calculation is rough, by determining daily usage, daily price to calculate monthly usage and average price NOT using reggression neither BlocPower's disaggeration method @@ -11,12 +11,12 @@ def bill_month_normalize_rough(raw_bill): outcome are 12 months of values. If the raw bill covers more than 12 months, then an average will be taken Args: + utility_type (string): utility type raw_bill (dictionary): dictionary of lists. Elements are 'date_from': list of datetime, 'date_to', list of datetime, 'usage', list of float values, 'charge', list of float, - 'utility_type', a char indicating utility type Returns: dictionary: dictionary of lists. Elements are 'monthly_usage': a list of 12 float values, month normalized average energy usage @@ -28,7 +28,6 @@ def bill_month_normalize_rough(raw_bill): bill_end = lib.convert_timestamp_date(raw_bill['date_to']) usage = raw_bill['usage'] charge = raw_bill['charge'] - utility_type = raw_bill['utility_type'] # !!!! we will need to update oil calculation test file if bill_start != []: @@ -51,3 +50,54 @@ def bill_month_normalize_rough(raw_bill): return {'monthly_usage': month_norm_usage, 'monthly_charge': month_norm_charge, 'monthly_price': month_norm_price} + + +# **** old copy **** +# def bill_month_normalize_rough(raw_bill): +# """ take raw bill as input, normalize the date from random dates to beg/end of a month +# math calculation is rough, by determining daily usage, daily price to calculate monthly usage and average price +# NOT using reggression neither BlocPower's disaggeration method +# inputs are at least 12 months of bills +# outcome are 12 months of values. If the raw bill covers more than 12 months, then an average will be taken + +# Args: +# raw_bill (dictionary): dictionary of lists. Elements are +# 'date_from': list of datetime, +# 'date_to', list of datetime, +# 'usage', list of float values, +# 'charge', list of float, +# 'utility_type', a char indicating utility type +# Returns: +# dictionary: dictionary of lists. Elements are +# 'monthly_usage': a list of 12 float values, month normalized average energy usage +# 'monthly_charge': a list of 12 float values, month normalized average energy charge +# 'monthly_price': a list of 12 float values, month normalized average energy price +# """ + +# bill_start = lib.convert_timestamp_date(raw_bill['date_from']) +# bill_end = lib.convert_timestamp_date(raw_bill['date_to']) +# usage = raw_bill['usage'] +# charge = raw_bill['charge'] +# utility_type = raw_bill['utility_type'] + +# # !!!! we will need to update oil calculation test file +# if bill_start != []: +# month_matrix = lib.form_month_matrix(bill_start, bill_end) +# daily_usage = lib.division(usage, lib.date_diff(bill_start, bill_end)) +# daily_charge = lib.division(charge, lib.date_diff(bill_start, bill_end)) + +# month_norm_usage = lib.monthly_average(daily_usage, month_matrix) +# if utility_type == 'oil': +# month_norm_price = bl.cal_oil_price(charge, usage) +# month_norm_charge = lib.product2list(month_norm_usage, month_norm_price) +# else: +# month_norm_charge = lib.monthly_average(daily_charge, month_matrix) +# month_norm_price = lib.division(month_norm_charge, month_norm_usage) +# else: +# month_norm_usage = [0] * 12 +# month_norm_charge = [0] * 12 +# month_norm_price = [0] * 12 + +# return {'monthly_usage': month_norm_usage, +# 'monthly_charge': month_norm_charge, +# 'monthly_price': month_norm_price} -- GitLab From a61bf8b18d5d4947718b3f76b88642e7e0f01571 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 11 May 2017 11:35:48 -0400 Subject: [PATCH 11/30] Add description for Bill --- bpfin/utilbills/bill.py | 49 ++++++++++++++++++- bpfin/utilbills/bill_month_normalize_rough.py | 1 - 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 33db4c3..e2c49df 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -16,6 +16,15 @@ utility_type_list = ['electricity', 'gas', 'oil', 'water'] def validate_raw_bill(raw_bill): """ + Raw bill validation. A valid raw_bill is not None, and it covers at least 12 months + Args: + raw_bill (dictionary): dictionary of lists. Keys are + 'date_from': list of datetime, + 'date_to', list of datetime, + 'usage', list of float values, + 'charge', list of float, + Return: + Bollean: True == data validated. False == data is not validated """ if raw_bill is not None: # validate date >= 12 months @@ -32,8 +41,11 @@ def validate_raw_bill(raw_bill): def estimate_annual_bill(raw_annual_bill, analysis_date): """ + Fill the annual charge for missing years, with average annual charge, for one utility type + Args: - raw_annual_bill (dictionary): {year: float}. annual bill allowing a few years missing + raw_annual_bill (dictionary): {year: float}. annual bill allowing some years data missing + analysis_date (dictionary): proforma's starting date and the years of proforma Return: dictionary: {year: float}. annual_bill with all years filled with average charge @@ -56,7 +68,42 @@ def estimate_annual_bill(raw_annual_bill, analysis_date): class Bill(): """ + Bill object for one utility type. 'bill' usually contains concepts of date ranges, usage, prices, and charges + Calculate and store monthly_normalized bill, price + Project bill for the past and for the future for pro-forma porpuse, with inflation applied + + Attributes: + monthly_normalized_bill_rough (dictionary): dict of lists, for 12 months of usage, charge and price + prior_bill_rough (dictionary): dictionary of lists, prior_saving bill, for pro-forma period + monthly_normailzed_bill_reg (dictionary): unknown + prior_bill_reg (dictionary): unknown + annual_bill (dictionary): dict of float, annual charge for pro-forma period. {year: float} + utility_type (string): utility type + analysis_date (dictionary): proforma's starting date and the years of proforma + proforma_date (list): list of dates, months of pro-forma time period + is_manual_input (boolean): flag of raw bill validation. + True == raw bill is not valid, annual bills are manually input + False == raw bill is valid, annual bills are calculated automatically + + Description: + monthly_normalized_bill_rough = { + 'monthly_usage': a list of 12 float values, month normalized average energy usage + 'monthly_charge': a list of 12 float values, month normalized average energy charge + 'monthly_price': a list of 12 float values, month normalized average energy price + } + prior_bill_rough = { + 'date_from': list of date, first day of month, for pro-forma period + 'date_to': list of date, last day of month, for pro-forma period + 'usage': list of floate, prior_saving_monthly_usage, + 'charge': list of floate, prior_saving_monthly_charge, + 'price': list of floate, prior_saving_monthly_price, + } + + To Do: + Add in regression method as normalization and projection method + Add in rate plan feature to replace/supplement current rough monthly pricing method """ + monthly_normalized_bill_rough = None prior_bill_rough = None monthly_normailzed_bill_reg = None diff --git a/bpfin/utilbills/bill_month_normalize_rough.py b/bpfin/utilbills/bill_month_normalize_rough.py index 2d443d7..a9ae61b 100644 --- a/bpfin/utilbills/bill_month_normalize_rough.py +++ b/bpfin/utilbills/bill_month_normalize_rough.py @@ -29,7 +29,6 @@ def bill_month_normalize_rough(utility_type, raw_bill): usage = raw_bill['usage'] charge = raw_bill['charge'] - # !!!! we will need to update oil calculation test file if bill_start != []: month_matrix = lib.form_month_matrix(bill_start, bill_end) daily_usage = lib.division(usage, lib.date_diff(bill_start, bill_end)) -- GitLab From 48423b46e7b4d5b2d8da896a45e53f8872dab5a4 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 11 May 2017 14:46:14 -0400 Subject: [PATCH 12/30] create Bill and Bill_Table class. Add feature_data for Bill class iteration --- bpfin/financials/financial_saving.py | 384 ++++++++++++++++++++++++ bpfin/financials/saving.py | 1 + bpfin/tests/testdata/data_generation.py | 0 bpfin/tests/testdata/feature_data.py | 104 +++++-- bpfin/utilbills/bill.py | 77 ++++- 5 files changed, 518 insertions(+), 48 deletions(-) create mode 100644 bpfin/financials/financial_saving.py create mode 100644 bpfin/tests/testdata/data_generation.py diff --git a/bpfin/financials/financial_saving.py b/bpfin/financials/financial_saving.py new file mode 100644 index 0000000..96d80c8 --- /dev/null +++ b/bpfin/financials/financial_saving.py @@ -0,0 +1,384 @@ +import datetime +import copy +from bpfin.utilbills.bill_lib import cal_last_day +from bpfin.utilbills.bill_lib import annualizing_projection +from bpfin.utilbills.bill_lib import form_year_calendar +from bpfin.utilbills.bill_lib import form_bill_calendar +from bpfin.utilbills.bill_post_proj_rough import bill_post_proj_rough +from bpfin.tests.testdata import sample_data as db + + +class Saving(): + """ + Calculate savings and project bills with commissioning date considered. For one utility type + Attributes: + is_manual_input (boolean): lable indicating is bill input by scraper or manual. Yes == manual input + commissioning_date (date): the date that construction work finished, saving starts at NEXT month + proforma_date (list): list of dates, months of pro-forma time period + proforma_usage (list) : list of monthly usage, for pro-forma period + Data before commissioning date == historical + Data after commissioning date == post saving data + proforma_charge (list) : list of monthly charge, for pro-forma period + Data before commissioning date == historical + Data after commissioning date == post saving data + proforma_saving_usage (list) : monthly savings on usage, for pro-forma period + Data before commissioning date == historical + Data after commissioning date == post saving data + proforma_saving_charge (list) : monthly savings on usage, for pro-forma period + Data before commissioning date == historical + Data after commissioning date == post saving data + first_year_saving_usage (float) : first 12 months saving on usage. + first_year_saving_charge (float) : first 12 months saving on charge. If data is annual, saving is on NEXT year + annual_proforma_usage (dictionary) : key is year, value is float, annual usage + annual_proforma_charge (dictionary) : key is year, value is float, annual charge + first_year_prior_charge (float): first 12 months prior_saving charge. If data is annual, charge is for NEXT year + percent_saving (float): percentage saving on first year charge. 0.2 == 20% + """ + def __init__(self, is_manual_input, commissioning_date, proforma_date): + """ + Initiate Saving object. + Args: + is_manual_input (boolean): lable indicating is bill input by scraper or manual. Yes == manual input + commissioning_date (date): the date that construction work finished, saving starts at NEXT month + proforma_date (list): list of dates, months of pro-forma time period + """ + self.is_manual_input = is_manual_input + self.commissioning_date = commissioning_date + self.proforma_date = proforma_date + self.proforma_usage = None + self.proforma_charge = None + self.proforma_saving_usage = None + self.proforma_saving_charge = None + self.first_year_saving_charge = None + self.first_year_saving_usage = None + self.annual_proforma_usage = None + self.annual_proforma_charge = None + self.first_year_prior_charge = None + self.percent_saving = 0 + + def put_monthly_proforma(self, prior_saving_usage, post_saving_usage, prior_saving_charge, post_saving_charge): + """ + Put monthly prior_saving and post_saving, generate: monthly usage and charge for pro-forma purpose. + Also generate first year savings, and annual usage and annual charge + + Args: + prior_saving_usage (list): list of float, usage without (prior to) savings + post_saving_usage (list): list of float, usage with (post to) savings + prior_saving_charge (list): list of float, charge without (prior to) savings + post_saving_charge (list): list of float, charge with (post to) savings + """ + if self.is_manual_input is False: + saving_start_date = datetime.date( + self.commissioning_date.year, + self.commissioning_date.month, + cal_last_day(self.commissioning_date.year, self.commissioning_date.month)) + + saving_usage_dict = {} + saving_charge_dict = {} + proforma_usage_dict = {} + proforma_charge_dict = {} + first_year_saving_usage = 0 + first_year_saving_charge = 0 + first_yr_counter = 0 + first_year_prior_charge = 0 + for date, prior_usage, post_usage, prior_charge, post_charge in zip( + self.proforma_date, + prior_saving_usage, + post_saving_usage, + prior_saving_charge, + post_saving_charge): + if date > saving_start_date: + saving_usage_dict[date] = prior_usage - post_usage + saving_charge_dict[date] = prior_charge - post_charge + proforma_usage_dict[date] = post_usage + proforma_charge_dict[date] = post_charge + if first_yr_counter <= 12: + first_year_saving_usage += prior_usage - post_usage + first_year_saving_charge += prior_charge - post_charge + first_year_prior_charge += prior_charge + first_yr_counter += 1 + else: + saving_usage_dict[date] = 0 + saving_charge_dict[date] = 0 + proforma_usage_dict[date] = prior_usage + proforma_charge_dict[date] = prior_charge + + self.proforma_usage = list(proforma_usage_dict.values()) + self.proforma_charge = list(proforma_charge_dict.values()) + self.proforma_saving_usage = list(saving_usage_dict.values()) + self.proforma_saving_charge = list(saving_charge_dict.values()) + self.first_year_saving_usage = first_year_saving_usage + self.first_year_saving_charge = first_year_saving_charge + self.annual_proforma_usage = annualizing_projection(self.proforma_date, self.proforma_charge) + self.annual_proforma_charge = annualizing_projection(self.proforma_date, self.proforma_charge) + self.first_year_prior_charge = first_year_prior_charge + self.percent_saving = first_year_saving_charge / first_year_prior_charge + + def put_annual_proforma(self, annual_charge, percent_saving): + """ + Put annual prior_saving charge, calculate saving on charge and overall percentage saving + + Args: + annual_charge (dictionary): dict of annual prior saving charge, {2014: 100, ...}, with future estimated + percent_saving (float): percent of saving, 0.030 == 3.0% + """ + if self.is_manual_input is True: + annual_saving_dict = {} + annual_charge_dict = {} + proforma_year = form_year_calendar(self.proforma_date[0], self.proforma_date[-1]) + for year in proforma_year: + if year < self.commissioning_date.year: + annual_saving_dict[year] = 0 + annual_charge_dict[year] = annual_charge[year] + if year > self.commissioning_date.year: + annual_saving_dict[year] = annual_charge[year] * percent_saving + annual_charge_dict[year] = annual_charge[year] * (1 - percent_saving) + if year == self.commissioning_date.year: + effective_saving = (12 - self.commissioning_date.month) / 12 * percent_saving # partial year saving + annual_saving_dict[year] = annual_charge[year] * effective_saving + annual_charge_dict[year] = annual_charge[year] * (1 - effective_saving) + + first_year = self.commissioning_date.year + 1 + self.first_year_saving_charge = annual_saving_dict[first_year] + self.annual_proforma_charge = annual_charge_dict + self.first_year_prior_charge = annual_charge[first_year] + self.percent_saving = ( + annual_saving_dict[first_year] / annual_charge[first_year] if annual_charge[first_year] != 0 else 0) + + def get_proforma_charge(self): + """ + Get proforma_charge, for monthly pro-forma, not available for annual + Return: + list: proforma_charge + """ + return self.proforma_charge + + def get_proforma_saving_charge(self): + """ + Get proforma_saving_charge, for monthly pro-forma, not available for annual + Return: + list: proforma_saving_charge + """ + return self.proforma_saving_charge + + def get_percent_saving(self): + """ + Get percentage saving on charge + Return: + float: overall percentage saving on charge + """ + return self.percent_saving + + def get_first_year_saving_charge(self): + """ + Get first year saving + Return: + float: sum of first 12 months of savings + """ + return self.first_year_saving_charge + + def get_first_year_prior_charge(self): + """ + Get prior charge for first year after commissioning + Return: + float: prior charge for first year after commissioning + """ + return self.first_year_prior_charge + + def get_annual_proforma_charge(self): + """ + Calculate and return annual pro-forma charge + Return: + dictionary: dict of annual item, {year: annual charge} + """ + return self.annual_proforma_charge + + +class Saving_Overview(): + """ + Generate saving schedule, annual usages and charges for 4 utility types + Also calculate percentage saving on usages and charges. + If rate plan applies, usage % saving can be different from charge % saving + Saving_Overview should be scenario based, it is an "attribute" of a scenario + + Attributes: + manual_input_dict(dictionary): key is utility type, value is boolean. True == mannual_input + prior_annual_bill_table (dictionary): annual bill, for 4 utility_types + proforma_date (list): list of dates, months of pro-forma time period + commissioning_date (date): the date that construction work finished, saving starts at NEXT month + utility_saving_dict (dictionary): dict of Saving objects, for 4 utility types + saving_percent_charge (dictionary): key is utility type, value is float {'electricity': float} + total_first_year_saving (float): dollar saving for the first 12 month after commissioning + total_saving_percent (float): percentage saving on charge for the first 12 month after commissioning + + Description: + prior_annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, ...} + electricity_bill = {2014: 100, 2015:200, ...} + """ + + def __init__( + self, + bill_overview, + prior_annual_bill_table, + analysis_date, # consider to replace it with proforma_date + commissioning_date): + """ + Initiate Saving_Overview. + Take in manual_input_dict, annual and monthly bills, commissioning_date and percentage savings + Generate Saving objects, pro-forma date period + + Args: + bill_overview (dictionary): original annual bill input, with blank cells, for 4 utility_types + prior_annual_bill_table (dictionary): dict of dict of annual charge, for 4 utility_types + analysis_date (dictionary): proforma's starting date and the years of proforma + commissioning_date (date): the date that construction work finished, saving starts at NEXT month + + Description: + bill_overview (dictionary): dict of a list, + list[0] = dict of annual bill, allow some year missing, for 4 utility_types + list[1] = boolean value, False == from manual input, True == from scraper + prior_annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, ...} + electricity_bill = {2014: 100, 2015:200, ...} + """ + self.manual_input_dict = { + 'electricity': bill_overview['electricity'][1], + 'gas': bill_overview['gas'][1], + 'oil': bill_overview['oil'][1], + 'water': bill_overview['water'][1]} + self.prior_annual_bill_table = prior_annual_bill_table + self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] + self.commissioning_date = commissioning_date + self.utility_saving_dict = {'electricity': None, 'gas': None, 'oil': None, 'water': None} + self.saving_percent_charge = {'electricity': None, 'gas': None, 'oil': None, 'water': None} + self.total_first_year_saving = 0 # dollar + self.total_saving_percent = 0 # dollar percentage + + def put_saving(self, prior_month_bill, percent_saving_dict, full_saving_dict): + """ + Put inputs to generage saving data. + Also calculate overall saving on charge, and percentage saving + + Args: + prior_month_bill (dictionary): dict of dict of lists, projected energy bill for prior_saving + percent_saving_dict (dictionary): dict of float, percentage saving for 4 utility types + full_saving_dict (dictionary): dict of undefined saving, from engineering saving analysis. can be None + + Description: + prior_month_bill = { + 'electricity': electricity_prior_bill_rough, + 'gas': None, + 'oil': None, + 'water': None} + + electricity_prior_bill_rough = { + 'date_from': list, + 'date_to': list, + 'usage': list, + 'charge': list, + 'price': list} + """ + utility_type_list = ['electricity', 'gas', 'oil', 'water'] + + total_first_year_saving = 0 + total_first_year_prior_charge = 0 + for utility in utility_type_list: + is_manual_input = self.manual_input_dict[utility] + + utility_saving = Saving(is_manual_input, self.commissioning_date, self.proforma_date) + + if utility_saving.is_manual_input is True: # bill is from manual input, monthly bill not available + utility_saving.put_annual_proforma(self.prior_annual_bill_table[utility], percent_saving_dict[utility]) + + if utility_saving.is_manual_input is False: # bill is from scraper, monthly bill is available + if full_saving_dict[utility] is None: # engineering saving is not available, use percentage saving + post_month_bill = bill_post_proj_rough(prior_month_bill[utility], percent_saving_dict[utility]) + # else: # engineering saving is available. Project monthly_bill with regression. not finished yet + # post_month_bill = bill_proj_reg(prior_utility, full_saving_dict[utility], HDD, CDD, occupancy) + + utility_saving.put_monthly_proforma( + prior_month_bill[utility]['usage'], + post_month_bill['usage'], + prior_month_bill[utility]['charge'], + post_month_bill['charge']) + + total_first_year_saving += utility_saving.get_first_year_saving_charge() + total_first_year_prior_charge += utility_saving.get_first_year_prior_charge() + self.saving_percent_charge[utility] = utility_saving.get_percent_saving() + self.utility_saving_dict[utility] = utility_saving + + self.total_first_year_saving = total_first_year_saving + self.total_saving_percent = ( + total_first_year_saving / total_first_year_prior_charge if total_first_year_prior_charge != 0 else 0) + + def get_total_first_year_saving(self): + """ + Get first year total dollar saving + This is used for: loan allocation, simple_payback calculation + Retrun: + float: a dollar number + """ + return self.total_first_year_saving + + def get_simple_payback(self, cost): + """ + Get the simple payback for a given project cost. cost / first year dollar saving + Args: + cost (float): project cost, estimated or quoted + Return: + float: simple paback, in year + """ + return float(cost / self.total_first_year_saving if self.total_first_year_saving != 0 else 0) + + def get_total_saving_percent(self): + """ + Get the total saving percentage. First dollar saving / First year prior_saving charge + Return: + float: total dollar saving percentage + """ + return self.total_saving_percent + + def get_post_annual_bill_table(self): + """ + Generate annual bill table for pro-forma, consiering commission date. + Return: + dictionary: dict of dict of annual charges, for 4 utility types + Description: + output = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} + electricity_bill = {2014: 100, 2015:200, ...} + } + """ + post_annual_bill_table = {} + utility_type_list = ['electricity', 'gas', 'oil', 'water'] + for utility_type in utility_type_list: + post_annual_bill_table[utility_type] = copy.deepcopy( + self.utility_saving_dict[utility_type].get_annual_proforma_charge()) + return post_annual_bill_table + + def get_utility_annual_saving_charge(self, utility_type): + """ + Get the proforma annual saving on charge for a utility_type + Args: + utility_type (string): 'electricity', 'gas', 'oil', 'water' + Return: + dictionary: {year: saving on charge} + """ + prior_charge_dict = copy.deepcopy(self.prior_annual_bill_table[utility_type]) + post_charge_dict = copy.deepcopy(self.utility_saving_dict[utility_type].get_annual_proforma_charge()) + saving_dict = {} + for year in prior_charge_dict: + saving_dict[year] = prior_charge_dict[year] - post_charge_dict[year] + return saving_dict + + + + + +# **** ugly test **** +# so = Saving_Overview(db.bill_overview, db.bill_overview_organized, db.analysis_date, datetime.date(2017, 3, 14)) +# so.put_saving(db.prior_month_bill, db.percent_saving_dict, db.full_saving_dict) + +# print('\n first_year_dollar_saving =', so.get_total_first_year_saving()) +# print('\n simple payback =', so.get_simple_payback(50000)) +# print('\n total saving percent =', so.get_total_saving_percent()) +# print('\n electricity_annual_saving_charge', so.get_utility_annual_saving_charge('electricity')) +# print(so.get_post_annual_bill_table()) diff --git a/bpfin/financials/saving.py b/bpfin/financials/saving.py index c76ad61..96d80c8 100644 --- a/bpfin/financials/saving.py +++ b/bpfin/financials/saving.py @@ -7,6 +7,7 @@ from bpfin.utilbills.bill_lib import form_bill_calendar from bpfin.utilbills.bill_post_proj_rough import bill_post_proj_rough from bpfin.tests.testdata import sample_data as db + class Saving(): """ Calculate savings and project bills with commissioning date considered. For one utility type diff --git a/bpfin/tests/testdata/data_generation.py b/bpfin/tests/testdata/data_generation.py new file mode 100644 index 0000000..e69de29 diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index d3e8f77..f582534 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -1,5 +1,7 @@ import datetime - +from datetime import date +from bpfin.utilbills.bill import Bill_Table +import pprint # pro-forma projection time base analysis_date = { @@ -7,45 +9,73 @@ analysis_date = { 'proforma_duration': 25} -# electricity raw bill -raw_bill = {} -raw_bill['utility_type'] = 'electricity' -raw_bill['date_from'] = [ - datetime.date(2014, 4, 17), datetime.date(2014, 5, 16), datetime.date(2014, 6, 17), - datetime.date(2014, 7, 17), datetime.date(2014, 8, 15), datetime.date(2014, 9, 16), - datetime.date(2014, 10, 16), datetime.date(2014, 11, 14), datetime.date(2014, 12, 17), - datetime.date(2015, 1, 16), datetime.date(2015, 2, 18), datetime.date(2015, 3, 19), - datetime.date(2015, 4, 17), datetime.date(2015, 5, 18), datetime.date(2015, 6, 17), - datetime.date(2015, 7, 17), datetime.date(2015, 8, 17), datetime.date(2015, 9, 16), - datetime.date(2015, 10, 16), datetime.date(2015, 11, 16), datetime.date(2015, 12, 17), - datetime.date(2016, 1, 19), datetime.date(2016, 2, 18), datetime.date(2016, 3, 18)] -raw_bill['date_to'] = [ - datetime.date(2014, 5, 16), datetime.date(2014, 6, 17), datetime.date(2014, 7, 17), - datetime.date(2014, 8, 15), datetime.date(2014, 9, 16), datetime.date(2014, 10, 16), - datetime.date(2014, 11, 14), datetime.date(2014, 12, 17), datetime.date(2015, 1, 16), - datetime.date(2015, 2, 18), datetime.date(2015, 3, 19), datetime.date(2015, 4, 17), - datetime.date(2015, 5, 18), datetime.date(2015, 6, 17), datetime.date(2015, 7, 17), - datetime.date(2015, 8, 17), datetime.date(2015, 9, 16), datetime.date(2015, 10, 16), - datetime.date(2015, 11, 16), datetime.date(2015, 12, 17), datetime.date(2016, 1, 19), - datetime.date(2016, 2, 18), datetime.date(2016, 3, 18), datetime.date(2016, 4, 18)] -raw_bill['charge'] = [ - 2601.6289696969693, 2642.1740261508003, 3437.551946607342, 2930.5070588449826, - 2514.1242835094554, 2804.4448749999997, 3237.7359972191325, 3289.4155027808674, - 2796.5700000000002, 2463.5113047830923, 2370.0007392051775, 2479.081440860215] -raw_bill['usage'] = [ - 0.094265231101663627, 0.10175446346513953, 0.12248981352003527, 0.11229655777596657, - 0.092170964664193167, 0.094173672324921492, 0.095187070309577237, 0.097177529967719486, - 0.095676831493476874, 0.091583582602530869, 0.089122740090923164, 0.091516956021057888] +# # electricity raw bill +# raw_bill = {} +# # raw_bill['utility_type'] = 'electricity' +# raw_bill['date_from'] = [ +# datetime.date(2014, 4, 17), datetime.date(2014, 5, 16), datetime.date(2014, 6, 17), +# datetime.date(2014, 7, 17), datetime.date(2014, 8, 15), datetime.date(2014, 9, 16), +# datetime.date(2014, 10, 16), datetime.date(2014, 11, 14), datetime.date(2014, 12, 17), +# datetime.date(2015, 1, 16), datetime.date(2015, 2, 18), datetime.date(2015, 3, 19), +# datetime.date(2015, 4, 17), datetime.date(2015, 5, 18), datetime.date(2015, 6, 17), +# datetime.date(2015, 7, 17), datetime.date(2015, 8, 17), datetime.date(2015, 9, 16), +# datetime.date(2015, 10, 16), datetime.date(2015, 11, 16), datetime.date(2015, 12, 17), +# datetime.date(2016, 1, 19), datetime.date(2016, 2, 18), datetime.date(2016, 3, 18)] +# raw_bill['date_to'] = [ +# datetime.date(2014, 5, 16), datetime.date(2014, 6, 17), datetime.date(2014, 7, 17), +# datetime.date(2014, 8, 15), datetime.date(2014, 9, 16), datetime.date(2014, 10, 16), +# datetime.date(2014, 11, 14), datetime.date(2014, 12, 17), datetime.date(2015, 1, 16), +# datetime.date(2015, 2, 18), datetime.date(2015, 3, 19), datetime.date(2015, 4, 17), +# datetime.date(2015, 5, 18), datetime.date(2015, 6, 17), datetime.date(2015, 7, 17), +# datetime.date(2015, 8, 17), datetime.date(2015, 9, 16), datetime.date(2015, 10, 16), +# datetime.date(2015, 11, 16), datetime.date(2015, 12, 17), datetime.date(2016, 1, 19), +# datetime.date(2016, 2, 18), datetime.date(2016, 3, 18), datetime.date(2016, 4, 18)] +# raw_bill['charge'] = [ +# 2601.6289696969693, 2642.1740261508003, 3437.551946607342, 2930.5070588449826, +# 2514.1242835094554, 2804.4448749999997, 3237.7359972191325, 3289.4155027808674, +# 2796.5700000000002, 2463.5113047830923, 2370.0007392051775, 2479.081440860215] +# raw_bill['usage'] = [ +# 0.094265231101663627, 0.10175446346513953, 0.12248981352003527, 0.11229655777596657, +# 0.092170964664193167, 0.094173672324921492, 0.095187070309577237, 0.097177529967719486, +# 0.095676831493476874, 0.091583582602530869, 0.089122740090923164, 0.091516956021057888] + +e_raw_bill = { + 'date_from': [ + date(2015, 1, 28), date(2015, 2, 27), date(2015, 3, 30), date(2015, 4, 28), + date(2015, 5, 28), date(2015, 6, 26), date(2015, 7, 28), date(2015, 8, 26), + date(2015, 9, 25), date(2015, 10, 27), date(2015, 11, 25), date(2015, 12, 29), + date(2016, 1, 28), date(2016, 2, 29), date(2016, 3, 29), date(2016, 4, 27), + date(2016, 5, 26), date(2016, 6, 27), date(2016, 7, 27), date(2016, 8, 25), + date(2016, 9, 26), date(2016, 10, 26), date(2016, 11, 28) + ], + 'date_to': [ + date(2015, 2, 27), date(2015, 3, 30), date(2015, 4, 28), date(2015, 5, 28), + date(2015, 6, 26), date(2015, 7, 28), date(2015, 8, 26), date(2015, 9, 25), + date(2015, 10, 27), date(2015, 11, 25), date(2015, 12, 29), date(2016, 1, 28), + date(2016, 2, 29), date(2016, 3, 29), date(2016, 4, 27), date(2016, 5, 26), + date(2016, 6, 27), date(2016, 7, 27), date(2016, 8, 25), date(2016, 9, 26), + date(2016, 10, 26), date(2016, 11, 28), date(2016, 12, 28) + ], + 'usage': [ + 214.66, 189.42, 190.8, 168.92, 189.02, 179.64, 154.95, 162.13, + 136.16, 144.06, 208.85, 182.55, 98.13, 104.19, 90.05, + 88.19, 101.05, 87.05, 86.88, 98.17, 89.07, 91.93, 117.88 + ], + 'charge': [ + 172.33, 163.88, 163.49, 196.46, 169.68, 152.56, 126.16, + 137.14, 136.1, 130.65, 172.37, 145.44, 71.73, 68.12, 63.57, + 68.28, 71.66, 56.21, 57.25, 63.6, 67.42, 39.62, 56.01 + ] +} raw_bill_table = { - 'electricity': raw_bill, + 'electricity': e_raw_bill, 'gas': None, 'oil': None, 'water': None, } - # raw annual_bill annual_bill_gas = {2014: 0, 2015: 1020, 2016: 1220, 2017: 1520} annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} @@ -57,3 +87,13 @@ raw_annual_bill_table = { 'oil': annual_bill_oil, 'water': annual_bill_water } + + +bill_table = Bill_Table(raw_bill_table, analysis_date) +bill_table.put_annual_bill_table(raw_annual_bill_table) +bill_list = bill_table.get_bill_list() +print(bill_list[0].get_prior_proj_rough()) + +# e_raw_bill['charge'].reverse() +# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) +# pp.pprint(e_raw_bill['charge']) diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index e2c49df..785d945 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -27,7 +27,6 @@ def validate_raw_bill(raw_bill): Bollean: True == data validated. False == data is not validated """ if raw_bill is not None: - # validate date >= 12 months month_list = form_date_calendar( raw_bill['date_from'][0], raw_bill['date_to'][-1])[1] @@ -113,9 +112,10 @@ class Bill(): def __init__(self, utility_type, analysis_date): """ - Also, we should do rough_projection with normalized data - maybe we don't need algo_flag - and we should have annual_bill as an attribute + Initialize Bill object. Issue utility type and pro-forma date. Set manual_input as True + Args: + utility_type (string): utility type + analysis_date (dictionary): proforma's starting date and the years of proforma """ self.utility_type = utility_type self.analysis_date = analysis_date @@ -125,8 +125,17 @@ class Bill(): def put_month_bill(self, raw_bill): """ - initialize bill. validate input data (either none or >=12 month) - generate rough_month_normalization, usage and price + Put raw monthly bill. + Validate input data (Not None and time coverage >=12 months) + Roughly month_normalize the bill and roughly project prior_saving bill + Determine annual bill with prior_saving bill + + Args: + raw_bill (dictionary): dictionary of lists. Keys are + 'date_from': list of date, + 'date_to', list of date, + 'usage', list of float, + 'charge', list of float, """ if validate_raw_bill(raw_bill) is True: self.monthly_normalized_bill_rough = bill_month_normalize_rough(self.utility_type, copy.deepcopy(raw_bill)) @@ -140,16 +149,43 @@ class Bill(): self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) def put_annual_bill(self, raw_annual_bill): + """ + Put annual bill to the bill with raw bill not valid (manual_input == True) + Calculate average annual charge and assign the average value to missing years + + Args: + raw_annual_bill (dictionary): {year: float}. annual bill allowing some years data missing + """ if self.is_manual_input is True: self.annual_bill = estimate_annual_bill(raw_annual_bill, self.analysis_date) def get_prior_proj_rough(self): + """ + Get roughly projected prior_saving monthly bill + Return: + dictionary: dictionary of lists, prior_saving bill, for pro-forma period + prior_bill_rough = { + 'date_from': list of date, first day of month, for pro-forma period + 'date_to': list of date, last day of month, for pro-forma period + 'usage': list of floate, prior_saving_monthly_usage, + 'charge': list of floate, prior_saving_monthly_charge, + 'price': list of floate, prior_saving_monthly_price, + } + """ return self.prior_bill_rough def get_prior_proj_reg(self): + """ + Get projected prior_saving monthly bill (regression method) + """ return self.prior_bill_reg def get_annual_bill(self): + """ + Get annual bill in dictionary format + Return: + dictionary: {year, float} + """ return self.annual_bill @@ -172,15 +208,12 @@ class Bill_Table(): self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) def put_annual_bill_table(self, raw_annual_bill_table): + """ + """ self.raw_annual_bill_table = raw_annual_bill_table for utility_type in utility_type_list: self.utility_bill_dict[utility_type].put_annual_bill(raw_annual_bill_table[utility_type]) - def rewrite_month_bill_utility(self, raw_bill, analysis_date, target_type): - if target_type not in utility_type_list: - return False - self.utility_bill_dict[target_type].rewrite_month_bill(raw_bill, analysis_date) - def get_bill_overview(self): """ should be deleted or replaced when future calling func got updated @@ -208,6 +241,18 @@ class Bill_Table(): annual_bill_table[utility_type] = self.utility_bill_dict[utility_type].get_annual_bill() return annual_bill_table + def get_bill_list(self): + """ + Get list of Bill objects. For future use in other functions + Return: + list: list of Bill objects. Order in 'electricity', 'gas', 'oil', 'water' + """ + bill_list = [] + for utility_type in utility_type_list: + bill_list.append(copy.deepcopy(self.utility_bill_dict[utility_type])) + return bill_list + + # **** ugly test **** # e_bill = Bill('electricity', db.analysis_date) @@ -218,8 +263,8 @@ class Bill_Table(): # print('\n annual_bill_oil', o_bill.get_annual_bill()) -bill_table = Bill_Table(db.raw_bill_table, db.analysis_date) -bill_table.put_annual_bill_table(db.raw_annual_bill_table) -print(bill_table.utility_bill_dict['oil'].get_annual_bill()) -print(bill_table.get_bill_overview()) -print(bill_table.get_annual_bill_table()) +# bill_table = Bill_Table(db.raw_bill_table, db.analysis_date) +# bill_table.put_annual_bill_table(db.raw_annual_bill_table) +# print(bill_table.utility_bill_dict['oil'].get_annual_bill()) +# print(bill_table.get_bill_overview()) +# print(bill_table.get_annual_bill_table()) -- GitLab From 58bd48348b7f9167191737d064605539d818fea7 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 11 May 2017 15:44:02 -0400 Subject: [PATCH 13/30] Create financial_saving.py to be incorporated with Bill class --- bpfin/financials/financial_saving.py | 125 ++++++++++++--------------- bpfin/tests/testdata/feature_data.py | 2 +- 2 files changed, 54 insertions(+), 73 deletions(-) diff --git a/bpfin/financials/financial_saving.py b/bpfin/financials/financial_saving.py index 96d80c8..d003efb 100644 --- a/bpfin/financials/financial_saving.py +++ b/bpfin/financials/financial_saving.py @@ -1,3 +1,7 @@ +""" +this is replacement for saving.py +The inputs would incorporate bill_list = [Bill objects] +""" import datetime import copy from bpfin.utilbills.bill_lib import cal_last_day @@ -6,6 +10,10 @@ from bpfin.utilbills.bill_lib import form_year_calendar from bpfin.utilbills.bill_lib import form_bill_calendar from bpfin.utilbills.bill_post_proj_rough import bill_post_proj_rough from bpfin.tests.testdata import sample_data as db +from bpfin.tests.testdata import feature_data as fdb + + +utility_type_list = ['electricity', 'gas', 'oil', 'water'] class Saving(): @@ -13,7 +21,7 @@ class Saving(): Calculate savings and project bills with commissioning date considered. For one utility type Attributes: is_manual_input (boolean): lable indicating is bill input by scraper or manual. Yes == manual input - commissioning_date (date): the date that construction work finished, saving starts at NEXT month + commission_date (date): the date that construction work finished, saving starts at NEXT month proforma_date (list): list of dates, months of pro-forma time period proforma_usage (list) : list of monthly usage, for pro-forma period Data before commissioning date == historical @@ -34,16 +42,16 @@ class Saving(): first_year_prior_charge (float): first 12 months prior_saving charge. If data is annual, charge is for NEXT year percent_saving (float): percentage saving on first year charge. 0.2 == 20% """ - def __init__(self, is_manual_input, commissioning_date, proforma_date): + def __init__(self, is_manual_input, commission_date, proforma_date): """ Initiate Saving object. Args: is_manual_input (boolean): lable indicating is bill input by scraper or manual. Yes == manual input - commissioning_date (date): the date that construction work finished, saving starts at NEXT month + commission_date (date): the date that construction work finished, saving starts at NEXT month proforma_date (list): list of dates, months of pro-forma time period """ self.is_manual_input = is_manual_input - self.commissioning_date = commissioning_date + self.commission_date = commission_date self.proforma_date = proforma_date self.proforma_usage = None self.proforma_charge = None @@ -69,9 +77,9 @@ class Saving(): """ if self.is_manual_input is False: saving_start_date = datetime.date( - self.commissioning_date.year, - self.commissioning_date.month, - cal_last_day(self.commissioning_date.year, self.commissioning_date.month)) + self.commission_date.year, + self.commission_date.month, + cal_last_day(self.commission_date.year, self.commission_date.month)) saving_usage_dict = {} saving_charge_dict = {} @@ -127,18 +135,18 @@ class Saving(): annual_charge_dict = {} proforma_year = form_year_calendar(self.proforma_date[0], self.proforma_date[-1]) for year in proforma_year: - if year < self.commissioning_date.year: + if year < self.commission_date.year: annual_saving_dict[year] = 0 annual_charge_dict[year] = annual_charge[year] - if year > self.commissioning_date.year: + if year > self.commission_date.year: annual_saving_dict[year] = annual_charge[year] * percent_saving annual_charge_dict[year] = annual_charge[year] * (1 - percent_saving) - if year == self.commissioning_date.year: - effective_saving = (12 - self.commissioning_date.month) / 12 * percent_saving # partial year saving + if year == self.commission_date.year: + effective_saving = (12 - self.commission_date.month) / 12 * percent_saving # partial year saving annual_saving_dict[year] = annual_charge[year] * effective_saving annual_charge_dict[year] = annual_charge[year] * (1 - effective_saving) - first_year = self.commissioning_date.year + 1 + first_year = self.commission_date.year + 1 self.first_year_saving_charge = annual_saving_dict[first_year] self.annual_proforma_charge = annual_charge_dict self.first_year_prior_charge = annual_charge[first_year] @@ -202,10 +210,11 @@ class Saving_Overview(): Saving_Overview should be scenario based, it is an "attribute" of a scenario Attributes: - manual_input_dict(dictionary): key is utility type, value is boolean. True == mannual_input + bill_list (list): list of Bill objects + # manual_input_dict(dictionary): key is utility type, value is boolean. True == mannual_input prior_annual_bill_table (dictionary): annual bill, for 4 utility_types proforma_date (list): list of dates, months of pro-forma time period - commissioning_date (date): the date that construction work finished, saving starts at NEXT month + commission_date (date): the date that construction work finished, saving starts at NEXT month utility_saving_dict (dictionary): dict of Saving objects, for 4 utility types saving_percent_charge (dictionary): key is utility type, value is float {'electricity': float} total_first_year_saving (float): dollar saving for the first 12 month after commissioning @@ -215,90 +224,64 @@ class Saving_Overview(): prior_annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, ...} electricity_bill = {2014: 100, 2015:200, ...} """ + prior_annual_bill_table = {} + utility_saving_dict = {'electricity': None, 'gas': None, 'oil': None, 'water': None} + saving_percent_charge = {'electricity': None, 'gas': None, 'oil': None, 'water': None} def __init__( self, - bill_overview, - prior_annual_bill_table, + bill_list, analysis_date, # consider to replace it with proforma_date - commissioning_date): + commission_date): """ Initiate Saving_Overview. - Take in manual_input_dict, annual and monthly bills, commissioning_date and percentage savings + Take in manual_input_dict, annual and monthly bills, commission_date and percentage savings Generate Saving objects, pro-forma date period Args: - bill_overview (dictionary): original annual bill input, with blank cells, for 4 utility_types - prior_annual_bill_table (dictionary): dict of dict of annual charge, for 4 utility_types + bill_list (list): list of Bill objects analysis_date (dictionary): proforma's starting date and the years of proforma - commissioning_date (date): the date that construction work finished, saving starts at NEXT month - - Description: - bill_overview (dictionary): dict of a list, - list[0] = dict of annual bill, allow some year missing, for 4 utility_types - list[1] = boolean value, False == from manual input, True == from scraper - prior_annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, ...} - electricity_bill = {2014: 100, 2015:200, ...} + commission_date (date): the date that construction work finished, saving starts at NEXT month """ - self.manual_input_dict = { - 'electricity': bill_overview['electricity'][1], - 'gas': bill_overview['gas'][1], - 'oil': bill_overview['oil'][1], - 'water': bill_overview['water'][1]} - self.prior_annual_bill_table = prior_annual_bill_table + self.bill_list = copy.deepcopy(bill_list) + for bill in copy.deepcopy(bill_list): + self.prior_annual_bill_table[bill.utility_type] = bill.get_annual_bill() self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] - self.commissioning_date = commissioning_date - self.utility_saving_dict = {'electricity': None, 'gas': None, 'oil': None, 'water': None} - self.saving_percent_charge = {'electricity': None, 'gas': None, 'oil': None, 'water': None} + self.commission_date = commission_date self.total_first_year_saving = 0 # dollar self.total_saving_percent = 0 # dollar percentage - def put_saving(self, prior_month_bill, percent_saving_dict, full_saving_dict): + def put_saving(self, percent_saving_dict, full_saving_dict): """ Put inputs to generage saving data. Also calculate overall saving on charge, and percentage saving Args: - prior_month_bill (dictionary): dict of dict of lists, projected energy bill for prior_saving percent_saving_dict (dictionary): dict of float, percentage saving for 4 utility types full_saving_dict (dictionary): dict of undefined saving, from engineering saving analysis. can be None - - Description: - prior_month_bill = { - 'electricity': electricity_prior_bill_rough, - 'gas': None, - 'oil': None, - 'water': None} - - electricity_prior_bill_rough = { - 'date_from': list, - 'date_to': list, - 'usage': list, - 'charge': list, - 'price': list} """ - utility_type_list = ['electricity', 'gas', 'oil', 'water'] - total_first_year_saving = 0 total_first_year_prior_charge = 0 - for utility in utility_type_list: - is_manual_input = self.manual_input_dict[utility] + for bill in self.bill_list: + utility = bill.utility_type + is_manual_input = bill.is_manual_input - utility_saving = Saving(is_manual_input, self.commissioning_date, self.proforma_date) + prior_month_bill = bill.get_prior_proj_rough() + utility_saving = Saving(is_manual_input, self.commission_date, self.proforma_date) if utility_saving.is_manual_input is True: # bill is from manual input, monthly bill not available - utility_saving.put_annual_proforma(self.prior_annual_bill_table[utility], percent_saving_dict[utility]) + utility_saving.put_annual_proforma(bill.get_annual_bill(), percent_saving_dict[utility]) if utility_saving.is_manual_input is False: # bill is from scraper, monthly bill is available if full_saving_dict[utility] is None: # engineering saving is not available, use percentage saving - post_month_bill = bill_post_proj_rough(prior_month_bill[utility], percent_saving_dict[utility]) + post_month_bill = bill_post_proj_rough(prior_month_bill, percent_saving_dict[utility]) # else: # engineering saving is available. Project monthly_bill with regression. not finished yet # post_month_bill = bill_proj_reg(prior_utility, full_saving_dict[utility], HDD, CDD, occupancy) utility_saving.put_monthly_proforma( - prior_month_bill[utility]['usage'], + prior_month_bill['usage'], post_month_bill['usage'], - prior_month_bill[utility]['charge'], + prior_month_bill['charge'], post_month_bill['charge']) total_first_year_saving += utility_saving.get_first_year_saving_charge() @@ -370,15 +353,13 @@ class Saving_Overview(): return saving_dict +# **** ugly test **** +# sav_o = Saving_Overview(fdb.bill_list, fdb.analysis_date, db.commission_date) +# sav_o.put_saving(db.percent_saving_dict, db.full_saving_dict) +# print('\n first_year_dollar_saving =', sav_o.get_total_first_year_saving()) +# print('\n simple payback =', sav_o.get_simple_payback(150000)) +# print('\n total saving percent =', sav_o.get_total_saving_percent()) +# print('\n electricity_annual_saving_charge', sav_o.get_utility_annual_saving_charge('electricity')) +# print(sav_o.get_post_annual_bill_table()) - -# **** ugly test **** -# so = Saving_Overview(db.bill_overview, db.bill_overview_organized, db.analysis_date, datetime.date(2017, 3, 14)) -# so.put_saving(db.prior_month_bill, db.percent_saving_dict, db.full_saving_dict) - -# print('\n first_year_dollar_saving =', so.get_total_first_year_saving()) -# print('\n simple payback =', so.get_simple_payback(50000)) -# print('\n total saving percent =', so.get_total_saving_percent()) -# print('\n electricity_annual_saving_charge', so.get_utility_annual_saving_charge('electricity')) -# print(so.get_post_annual_bill_table()) diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index f582534..9f15c63 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -92,7 +92,7 @@ raw_annual_bill_table = { bill_table = Bill_Table(raw_bill_table, analysis_date) bill_table.put_annual_bill_table(raw_annual_bill_table) bill_list = bill_table.get_bill_list() -print(bill_list[0].get_prior_proj_rough()) +# print(bill_list[0].get_prior_proj_rough()) # e_raw_bill['charge'].reverse() # pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) -- GitLab From ee7bd6d242f3f839a512c2fe014cab8c366b1d18 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 12 May 2017 10:47:54 -0400 Subject: [PATCH 14/30] Create form_bill_list() to replace Bill_Table class --- bpfin/tests/testdata/feature_data.py | 73 +++++++------- bpfin/utilbills/bill.py | 142 +++++++++++++++------------ 2 files changed, 119 insertions(+), 96 deletions(-) diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 9f15c63..23e1e28 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -9,35 +9,42 @@ analysis_date = { 'proforma_duration': 25} -# # electricity raw bill -# raw_bill = {} -# # raw_bill['utility_type'] = 'electricity' -# raw_bill['date_from'] = [ -# datetime.date(2014, 4, 17), datetime.date(2014, 5, 16), datetime.date(2014, 6, 17), -# datetime.date(2014, 7, 17), datetime.date(2014, 8, 15), datetime.date(2014, 9, 16), -# datetime.date(2014, 10, 16), datetime.date(2014, 11, 14), datetime.date(2014, 12, 17), -# datetime.date(2015, 1, 16), datetime.date(2015, 2, 18), datetime.date(2015, 3, 19), -# datetime.date(2015, 4, 17), datetime.date(2015, 5, 18), datetime.date(2015, 6, 17), -# datetime.date(2015, 7, 17), datetime.date(2015, 8, 17), datetime.date(2015, 9, 16), -# datetime.date(2015, 10, 16), datetime.date(2015, 11, 16), datetime.date(2015, 12, 17), -# datetime.date(2016, 1, 19), datetime.date(2016, 2, 18), datetime.date(2016, 3, 18)] -# raw_bill['date_to'] = [ -# datetime.date(2014, 5, 16), datetime.date(2014, 6, 17), datetime.date(2014, 7, 17), -# datetime.date(2014, 8, 15), datetime.date(2014, 9, 16), datetime.date(2014, 10, 16), -# datetime.date(2014, 11, 14), datetime.date(2014, 12, 17), datetime.date(2015, 1, 16), -# datetime.date(2015, 2, 18), datetime.date(2015, 3, 19), datetime.date(2015, 4, 17), -# datetime.date(2015, 5, 18), datetime.date(2015, 6, 17), datetime.date(2015, 7, 17), -# datetime.date(2015, 8, 17), datetime.date(2015, 9, 16), datetime.date(2015, 10, 16), -# datetime.date(2015, 11, 16), datetime.date(2015, 12, 17), datetime.date(2016, 1, 19), -# datetime.date(2016, 2, 18), datetime.date(2016, 3, 18), datetime.date(2016, 4, 18)] -# raw_bill['charge'] = [ -# 2601.6289696969693, 2642.1740261508003, 3437.551946607342, 2930.5070588449826, -# 2514.1242835094554, 2804.4448749999997, 3237.7359972191325, 3289.4155027808674, -# 2796.5700000000002, 2463.5113047830923, 2370.0007392051775, 2479.081440860215] -# raw_bill['usage'] = [ -# 0.094265231101663627, 0.10175446346513953, 0.12248981352003527, 0.11229655777596657, -# 0.092170964664193167, 0.094173672324921492, 0.095187070309577237, 0.097177529967719486, -# 0.095676831493476874, 0.091583582602530869, 0.089122740090923164, 0.091516956021057888] +# Engineering Scenarios and project economics +cost_estimation = 150000.00 +commission_date = datetime.date(2017, 3, 14) + + +# required debt service coverage ratio +req_dscr = { + 'req_noi_dscr': 1.15, + 'req_cash_dscr': 1.15, + 'req_saving_dscr': 1.10 +} + + +# Customer Preference +customer_preference = { + 'downpayment_max': 5000.00, + 'expected_payback': 120, # in months + 'cust_saving_dscr': 1.15 +} + + +percent_saving_dict = { + 'electricity': 0.25, + 'gas': 0.10, + 'oil': 0.80, + 'water': 0.0 +} + + +full_saving_dict = { + 'electricity': None, # dict of engineering analysis, {'date_from': [], 'date_to': [], 'heating': [], 'cooling': [], 'other': []} + 'gas': None, + 'oil': None, + 'water': None +} + e_raw_bill = { 'date_from': [ @@ -71,9 +78,9 @@ e_raw_bill = { raw_bill_table = { 'electricity': e_raw_bill, - 'gas': None, - 'oil': None, - 'water': None, + # 'gas': None, + # 'oil': None, + # 'water': None, } # raw annual_bill @@ -82,7 +89,7 @@ annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} annual_bill_water = {2015: 0, 2016: 0, 2017: 0} raw_annual_bill_table = { - 'electricity': None, # True == Mannual Input + # 'electricity': None, # True == Mannual Input 'gas': annual_bill_gas, 'oil': annual_bill_oil, 'water': annual_bill_water diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 785d945..de6fd50 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -120,7 +120,6 @@ class Bill(): self.utility_type = utility_type self.analysis_date = analysis_date self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] - # self.raw_bill_dict = raw_bill self.is_manual_input = True def put_month_bill(self, raw_bill): @@ -189,68 +188,85 @@ class Bill(): return self.annual_bill -class Bill_Table(): - """ - """ - def __init__(self, raw_bill_table, analysis_date): - """ - """ - self.analysis_date = analysis_date - self.raw_bill_table = raw_bill_table - self.manual_input_dict = {} - self.raw_annual_bill_table = None - - self.utility_bill_dict = {} # dict of object - for utility_type in utility_type_list: - self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) - if raw_bill_table[utility_type] is not None: - self.utility_bill_dict[utility_type].put_month_bill(raw_bill_table[utility_type]) - self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) - - def put_annual_bill_table(self, raw_annual_bill_table): - """ - """ - self.raw_annual_bill_table = raw_annual_bill_table - for utility_type in utility_type_list: - self.utility_bill_dict[utility_type].put_annual_bill(raw_annual_bill_table[utility_type]) - - def get_bill_overview(self): - """ - should be deleted or replaced when future calling func got updated - because all functions would need to use either the bill or the yes/no flag. - """ - bill_overview = {} - for utility_type in utility_type_list: - bill_overview[utility_type] = [ - ( - self.raw_annual_bill_table[utility_type] if - self.utility_bill_dict[utility_type].is_manual_input is True - else self.utility_bill_dict[utility_type].get_annual_bill() - ), - self.utility_bill_dict[utility_type].is_manual_input - ] - return bill_overview - - def get_annual_bill_table(self): - """ - Annual bill table, for 4 utility types, with annualized projected charge, or average annual charge - sample_data.bill_overview_organized - """ - annual_bill_table = {} - for utility_type in utility_type_list: - annual_bill_table[utility_type] = self.utility_bill_dict[utility_type].get_annual_bill() - return annual_bill_table - - def get_bill_list(self): - """ - Get list of Bill objects. For future use in other functions - Return: - list: list of Bill objects. Order in 'electricity', 'gas', 'oil', 'water' - """ - bill_list = [] - for utility_type in utility_type_list: - bill_list.append(copy.deepcopy(self.utility_bill_dict[utility_type])) - return bill_list +def form_bill_list(raw_bill_table, raw_annual_bill_table, analysis_date): + bill_list = [] + # annual_bill_table = {} + for utility in utility_type_list: + current_bill = Bill(utility, analysis_date) + if utility in raw_annual_bill: + current_bill.put_month_bill(raw_annual_bill[utility]) + else: + if utility in raw_annual_bill_table: + current_bill.put_annual_bill(raw_annual_bill_table[utility]) + + bill_list.append(current_bill) + # annual_bill_table[utility] = current_bill.get_annual_bill() + return bill_list + + +# **** Bill_Table class is not needed **** +# class Bill_Table(): +# """ +# """ +# def __init__(self, raw_bill_table, analysis_date): +# """ +# """ +# self.analysis_date = analysis_date +# self.raw_bill_table = raw_bill_table +# self.manual_input_dict = {} +# self.raw_annual_bill_table = None + +# self.utility_bill_dict = {} # dict of object +# for utility_type in utility_type_list: +# self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) +# if raw_bill_table[utility_type] is not None: +# self.utility_bill_dict[utility_type].put_month_bill(raw_bill_table[utility_type]) +# self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) + +# def put_annual_bill_table(self, raw_annual_bill_table): +# """ +# """ +# self.raw_annual_bill_table = raw_annual_bill_table +# for utility_type in utility_type_list: +# self.utility_bill_dict[utility_type].put_annual_bill(raw_annual_bill_table[utility_type]) + +# def get_bill_overview(self): +# """ +# should be deleted or replaced when future calling func got updated +# because all functions would need to use either the bill or the yes/no flag. +# """ +# bill_overview = {} +# for utility_type in utility_type_list: +# bill_overview[utility_type] = [ +# ( +# self.raw_annual_bill_table[utility_type] if +# self.utility_bill_dict[utility_type].is_manual_input is True +# else self.utility_bill_dict[utility_type].get_annual_bill() +# ), +# self.utility_bill_dict[utility_type].is_manual_input +# ] +# return bill_overview + +# def get_annual_bill_table(self): +# """ +# Annual bill table, for 4 utility types, with annualized projected charge, or average annual charge +# sample_data.bill_overview_organized +# """ +# annual_bill_table = {} +# for utility_type in utility_type_list: +# annual_bill_table[utility_type] = self.utility_bill_dict[utility_type].get_annual_bill() +# return annual_bill_table + +# def get_bill_list(self): +# """ +# Get list of Bill objects. For future use in other functions +# Return: +# list: list of Bill objects. Order in 'electricity', 'gas', 'oil', 'water' +# """ +# bill_list = [] +# for utility_type in utility_type_list: +# bill_list.append(copy.deepcopy(self.utility_bill_dict[utility_type])) +# return bill_list -- GitLab From c25bf522901a2019ea4d5b7c6875458c33d8a674 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 12 May 2017 10:48:21 -0400 Subject: [PATCH 15/30] Minor correction --- bpfin/utilbills/bill.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index de6fd50..98f2e7e 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -193,8 +193,8 @@ def form_bill_list(raw_bill_table, raw_annual_bill_table, analysis_date): # annual_bill_table = {} for utility in utility_type_list: current_bill = Bill(utility, analysis_date) - if utility in raw_annual_bill: - current_bill.put_month_bill(raw_annual_bill[utility]) + if utility in raw_bill_table: + current_bill.put_month_bill(raw_bill_table[utility]) else: if utility in raw_annual_bill_table: current_bill.put_annual_bill(raw_annual_bill_table[utility]) -- GitLab From bc3a7c47a8dd4b08a2fb00235f37f3f59d5c9787 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 12 May 2017 10:51:11 -0400 Subject: [PATCH 16/30] Update feature_data bill_list object --- bpfin/tests/testdata/feature_data.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 23e1e28..ce07a9e 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -1,6 +1,6 @@ import datetime from datetime import date -from bpfin.utilbills.bill import Bill_Table +from bpfin.utilbills.bill import form_bill_list import pprint # pro-forma projection time base @@ -96,9 +96,10 @@ raw_annual_bill_table = { } -bill_table = Bill_Table(raw_bill_table, analysis_date) -bill_table.put_annual_bill_table(raw_annual_bill_table) -bill_list = bill_table.get_bill_list() +# bill_table = Bill_Table(raw_bill_table, analysis_date) +# bill_table.put_annual_bill_table(raw_annual_bill_table) +# bill_list = bill_table.get_bill_list() +bill_list = form_bill_list(raw_bill_table, raw_annual_bill_table, analysis_date) # print(bill_list[0].get_prior_proj_rough()) # e_raw_bill['charge'].reverse() -- GitLab From e4c666232a7242cfeb9ee0a311e1abfca40c093f Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 12 May 2017 11:20:38 -0400 Subject: [PATCH 17/30] Create financial_income to saperate income_statement class from financial_lib Create form_bill_list_month --- bpfin/financials/financial_income.py | 464 +++++++++++++++++++++++++++ bpfin/tests/testdata/feature_data.py | 5 +- bpfin/utilbills/bill.py | 9 +- 3 files changed, 468 insertions(+), 10 deletions(-) create mode 100644 bpfin/financials/financial_income.py diff --git a/bpfin/financials/financial_income.py b/bpfin/financials/financial_income.py new file mode 100644 index 0000000..fe72b21 --- /dev/null +++ b/bpfin/financials/financial_income.py @@ -0,0 +1,464 @@ +""" +Rewrite Income_Statement class to saperate income statement related class and funcs from Financial_lib.py +Income_Statement is incorporate with new Bill class, taking in bill_list as input +""" + +import copy +from bpfin.lib.other import cal_cagr +from bpfin.utilbills.bill_lib import form_bill_year +from numpy import mean +# from bpfin.tests.testdata import sample_data as db +# import pprint + + +class Income_Statement(): + """ + Generate Income Statement for single year, with standard accounting line items + Will be called + """ + def __init__(self): + self.year = None + self.revenue = None + self.utility_expense = None + self.non_utility_expense = None + self.electricity_opex = None + self.gas_opex = None + self.oil_opex = None + self.water_opex = None + self.energy_opex = None + self.other_utility = None + self.net_non_energy_opex = None + self.total_opex = None + self.noi = None + # energy_debt_service = + # other_debt_service = + # total_debt_service = + # bill_saving = + # energy_DSCR = + # total_DSCR = + + def put_hist(self, year, income_input, annual_bill_table): + """ + Put historical income statement data and generate a single year income statement + Inputs are incomplete income statement items, and energy bill overview + Final output is an single year income_statement objectvie with standarized items filled + + Args: + year (int): the year of this income statement + income_input (dictionary): 3 elements of inputs + annual_bill_table (dictionary): annual bill, for 4 utility_types + + Description Sample: + income_input = {revenue': 100000, 'utility_expense': 60000,'non_utility_expense': 3000} + annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} + """ + self.year = year + self.revenue = income_input['revenue'] + self.utility_expense = income_input['utility_expense'] + self.non_utility_expense = income_input['non_utility_expense'] + self.electricity_opex = annual_bill_table['electricity'][self.year] + self.gas_opex = annual_bill_table['gas'][self.year] + self.oil_opex = annual_bill_table['oil'][self.year] + self.water_opex = annual_bill_table['water'][self.year] + self.energy_opex = self.electricity_opex + self.oil_opex + self.gas_opex + self.water_opex + self.other_utility = self.utility_expense - self.energy_opex + self.net_non_energy_opex = self.other_utility + self.non_utility_expense + self.total_opex = self.energy_opex + self.net_non_energy_opex + self.noi = self.revenue - self.total_opex # net operating income + + def put_average(self, average_revenue, annual_bills, characters): + """ + Calculate and create average income statement. + Revenue, energy bills are average of historical data. + Other items are calculated by historical characters. + Args: + average_revenue (float): average revenue calculated from historical income statements + annual_bills (dictionary): dictionary of float value of average annual bills, for 4 utility_types + characters (dictionary): 6 characters calculated from historical income statements + Final instance is a single year income statement without named year + + Description: + characters = { + 'start_year': (int) 2014, + 'end_year': (int) 2016, + 'cagr': (float) 0.054, + 'other_utility_percent': (float) 0.020 == 2.0% + 'non_utility_expense_percent': (float) 0.030 == 3.0% + 'revenue_average': (float) 3000.00 + } + annual_bills = {'electricity': 100.0, 'oil': 200.0, 'gas': 300.0, 'water': 400.0} + """ + self.revenue = average_revenue + self.electricity_opex = annual_bills['electricity'] + self.gas_opex = annual_bills['gas'] + self.oil_opex = annual_bills['oil'] + self.water_opex = annual_bills['water'] + self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex + self.other_utility = self.revenue * characters['other_utility_percent'] + self.non_utility_expense = self.revenue * characters[ + 'non_utility_expense_percent'] + self.utility_expense = self.energy_opex + self.other_utility + self.net_non_energy_opex = self.other_utility + self.non_utility_expense + self.total_opex = self.energy_opex + self.net_non_energy_opex + self.noi = self.revenue - self.total_opex + + # def assign_next(self, income_statement_characters, + # bill_overview_organized): + # """ + # !!! this function will be deleted in the next version!! + # """ + # self.electricity_opex = bill_overview_organized['electricity'][ + # self.year] + # self.gas_opex = bill_overview_organized['gas'][self.year] + # self.oil_opex = bill_overview_organized['oil'][self.year] + # self.water_opex = bill_overview_organized['water'][self.year] + # self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex + # self.other_utility = self.revenue * income_statement_characters[ + # 'other_utility_percent'] + # self.non_utility_expense = self.revenue * income_statement_characters[ + # 'non_utility_expense_percent'] + # self.utility_expense = self.energy_opex + self.other_utility + # self.net_non_energy_opex = self.other_utility + self.non_utility_expense + # self.total_opex = self.energy_opex + self.net_non_energy_opex + # self.noi = self.revenue - self.total_opex + + +def convert_income_statement_class(income_statement_class): + """ + Convert single year income statement object into a dictionary format + Args: + income_statement_class (object): single year income statement object + Return: + income_statement_dict (dictionary) + """ + income_statement_dict = { + 'year': income_statement_class.year, + 'revenue': income_statement_class.revenue, + 'utility_expense': income_statement_class.utility_expense, + 'energy_opex': income_statement_class.energy_opex, + 'electricity_opex': income_statement_class.electricity_opex, + 'gas_opex': income_statement_class.gas_opex, + 'oil_opex': income_statement_class.oil_opex, + 'water_opex': income_statement_class.water_opex, + 'other_utility': income_statement_class.other_utility, + 'non_utility_expense': income_statement_class.non_utility_expense, + 'net_non_energy_opex': income_statement_class.net_non_energy_opex, + 'total_opex': income_statement_class.total_opex, + 'noi': income_statement_class.noi + } + return income_statement_dict + + +class Income_Statement_Next(): + """ + Create single year income_statement object, + with input of last year data, income statement characters, and projected annual bill + """ + + def __init__(self, year, last_revenue, growth_rate_flag, characters, + annual_bill_table): + """ + Calculation is done in initiation. + Args: + year (int): the year that will be projected on + last_revenue (float): last year revenue + growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average + characters (dictionary): 6 characters calculated from historical income statements + annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + Final instance is a single year income_statement object + + Description: + characters = { + 'start_year': (int) 2014, + 'end_year': (int) 2016, + 'cagr': (float) 0.054, + 'other_utility_percent': (float) 0.020 == 2.0% + 'non_utility_expense_percent': (float) 0.035 == 3.5% + 'revenue_average': (float) 3000.00 + } + annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} + electricity_bill = {2014: 100, 2015:200, ...} + """ + self.year = year + + if growth_rate_flag == -1.0: # growth_rate_flag == average + current_revenue = characters['revenue_average'] + else: + if growth_rate_flag == -2.0: # growth_rate_flag == cagr + growth_rate = characters['cagr'] + else: + growth_rate = growth_rate_flag # growth_rate_flag == 0.00, 0.01, ... + current_revenue = last_revenue * (1 + growth_rate) + self.revenue = current_revenue + self.electricity_opex = annual_bill_table['electricity'][self.year] + self.gas_opex = annual_bill_table['gas'][self.year] + self.oil_opex = annual_bill_table['oil'][self.year] + self.water_opex = annual_bill_table['water'][self.year] + self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex + self.other_utility = self.revenue * characters['other_utility_percent'] + self.non_utility_expense = self.revenue * characters[ + 'non_utility_expense_percent'] + self.utility_expense = self.energy_opex + self.other_utility + self.net_non_energy_opex = self.other_utility + self.non_utility_expense + self.total_opex = self.energy_opex + self.net_non_energy_opex + self.noi = self.revenue - self.total_opex + + +class Income_Statement_Table(): + """ + Create a income statement table, containing multiple years, including historical and projected data + Key Attributes are: + hist_table (list): list of single year income_statement objects, containing historical data + table(list): list of single year income_statement objects, containing historical and projected data + """ + + def __init__(self, raw_income_input, annual_bill_table): + """ + Create hist_table to store historical income statement data, and calculate some key characters. + Args: + raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year + annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + + Key Attributes: + cagr (float): compound annual growth rate + other_utility_percent (float): percentage, (average other_utility / average revenue) + non_utility_expense_percent (float): percentage, (average non_utility_expense_percent / average revenue) + hist_table (list): list of single year income_statement objects, containing historical data + table(list): list of single year income_statement objects, containing historical and projected data + characters (dictionary): contains key characters determined from historical data. Is used to project + + Description: + raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} + annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} + electricity_bill = {2014: 100, 2015:200, ...} + characters = { + 'start_year': (int) 2014, + 'end_year': (int) 2016, + 'cagr': (float) 0.054, + 'other_utility_percent': (float) 0.02, + 'non_utility_expense_percent': (float) 0.03, + 'revenue_average': (float) 3000.00 + } + """ + self.hist_start_year = None + self.hist_end_year = None + self.cagr = 0.00 + self.other_utility_percent = 0.00 + self.non_utility_expense_percent = 0.00 + self.revenue_average = 0.00 # average revenue + self.hist_table = [] + self.table = [] + self.characters = {} + + for year in raw_income_input: + current_income_statement = Income_Statement() + current_income_statement.put_hist(year, raw_income_input[year], + annual_bill_table) + self.hist_table.append(current_income_statement) + + sorted_income_hist_year = sorted(raw_income_input) + self.hist_start_year = sorted_income_hist_year[0] + self.hist_end_year = sorted_income_hist_year[-1] + # if start_year == None: return None + self.cagr = cal_cagr( + raw_income_input[self.hist_start_year]['revenue'], + raw_income_input[self.hist_end_year]['revenue'], + self.hist_end_year - self.hist_start_year) + + revenue_sum = 0 + other_utility_sum = 0 + non_utility_expense_sum = 0 + for current_income_statement in self.hist_table: + revenue_sum += current_income_statement.revenue + other_utility_sum += current_income_statement.other_utility + non_utility_expense_sum += current_income_statement.non_utility_expense + + self.other_utility_percent = other_utility_sum / revenue_sum + self.non_utility_expense_percent = non_utility_expense_sum / revenue_sum + self.revenue_average = revenue_sum / ( + self.hist_end_year - self.hist_start_year + 1) + + self.table = self.hist_table + self.characters = { + 'start_year': self.hist_start_year, + 'end_year': self.hist_end_year, + 'cagr': self.cagr, + 'other_utility_percent': self.other_utility_percent, + 'non_utility_expense_percent': self.non_utility_expense_percent, + 'revenue_average': self.revenue_average + } + + def project(self, growth_rate_flag, analysis_date, annual_bill_table): + """ + Project future income statement. Append multiple single year income_statement objects to self.table + Args: + growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average + analysis_date (dictionary): proforma's starting date and the years of proforma + annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + Return: + list: list of single year income_statement objects, containing historical and projection + Note: + project() overwrites existing projection data + """ + # characters = copy.deepcopy(self.characters) + proforma_year = form_bill_year(analysis_date['proforma_start'], + analysis_date['proforma_duration']) + current_table = copy.deepcopy(self.hist_table) + for year in proforma_year: + last_revenue = current_table[-1].revenue + if year <= current_table[-1].year: + continue + current_income_statement = Income_Statement_Next( + year, last_revenue, growth_rate_flag, self.characters, + annual_bill_table) + current_table.append(current_income_statement) + self.table = current_table + # return current_table + + def get_hist_table(self): + """ + Get historical table, in dictionary formatting + Return: + dictionary: dict of dict of income statement. Key is year + Description: + hist_table_dict = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} + """ + hist_table_dict = {} + for current_income_statement in self.hist_table: + hist_table_dict[current_income_statement.year] = { + 'year': + current_income_statement.year, + 'revenue': + current_income_statement.revenue, + 'utility_expense': + current_income_statement.utility_expense, + 'energy_opex': + current_income_statement.energy_opex, + 'electricity_opex': + current_income_statement.electricity_opex, + 'gas_opex': + current_income_statement.gas_opex, + 'oil_opex': + current_income_statement.oil_opex, + 'water_opex': + current_income_statement.water_opex, + 'other_utility': + current_income_statement.other_utility, + 'non_utility_expense': + current_income_statement.non_utility_expense, + 'net_non_energy_opex': + current_income_statement.net_non_energy_opex, + 'total_opex': + current_income_statement.total_opex, + 'noi': + current_income_statement.noi + } + return hist_table_dict + + def get_cagr(self): + """ + Get compound annual growth rate + Return: + float: compound annual growth rate + """ + return copy.deepcopy(self.cagr) + + def get_average(self): + """ + Get average income statement. Only need initialized Income_Statement_Table + Return: + dictionary: dict of average income statement, Keys are income statement items. + Description: + output_dict = {'year': None, 'revenue': 95000.0, ... ,'noi': 35166.666666666657} + """ + # current_year = 'Average' + current_income_statement = Income_Statement() + average_revenue = mean(list(i_s.revenue for i_s in self.hist_table)) + annual_bills = { + 'electricity': + mean(list(i_s.electricity_opex for i_s in self.hist_table)), + 'gas': + mean(list(i_s.gas_opex for i_s in self.hist_table)), + 'oil': + mean(list(i_s.oil_opex for i_s in self.hist_table)), + 'water': + mean(list(i_s.water_opex for i_s in self.hist_table)) + } + current_income_statement.put_average(average_revenue, annual_bills, + self.characters) + + return convert_income_statement_class(current_income_statement) + + def get_single_year(self, year): + """ + Get single year income statement from self.table. Table can either contain or not contain projection + Args: + year (int): the year that need to be extracted + Return: + dict: single year income statement. Keys are income statement items. + Note: + if the target year is not in self.table, then return None. + """ + for current_income_statement in self.table: + if current_income_statement.year == year: + return { + 'year': + current_income_statement.year, + 'revenue': + current_income_statement.revenue, + 'utility_expense': + current_income_statement.utility_expense, + 'energy_opex': + current_income_statement.energy_opex, + 'electricity_opex': + current_income_statement.electricity_opex, + 'gas_opex': + current_income_statement.gas_opex, + 'oil_opex': + current_income_statement.oil_opex, + 'water_opex': + current_income_statement.water_opex, + 'other_utility': + current_income_statement.other_utility, + 'non_utility_expense': + current_income_statement.non_utility_expense, + 'net_non_energy_opex': + current_income_statement.net_non_energy_opex, + 'total_opex': + current_income_statement.total_opex, + 'noi': + current_income_statement.noi + } + return None + + def get_noi_dict(self): + """ + Get a dictionary of net operating income. + Return: + dictionary: net operating income for each year in income statement table. Key is year + """ + noi_dict = {} + for current_income_statement in self.table: + noi_dict[ + current_income_statement.year] = current_income_statement.noi + return noi_dict + + def get_first_year_noi(self, commission_date): + """ + Get first year noi after commissioning year, from post_saving income statement + Args: + commission_date (date): construction finishing date. Saving starts NEXT month/year + Return: + float: noi of the next year after commission date + """ + first_year = commission_date.year + 1 + for current_income_statement in self.table: + if current_income_statement.year == first_year: + return current_income_statement.noi + + def get_total_energy_dict(self): + total_energy_dict = {} + for current_income_statement in self.table: + total_energy_dict[ + current_income_statement.year] = current_income_statement.energy_opex + return total_energy_dict diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index ce07a9e..de10cc9 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -1,6 +1,6 @@ import datetime from datetime import date -from bpfin.utilbills.bill import form_bill_list +from bpfin.utilbills.bill import form_bill_list_month import pprint # pro-forma projection time base @@ -75,7 +75,6 @@ e_raw_bill = { ] } - raw_bill_table = { 'electricity': e_raw_bill, # 'gas': None, @@ -83,11 +82,11 @@ raw_bill_table = { # 'water': None, } + # raw annual_bill annual_bill_gas = {2014: 0, 2015: 1020, 2016: 1220, 2017: 1520} annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} annual_bill_water = {2015: 0, 2016: 0, 2017: 0} - raw_annual_bill_table = { # 'electricity': None, # True == Mannual Input 'gas': annual_bill_gas, diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 98f2e7e..2f7e144 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -188,22 +188,17 @@ class Bill(): return self.annual_bill -def form_bill_list(raw_bill_table, raw_annual_bill_table, analysis_date): +def form_bill_list_month(raw_bill_table, analysis_date): bill_list = [] - # annual_bill_table = {} for utility in utility_type_list: current_bill = Bill(utility, analysis_date) if utility in raw_bill_table: current_bill.put_month_bill(raw_bill_table[utility]) - else: - if utility in raw_annual_bill_table: - current_bill.put_annual_bill(raw_annual_bill_table[utility]) - bill_list.append(current_bill) - # annual_bill_table[utility] = current_bill.get_annual_bill() return bill_list + # **** Bill_Table class is not needed **** # class Bill_Table(): # """ -- GitLab From d3bbf5a6355020462043c6fbf31b79492ca118dc Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 12 May 2017 12:13:50 -0400 Subject: [PATCH 18/30] create form_bill_list_annual to calculate annual bill table, the widely used intermediate variable --- bpfin/tests/testdata/feature_data.py | 11 +++++++---- bpfin/utilbills/bill.py | 9 +++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index de10cc9..fb2ad95 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -1,6 +1,7 @@ import datetime from datetime import date from bpfin.utilbills.bill import form_bill_list_month +from bpfin.utilbills.bill import form_annual_bill_table import pprint # pro-forma projection time base @@ -77,9 +78,9 @@ e_raw_bill = { raw_bill_table = { 'electricity': e_raw_bill, - # 'gas': None, - # 'oil': None, - # 'water': None, + 'gas': None, + 'oil': None, + 'water': None, } @@ -98,8 +99,10 @@ raw_annual_bill_table = { # bill_table = Bill_Table(raw_bill_table, analysis_date) # bill_table.put_annual_bill_table(raw_annual_bill_table) # bill_list = bill_table.get_bill_list() -bill_list = form_bill_list(raw_bill_table, raw_annual_bill_table, analysis_date) +bill_list = form_bill_list_month(raw_bill_table, analysis_date) +annual_bill_table = form_annual_bill_table(bill_list, raw_annual_bill_table) # print(bill_list[0].get_prior_proj_rough()) +print(annual_bill_table) # e_raw_bill['charge'].reverse() # pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 2f7e144..b6261c7 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -198,6 +198,15 @@ def form_bill_list_month(raw_bill_table, analysis_date): return bill_list +def form_annual_bill_table(bill_list, raw_annual_bill_table): + bill_list_anual = copy.deepcopy(bill_list) + annual_bill_table = {} + for current_bill in bill_list_anual: + if current_bill.utility_type in raw_annual_bill_table: + current_bill.put_annual_bill(raw_annual_bill_table[current_bill.utility_type]) + annual_bill_table[current_bill.utility_type] = current_bill.get_annual_bill() + return annual_bill_table + # **** Bill_Table class is not needed **** # class Bill_Table(): -- GitLab From 34980ee406b622bea2201f90c8513ec759bb54b7 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 12 May 2017 21:22:21 -0400 Subject: [PATCH 19/30] Fix following PR review comment --- bpfin/financials/financial_saving.py | 25 +++--- bpfin/tests/testdata/feature_data.py | 6 +- bpfin/utilbills/bill.py | 128 +++++++++++++-------------- 3 files changed, 79 insertions(+), 80 deletions(-) diff --git a/bpfin/financials/financial_saving.py b/bpfin/financials/financial_saving.py index d003efb..d13c539 100644 --- a/bpfin/financials/financial_saving.py +++ b/bpfin/financials/financial_saving.py @@ -13,14 +13,14 @@ from bpfin.tests.testdata import sample_data as db from bpfin.tests.testdata import feature_data as fdb -utility_type_list = ['electricity', 'gas', 'oil', 'water'] +UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] class Saving(): """ Calculate savings and project bills with commissioning date considered. For one utility type Attributes: - is_manual_input (boolean): lable indicating is bill input by scraper or manual. Yes == manual input + is_manual_input (boolean): label indicating is bill input by scraper or manual. Yes == manual input commission_date (date): the date that construction work finished, saving starts at NEXT month proforma_date (list): list of dates, months of pro-forma time period proforma_usage (list) : list of monthly usage, for pro-forma period @@ -46,7 +46,7 @@ class Saving(): """ Initiate Saving object. Args: - is_manual_input (boolean): lable indicating is bill input by scraper or manual. Yes == manual input + is_manual_input (boolean): label indicating is bill input by scraper or manual. Yes == manual input commission_date (date): the date that construction work finished, saving starts at NEXT month proforma_date (list): list of dates, months of pro-forma time period """ @@ -75,7 +75,7 @@ class Saving(): prior_saving_charge (list): list of float, charge without (prior to) savings post_saving_charge (list): list of float, charge with (post to) savings """ - if self.is_manual_input is False: + if not self.is_manual_input: saving_start_date = datetime.date( self.commission_date.year, self.commission_date.month, @@ -130,7 +130,7 @@ class Saving(): annual_charge (dictionary): dict of annual prior saving charge, {2014: 100, ...}, with future estimated percent_saving (float): percent of saving, 0.030 == 3.0% """ - if self.is_manual_input is True: + if self.is_manual_input: annual_saving_dict = {} annual_charge_dict = {} proforma_year = form_year_calendar(self.proforma_date[0], self.proforma_date[-1]) @@ -155,7 +155,7 @@ class Saving(): def get_proforma_charge(self): """ - Get proforma_charge, for monthly pro-forma, not available for annual + Get proforma_charge, for monthly pro-forma, not availabel for annual Return: list: proforma_charge """ @@ -163,7 +163,7 @@ class Saving(): def get_proforma_saving_charge(self): """ - Get proforma_saving_charge, for monthly pro-forma, not available for annual + Get proforma_saving_charge, for monthly pro-forma, not availabel for annual Return: list: proforma_saving_charge """ @@ -269,13 +269,13 @@ class Saving_Overview(): prior_month_bill = bill.get_prior_proj_rough() utility_saving = Saving(is_manual_input, self.commission_date, self.proforma_date) - if utility_saving.is_manual_input is True: # bill is from manual input, monthly bill not available + if utility_saving.is_manual_input: # bill is from manual input, monthly bill not availabel utility_saving.put_annual_proforma(bill.get_annual_bill(), percent_saving_dict[utility]) - if utility_saving.is_manual_input is False: # bill is from scraper, monthly bill is available - if full_saving_dict[utility] is None: # engineering saving is not available, use percentage saving + if not utility_saving.is_manual_input: # bill is from scraper, monthly bill is availabel + if full_saving_dict[utility] is None: # engineering saving is not availabel, use percentage saving post_month_bill = bill_post_proj_rough(prior_month_bill, percent_saving_dict[utility]) - # else: # engineering saving is available. Project monthly_bill with regression. not finished yet + # else: # engineering saving is availabel. Project monthly_bill with regression. not finished yet # post_month_bill = bill_proj_reg(prior_utility, full_saving_dict[utility], HDD, CDD, occupancy) utility_saving.put_monthly_proforma( @@ -331,8 +331,7 @@ class Saving_Overview(): } """ post_annual_bill_table = {} - utility_type_list = ['electricity', 'gas', 'oil', 'water'] - for utility_type in utility_type_list: + for utility_type in UTILITY_TYPE_LIST: post_annual_bill_table[utility_type] = copy.deepcopy( self.utility_saving_dict[utility_type].get_annual_proforma_charge()) return post_annual_bill_table diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index fb2ad95..e662038 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -78,9 +78,9 @@ e_raw_bill = { raw_bill_table = { 'electricity': e_raw_bill, - 'gas': None, - 'oil': None, - 'water': None, + # 'gas': None, + # 'oil': None, + # 'water': None, } diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index b6261c7..e38d770 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -11,7 +11,7 @@ from bpfin.utilbills.inflation_sample_data import inflation_coeff_dict from bpfin.tests.testdata import feature_data as db -utility_type_list = ['electricity', 'gas', 'oil', 'water'] +UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] def validate_raw_bill(raw_bill): @@ -190,7 +190,7 @@ class Bill(): def form_bill_list_month(raw_bill_table, analysis_date): bill_list = [] - for utility in utility_type_list: + for utility in UTILITY_TYPE_LIST: current_bill = Bill(utility, analysis_date) if utility in raw_bill_table: current_bill.put_month_bill(raw_bill_table[utility]) @@ -209,68 +209,68 @@ def form_annual_bill_table(bill_list, raw_annual_bill_table): # **** Bill_Table class is not needed **** -# class Bill_Table(): -# """ -# """ -# def __init__(self, raw_bill_table, analysis_date): -# """ -# """ -# self.analysis_date = analysis_date -# self.raw_bill_table = raw_bill_table -# self.manual_input_dict = {} -# self.raw_annual_bill_table = None - -# self.utility_bill_dict = {} # dict of object -# for utility_type in utility_type_list: -# self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) -# if raw_bill_table[utility_type] is not None: -# self.utility_bill_dict[utility_type].put_month_bill(raw_bill_table[utility_type]) -# self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) - -# def put_annual_bill_table(self, raw_annual_bill_table): -# """ -# """ -# self.raw_annual_bill_table = raw_annual_bill_table -# for utility_type in utility_type_list: -# self.utility_bill_dict[utility_type].put_annual_bill(raw_annual_bill_table[utility_type]) - -# def get_bill_overview(self): -# """ -# should be deleted or replaced when future calling func got updated -# because all functions would need to use either the bill or the yes/no flag. -# """ -# bill_overview = {} -# for utility_type in utility_type_list: -# bill_overview[utility_type] = [ -# ( -# self.raw_annual_bill_table[utility_type] if -# self.utility_bill_dict[utility_type].is_manual_input is True -# else self.utility_bill_dict[utility_type].get_annual_bill() -# ), -# self.utility_bill_dict[utility_type].is_manual_input -# ] -# return bill_overview - -# def get_annual_bill_table(self): -# """ -# Annual bill table, for 4 utility types, with annualized projected charge, or average annual charge -# sample_data.bill_overview_organized -# """ -# annual_bill_table = {} -# for utility_type in utility_type_list: -# annual_bill_table[utility_type] = self.utility_bill_dict[utility_type].get_annual_bill() -# return annual_bill_table - -# def get_bill_list(self): -# """ -# Get list of Bill objects. For future use in other functions -# Return: -# list: list of Bill objects. Order in 'electricity', 'gas', 'oil', 'water' -# """ -# bill_list = [] -# for utility_type in utility_type_list: -# bill_list.append(copy.deepcopy(self.utility_bill_dict[utility_type])) -# return bill_list +class Bill_Table(): + """ + """ + def __init__(self, raw_bill_table, analysis_date): + """ + """ + self.analysis_date = analysis_date + self.raw_bill_table = raw_bill_table + self.manual_input_dict = {} + self.raw_annual_bill_table = None + + self.utility_bill_dict = {} # dict of object + for utility_type in UTILITY_TYPE_LIST: + self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) + if raw_bill_table[utility_type] is not None: + self.utility_bill_dict[utility_type].put_month_bill(raw_bill_table[utility_type]) + self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) + + def put_annual_bill_table(self, raw_annual_bill_table): + """ + """ + self.raw_annual_bill_table = raw_annual_bill_table + for utility_type in UTILITY_TYPE_LIST: + self.utility_bill_dict[utility_type].put_annual_bill(raw_annual_bill_table[utility_type]) + + def get_bill_overview(self): + """ + should be deleted or replaced when future calling func got updated + because all functions would need to use either the bill or the yes/no flag. + """ + bill_overview = {} + for utility_type in UTILITY_TYPE_LIST: + bill_overview[utility_type] = [ + ( + self.raw_annual_bill_table[utility_type] if + self.utility_bill_dict[utility_type].is_manual_input is True + else self.utility_bill_dict[utility_type].get_annual_bill() + ), + self.utility_bill_dict[utility_type].is_manual_input + ] + return bill_overview + + def get_annual_bill_table(self): + """ + Annual bill table, for 4 utility types, with annualized projected charge, or average annual charge + sample_data.bill_overview_organized + """ + annual_bill_table = {} + for utility_type in UTILITY_TYPE_LIST: + annual_bill_table[utility_type] = self.utility_bill_dict[utility_type].get_annual_bill() + return annual_bill_table + + def get_bill_list(self): + """ + Get list of Bill objects. For future use in other functions + Return: + list: list of Bill objects. Order in 'electricity', 'gas', 'oil', 'water' + """ + bill_list = [] + for utility_type in UTILITY_TYPE_LIST: + bill_list.append(copy.deepcopy(self.utility_bill_dict[utility_type])) + return bill_list -- GitLab From 13f30091bd9dcb2c0db03af437abb0565b7c06cd Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Mon, 15 May 2017 11:52:11 -0400 Subject: [PATCH 20/30] Update Bill and Financial_Saving according to comments --- bpfin/financials/financial_lib.py | 2 +- bpfin/financials/financial_saving.py | 33 ++++++++++++++++++---------- bpfin/utilbills/bill.py | 14 +++++++----- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/bpfin/financials/financial_lib.py b/bpfin/financials/financial_lib.py index 67dda03..796c5ed 100644 --- a/bpfin/financials/financial_lib.py +++ b/bpfin/financials/financial_lib.py @@ -31,7 +31,7 @@ def organize_bill_overview(bill_overview, analysis_date): average_bill_dict = {} for i in range(4): - if bill_dict[i + 1][1] is True: + if bill_dict[i + 1][1]: sum_bill = 0 year_number = 0 for year in bill_dict[i + 1][0]: diff --git a/bpfin/financials/financial_saving.py b/bpfin/financials/financial_saving.py index d13c539..77cadf2 100644 --- a/bpfin/financials/financial_saving.py +++ b/bpfin/financials/financial_saving.py @@ -24,15 +24,19 @@ class Saving(): commission_date (date): the date that construction work finished, saving starts at NEXT month proforma_date (list): list of dates, months of pro-forma time period proforma_usage (list) : list of monthly usage, for pro-forma period + calculated by put_monthly_proforma() Data before commissioning date == historical Data after commissioning date == post saving data proforma_charge (list) : list of monthly charge, for pro-forma period - Data before commissioning date == historical - Data after commissioning date == post saving data + calculated by put_monthly_proforma() + Data before commissioning date == historical + Data after commissioning date == post saving data proforma_saving_usage (list) : monthly savings on usage, for pro-forma period - Data before commissioning date == historical - Data after commissioning date == post saving data + calculated by put_monthly_proforma() + Data before commissioning date == historical + Data after commissioning date == post saving data proforma_saving_charge (list) : monthly savings on usage, for pro-forma period + calculated by put_monthly_proforma() Data before commissioning date == historical Data after commissioning date == post saving data first_year_saving_usage (float) : first 12 months saving on usage. @@ -121,6 +125,8 @@ class Saving(): self.annual_proforma_charge = annualizing_projection(self.proforma_date, self.proforma_charge) self.first_year_prior_charge = first_year_prior_charge self.percent_saving = first_year_saving_charge / first_year_prior_charge + else: + raise ValueError('Put_monthly_saving, please check: manual_input ==', self.is_manual_input) def put_annual_proforma(self, annual_charge, percent_saving): """ @@ -152,6 +158,8 @@ class Saving(): self.first_year_prior_charge = annual_charge[first_year] self.percent_saving = ( annual_saving_dict[first_year] / annual_charge[first_year] if annual_charge[first_year] != 0 else 0) + else: + raise ValueError('Put_annual_saving, please check: manual_input ==', self.is_manual_input) def get_proforma_charge(self): """ @@ -210,12 +218,12 @@ class Saving_Overview(): Saving_Overview should be scenario based, it is an "attribute" of a scenario Attributes: - bill_list (list): list of Bill objects + bill_list (list): list of Bill objects, each element is a Bill class object. Contains 4 utilities # manual_input_dict(dictionary): key is utility type, value is boolean. True == mannual_input - prior_annual_bill_table (dictionary): annual bill, for 4 utility_types + prior_annual_bill_table (dictionary): annual bill, for 4 utility_types. See description for detail proforma_date (list): list of dates, months of pro-forma time period commission_date (date): the date that construction work finished, saving starts at NEXT month - utility_saving_dict (dictionary): dict of Saving objects, for 4 utility types + utility_saving_dict (dictionary): dict of objects, each element is a Saving class object. Contains 4 utilities saving_percent_charge (dictionary): key is utility type, value is float {'electricity': float} total_first_year_saving (float): dollar saving for the first 12 month after commissioning total_saving_percent (float): percentage saving on charge for the first 12 month after commissioning @@ -224,9 +232,6 @@ class Saving_Overview(): prior_annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, ...} electricity_bill = {2014: 100, 2015:200, ...} """ - prior_annual_bill_table = {} - utility_saving_dict = {'electricity': None, 'gas': None, 'oil': None, 'water': None} - saving_percent_charge = {'electricity': None, 'gas': None, 'oil': None, 'water': None} def __init__( self, @@ -243,8 +248,12 @@ class Saving_Overview(): analysis_date (dictionary): proforma's starting date and the years of proforma commission_date (date): the date that construction work finished, saving starts at NEXT month """ - self.bill_list = copy.deepcopy(bill_list) - for bill in copy.deepcopy(bill_list): + self.prior_annual_bill_table = {} + self.utility_saving_dict = {'electricity': None, 'gas': None, 'oil': None, 'water': None} + self.saving_percent_charge = {'electricity': None, 'gas': None, 'oil': None, 'water': None} + + self.bill_list = bill_list + for bill in bill_list: self.prior_annual_bill_table[bill.utility_type] = bill.get_annual_bill() self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] self.commission_date = commission_date diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index e38d770..7a797fa 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -26,7 +26,7 @@ def validate_raw_bill(raw_bill): Return: Bollean: True == data validated. False == data is not validated """ - if raw_bill is not None: + if raw_bill: month_list = form_date_calendar( raw_bill['date_from'][0], raw_bill['date_to'][-1])[1] @@ -52,7 +52,7 @@ def estimate_annual_bill(raw_annual_bill, analysis_date): """ proforma_year = form_bill_year(analysis_date['proforma_start'], analysis_date['proforma_duration']) - if raw_annual_bill is None: + if not raw_annual_bill: average_charge = 0 annual_bill_dict = {} else: @@ -136,7 +136,7 @@ class Bill(): 'usage', list of float, 'charge', list of float, """ - if validate_raw_bill(raw_bill) is True: + if validate_raw_bill(raw_bill): self.monthly_normalized_bill_rough = bill_month_normalize_rough(self.utility_type, copy.deepcopy(raw_bill)) self.is_manual_input = False self.prior_bill_rough = bill_prior_proj_rough( @@ -146,6 +146,8 @@ class Bill(): inflation_coeff_dict ) self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) + else: + raise ValueError('Please load a valid bill for ', self.utility_type) def put_annual_bill(self, raw_annual_bill): """ @@ -155,7 +157,7 @@ class Bill(): Args: raw_annual_bill (dictionary): {year: float}. annual bill allowing some years data missing """ - if self.is_manual_input is True: + if self.is_manual_input: self.annual_bill = estimate_annual_bill(raw_annual_bill, self.analysis_date) def get_prior_proj_rough(self): @@ -223,7 +225,7 @@ class Bill_Table(): self.utility_bill_dict = {} # dict of object for utility_type in UTILITY_TYPE_LIST: self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) - if raw_bill_table[utility_type] is not None: + if raw_bill_table[utility_type]: self.utility_bill_dict[utility_type].put_month_bill(raw_bill_table[utility_type]) self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) @@ -244,7 +246,7 @@ class Bill_Table(): bill_overview[utility_type] = [ ( self.raw_annual_bill_table[utility_type] if - self.utility_bill_dict[utility_type].is_manual_input is True + self.utility_bill_dict[utility_type].is_manual_input else self.utility_bill_dict[utility_type].get_annual_bill() ), self.utility_bill_dict[utility_type].is_manual_input -- GitLab From ba09549e223b555bc8d0568d0f4690e41beda8cb Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Mon, 15 May 2017 16:05:13 -0400 Subject: [PATCH 21/30] Improve Bill_Table, create test_financial_saving --- bpfin/financials/financial_saving.py | 2 + .../test_financials/test_financial_saving.py | 142 +++++++++++++++++ bpfin/tests/testdata/feature_data.py | 144 +++++++++++++++++- bpfin/utilbills/bill.py | 48 +++--- 4 files changed, 305 insertions(+), 31 deletions(-) create mode 100644 bpfin/tests/test_financials/test_financial_saving.py diff --git a/bpfin/financials/financial_saving.py b/bpfin/financials/financial_saving.py index 77cadf2..a3d78ad 100644 --- a/bpfin/financials/financial_saving.py +++ b/bpfin/financials/financial_saving.py @@ -247,6 +247,8 @@ class Saving_Overview(): bill_list (list): list of Bill objects analysis_date (dictionary): proforma's starting date and the years of proforma commission_date (date): the date that construction work finished, saving starts at NEXT month + + To Do: validate bill_list """ self.prior_annual_bill_table = {} self.utility_saving_dict = {'electricity': None, 'gas': None, 'oil': None, 'water': None} diff --git a/bpfin/tests/test_financials/test_financial_saving.py b/bpfin/tests/test_financials/test_financial_saving.py new file mode 100644 index 0000000..a8de76b --- /dev/null +++ b/bpfin/tests/test_financials/test_financial_saving.py @@ -0,0 +1,142 @@ +from datetime import date +from bpfin.financials.financial_saving import Saving +from bpfin.financials.financial_saving import Saving_Overview +from bpfin.tests.testdata import feature_data as fdb + + +def test_Saving_put_monthly_proforma(): + input_proforma_date = [ + date(2017, 1, 31), + date(2017, 2, 28), + date(2017, 3, 31), + date(2017, 4, 30), + date(2017, 5, 31), + date(2017, 6, 30), + date(2017, 7, 31), + date(2017, 8, 31), + date(2017, 9, 30), + date(2017, 10, 31), + date(2017, 11, 30), + date(2017, 12, 31)] + input_commissioning_date = date(2017, 3, 14) + input_prior_charge = [100] * 12 + input_post_charge = [100, 90, 80, 70, 60, 50, 40, 30, 40, 50, 60, 70] + input_prior_usage = [15] * 12 + input_post_usage = [10, 9, 8, 7, 6, 5, 4, 3, 4, 5, 6, 7] + output_saving = [0, 0, 0, 30, 40, 50, 60, 70, 60, 50, 40, 30] + output_charge = [100, 100, 100, 70, 60, 50, 40, 30, 40, 50, 60, 70] + output_overall_saving = 0.4777777777777778 + + elec_saving = Saving(False, input_commissioning_date, input_proforma_date) + elec_saving.put_monthly_proforma(input_prior_usage, input_post_usage, input_prior_charge, input_post_charge) + result_saving = elec_saving.get_proforma_saving_charge() + result_charge = elec_saving.get_proforma_charge() + + assert output_saving == result_saving + assert output_charge == result_charge + assert elec_saving.get_first_year_saving_charge() == 430 + assert elec_saving.get_annual_proforma_charge() == {2017: 770} + assert elec_saving.get_percent_saving() == output_overall_saving + + +def test_Saving_put_annual_proforma(): + input_proforma_date = [ + date(2017, 1, 31), + date(2017, 2, 28), + date(2017, 3, 31), + date(2017, 4, 30), + date(2017, 5, 31), + date(2017, 6, 30), + date(2017, 7, 31), + date(2017, 8, 31), + date(2017, 9, 30), + date(2017, 10, 31), + date(2017, 11, 30), + date(2017, 12, 31), + date(2018, 1, 31), + date(2018, 2, 28), + date(2018, 3, 31), + date(2018, 4, 30), + date(2018, 5, 31), + date(2018, 6, 30), + date(2018, 7, 31), + date(2018, 8, 31), + date(2018, 9, 30), + date(2018, 10, 31), + date(2018, 11, 30), + date(2018, 12, 31)] + input_commissioning_date = date(2017, 3, 14) + input_annual_charge = {2017: 100, 2018: 100} + input_percent_saving = 0.20 + output_first_year_saving_charge = 20 + output_annual_proforma_charge = {2017: 85, 2018: 80} + output_overall_saving = 0.20 + + elec_saving = Saving(True, input_commissioning_date, input_proforma_date) + elec_saving.put_annual_proforma(input_annual_charge, input_percent_saving) + + result_first_year_saving_charge = elec_saving.get_first_year_saving_charge() + result_annual_proforma_charge = elec_saving.get_annual_proforma_charge() + result_overall_saving = elec_saving.get_percent_saving() + + assert result_first_year_saving_charge == output_first_year_saving_charge + assert result_annual_proforma_charge == output_annual_proforma_charge + assert result_overall_saving == output_overall_saving + + +def test_Saving_Overview(): + input_bill_list = fdb.bill_list + input_analysis_date = fdb.analysis_date + input_commissioning_date = fdb.commission_date + + input_percent_saving_dict = fdb.percent_saving_dict + input_full_saving_dict = fdb.full_saving_dict + + output_first_year_saving = 1502.6386560837202 + output_simple_payback = 33.274799498579 + output_total_saving_percent = 0.37312045887352324 + output_electricity_annual_saving_charge = { + 2012: 0.0, + 2013: 0.0, + 2014: 0.0, + 2015: 0.0, + 2016: 0.0, + 2017: 251.64464541815892, + 2018: 357.48478459061994, + 2019: 366.28049606569721, + 2020: 374.86074424735079, + 2021: 382.88611271044329, + 2022: 390.15410099072778, + 2023: 397.27620075339473, + 2024: 404.54734630353755, + 2025: 412.06528286992216, + 2026: 420.14359538042595, + 2027: 428.86477571357136, + 2028: 437.81793503683912, + 2029: 446.78365645196686, + 2030: 455.7489720441697, + 2031: 464.74135726047848, + 2032: 473.83322571714552, + 2033: 483.13050892569618, + 2034: 492.67630130304224, + 2035: 502.48247134137955, + 2036: 512.55020795318637 + } + output_post_annual_bill_table = fdb.post_annual_bill_table + saving_overview = Saving_Overview( + input_bill_list, + input_analysis_date, + input_commissioning_date) + + saving_overview.put_saving(input_percent_saving_dict, input_full_saving_dict) + result_first_year_saving = saving_overview.get_total_first_year_saving() + result_simple_payback = saving_overview.get_simple_payback(50000) + result_total_saving_percent = saving_overview.get_total_saving_percent() + result_electricity_annual_saving_charge = saving_overview.get_utility_annual_saving_charge('electricity') + result_post_annual_bill_table = saving_overview.get_post_annual_bill_table() + + assert result_first_year_saving == output_first_year_saving + assert result_simple_payback == output_simple_payback + assert result_total_saving_percent == output_total_saving_percent + assert result_electricity_annual_saving_charge == output_electricity_annual_saving_charge + assert result_post_annual_bill_table == output_post_annual_bill_table diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index e662038..5d36ebd 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -1,7 +1,8 @@ import datetime from datetime import date -from bpfin.utilbills.bill import form_bill_list_month -from bpfin.utilbills.bill import form_annual_bill_table +# from bpfin.utilbills.bill import form_bill_list_month +# from bpfin.utilbills.bill import form_annual_bill_table +from bpfin.utilbills.bill import Bill_Table import pprint # pro-forma projection time base @@ -96,13 +97,140 @@ raw_annual_bill_table = { } -# bill_table = Bill_Table(raw_bill_table, analysis_date) -# bill_table.put_annual_bill_table(raw_annual_bill_table) -# bill_list = bill_table.get_bill_list() -bill_list = form_bill_list_month(raw_bill_table, analysis_date) -annual_bill_table = form_annual_bill_table(bill_list, raw_annual_bill_table) +bill_table = Bill_Table(raw_bill_table, analysis_date) +bill_table.put_annual_bill_table(raw_annual_bill_table) +bill_list = bill_table.get_bill_list() + + +# saving scenarios +percent_saving_dict = { + 'electricity': 0.25, + 'gas': 0.10, + 'oil': 0.80, + 'water': 0.0 +} + +full_saving_dict = { + 'electricity': None, # dict of engineering analysis, {'date_from': [], 'date_to': [], 'heating': [], 'cooling': [], 'other': []} + 'gas': None, + 'oil': None, + 'water': None +} + +post_annual_bill_table = { + 'electricity': { + 2012: 1302.3894189123205, + 2013: 1325.5398892097569, + 2014: 1346.0795010524571, + 2015: 1357.4370596899435, + 2016: 1368.4645719731475, + 2017: 1143.5987943889527, + 2018: 1072.4543537718594, + 2019: 1098.8414881970916, + 2020: 1124.5822327420522, + 2021: 1148.6583381313299, + 2022: 1170.4623029721829, + 2023: 1191.8286022601849, + 2024: 1213.6420389106127, + 2025: 1236.195848609766, + 2026: 1260.4307861412767, + 2027: 1286.5943271407125, + 2028: 1313.453805110518, + 2029: 1340.3509693559004, + 2030: 1367.2469161325098, + 2031: 1394.224071781435, + 2032: 1421.4996771514368, + 2033: 1449.3915267770883, + 2034: 1478.0289039091267, + 2035: 1507.4474140241387, + 2036: 1537.6506238595582 + }, + 'gas': { + 2012: 1253.3333333333333, + 2013: 1253.3333333333333, + 2014: 0, + 2015: 1020, + 2016: 1220, + 2017: 1406.0, + 2018: 1128.0, + 2019: 1128.0, + 2020: 1128.0, + 2021: 1128.0, + 2022: 1128.0, + 2023: 1128.0, + 2024: 1128.0, + 2025: 1128.0, + 2026: 1128.0, + 2027: 1128.0, + 2028: 1128.0, + 2029: 1128.0, + 2030: 1128.0, + 2031: 1128.0, + 2032: 1128.0, + 2033: 1128.0, + 2034: 1128.0, + 2035: 1128.0, + 2036: 1128.0 + }, + 'oil': { + 2012: 1243.3333333333333, + 2013: 1243.3333333333333, + 2014: 1243.3333333333333, + 2015: 1010, + 2016: 1210, + 2017: 603.9999999999999, + 2018: 248.6666666666666, + 2019: 248.6666666666666, + 2020: 248.6666666666666, + 2021: 248.6666666666666, + 2022: 248.6666666666666, + 2023: 248.6666666666666, + 2024: 248.6666666666666, + 2025: 248.6666666666666, + 2026: 248.6666666666666, + 2027: 248.6666666666666, + 2028: 248.6666666666666, + 2029: 248.6666666666666, + 2030: 248.6666666666666, + 2031: 248.6666666666666, + 2032: 248.6666666666666, + 2033: 248.6666666666666, + 2034: 248.6666666666666, + 2035: 248.6666666666666, + 2036: 248.6666666666666 + }, + 'water': { + 2012: 0, + 2013: 0, + 2014: 0, + 2015: 0, + 2016: 0, + 2017: 0.0, + 2018: 0.0, + 2019: 0.0, + 2020: 0.0, + 2021: 0.0, + 2022: 0.0, + 2023: 0.0, + 2024: 0.0, + 2025: 0.0, + 2026: 0.0, + 2027: 0.0, + 2028: 0.0, + 2029: 0.0, + 2030: 0.0, + 2031: 0.0, + 2032: 0.0, + 2033: 0.0, + 2034: 0.0, + 2035: 0.0, + 2036: 0.0 + } +} +# bill_list = form_bill_list_month(raw_bill_table, analysis_date) +# annual_bill_table = form_annual_bill_table(bill_list, raw_annual_bill_table) # print(bill_list[0].get_prior_proj_rough()) -print(annual_bill_table) +# print(annual_bill_table) # e_raw_bill['charge'].reverse() # pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 7a797fa..b764320 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -190,27 +190,26 @@ class Bill(): return self.annual_bill -def form_bill_list_month(raw_bill_table, analysis_date): - bill_list = [] - for utility in UTILITY_TYPE_LIST: - current_bill = Bill(utility, analysis_date) - if utility in raw_bill_table: - current_bill.put_month_bill(raw_bill_table[utility]) - bill_list.append(current_bill) - return bill_list - - -def form_annual_bill_table(bill_list, raw_annual_bill_table): - bill_list_anual = copy.deepcopy(bill_list) - annual_bill_table = {} - for current_bill in bill_list_anual: - if current_bill.utility_type in raw_annual_bill_table: - current_bill.put_annual_bill(raw_annual_bill_table[current_bill.utility_type]) - annual_bill_table[current_bill.utility_type] = current_bill.get_annual_bill() - return annual_bill_table - - -# **** Bill_Table class is not needed **** +# def form_bill_list_month(raw_bill_table, analysis_date): +# bill_list = [] +# for utility in UTILITY_TYPE_LIST: +# current_bill = Bill(utility, analysis_date) +# if utility in raw_bill_table: +# current_bill.put_month_bill(raw_bill_table[utility]) +# bill_list.append(current_bill) +# return bill_list + + +# def form_annual_bill_table(bill_list, raw_annual_bill_table): +# bill_list_anual = copy.deepcopy(bill_list) +# annual_bill_table = {} +# for current_bill in bill_list_anual: +# if current_bill.utility_type in raw_annual_bill_table: +# current_bill.put_annual_bill(raw_annual_bill_table[current_bill.utility_type]) +# annual_bill_table[current_bill.utility_type] = current_bill.get_annual_bill() +# return annual_bill_table + + class Bill_Table(): """ """ @@ -225,8 +224,10 @@ class Bill_Table(): self.utility_bill_dict = {} # dict of object for utility_type in UTILITY_TYPE_LIST: self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) - if raw_bill_table[utility_type]: + if utility_type in raw_bill_table: self.utility_bill_dict[utility_type].put_month_bill(raw_bill_table[utility_type]) + # else: + # self.utility_bill_dict[utility_type].put_month_bill(None) self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) def put_annual_bill_table(self, raw_annual_bill_table): @@ -234,7 +235,8 @@ class Bill_Table(): """ self.raw_annual_bill_table = raw_annual_bill_table for utility_type in UTILITY_TYPE_LIST: - self.utility_bill_dict[utility_type].put_annual_bill(raw_annual_bill_table[utility_type]) + if utility_type in self.raw_annual_bill_table: + self.utility_bill_dict[utility_type].put_annual_bill(raw_annual_bill_table[utility_type]) def get_bill_overview(self): """ -- GitLab From 3d6c1efee7f5c417bccc8bad1647aafe8cfc96e5 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Mon, 15 May 2017 16:23:19 -0400 Subject: [PATCH 22/30] Create test_bill file --- .../tests/test_financials/test_financial_saving.py | 14 +++++++------- bpfin/tests/test_utilbills/test_bill.py | 10 ++++++++++ bpfin/tests/testdata/feature_data.py | 3 ++- bpfin/utilbills/bill.py | 10 +++++++--- 4 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 bpfin/tests/test_utilbills/test_bill.py diff --git a/bpfin/tests/test_financials/test_financial_saving.py b/bpfin/tests/test_financials/test_financial_saving.py index a8de76b..22e49e9 100644 --- a/bpfin/tests/test_financials/test_financial_saving.py +++ b/bpfin/tests/test_financials/test_financial_saving.py @@ -1,7 +1,7 @@ from datetime import date from bpfin.financials.financial_saving import Saving from bpfin.financials.financial_saving import Saving_Overview -from bpfin.tests.testdata import feature_data as fdb +from bpfin.tests.testdata import feature_data as db def test_Saving_put_monthly_proforma(): @@ -85,12 +85,12 @@ def test_Saving_put_annual_proforma(): def test_Saving_Overview(): - input_bill_list = fdb.bill_list - input_analysis_date = fdb.analysis_date - input_commissioning_date = fdb.commission_date + input_bill_list = db.bill_list + input_analysis_date = db.analysis_date + input_commissioning_date = db.commission_date - input_percent_saving_dict = fdb.percent_saving_dict - input_full_saving_dict = fdb.full_saving_dict + input_percent_saving_dict = db.percent_saving_dict + input_full_saving_dict = db.full_saving_dict output_first_year_saving = 1502.6386560837202 output_simple_payback = 33.274799498579 @@ -122,7 +122,7 @@ def test_Saving_Overview(): 2035: 502.48247134137955, 2036: 512.55020795318637 } - output_post_annual_bill_table = fdb.post_annual_bill_table + output_post_annual_bill_table = db.post_annual_bill_table saving_overview = Saving_Overview( input_bill_list, input_analysis_date, diff --git a/bpfin/tests/test_utilbills/test_bill.py b/bpfin/tests/test_utilbills/test_bill.py new file mode 100644 index 0000000..ec4ba06 --- /dev/null +++ b/bpfin/tests/test_utilbills/test_bill.py @@ -0,0 +1,10 @@ +from datetime import date +from bpfin.utilbills.bill import Bill_Table +from bpfin.tests.testdata import feature_data as fdb + + +def test_Bill_Table(): + + bill_table = Bill_Table(raw_bill_table, analysis_date) + bill_table.put_annual_bill_table(raw_annual_bill_table) + bill_list = bill_table.get_bill_list() diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 5d36ebd..f4aba87 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -97,7 +97,8 @@ raw_annual_bill_table = { } -bill_table = Bill_Table(raw_bill_table, analysis_date) +bill_table = Bill_Table(analysis_date) +bill_table.put_month_bill_table(raw_bill_table) bill_table.put_annual_bill_table(raw_annual_bill_table) bill_list = bill_table.get_bill_list() diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index b764320..ad980a2 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -213,17 +213,21 @@ class Bill(): class Bill_Table(): """ """ - def __init__(self, raw_bill_table, analysis_date): + def __init__(self, analysis_date): """ """ self.analysis_date = analysis_date - self.raw_bill_table = raw_bill_table + self.raw_bill_table = None self.manual_input_dict = {} self.raw_annual_bill_table = None self.utility_bill_dict = {} # dict of object + + def put_month_bill_table(self, raw_bill_table): + """ + """ for utility_type in UTILITY_TYPE_LIST: - self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) + self.utility_bill_dict[utility_type] = Bill(utility_type, self.analysis_date) if utility_type in raw_bill_table: self.utility_bill_dict[utility_type].put_month_bill(raw_bill_table[utility_type]) # else: -- GitLab From d133be819edb0da9609c2f297a32ee657a6c29b6 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Mon, 15 May 2017 16:56:05 -0400 Subject: [PATCH 23/30] create test files for Bill_Table --- bpfin/tests/test_utilbills/test_bill.py | 47 ++++- bpfin/tests/testdata/data_generation.py | 84 +++++++++ bpfin/tests/testdata/feature_data.py | 241 +++++++++++++++++++++++- bpfin/utilbills/bill.py | 15 +- 4 files changed, 370 insertions(+), 17 deletions(-) diff --git a/bpfin/tests/test_utilbills/test_bill.py b/bpfin/tests/test_utilbills/test_bill.py index ec4ba06..1359fc0 100644 --- a/bpfin/tests/test_utilbills/test_bill.py +++ b/bpfin/tests/test_utilbills/test_bill.py @@ -1,10 +1,47 @@ from datetime import date from bpfin.utilbills.bill import Bill_Table -from bpfin.tests.testdata import feature_data as fdb +from bpfin.tests.testdata import feature_data as db -def test_Bill_Table(): +def test_Bill_Table_put_month(): + """ + test Bill_Table with only inputing month bill + """ + input_raw_bill_table = db.raw_bill_table + input_analysis_date = db.analysis_date - bill_table = Bill_Table(raw_bill_table, analysis_date) - bill_table.put_annual_bill_table(raw_annual_bill_table) - bill_list = bill_table.get_bill_list() + bill_table = Bill_Table(input_analysis_date) + bill_table.put_month_bill_table(input_raw_bill_table) + + output_annual_bill_table = db.annual_bill_table_month_only + result_annual_bill_table = bill_table.get_annual_bill_table() + assert result_annual_bill_table == output_annual_bill_table + + +def test_Bill_Table_put_annual(): + """ + test Bill_Table with only inputing annual bill + """ + input_raw_annual_bill_table = db.raw_annual_bill_table + input_analysis_date = db.analysis_date + + bill_table = Bill_Table(input_analysis_date) + bill_table.put_annual_bill_table(input_raw_annual_bill_table) + + output_annual_bill_table = db.annual_bill_table_annual_only + result_annual_bill_table = bill_table.get_annual_bill_table() + assert result_annual_bill_table == output_annual_bill_table + + +def test_Bill_Table_put_both(): + input_analysis_date = db.analysis_date + input_raw_annual_bill_table = db.raw_annual_bill_table + input_raw_bill_table = db.raw_bill_table + + bill_table = Bill_Table(input_analysis_date) + bill_table.put_month_bill_table(input_raw_bill_table) + bill_table.put_annual_bill_table(input_raw_annual_bill_table) + + output_annual_bill_table = db.prior_annual_bill + result_annual_bill_table = bill_table.get_annual_bill_table() + assert result_annual_bill_table == output_annual_bill_table diff --git a/bpfin/tests/testdata/data_generation.py b/bpfin/tests/testdata/data_generation.py index e69de29..71343ae 100644 --- a/bpfin/tests/testdata/data_generation.py +++ b/bpfin/tests/testdata/data_generation.py @@ -0,0 +1,84 @@ +{ + 'electricity': None, + 'gas': { + 2014: 0, + 2015: 1020, + 2016: 1220, + 2017: 1520, + 2012: 1253.3333333333333, + 2013: 1253.3333333333333, + 2018: 1253.3333333333333, + 2019: 1253.3333333333333, + 2020: 1253.3333333333333, + 2021: 1253.3333333333333, + 2022: 1253.3333333333333, + 2023: 1253.3333333333333, + 2024: 1253.3333333333333, + 2025: 1253.3333333333333, + 2026: 1253.3333333333333, + 2027: 1253.3333333333333, + 2028: 1253.3333333333333, + 2029: 1253.3333333333333, + 2030: 1253.3333333333333, + 2031: 1253.3333333333333, + 2032: 1253.3333333333333, + 2033: 1253.3333333333333, + 2034: 1253.3333333333333, + 2035: 1253.3333333333333, + 2036: 1253.3333333333333 + }, + 'oil': { + 2015: 1010, + 2016: 1210, + 2017: 1510, + 2012: 1243.3333333333333, + 2013: 1243.3333333333333, + 2014: 1243.3333333333333, + 2018: 1243.3333333333333, + 2019: 1243.3333333333333, + 2020: 1243.3333333333333, + 2021: 1243.3333333333333, + 2022: 1243.3333333333333, + 2023: 1243.3333333333333, + 2024: 1243.3333333333333, + 2025: 1243.3333333333333, + 2026: 1243.3333333333333, + 2027: 1243.3333333333333, + 2028: 1243.3333333333333, + 2029: 1243.3333333333333, + 2030: 1243.3333333333333, + 2031: 1243.3333333333333, + 2032: 1243.3333333333333, + 2033: 1243.3333333333333, + 2034: 1243.3333333333333, + 2035: 1243.3333333333333, + 2036: 1243.3333333333333 + }, + 'water': { + 2015: 0, + 2016: 0, + 2017: 0, + 2012: 0, + 2013: 0, + 2014: 0, + 2018: 0, + 2019: 0, + 2020: 0, + 2021: 0, + 2022: 0, + 2023: 0, + 2024: 0, + 2025: 0, + 2026: 0, + 2027: 0, + 2028: 0, + 2029: 0, + 2030: 0, + 2031: 0, + 2032: 0, + 2033: 0, + 2034: 0, + 2035: 0, + 2036: 0 + } +} diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index f4aba87..3c70030 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -2,7 +2,7 @@ import datetime from datetime import date # from bpfin.utilbills.bill import form_bill_list_month # from bpfin.utilbills.bill import form_annual_bill_table -from bpfin.utilbills.bill import Bill_Table +# from bpfin.utilbills.bill import Bill_Table import pprint # pro-forma projection time base @@ -97,10 +97,10 @@ raw_annual_bill_table = { } -bill_table = Bill_Table(analysis_date) -bill_table.put_month_bill_table(raw_bill_table) -bill_table.put_annual_bill_table(raw_annual_bill_table) -bill_list = bill_table.get_bill_list() +# bill_table = Bill_Table(analysis_date) +# bill_table.put_month_bill_table(raw_bill_table) +# bill_table.put_annual_bill_table(raw_annual_bill_table) +# bill_list = bill_table.get_bill_list() # saving scenarios @@ -118,6 +118,237 @@ full_saving_dict = { 'water': None } +prior_annual_bill = { + 'electricity': { + 2012: 1302.3894189123205, + 2013: 1325.5398892097569, + 2014: 1346.0795010524571, + 2015: 1357.4370596899435, + 2016: 1368.4645719731475, + 2017: 1395.2434398071116, + 2018: 1429.9391383624793, + 2019: 1465.1219842627888, + 2020: 1499.442976989403, + 2021: 1531.5444508417731, + 2022: 1560.6164039629107, + 2023: 1589.1048030135796, + 2024: 1618.1893852141502, + 2025: 1648.2611314796882, + 2026: 1680.5743815217027, + 2027: 1715.4591028542839, + 2028: 1751.2717401473571, + 2029: 1787.1346258078672, + 2030: 1822.9958881766795, + 2031: 1858.9654290419135, + 2032: 1895.3329028685823, + 2033: 1932.5220357027845, + 2034: 1970.7052052121689, + 2035: 2009.9298853655182, + 2036: 2050.2008318127446 + }, + 'gas': { + 2014: 0, + 2015: 1020, + 2016: 1220, + 2017: 1520, + 2012: 1253.3333333333333, + 2013: 1253.3333333333333, + 2018: 1253.3333333333333, + 2019: 1253.3333333333333, + 2020: 1253.3333333333333, + 2021: 1253.3333333333333, + 2022: 1253.3333333333333, + 2023: 1253.3333333333333, + 2024: 1253.3333333333333, + 2025: 1253.3333333333333, + 2026: 1253.3333333333333, + 2027: 1253.3333333333333, + 2028: 1253.3333333333333, + 2029: 1253.3333333333333, + 2030: 1253.3333333333333, + 2031: 1253.3333333333333, + 2032: 1253.3333333333333, + 2033: 1253.3333333333333, + 2034: 1253.3333333333333, + 2035: 1253.3333333333333, + 2036: 1253.3333333333333 + }, + 'oil': { + 2015: 1010, + 2016: 1210, + 2017: 1510, + 2012: 1243.3333333333333, + 2013: 1243.3333333333333, + 2014: 1243.3333333333333, + 2018: 1243.3333333333333, + 2019: 1243.3333333333333, + 2020: 1243.3333333333333, + 2021: 1243.3333333333333, + 2022: 1243.3333333333333, + 2023: 1243.3333333333333, + 2024: 1243.3333333333333, + 2025: 1243.3333333333333, + 2026: 1243.3333333333333, + 2027: 1243.3333333333333, + 2028: 1243.3333333333333, + 2029: 1243.3333333333333, + 2030: 1243.3333333333333, + 2031: 1243.3333333333333, + 2032: 1243.3333333333333, + 2033: 1243.3333333333333, + 2034: 1243.3333333333333, + 2035: 1243.3333333333333, + 2036: 1243.3333333333333 + }, + 'water': { + 2015: 0, + 2016: 0, + 2017: 0, + 2012: 0, + 2013: 0, + 2014: 0, + 2018: 0, + 2019: 0, + 2020: 0, + 2021: 0, + 2022: 0, + 2023: 0, + 2024: 0, + 2025: 0, + 2026: 0, + 2027: 0, + 2028: 0, + 2029: 0, + 2030: 0, + 2031: 0, + 2032: 0, + 2033: 0, + 2034: 0, + 2035: 0, + 2036: 0 + } +} + +annual_bill_table_month_only = { + 'electricity': { + 2012: 1302.3894189123205, + 2013: 1325.5398892097569, + 2014: 1346.0795010524571, + 2015: 1357.4370596899435, + 2016: 1368.4645719731475, + 2017: 1395.2434398071116, + 2018: 1429.9391383624793, + 2019: 1465.1219842627888, + 2020: 1499.442976989403, + 2021: 1531.5444508417731, + 2022: 1560.6164039629107, + 2023: 1589.1048030135796, + 2024: 1618.1893852141502, + 2025: 1648.2611314796882, + 2026: 1680.5743815217027, + 2027: 1715.4591028542839, + 2028: 1751.2717401473571, + 2029: 1787.1346258078672, + 2030: 1822.9958881766795, + 2031: 1858.9654290419135, + 2032: 1895.3329028685823, + 2033: 1932.5220357027845, + 2034: 1970.7052052121689, + 2035: 2009.9298853655182, + 2036: 2050.2008318127446 + }, + 'gas': None, + 'oil': None, + 'water': None +} + + +annual_bill_table_annual_only = { + 'electricity': None, + 'gas': { + 2014: 0, + 2015: 1020, + 2016: 1220, + 2017: 1520, + 2012: 1253.3333333333333, + 2013: 1253.3333333333333, + 2018: 1253.3333333333333, + 2019: 1253.3333333333333, + 2020: 1253.3333333333333, + 2021: 1253.3333333333333, + 2022: 1253.3333333333333, + 2023: 1253.3333333333333, + 2024: 1253.3333333333333, + 2025: 1253.3333333333333, + 2026: 1253.3333333333333, + 2027: 1253.3333333333333, + 2028: 1253.3333333333333, + 2029: 1253.3333333333333, + 2030: 1253.3333333333333, + 2031: 1253.3333333333333, + 2032: 1253.3333333333333, + 2033: 1253.3333333333333, + 2034: 1253.3333333333333, + 2035: 1253.3333333333333, + 2036: 1253.3333333333333 + }, + 'oil': { + 2015: 1010, + 2016: 1210, + 2017: 1510, + 2012: 1243.3333333333333, + 2013: 1243.3333333333333, + 2014: 1243.3333333333333, + 2018: 1243.3333333333333, + 2019: 1243.3333333333333, + 2020: 1243.3333333333333, + 2021: 1243.3333333333333, + 2022: 1243.3333333333333, + 2023: 1243.3333333333333, + 2024: 1243.3333333333333, + 2025: 1243.3333333333333, + 2026: 1243.3333333333333, + 2027: 1243.3333333333333, + 2028: 1243.3333333333333, + 2029: 1243.3333333333333, + 2030: 1243.3333333333333, + 2031: 1243.3333333333333, + 2032: 1243.3333333333333, + 2033: 1243.3333333333333, + 2034: 1243.3333333333333, + 2035: 1243.3333333333333, + 2036: 1243.3333333333333 + }, + 'water': { + 2015: 0, + 2016: 0, + 2017: 0, + 2012: 0, + 2013: 0, + 2014: 0, + 2018: 0, + 2019: 0, + 2020: 0, + 2021: 0, + 2022: 0, + 2023: 0, + 2024: 0, + 2025: 0, + 2026: 0, + 2027: 0, + 2028: 0, + 2029: 0, + 2030: 0, + 2031: 0, + 2032: 0, + 2033: 0, + 2034: 0, + 2035: 0, + 2036: 0 + } +} + + post_annual_bill_table = { 'electricity': { 2012: 1302.3894189123205, diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index ad980a2..b6c154e 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -146,8 +146,8 @@ class Bill(): inflation_coeff_dict ) self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) - else: - raise ValueError('Please load a valid bill for ', self.utility_type) + # else: + # raise ValueError('Please load a valid bill for ', self.utility_type) def put_annual_bill(self, raw_annual_bill): """ @@ -220,16 +220,17 @@ class Bill_Table(): self.raw_bill_table = None self.manual_input_dict = {} self.raw_annual_bill_table = None - self.utility_bill_dict = {} # dict of object + for utility_type in UTILITY_TYPE_LIST: + self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) def put_month_bill_table(self, raw_bill_table): """ """ + self.raw_bill_table = raw_bill_table for utility_type in UTILITY_TYPE_LIST: - self.utility_bill_dict[utility_type] = Bill(utility_type, self.analysis_date) - if utility_type in raw_bill_table: - self.utility_bill_dict[utility_type].put_month_bill(raw_bill_table[utility_type]) + if utility_type in self.raw_bill_table: + self.utility_bill_dict[utility_type].put_month_bill(self.raw_bill_table[utility_type]) # else: # self.utility_bill_dict[utility_type].put_month_bill(None) self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) @@ -240,7 +241,7 @@ class Bill_Table(): self.raw_annual_bill_table = raw_annual_bill_table for utility_type in UTILITY_TYPE_LIST: if utility_type in self.raw_annual_bill_table: - self.utility_bill_dict[utility_type].put_annual_bill(raw_annual_bill_table[utility_type]) + self.utility_bill_dict[utility_type].put_annual_bill(self.raw_annual_bill_table[utility_type]) def get_bill_overview(self): """ -- GitLab From ffde9825dc6290b6b0c61eb87e1d84578cf48056 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 17 May 2017 17:51:05 -0400 Subject: [PATCH 24/30] Update Bill class according to recent discuss. Create test file, test data for monthly_bill func --- bpfin/lib/back_end_call.py | 122 ++++++++ .../test_financials/test_financial_saving.py | 254 ++++++++-------- bpfin/tests/test_financials/test_saving.py | 286 +++++++++--------- .../test_utilbills/test_back_end_call.py | 14 + bpfin/tests/test_utilbills/test_bill.py | 70 ++--- bpfin/tests/testdata/feature_data.py | 138 +-------- bpfin/utilbills/bill.py | 179 ++++++----- 7 files changed, 536 insertions(+), 527 deletions(-) create mode 100644 bpfin/lib/back_end_call.py create mode 100644 bpfin/tests/test_utilbills/test_back_end_call.py diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py new file mode 100644 index 0000000..3f49f75 --- /dev/null +++ b/bpfin/lib/back_end_call.py @@ -0,0 +1,122 @@ +from bpfin.utilbills.bill import Bill +from bpfin.tests.testdata import feature_data as db + + +UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] + + +# Bill Overview Monthly Input +def monthly_bill(raw_bill_table, analysis_date): + """ + Take in raw_bill_table + Generate annual bill for utility type with available bill + Generate manual_input dictionary indicating manual input label for 4 utilities + + Args: + raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types + analysis_date (dictionary): proforma's starting date and the years of proforma + + Returns: + bill_overview_dict (dictionary): dictionary of dictionary of annual bills, see description for detail + # manual_input_dict (dictionary): dictionary of boolean values, keys are utilities + + Error Validate: + raw_bill_table, analysis_date == None, Raise empty error + bill_overview_dict == None or missing utility, Raise calculation error + manual_input_dict == None or missing utility, Raise calculation error + Data Validate: + raw_bill == None: no annual bill generated, manual_input == True + raw_bill != None: + raw_bill < 12 months, no annual bill generated, manual_input == True + raw_bill > 12 months, annual bill generated, manual_input == False + + Description: + raw_bill_table = { + 'electricity' : { + 'date_from': list of date, + 'date_to', list of date, + 'usage', list of float, + 'charge', list of float + }, + 'gas': {}, + 'oil': {}, + 'water': {} + } + bill_overview_dict = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} + """ + if not raw_bill_table: + raise ValueError('Bill_Overview - monthly_bill has empty raw_bill_table') + if not analysis_date: + raise ValueError('Bill_Overview - monthly_bill has empty analysis_date') + bill_overview_dict = {} + manual_input_dict = {} + for utility in UTILITY_TYPE_LIST: + current_bill = Bill(utility, analysis_date) + if utility in raw_bill_table: + current_bill.put_month_bill(raw_bill_table[utility]) + else: + current_bill.put_month_bill(None) + bill_overview_dict[utility] = current_bill.get_annual_bill() + manual_input_dict[utility] = current_bill.get_manual_input() + if len(bill_overview_dict.keys()) != len(UTILITY_TYPE_LIST): + raise ValueError('Bill_Overview - monthly_bill has incomplete result in bill_overview_dict') + # if len(manual_input_dict.keys()) != len(UTILITY_TYPE_LIST): + # raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') + # return bill_overview_dict, manual_input_dict + return bill_overview_dict + + +def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): + """ + Take in raw_annual_bill_table + Generate annual bill for utility type with and without available bill + + Args: + raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types + analysis_date (dictionary): proforma's starting date and the years of proforma + raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required + + Returns: + annual_bill_table (dictionary): dictionary of dictionary of annual bills, see description for detail + + Description: + raw_annual_bill_table = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} + annual_bill_table = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} + """ + if not raw_bill_table: + raise ValueError('Bill_Overview - annual_bill has empty raw_bill_table') + if not raw_annual_bill_table: + raise ValueError('Bill_Overview - annual_bill has empty raw_annual_bill_table') + if not analysis_date: + raise ValueError('Bill_Overview - annual_bill has empty analysis_date') + + annual_bill_table = {} + for utility in UTILITY_TYPE_LIST: + current_bill = Bill(utility, analysis_date) + if utility in raw_bill_table: + current_bill.put_month_bill(raw_bill_table[utility]) + else: + current_bill.put_month_bill(None) + if utility in raw_annual_bill_table: + current_bill.put_annual_bill(raw_annual_bill_table[utility]) + annual_bill_table[utility] = current_bill.get_annual_bill() + if len(annual_bill_table.keys()) != len(UTILITY_TYPE_LIST): + raise ValueError('Bill_Overview - annual_bill has incomplete result in annual_bill_table') + return annual_bill_table + + +# **** ugly test **** +# print('\nmonthly_bill =', monthly_bill(db.raw_bill_table, db.analysis_date)) +# print('\nannual_bill =', annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)) diff --git a/bpfin/tests/test_financials/test_financial_saving.py b/bpfin/tests/test_financials/test_financial_saving.py index 22e49e9..49e6fe1 100644 --- a/bpfin/tests/test_financials/test_financial_saving.py +++ b/bpfin/tests/test_financials/test_financial_saving.py @@ -1,142 +1,142 @@ -from datetime import date -from bpfin.financials.financial_saving import Saving -from bpfin.financials.financial_saving import Saving_Overview -from bpfin.tests.testdata import feature_data as db +# from datetime import date +# from bpfin.financials.financial_saving import Saving +# from bpfin.financials.financial_saving import Saving_Overview +# from bpfin.tests.testdata import feature_data as db -def test_Saving_put_monthly_proforma(): - input_proforma_date = [ - date(2017, 1, 31), - date(2017, 2, 28), - date(2017, 3, 31), - date(2017, 4, 30), - date(2017, 5, 31), - date(2017, 6, 30), - date(2017, 7, 31), - date(2017, 8, 31), - date(2017, 9, 30), - date(2017, 10, 31), - date(2017, 11, 30), - date(2017, 12, 31)] - input_commissioning_date = date(2017, 3, 14) - input_prior_charge = [100] * 12 - input_post_charge = [100, 90, 80, 70, 60, 50, 40, 30, 40, 50, 60, 70] - input_prior_usage = [15] * 12 - input_post_usage = [10, 9, 8, 7, 6, 5, 4, 3, 4, 5, 6, 7] - output_saving = [0, 0, 0, 30, 40, 50, 60, 70, 60, 50, 40, 30] - output_charge = [100, 100, 100, 70, 60, 50, 40, 30, 40, 50, 60, 70] - output_overall_saving = 0.4777777777777778 +# def test_Saving_put_monthly_proforma(): +# input_proforma_date = [ +# date(2017, 1, 31), +# date(2017, 2, 28), +# date(2017, 3, 31), +# date(2017, 4, 30), +# date(2017, 5, 31), +# date(2017, 6, 30), +# date(2017, 7, 31), +# date(2017, 8, 31), +# date(2017, 9, 30), +# date(2017, 10, 31), +# date(2017, 11, 30), +# date(2017, 12, 31)] +# input_commissioning_date = date(2017, 3, 14) +# input_prior_charge = [100] * 12 +# input_post_charge = [100, 90, 80, 70, 60, 50, 40, 30, 40, 50, 60, 70] +# input_prior_usage = [15] * 12 +# input_post_usage = [10, 9, 8, 7, 6, 5, 4, 3, 4, 5, 6, 7] +# output_saving = [0, 0, 0, 30, 40, 50, 60, 70, 60, 50, 40, 30] +# output_charge = [100, 100, 100, 70, 60, 50, 40, 30, 40, 50, 60, 70] +# output_overall_saving = 0.4777777777777778 - elec_saving = Saving(False, input_commissioning_date, input_proforma_date) - elec_saving.put_monthly_proforma(input_prior_usage, input_post_usage, input_prior_charge, input_post_charge) - result_saving = elec_saving.get_proforma_saving_charge() - result_charge = elec_saving.get_proforma_charge() +# elec_saving = Saving(False, input_commissioning_date, input_proforma_date) +# elec_saving.put_monthly_proforma(input_prior_usage, input_post_usage, input_prior_charge, input_post_charge) +# result_saving = elec_saving.get_proforma_saving_charge() +# result_charge = elec_saving.get_proforma_charge() - assert output_saving == result_saving - assert output_charge == result_charge - assert elec_saving.get_first_year_saving_charge() == 430 - assert elec_saving.get_annual_proforma_charge() == {2017: 770} - assert elec_saving.get_percent_saving() == output_overall_saving +# assert output_saving == result_saving +# assert output_charge == result_charge +# assert elec_saving.get_first_year_saving_charge() == 430 +# assert elec_saving.get_annual_proforma_charge() == {2017: 770} +# assert elec_saving.get_percent_saving() == output_overall_saving -def test_Saving_put_annual_proforma(): - input_proforma_date = [ - date(2017, 1, 31), - date(2017, 2, 28), - date(2017, 3, 31), - date(2017, 4, 30), - date(2017, 5, 31), - date(2017, 6, 30), - date(2017, 7, 31), - date(2017, 8, 31), - date(2017, 9, 30), - date(2017, 10, 31), - date(2017, 11, 30), - date(2017, 12, 31), - date(2018, 1, 31), - date(2018, 2, 28), - date(2018, 3, 31), - date(2018, 4, 30), - date(2018, 5, 31), - date(2018, 6, 30), - date(2018, 7, 31), - date(2018, 8, 31), - date(2018, 9, 30), - date(2018, 10, 31), - date(2018, 11, 30), - date(2018, 12, 31)] - input_commissioning_date = date(2017, 3, 14) - input_annual_charge = {2017: 100, 2018: 100} - input_percent_saving = 0.20 - output_first_year_saving_charge = 20 - output_annual_proforma_charge = {2017: 85, 2018: 80} - output_overall_saving = 0.20 +# def test_Saving_put_annual_proforma(): +# input_proforma_date = [ +# date(2017, 1, 31), +# date(2017, 2, 28), +# date(2017, 3, 31), +# date(2017, 4, 30), +# date(2017, 5, 31), +# date(2017, 6, 30), +# date(2017, 7, 31), +# date(2017, 8, 31), +# date(2017, 9, 30), +# date(2017, 10, 31), +# date(2017, 11, 30), +# date(2017, 12, 31), +# date(2018, 1, 31), +# date(2018, 2, 28), +# date(2018, 3, 31), +# date(2018, 4, 30), +# date(2018, 5, 31), +# date(2018, 6, 30), +# date(2018, 7, 31), +# date(2018, 8, 31), +# date(2018, 9, 30), +# date(2018, 10, 31), +# date(2018, 11, 30), +# date(2018, 12, 31)] +# input_commissioning_date = date(2017, 3, 14) +# input_annual_charge = {2017: 100, 2018: 100} +# input_percent_saving = 0.20 +# output_first_year_saving_charge = 20 +# output_annual_proforma_charge = {2017: 85, 2018: 80} +# output_overall_saving = 0.20 - elec_saving = Saving(True, input_commissioning_date, input_proforma_date) - elec_saving.put_annual_proforma(input_annual_charge, input_percent_saving) +# elec_saving = Saving(True, input_commissioning_date, input_proforma_date) +# elec_saving.put_annual_proforma(input_annual_charge, input_percent_saving) - result_first_year_saving_charge = elec_saving.get_first_year_saving_charge() - result_annual_proforma_charge = elec_saving.get_annual_proforma_charge() - result_overall_saving = elec_saving.get_percent_saving() +# result_first_year_saving_charge = elec_saving.get_first_year_saving_charge() +# result_annual_proforma_charge = elec_saving.get_annual_proforma_charge() +# result_overall_saving = elec_saving.get_percent_saving() - assert result_first_year_saving_charge == output_first_year_saving_charge - assert result_annual_proforma_charge == output_annual_proforma_charge - assert result_overall_saving == output_overall_saving +# assert result_first_year_saving_charge == output_first_year_saving_charge +# assert result_annual_proforma_charge == output_annual_proforma_charge +# assert result_overall_saving == output_overall_saving -def test_Saving_Overview(): - input_bill_list = db.bill_list - input_analysis_date = db.analysis_date - input_commissioning_date = db.commission_date +# def test_Saving_Overview(): +# input_bill_list = db.bill_list +# input_analysis_date = db.analysis_date +# input_commissioning_date = db.commission_date - input_percent_saving_dict = db.percent_saving_dict - input_full_saving_dict = db.full_saving_dict +# input_percent_saving_dict = db.percent_saving_dict +# input_full_saving_dict = db.full_saving_dict - output_first_year_saving = 1502.6386560837202 - output_simple_payback = 33.274799498579 - output_total_saving_percent = 0.37312045887352324 - output_electricity_annual_saving_charge = { - 2012: 0.0, - 2013: 0.0, - 2014: 0.0, - 2015: 0.0, - 2016: 0.0, - 2017: 251.64464541815892, - 2018: 357.48478459061994, - 2019: 366.28049606569721, - 2020: 374.86074424735079, - 2021: 382.88611271044329, - 2022: 390.15410099072778, - 2023: 397.27620075339473, - 2024: 404.54734630353755, - 2025: 412.06528286992216, - 2026: 420.14359538042595, - 2027: 428.86477571357136, - 2028: 437.81793503683912, - 2029: 446.78365645196686, - 2030: 455.7489720441697, - 2031: 464.74135726047848, - 2032: 473.83322571714552, - 2033: 483.13050892569618, - 2034: 492.67630130304224, - 2035: 502.48247134137955, - 2036: 512.55020795318637 - } - output_post_annual_bill_table = db.post_annual_bill_table - saving_overview = Saving_Overview( - input_bill_list, - input_analysis_date, - input_commissioning_date) +# output_first_year_saving = 1502.6386560837202 +# output_simple_payback = 33.274799498579 +# output_total_saving_percent = 0.37312045887352324 +# output_electricity_annual_saving_charge = { +# 2012: 0.0, +# 2013: 0.0, +# 2014: 0.0, +# 2015: 0.0, +# 2016: 0.0, +# 2017: 251.64464541815892, +# 2018: 357.48478459061994, +# 2019: 366.28049606569721, +# 2020: 374.86074424735079, +# 2021: 382.88611271044329, +# 2022: 390.15410099072778, +# 2023: 397.27620075339473, +# 2024: 404.54734630353755, +# 2025: 412.06528286992216, +# 2026: 420.14359538042595, +# 2027: 428.86477571357136, +# 2028: 437.81793503683912, +# 2029: 446.78365645196686, +# 2030: 455.7489720441697, +# 2031: 464.74135726047848, +# 2032: 473.83322571714552, +# 2033: 483.13050892569618, +# 2034: 492.67630130304224, +# 2035: 502.48247134137955, +# 2036: 512.55020795318637 +# } +# output_post_annual_bill_table = db.post_annual_bill_table +# saving_overview = Saving_Overview( +# input_bill_list, +# input_analysis_date, +# input_commissioning_date) - saving_overview.put_saving(input_percent_saving_dict, input_full_saving_dict) - result_first_year_saving = saving_overview.get_total_first_year_saving() - result_simple_payback = saving_overview.get_simple_payback(50000) - result_total_saving_percent = saving_overview.get_total_saving_percent() - result_electricity_annual_saving_charge = saving_overview.get_utility_annual_saving_charge('electricity') - result_post_annual_bill_table = saving_overview.get_post_annual_bill_table() +# saving_overview.put_saving(input_percent_saving_dict, input_full_saving_dict) +# result_first_year_saving = saving_overview.get_total_first_year_saving() +# result_simple_payback = saving_overview.get_simple_payback(50000) +# result_total_saving_percent = saving_overview.get_total_saving_percent() +# result_electricity_annual_saving_charge = saving_overview.get_utility_annual_saving_charge('electricity') +# result_post_annual_bill_table = saving_overview.get_post_annual_bill_table() - assert result_first_year_saving == output_first_year_saving - assert result_simple_payback == output_simple_payback - assert result_total_saving_percent == output_total_saving_percent - assert result_electricity_annual_saving_charge == output_electricity_annual_saving_charge - assert result_post_annual_bill_table == output_post_annual_bill_table +# assert result_first_year_saving == output_first_year_saving +# assert result_simple_payback == output_simple_payback +# assert result_total_saving_percent == output_total_saving_percent +# assert result_electricity_annual_saving_charge == output_electricity_annual_saving_charge +# assert result_post_annual_bill_table == output_post_annual_bill_table diff --git a/bpfin/tests/test_financials/test_saving.py b/bpfin/tests/test_financials/test_saving.py index 042a2e9..1681c81 100644 --- a/bpfin/tests/test_financials/test_saving.py +++ b/bpfin/tests/test_financials/test_saving.py @@ -1,143 +1,143 @@ -import datetime -from bpfin.financials.saving import Saving -from bpfin.financials.saving import Saving_Overview -from bpfin.tests.testdata import sample_data as db - - -def test_Saving_put_monthly_proforma(): - input_proforma_date = [ - datetime.date(2017, 1, 31), - datetime.date(2017, 2, 28), - datetime.date(2017, 3, 31), - datetime.date(2017, 4, 30), - datetime.date(2017, 5, 31), - datetime.date(2017, 6, 30), - datetime.date(2017, 7, 31), - datetime.date(2017, 8, 31), - datetime.date(2017, 9, 30), - datetime.date(2017, 10, 31), - datetime.date(2017, 11, 30), - datetime.date(2017, 12, 31)] - input_commissioning_date = datetime.date(2017, 3, 14) - input_prior_charge = [100] * 12 - input_post_charge = [100, 90, 80, 70, 60, 50, 40, 30, 40, 50, 60, 70] - input_prior_usage = [15] * 12 - input_post_usage = [10, 9, 8, 7, 6, 5, 4, 3, 4, 5, 6, 7] - output_saving = [0, 0, 0, 30, 40, 50, 60, 70, 60, 50, 40, 30] - output_charge = [100, 100, 100, 70, 60, 50, 40, 30, 40, 50, 60, 70] - output_overall_saving = 0.4777777777777778 - - elec_saving = Saving(False, input_commissioning_date, input_proforma_date) - elec_saving.put_monthly_proforma(input_prior_usage, input_post_usage, input_prior_charge, input_post_charge) - result_saving = elec_saving.get_proforma_saving_charge() - result_charge = elec_saving.get_proforma_charge() - - assert output_saving == result_saving - assert output_charge == result_charge - assert elec_saving.get_first_year_saving_charge() == 430 - assert elec_saving.get_annual_proforma_charge() == {2017: 770} - assert elec_saving.get_percent_saving() == output_overall_saving - - -def test_Saving_put_annual_proforma(): - input_proforma_date = [ - datetime.date(2017, 1, 31), - datetime.date(2017, 2, 28), - datetime.date(2017, 3, 31), - datetime.date(2017, 4, 30), - datetime.date(2017, 5, 31), - datetime.date(2017, 6, 30), - datetime.date(2017, 7, 31), - datetime.date(2017, 8, 31), - datetime.date(2017, 9, 30), - datetime.date(2017, 10, 31), - datetime.date(2017, 11, 30), - datetime.date(2017, 12, 31), - datetime.date(2018, 1, 31), - datetime.date(2018, 2, 28), - datetime.date(2018, 3, 31), - datetime.date(2018, 4, 30), - datetime.date(2018, 5, 31), - datetime.date(2018, 6, 30), - datetime.date(2018, 7, 31), - datetime.date(2018, 8, 31), - datetime.date(2018, 9, 30), - datetime.date(2018, 10, 31), - datetime.date(2018, 11, 30), - datetime.date(2018, 12, 31)] - input_commissioning_date = datetime.date(2017, 3, 14) - input_annual_charge = {2017: 100, 2018: 100} - input_percent_saving = 0.20 - output_first_year_saving_charge = 20 - output_annual_proforma_charge = {2017: 85, 2018: 80} - output_overall_saving = 0.20 - - elec_saving = Saving(True, input_commissioning_date, input_proforma_date) - elec_saving.put_annual_proforma(input_annual_charge, input_percent_saving) - - result_first_year_saving_charge = elec_saving.get_first_year_saving_charge() - result_annual_proforma_charge = elec_saving.get_annual_proforma_charge() - result_overall_saving = elec_saving.get_percent_saving() - - assert result_first_year_saving_charge == output_first_year_saving_charge - assert result_annual_proforma_charge == output_annual_proforma_charge - assert result_overall_saving == output_overall_saving - - -def test_Saving_Overview(): - input_bill_overview = db.bill_overview - input_prior_annual_bill_table = db.bill_overview_organized - input_analysis_date = db.analysis_date - input_commissioning_date = datetime.date(2017, 3, 14) - - input_prior_month_bill = db.prior_month_bill - input_percent_saving_dict = db.percent_saving_dict - input_full_saving_dict = db.full_saving_dict - - output_first_year_saving = 10479.289108609044 - output_simple_payback = 4.771316019797902 - output_total_saving_percent = 0.2624163752635955 - output_electricity_annual_saving_charge = { - 2012: 0.0, - 2013: 0.0, - 2014: 0.0, - 2015: 0.0, - 2016: 0.0, - 2017: 6757.962319199603, - 2018: 8846.241570765102, - 2019: 9062.872690379008, - 2020: 9275.049342309605, - 2021: 9471.770503408214, - 2022: 9650.941259401636, - 2023: 9826.981304138018, - 2024: 10007.019419843535, - 2025: 10193.107315596935, - 2026: 10393.916746353145, - 2027: 10609.979647682601, - 2028: 10831.30886589543, - 2029: 11052.82469894908, - 2030: 11274.416949194128, - 2031: 11496.666332412671, - 2032: 11721.576357170983, - 2033: 11951.64472721766, - 2034: 12187.887773507784, - 2035: 12430.56386264111, - 2036: 12679.70824604718 - } - output_post_annual_bill_table = db.bill_overview_post - - saving_overview = Saving_Overview( - input_bill_overview, input_prior_annual_bill_table, input_analysis_date, input_commissioning_date) - saving_overview.put_saving(input_prior_month_bill, input_percent_saving_dict, input_full_saving_dict) - - result_first_year_saving = saving_overview.get_total_first_year_saving() - result_simple_payback = saving_overview.get_simple_payback(50000) - result_total_saving_percent = saving_overview.get_total_saving_percent() - result_electricity_annual_saving_charge = saving_overview.get_utility_annual_saving_charge('electricity') - result_post_annual_bill_table = saving_overview.get_post_annual_bill_table() - - assert result_first_year_saving == output_first_year_saving - assert result_simple_payback == output_simple_payback - assert result_total_saving_percent == output_total_saving_percent - assert result_electricity_annual_saving_charge == output_electricity_annual_saving_charge - assert result_post_annual_bill_table == output_post_annual_bill_table +# import datetime +# from bpfin.financials.saving import Saving +# from bpfin.financials.saving import Saving_Overview +# from bpfin.tests.testdata import sample_data as db + + +# def test_Saving_put_monthly_proforma(): +# input_proforma_date = [ +# datetime.date(2017, 1, 31), +# datetime.date(2017, 2, 28), +# datetime.date(2017, 3, 31), +# datetime.date(2017, 4, 30), +# datetime.date(2017, 5, 31), +# datetime.date(2017, 6, 30), +# datetime.date(2017, 7, 31), +# datetime.date(2017, 8, 31), +# datetime.date(2017, 9, 30), +# datetime.date(2017, 10, 31), +# datetime.date(2017, 11, 30), +# datetime.date(2017, 12, 31)] +# input_commissioning_date = datetime.date(2017, 3, 14) +# input_prior_charge = [100] * 12 +# input_post_charge = [100, 90, 80, 70, 60, 50, 40, 30, 40, 50, 60, 70] +# input_prior_usage = [15] * 12 +# input_post_usage = [10, 9, 8, 7, 6, 5, 4, 3, 4, 5, 6, 7] +# output_saving = [0, 0, 0, 30, 40, 50, 60, 70, 60, 50, 40, 30] +# output_charge = [100, 100, 100, 70, 60, 50, 40, 30, 40, 50, 60, 70] +# output_overall_saving = 0.4777777777777778 + +# elec_saving = Saving(False, input_commissioning_date, input_proforma_date) +# elec_saving.put_monthly_proforma(input_prior_usage, input_post_usage, input_prior_charge, input_post_charge) +# result_saving = elec_saving.get_proforma_saving_charge() +# result_charge = elec_saving.get_proforma_charge() + +# assert output_saving == result_saving +# assert output_charge == result_charge +# assert elec_saving.get_first_year_saving_charge() == 430 +# assert elec_saving.get_annual_proforma_charge() == {2017: 770} +# assert elec_saving.get_percent_saving() == output_overall_saving + + +# def test_Saving_put_annual_proforma(): +# input_proforma_date = [ +# datetime.date(2017, 1, 31), +# datetime.date(2017, 2, 28), +# datetime.date(2017, 3, 31), +# datetime.date(2017, 4, 30), +# datetime.date(2017, 5, 31), +# datetime.date(2017, 6, 30), +# datetime.date(2017, 7, 31), +# datetime.date(2017, 8, 31), +# datetime.date(2017, 9, 30), +# datetime.date(2017, 10, 31), +# datetime.date(2017, 11, 30), +# datetime.date(2017, 12, 31), +# datetime.date(2018, 1, 31), +# datetime.date(2018, 2, 28), +# datetime.date(2018, 3, 31), +# datetime.date(2018, 4, 30), +# datetime.date(2018, 5, 31), +# datetime.date(2018, 6, 30), +# datetime.date(2018, 7, 31), +# datetime.date(2018, 8, 31), +# datetime.date(2018, 9, 30), +# datetime.date(2018, 10, 31), +# datetime.date(2018, 11, 30), +# datetime.date(2018, 12, 31)] +# input_commissioning_date = datetime.date(2017, 3, 14) +# input_annual_charge = {2017: 100, 2018: 100} +# input_percent_saving = 0.20 +# output_first_year_saving_charge = 20 +# output_annual_proforma_charge = {2017: 85, 2018: 80} +# output_overall_saving = 0.20 + +# elec_saving = Saving(True, input_commissioning_date, input_proforma_date) +# elec_saving.put_annual_proforma(input_annual_charge, input_percent_saving) + +# result_first_year_saving_charge = elec_saving.get_first_year_saving_charge() +# result_annual_proforma_charge = elec_saving.get_annual_proforma_charge() +# result_overall_saving = elec_saving.get_percent_saving() + +# assert result_first_year_saving_charge == output_first_year_saving_charge +# assert result_annual_proforma_charge == output_annual_proforma_charge +# assert result_overall_saving == output_overall_saving + + +# def test_Saving_Overview(): +# input_bill_overview = db.bill_overview +# input_prior_annual_bill_table = db.bill_overview_organized +# input_analysis_date = db.analysis_date +# input_commissioning_date = datetime.date(2017, 3, 14) + +# input_prior_month_bill = db.prior_month_bill +# input_percent_saving_dict = db.percent_saving_dict +# input_full_saving_dict = db.full_saving_dict + +# output_first_year_saving = 10479.289108609044 +# output_simple_payback = 4.771316019797902 +# output_total_saving_percent = 0.2624163752635955 +# output_electricity_annual_saving_charge = { +# 2012: 0.0, +# 2013: 0.0, +# 2014: 0.0, +# 2015: 0.0, +# 2016: 0.0, +# 2017: 6757.962319199603, +# 2018: 8846.241570765102, +# 2019: 9062.872690379008, +# 2020: 9275.049342309605, +# 2021: 9471.770503408214, +# 2022: 9650.941259401636, +# 2023: 9826.981304138018, +# 2024: 10007.019419843535, +# 2025: 10193.107315596935, +# 2026: 10393.916746353145, +# 2027: 10609.979647682601, +# 2028: 10831.30886589543, +# 2029: 11052.82469894908, +# 2030: 11274.416949194128, +# 2031: 11496.666332412671, +# 2032: 11721.576357170983, +# 2033: 11951.64472721766, +# 2034: 12187.887773507784, +# 2035: 12430.56386264111, +# 2036: 12679.70824604718 +# } +# output_post_annual_bill_table = db.bill_overview_post + +# saving_overview = Saving_Overview( +# input_bill_overview, input_prior_annual_bill_table, input_analysis_date, input_commissioning_date) +# saving_overview.put_saving(input_prior_month_bill, input_percent_saving_dict, input_full_saving_dict) + +# result_first_year_saving = saving_overview.get_total_first_year_saving() +# result_simple_payback = saving_overview.get_simple_payback(50000) +# result_total_saving_percent = saving_overview.get_total_saving_percent() +# result_electricity_annual_saving_charge = saving_overview.get_utility_annual_saving_charge('electricity') +# result_post_annual_bill_table = saving_overview.get_post_annual_bill_table() + +# assert result_first_year_saving == output_first_year_saving +# assert result_simple_payback == output_simple_payback +# assert result_total_saving_percent == output_total_saving_percent +# assert result_electricity_annual_saving_charge == output_electricity_annual_saving_charge +# assert result_post_annual_bill_table == output_post_annual_bill_table diff --git a/bpfin/tests/test_utilbills/test_back_end_call.py b/bpfin/tests/test_utilbills/test_back_end_call.py new file mode 100644 index 0000000..ca287f5 --- /dev/null +++ b/bpfin/tests/test_utilbills/test_back_end_call.py @@ -0,0 +1,14 @@ +from bpfin.lib.back_end_call import monthly_bill, annual_bill +from bpfin.tests.testdata import feature_data as db + + +def test_monthly_bill(): + output_dict = { + 'electricity': db.prior_annual_bill['electricity'], + 'gas': None, + 'oil': None, + 'water': None + } + + result_dict = monthly_bill(db.raw_bill_table, db.analysis_date) + assert output_dict == result_dict diff --git a/bpfin/tests/test_utilbills/test_bill.py b/bpfin/tests/test_utilbills/test_bill.py index 1359fc0..5e27f23 100644 --- a/bpfin/tests/test_utilbills/test_bill.py +++ b/bpfin/tests/test_utilbills/test_bill.py @@ -1,47 +1,47 @@ -from datetime import date -from bpfin.utilbills.bill import Bill_Table -from bpfin.tests.testdata import feature_data as db +# from datetime import date +# from bpfin.utilbills.bill import Bill_Table +# from bpfin.tests.testdata import feature_data as db -def test_Bill_Table_put_month(): - """ - test Bill_Table with only inputing month bill - """ - input_raw_bill_table = db.raw_bill_table - input_analysis_date = db.analysis_date +# def test_Bill_Table_put_month(): +# """ +# test Bill_Table with only inputing month bill +# """ +# input_raw_bill_table = db.raw_bill_table +# input_analysis_date = db.analysis_date - bill_table = Bill_Table(input_analysis_date) - bill_table.put_month_bill_table(input_raw_bill_table) +# bill_table = Bill_Table(input_analysis_date) +# bill_table.put_month_bill_table(input_raw_bill_table) - output_annual_bill_table = db.annual_bill_table_month_only - result_annual_bill_table = bill_table.get_annual_bill_table() - assert result_annual_bill_table == output_annual_bill_table +# output_annual_bill_table = db.annual_bill_table_month_only +# result_annual_bill_table = bill_table.get_annual_bill_table() +# assert result_annual_bill_table == output_annual_bill_table -def test_Bill_Table_put_annual(): - """ - test Bill_Table with only inputing annual bill - """ - input_raw_annual_bill_table = db.raw_annual_bill_table - input_analysis_date = db.analysis_date +# def test_Bill_Table_put_annual(): +# """ +# test Bill_Table with only inputing annual bill +# """ +# input_raw_annual_bill_table = db.raw_annual_bill_table +# input_analysis_date = db.analysis_date - bill_table = Bill_Table(input_analysis_date) - bill_table.put_annual_bill_table(input_raw_annual_bill_table) +# bill_table = Bill_Table(input_analysis_date) +# bill_table.put_annual_bill_table(input_raw_annual_bill_table) - output_annual_bill_table = db.annual_bill_table_annual_only - result_annual_bill_table = bill_table.get_annual_bill_table() - assert result_annual_bill_table == output_annual_bill_table +# output_annual_bill_table = db.annual_bill_table_annual_only +# result_annual_bill_table = bill_table.get_annual_bill_table() +# assert result_annual_bill_table == output_annual_bill_table -def test_Bill_Table_put_both(): - input_analysis_date = db.analysis_date - input_raw_annual_bill_table = db.raw_annual_bill_table - input_raw_bill_table = db.raw_bill_table +# def test_Bill_Table_put_both(): +# input_analysis_date = db.analysis_date +# input_raw_annual_bill_table = db.raw_annual_bill_table +# input_raw_bill_table = db.raw_bill_table - bill_table = Bill_Table(input_analysis_date) - bill_table.put_month_bill_table(input_raw_bill_table) - bill_table.put_annual_bill_table(input_raw_annual_bill_table) +# bill_table = Bill_Table(input_analysis_date) +# bill_table.put_month_bill_table(input_raw_bill_table) +# bill_table.put_annual_bill_table(input_raw_annual_bill_table) - output_annual_bill_table = db.prior_annual_bill - result_annual_bill_table = bill_table.get_annual_bill_table() - assert result_annual_bill_table == output_annual_bill_table +# output_annual_bill_table = db.prior_annual_bill +# result_annual_bill_table = bill_table.get_annual_bill_table() +# assert result_annual_bill_table == output_annual_bill_table diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 3c70030..61450d6 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -1,3 +1,6 @@ +""" +feature_data.py is a fake database to store demo test data +""" import datetime from datetime import date # from bpfin.utilbills.bill import form_bill_list_month @@ -5,6 +8,10 @@ from datetime import date # from bpfin.utilbills.bill import Bill_Table import pprint + +# one big decision is, one frontend request should either call data from database, or frontend. +# considering frondend inputs will be stored in database eventually, all data should come from database + # pro-forma projection time base analysis_date = { 'proforma_start': datetime.date(2012, 1, 15), @@ -41,11 +48,12 @@ percent_saving_dict = { full_saving_dict = { - 'electricity': None, # dict of engineering analysis, {'date_from': [], 'date_to': [], 'heating': [], 'cooling': [], 'other': []} + 'electricity': None, 'gas': None, 'oil': None, 'water': None } +# dict of engineering analysis, {'date_from': [], 'date_to': [], 'heating': [], 'cooling': [], 'other': []} e_raw_bill = { @@ -229,126 +237,6 @@ prior_annual_bill = { } } -annual_bill_table_month_only = { - 'electricity': { - 2012: 1302.3894189123205, - 2013: 1325.5398892097569, - 2014: 1346.0795010524571, - 2015: 1357.4370596899435, - 2016: 1368.4645719731475, - 2017: 1395.2434398071116, - 2018: 1429.9391383624793, - 2019: 1465.1219842627888, - 2020: 1499.442976989403, - 2021: 1531.5444508417731, - 2022: 1560.6164039629107, - 2023: 1589.1048030135796, - 2024: 1618.1893852141502, - 2025: 1648.2611314796882, - 2026: 1680.5743815217027, - 2027: 1715.4591028542839, - 2028: 1751.2717401473571, - 2029: 1787.1346258078672, - 2030: 1822.9958881766795, - 2031: 1858.9654290419135, - 2032: 1895.3329028685823, - 2033: 1932.5220357027845, - 2034: 1970.7052052121689, - 2035: 2009.9298853655182, - 2036: 2050.2008318127446 - }, - 'gas': None, - 'oil': None, - 'water': None -} - - -annual_bill_table_annual_only = { - 'electricity': None, - 'gas': { - 2014: 0, - 2015: 1020, - 2016: 1220, - 2017: 1520, - 2012: 1253.3333333333333, - 2013: 1253.3333333333333, - 2018: 1253.3333333333333, - 2019: 1253.3333333333333, - 2020: 1253.3333333333333, - 2021: 1253.3333333333333, - 2022: 1253.3333333333333, - 2023: 1253.3333333333333, - 2024: 1253.3333333333333, - 2025: 1253.3333333333333, - 2026: 1253.3333333333333, - 2027: 1253.3333333333333, - 2028: 1253.3333333333333, - 2029: 1253.3333333333333, - 2030: 1253.3333333333333, - 2031: 1253.3333333333333, - 2032: 1253.3333333333333, - 2033: 1253.3333333333333, - 2034: 1253.3333333333333, - 2035: 1253.3333333333333, - 2036: 1253.3333333333333 - }, - 'oil': { - 2015: 1010, - 2016: 1210, - 2017: 1510, - 2012: 1243.3333333333333, - 2013: 1243.3333333333333, - 2014: 1243.3333333333333, - 2018: 1243.3333333333333, - 2019: 1243.3333333333333, - 2020: 1243.3333333333333, - 2021: 1243.3333333333333, - 2022: 1243.3333333333333, - 2023: 1243.3333333333333, - 2024: 1243.3333333333333, - 2025: 1243.3333333333333, - 2026: 1243.3333333333333, - 2027: 1243.3333333333333, - 2028: 1243.3333333333333, - 2029: 1243.3333333333333, - 2030: 1243.3333333333333, - 2031: 1243.3333333333333, - 2032: 1243.3333333333333, - 2033: 1243.3333333333333, - 2034: 1243.3333333333333, - 2035: 1243.3333333333333, - 2036: 1243.3333333333333 - }, - 'water': { - 2015: 0, - 2016: 0, - 2017: 0, - 2012: 0, - 2013: 0, - 2014: 0, - 2018: 0, - 2019: 0, - 2020: 0, - 2021: 0, - 2022: 0, - 2023: 0, - 2024: 0, - 2025: 0, - 2026: 0, - 2027: 0, - 2028: 0, - 2029: 0, - 2030: 0, - 2031: 0, - 2032: 0, - 2033: 0, - 2034: 0, - 2035: 0, - 2036: 0 - } -} - - post_annual_bill_table = { 'electricity': { 2012: 1302.3894189123205, @@ -459,11 +347,3 @@ post_annual_bill_table = { 2036: 0.0 } } -# bill_list = form_bill_list_month(raw_bill_table, analysis_date) -# annual_bill_table = form_annual_bill_table(bill_list, raw_annual_bill_table) -# print(bill_list[0].get_prior_proj_rough()) -# print(annual_bill_table) - -# e_raw_bill['charge'].reverse() -# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) -# pp.pprint(e_raw_bill['charge']) diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index b6c154e..161f54d 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -1,5 +1,6 @@ from datetime import date import copy +import sys from bpfin.utilbills.bill_lib import annualizing_projection from bpfin.utilbills.bill_lib import form_bill_calendar from bpfin.utilbills.bill_lib import form_bill_year @@ -8,7 +9,7 @@ from bpfin.lib.other import form_date_calendar from bpfin.utilbills.bill_month_normalize_rough import bill_month_normalize_rough from bpfin.utilbills.bill_prior_proj_rough import bill_prior_proj_rough from bpfin.utilbills.inflation_sample_data import inflation_coeff_dict -from bpfin.tests.testdata import feature_data as db +# from bpfin.tests.testdata import feature_data as db UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] @@ -33,7 +34,7 @@ def validate_raw_bill(raw_bill): if len(month_list) >= 12: return True else: - return False + return -1 else: return False @@ -136,7 +137,8 @@ class Bill(): 'usage', list of float, 'charge', list of float, """ - if validate_raw_bill(raw_bill): + validation = validate_raw_bill(raw_bill) + if validation: self.monthly_normalized_bill_rough = bill_month_normalize_rough(self.utility_type, copy.deepcopy(raw_bill)) self.is_manual_input = False self.prior_bill_rough = bill_prior_proj_rough( @@ -146,8 +148,11 @@ class Bill(): inflation_coeff_dict ) self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) - # else: - # raise ValueError('Please load a valid bill for ', self.utility_type) + else: + if validation == -1: + raise ValueError('monthly bill input not valid for ', self.utility_type) + else: + pass def put_annual_bill(self, raw_annual_bill): """ @@ -189,97 +194,85 @@ class Bill(): """ return self.annual_bill - -# def form_bill_list_month(raw_bill_table, analysis_date): -# bill_list = [] -# for utility in UTILITY_TYPE_LIST: -# current_bill = Bill(utility, analysis_date) -# if utility in raw_bill_table: -# current_bill.put_month_bill(raw_bill_table[utility]) -# bill_list.append(current_bill) -# return bill_list - - -# def form_annual_bill_table(bill_list, raw_annual_bill_table): -# bill_list_anual = copy.deepcopy(bill_list) -# annual_bill_table = {} -# for current_bill in bill_list_anual: -# if current_bill.utility_type in raw_annual_bill_table: -# current_bill.put_annual_bill(raw_annual_bill_table[current_bill.utility_type]) -# annual_bill_table[current_bill.utility_type] = current_bill.get_annual_bill() -# return annual_bill_table - - -class Bill_Table(): - """ - """ - def __init__(self, analysis_date): - """ - """ - self.analysis_date = analysis_date - self.raw_bill_table = None - self.manual_input_dict = {} - self.raw_annual_bill_table = None - self.utility_bill_dict = {} # dict of object - for utility_type in UTILITY_TYPE_LIST: - self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) - - def put_month_bill_table(self, raw_bill_table): - """ - """ - self.raw_bill_table = raw_bill_table - for utility_type in UTILITY_TYPE_LIST: - if utility_type in self.raw_bill_table: - self.utility_bill_dict[utility_type].put_month_bill(self.raw_bill_table[utility_type]) - # else: - # self.utility_bill_dict[utility_type].put_month_bill(None) - self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) - - def put_annual_bill_table(self, raw_annual_bill_table): - """ - """ - self.raw_annual_bill_table = raw_annual_bill_table - for utility_type in UTILITY_TYPE_LIST: - if utility_type in self.raw_annual_bill_table: - self.utility_bill_dict[utility_type].put_annual_bill(self.raw_annual_bill_table[utility_type]) - - def get_bill_overview(self): - """ - should be deleted or replaced when future calling func got updated - because all functions would need to use either the bill or the yes/no flag. - """ - bill_overview = {} - for utility_type in UTILITY_TYPE_LIST: - bill_overview[utility_type] = [ - ( - self.raw_annual_bill_table[utility_type] if - self.utility_bill_dict[utility_type].is_manual_input - else self.utility_bill_dict[utility_type].get_annual_bill() - ), - self.utility_bill_dict[utility_type].is_manual_input - ] - return bill_overview - - def get_annual_bill_table(self): - """ - Annual bill table, for 4 utility types, with annualized projected charge, or average annual charge - sample_data.bill_overview_organized - """ - annual_bill_table = {} - for utility_type in UTILITY_TYPE_LIST: - annual_bill_table[utility_type] = self.utility_bill_dict[utility_type].get_annual_bill() - return annual_bill_table - - def get_bill_list(self): + def get_manual_input(self): """ - Get list of Bill objects. For future use in other functions + Get manual_input label Return: - list: list of Bill objects. Order in 'electricity', 'gas', 'oil', 'water' + boolean: Yes == Bill is manually input """ - bill_list = [] - for utility_type in UTILITY_TYPE_LIST: - bill_list.append(copy.deepcopy(self.utility_bill_dict[utility_type])) - return bill_list + return self.is_manual_input + + +# class Bill_Table(): +# """ +# """ +# def __init__(self, analysis_date): +# """ +# """ +# self.analysis_date = analysis_date +# self.raw_bill_table = None +# self.manual_input_dict = {} +# self.raw_annual_bill_table = None +# self.utility_bill_dict = {} # dict of object +# for utility_type in UTILITY_TYPE_LIST: +# self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) + +# def put_month_bill_table(self, raw_bill_table): +# """ +# """ +# self.raw_bill_table = raw_bill_table +# for utility_type in UTILITY_TYPE_LIST: +# if utility_type in self.raw_bill_table: +# self.utility_bill_dict[utility_type].put_month_bill(self.raw_bill_table[utility_type]) +# # else: +# # self.utility_bill_dict[utility_type].put_month_bill(None) +# self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) + +# def put_annual_bill_table(self, raw_annual_bill_table): +# """ +# """ +# self.raw_annual_bill_table = raw_annual_bill_table +# for utility_type in UTILITY_TYPE_LIST: +# if utility_type in self.raw_annual_bill_table: +# self.utility_bill_dict[utility_type].put_annual_bill(self.raw_annual_bill_table[utility_type]) + +# def get_bill_overview(self): +# """ +# should be deleted or replaced when future calling func got updated +# because all functions would need to use either the bill or the yes/no flag. +# """ +# bill_overview = {} +# for utility_type in UTILITY_TYPE_LIST: +# bill_overview[utility_type] = [ +# ( +# self.raw_annual_bill_table[utility_type] if +# self.utility_bill_dict[utility_type].is_manual_input +# else self.utility_bill_dict[utility_type].get_annual_bill() +# ), +# self.utility_bill_dict[utility_type].is_manual_input +# ] +# return bill_overview + +# def get_annual_bill_table(self): +# """ +# Annual bill table, for 4 utility types, with annualized projected charge, or average annual charge +# sample_data.bill_overview_organized +# """ +# annual_bill_table = {} +# for utility_type in UTILITY_TYPE_LIST: +# annual_bill_table[utility_type] = self.utility_bill_dict[utility_type].get_annual_bill() +# return annual_bill_table + +# def get_bill_list(self): +# """ +# Get list of Bill objects. For future use in other functions +# Return: +# list: list of Bill objects. Order in 'electricity', 'gas', 'oil', 'water' +# """ +# bill_list = [] +# for utility_type in UTILITY_TYPE_LIST: +# bill_list.append(copy.deepcopy(self.utility_bill_dict[utility_type])) +# return bill_list -- GitLab From 7b0e99e02c91eeb3f8662fc41d3af24dea23e0ed Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 17 May 2017 18:04:34 -0400 Subject: [PATCH 25/30] Improve test file for backend call --- bpfin/tests/test_utilbills/test_back_end_call.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bpfin/tests/test_utilbills/test_back_end_call.py b/bpfin/tests/test_utilbills/test_back_end_call.py index ca287f5..c6d7c06 100644 --- a/bpfin/tests/test_utilbills/test_back_end_call.py +++ b/bpfin/tests/test_utilbills/test_back_end_call.py @@ -12,3 +12,9 @@ def test_monthly_bill(): result_dict = monthly_bill(db.raw_bill_table, db.analysis_date) assert output_dict == result_dict + + +def test_annual_bill(): + output_dict = db.prior_annual_bill + result_dict = annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date) + assert output_dict == result_dict -- GitLab From 4531afc652aa1068923939f5b91e3137ef781c21 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 17 May 2017 18:10:27 -0400 Subject: [PATCH 26/30] put financial_income and financial_saving in comments --- bpfin/financials/financial_income.py | 928 +++++++++++++-------------- bpfin/financials/financial_lib.py | 27 +- bpfin/financials/financial_saving.py | 748 ++++++++++----------- 3 files changed, 852 insertions(+), 851 deletions(-) diff --git a/bpfin/financials/financial_income.py b/bpfin/financials/financial_income.py index fe72b21..f13b4ea 100644 --- a/bpfin/financials/financial_income.py +++ b/bpfin/financials/financial_income.py @@ -1,464 +1,464 @@ -""" -Rewrite Income_Statement class to saperate income statement related class and funcs from Financial_lib.py -Income_Statement is incorporate with new Bill class, taking in bill_list as input -""" - -import copy -from bpfin.lib.other import cal_cagr -from bpfin.utilbills.bill_lib import form_bill_year -from numpy import mean -# from bpfin.tests.testdata import sample_data as db -# import pprint - - -class Income_Statement(): - """ - Generate Income Statement for single year, with standard accounting line items - Will be called - """ - def __init__(self): - self.year = None - self.revenue = None - self.utility_expense = None - self.non_utility_expense = None - self.electricity_opex = None - self.gas_opex = None - self.oil_opex = None - self.water_opex = None - self.energy_opex = None - self.other_utility = None - self.net_non_energy_opex = None - self.total_opex = None - self.noi = None - # energy_debt_service = - # other_debt_service = - # total_debt_service = - # bill_saving = - # energy_DSCR = - # total_DSCR = - - def put_hist(self, year, income_input, annual_bill_table): - """ - Put historical income statement data and generate a single year income statement - Inputs are incomplete income statement items, and energy bill overview - Final output is an single year income_statement objectvie with standarized items filled - - Args: - year (int): the year of this income statement - income_input (dictionary): 3 elements of inputs - annual_bill_table (dictionary): annual bill, for 4 utility_types - - Description Sample: - income_input = {revenue': 100000, 'utility_expense': 60000,'non_utility_expense': 3000} - annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} - """ - self.year = year - self.revenue = income_input['revenue'] - self.utility_expense = income_input['utility_expense'] - self.non_utility_expense = income_input['non_utility_expense'] - self.electricity_opex = annual_bill_table['electricity'][self.year] - self.gas_opex = annual_bill_table['gas'][self.year] - self.oil_opex = annual_bill_table['oil'][self.year] - self.water_opex = annual_bill_table['water'][self.year] - self.energy_opex = self.electricity_opex + self.oil_opex + self.gas_opex + self.water_opex - self.other_utility = self.utility_expense - self.energy_opex - self.net_non_energy_opex = self.other_utility + self.non_utility_expense - self.total_opex = self.energy_opex + self.net_non_energy_opex - self.noi = self.revenue - self.total_opex # net operating income - - def put_average(self, average_revenue, annual_bills, characters): - """ - Calculate and create average income statement. - Revenue, energy bills are average of historical data. - Other items are calculated by historical characters. - Args: - average_revenue (float): average revenue calculated from historical income statements - annual_bills (dictionary): dictionary of float value of average annual bills, for 4 utility_types - characters (dictionary): 6 characters calculated from historical income statements - Final instance is a single year income statement without named year - - Description: - characters = { - 'start_year': (int) 2014, - 'end_year': (int) 2016, - 'cagr': (float) 0.054, - 'other_utility_percent': (float) 0.020 == 2.0% - 'non_utility_expense_percent': (float) 0.030 == 3.0% - 'revenue_average': (float) 3000.00 - } - annual_bills = {'electricity': 100.0, 'oil': 200.0, 'gas': 300.0, 'water': 400.0} - """ - self.revenue = average_revenue - self.electricity_opex = annual_bills['electricity'] - self.gas_opex = annual_bills['gas'] - self.oil_opex = annual_bills['oil'] - self.water_opex = annual_bills['water'] - self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex - self.other_utility = self.revenue * characters['other_utility_percent'] - self.non_utility_expense = self.revenue * characters[ - 'non_utility_expense_percent'] - self.utility_expense = self.energy_opex + self.other_utility - self.net_non_energy_opex = self.other_utility + self.non_utility_expense - self.total_opex = self.energy_opex + self.net_non_energy_opex - self.noi = self.revenue - self.total_opex - - # def assign_next(self, income_statement_characters, - # bill_overview_organized): - # """ - # !!! this function will be deleted in the next version!! - # """ - # self.electricity_opex = bill_overview_organized['electricity'][ - # self.year] - # self.gas_opex = bill_overview_organized['gas'][self.year] - # self.oil_opex = bill_overview_organized['oil'][self.year] - # self.water_opex = bill_overview_organized['water'][self.year] - # self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex - # self.other_utility = self.revenue * income_statement_characters[ - # 'other_utility_percent'] - # self.non_utility_expense = self.revenue * income_statement_characters[ - # 'non_utility_expense_percent'] - # self.utility_expense = self.energy_opex + self.other_utility - # self.net_non_energy_opex = self.other_utility + self.non_utility_expense - # self.total_opex = self.energy_opex + self.net_non_energy_opex - # self.noi = self.revenue - self.total_opex - - -def convert_income_statement_class(income_statement_class): - """ - Convert single year income statement object into a dictionary format - Args: - income_statement_class (object): single year income statement object - Return: - income_statement_dict (dictionary) - """ - income_statement_dict = { - 'year': income_statement_class.year, - 'revenue': income_statement_class.revenue, - 'utility_expense': income_statement_class.utility_expense, - 'energy_opex': income_statement_class.energy_opex, - 'electricity_opex': income_statement_class.electricity_opex, - 'gas_opex': income_statement_class.gas_opex, - 'oil_opex': income_statement_class.oil_opex, - 'water_opex': income_statement_class.water_opex, - 'other_utility': income_statement_class.other_utility, - 'non_utility_expense': income_statement_class.non_utility_expense, - 'net_non_energy_opex': income_statement_class.net_non_energy_opex, - 'total_opex': income_statement_class.total_opex, - 'noi': income_statement_class.noi - } - return income_statement_dict - - -class Income_Statement_Next(): - """ - Create single year income_statement object, - with input of last year data, income statement characters, and projected annual bill - """ - - def __init__(self, year, last_revenue, growth_rate_flag, characters, - annual_bill_table): - """ - Calculation is done in initiation. - Args: - year (int): the year that will be projected on - last_revenue (float): last year revenue - growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average - characters (dictionary): 6 characters calculated from historical income statements - annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types - Final instance is a single year income_statement object - - Description: - characters = { - 'start_year': (int) 2014, - 'end_year': (int) 2016, - 'cagr': (float) 0.054, - 'other_utility_percent': (float) 0.020 == 2.0% - 'non_utility_expense_percent': (float) 0.035 == 3.5% - 'revenue_average': (float) 3000.00 - } - annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} - electricity_bill = {2014: 100, 2015:200, ...} - """ - self.year = year - - if growth_rate_flag == -1.0: # growth_rate_flag == average - current_revenue = characters['revenue_average'] - else: - if growth_rate_flag == -2.0: # growth_rate_flag == cagr - growth_rate = characters['cagr'] - else: - growth_rate = growth_rate_flag # growth_rate_flag == 0.00, 0.01, ... - current_revenue = last_revenue * (1 + growth_rate) - self.revenue = current_revenue - self.electricity_opex = annual_bill_table['electricity'][self.year] - self.gas_opex = annual_bill_table['gas'][self.year] - self.oil_opex = annual_bill_table['oil'][self.year] - self.water_opex = annual_bill_table['water'][self.year] - self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex - self.other_utility = self.revenue * characters['other_utility_percent'] - self.non_utility_expense = self.revenue * characters[ - 'non_utility_expense_percent'] - self.utility_expense = self.energy_opex + self.other_utility - self.net_non_energy_opex = self.other_utility + self.non_utility_expense - self.total_opex = self.energy_opex + self.net_non_energy_opex - self.noi = self.revenue - self.total_opex - - -class Income_Statement_Table(): - """ - Create a income statement table, containing multiple years, including historical and projected data - Key Attributes are: - hist_table (list): list of single year income_statement objects, containing historical data - table(list): list of single year income_statement objects, containing historical and projected data - """ - - def __init__(self, raw_income_input, annual_bill_table): - """ - Create hist_table to store historical income statement data, and calculate some key characters. - Args: - raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year - annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types - - Key Attributes: - cagr (float): compound annual growth rate - other_utility_percent (float): percentage, (average other_utility / average revenue) - non_utility_expense_percent (float): percentage, (average non_utility_expense_percent / average revenue) - hist_table (list): list of single year income_statement objects, containing historical data - table(list): list of single year income_statement objects, containing historical and projected data - characters (dictionary): contains key characters determined from historical data. Is used to project - - Description: - raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} - annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} - electricity_bill = {2014: 100, 2015:200, ...} - characters = { - 'start_year': (int) 2014, - 'end_year': (int) 2016, - 'cagr': (float) 0.054, - 'other_utility_percent': (float) 0.02, - 'non_utility_expense_percent': (float) 0.03, - 'revenue_average': (float) 3000.00 - } - """ - self.hist_start_year = None - self.hist_end_year = None - self.cagr = 0.00 - self.other_utility_percent = 0.00 - self.non_utility_expense_percent = 0.00 - self.revenue_average = 0.00 # average revenue - self.hist_table = [] - self.table = [] - self.characters = {} - - for year in raw_income_input: - current_income_statement = Income_Statement() - current_income_statement.put_hist(year, raw_income_input[year], - annual_bill_table) - self.hist_table.append(current_income_statement) - - sorted_income_hist_year = sorted(raw_income_input) - self.hist_start_year = sorted_income_hist_year[0] - self.hist_end_year = sorted_income_hist_year[-1] - # if start_year == None: return None - self.cagr = cal_cagr( - raw_income_input[self.hist_start_year]['revenue'], - raw_income_input[self.hist_end_year]['revenue'], - self.hist_end_year - self.hist_start_year) - - revenue_sum = 0 - other_utility_sum = 0 - non_utility_expense_sum = 0 - for current_income_statement in self.hist_table: - revenue_sum += current_income_statement.revenue - other_utility_sum += current_income_statement.other_utility - non_utility_expense_sum += current_income_statement.non_utility_expense - - self.other_utility_percent = other_utility_sum / revenue_sum - self.non_utility_expense_percent = non_utility_expense_sum / revenue_sum - self.revenue_average = revenue_sum / ( - self.hist_end_year - self.hist_start_year + 1) - - self.table = self.hist_table - self.characters = { - 'start_year': self.hist_start_year, - 'end_year': self.hist_end_year, - 'cagr': self.cagr, - 'other_utility_percent': self.other_utility_percent, - 'non_utility_expense_percent': self.non_utility_expense_percent, - 'revenue_average': self.revenue_average - } - - def project(self, growth_rate_flag, analysis_date, annual_bill_table): - """ - Project future income statement. Append multiple single year income_statement objects to self.table - Args: - growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average - analysis_date (dictionary): proforma's starting date and the years of proforma - annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types - Return: - list: list of single year income_statement objects, containing historical and projection - Note: - project() overwrites existing projection data - """ - # characters = copy.deepcopy(self.characters) - proforma_year = form_bill_year(analysis_date['proforma_start'], - analysis_date['proforma_duration']) - current_table = copy.deepcopy(self.hist_table) - for year in proforma_year: - last_revenue = current_table[-1].revenue - if year <= current_table[-1].year: - continue - current_income_statement = Income_Statement_Next( - year, last_revenue, growth_rate_flag, self.characters, - annual_bill_table) - current_table.append(current_income_statement) - self.table = current_table - # return current_table - - def get_hist_table(self): - """ - Get historical table, in dictionary formatting - Return: - dictionary: dict of dict of income statement. Key is year - Description: - hist_table_dict = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} - """ - hist_table_dict = {} - for current_income_statement in self.hist_table: - hist_table_dict[current_income_statement.year] = { - 'year': - current_income_statement.year, - 'revenue': - current_income_statement.revenue, - 'utility_expense': - current_income_statement.utility_expense, - 'energy_opex': - current_income_statement.energy_opex, - 'electricity_opex': - current_income_statement.electricity_opex, - 'gas_opex': - current_income_statement.gas_opex, - 'oil_opex': - current_income_statement.oil_opex, - 'water_opex': - current_income_statement.water_opex, - 'other_utility': - current_income_statement.other_utility, - 'non_utility_expense': - current_income_statement.non_utility_expense, - 'net_non_energy_opex': - current_income_statement.net_non_energy_opex, - 'total_opex': - current_income_statement.total_opex, - 'noi': - current_income_statement.noi - } - return hist_table_dict - - def get_cagr(self): - """ - Get compound annual growth rate - Return: - float: compound annual growth rate - """ - return copy.deepcopy(self.cagr) - - def get_average(self): - """ - Get average income statement. Only need initialized Income_Statement_Table - Return: - dictionary: dict of average income statement, Keys are income statement items. - Description: - output_dict = {'year': None, 'revenue': 95000.0, ... ,'noi': 35166.666666666657} - """ - # current_year = 'Average' - current_income_statement = Income_Statement() - average_revenue = mean(list(i_s.revenue for i_s in self.hist_table)) - annual_bills = { - 'electricity': - mean(list(i_s.electricity_opex for i_s in self.hist_table)), - 'gas': - mean(list(i_s.gas_opex for i_s in self.hist_table)), - 'oil': - mean(list(i_s.oil_opex for i_s in self.hist_table)), - 'water': - mean(list(i_s.water_opex for i_s in self.hist_table)) - } - current_income_statement.put_average(average_revenue, annual_bills, - self.characters) - - return convert_income_statement_class(current_income_statement) - - def get_single_year(self, year): - """ - Get single year income statement from self.table. Table can either contain or not contain projection - Args: - year (int): the year that need to be extracted - Return: - dict: single year income statement. Keys are income statement items. - Note: - if the target year is not in self.table, then return None. - """ - for current_income_statement in self.table: - if current_income_statement.year == year: - return { - 'year': - current_income_statement.year, - 'revenue': - current_income_statement.revenue, - 'utility_expense': - current_income_statement.utility_expense, - 'energy_opex': - current_income_statement.energy_opex, - 'electricity_opex': - current_income_statement.electricity_opex, - 'gas_opex': - current_income_statement.gas_opex, - 'oil_opex': - current_income_statement.oil_opex, - 'water_opex': - current_income_statement.water_opex, - 'other_utility': - current_income_statement.other_utility, - 'non_utility_expense': - current_income_statement.non_utility_expense, - 'net_non_energy_opex': - current_income_statement.net_non_energy_opex, - 'total_opex': - current_income_statement.total_opex, - 'noi': - current_income_statement.noi - } - return None - - def get_noi_dict(self): - """ - Get a dictionary of net operating income. - Return: - dictionary: net operating income for each year in income statement table. Key is year - """ - noi_dict = {} - for current_income_statement in self.table: - noi_dict[ - current_income_statement.year] = current_income_statement.noi - return noi_dict - - def get_first_year_noi(self, commission_date): - """ - Get first year noi after commissioning year, from post_saving income statement - Args: - commission_date (date): construction finishing date. Saving starts NEXT month/year - Return: - float: noi of the next year after commission date - """ - first_year = commission_date.year + 1 - for current_income_statement in self.table: - if current_income_statement.year == first_year: - return current_income_statement.noi - - def get_total_energy_dict(self): - total_energy_dict = {} - for current_income_statement in self.table: - total_energy_dict[ - current_income_statement.year] = current_income_statement.energy_opex - return total_energy_dict +# """ +# Rewrite Income_Statement class to saperate income statement related class and funcs from Financial_lib.py +# Income_Statement is incorporate with new Bill class, taking in bill_list as input +# """ + +# import copy +# from bpfin.lib.other import cal_cagr +# from bpfin.utilbills.bill_lib import form_bill_year +# from numpy import mean +# # from bpfin.tests.testdata import sample_data as db +# # import pprint + + +# class Income_Statement(): +# """ +# Generate Income Statement for single year, with standard accounting line items +# Will be called +# """ +# def __init__(self): +# self.year = None +# self.revenue = None +# self.utility_expense = None +# self.non_utility_expense = None +# self.electricity_opex = None +# self.gas_opex = None +# self.oil_opex = None +# self.water_opex = None +# self.energy_opex = None +# self.other_utility = None +# self.net_non_energy_opex = None +# self.total_opex = None +# self.noi = None +# # energy_debt_service = +# # other_debt_service = +# # total_debt_service = +# # bill_saving = +# # energy_DSCR = +# # total_DSCR = + +# def put_hist(self, year, income_input, annual_bill_table): +# """ +# Put historical income statement data and generate a single year income statement +# Inputs are incomplete income statement items, and energy bill overview +# Final output is an single year income_statement objectvie with standarized items filled + +# Args: +# year (int): the year of this income statement +# income_input (dictionary): 3 elements of inputs +# annual_bill_table (dictionary): annual bill, for 4 utility_types + +# Description Sample: +# income_input = {revenue': 100000, 'utility_expense': 60000,'non_utility_expense': 3000} +# annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} +# """ +# self.year = year +# self.revenue = income_input['revenue'] +# self.utility_expense = income_input['utility_expense'] +# self.non_utility_expense = income_input['non_utility_expense'] +# self.electricity_opex = annual_bill_table['electricity'][self.year] +# self.gas_opex = annual_bill_table['gas'][self.year] +# self.oil_opex = annual_bill_table['oil'][self.year] +# self.water_opex = annual_bill_table['water'][self.year] +# self.energy_opex = self.electricity_opex + self.oil_opex + self.gas_opex + self.water_opex +# self.other_utility = self.utility_expense - self.energy_opex +# self.net_non_energy_opex = self.other_utility + self.non_utility_expense +# self.total_opex = self.energy_opex + self.net_non_energy_opex +# self.noi = self.revenue - self.total_opex # net operating income + +# def put_average(self, average_revenue, annual_bills, characters): +# """ +# Calculate and create average income statement. +# Revenue, energy bills are average of historical data. +# Other items are calculated by historical characters. +# Args: +# average_revenue (float): average revenue calculated from historical income statements +# annual_bills (dictionary): dictionary of float value of average annual bills, for 4 utility_types +# characters (dictionary): 6 characters calculated from historical income statements +# Final instance is a single year income statement without named year + +# Description: +# characters = { +# 'start_year': (int) 2014, +# 'end_year': (int) 2016, +# 'cagr': (float) 0.054, +# 'other_utility_percent': (float) 0.020 == 2.0% +# 'non_utility_expense_percent': (float) 0.030 == 3.0% +# 'revenue_average': (float) 3000.00 +# } +# annual_bills = {'electricity': 100.0, 'oil': 200.0, 'gas': 300.0, 'water': 400.0} +# """ +# self.revenue = average_revenue +# self.electricity_opex = annual_bills['electricity'] +# self.gas_opex = annual_bills['gas'] +# self.oil_opex = annual_bills['oil'] +# self.water_opex = annual_bills['water'] +# self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex +# self.other_utility = self.revenue * characters['other_utility_percent'] +# self.non_utility_expense = self.revenue * characters[ +# 'non_utility_expense_percent'] +# self.utility_expense = self.energy_opex + self.other_utility +# self.net_non_energy_opex = self.other_utility + self.non_utility_expense +# self.total_opex = self.energy_opex + self.net_non_energy_opex +# self.noi = self.revenue - self.total_opex + +# # def assign_next(self, income_statement_characters, +# # bill_overview_organized): +# # """ +# # !!! this function will be deleted in the next version!! +# # """ +# # self.electricity_opex = bill_overview_organized['electricity'][ +# # self.year] +# # self.gas_opex = bill_overview_organized['gas'][self.year] +# # self.oil_opex = bill_overview_organized['oil'][self.year] +# # self.water_opex = bill_overview_organized['water'][self.year] +# # self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex +# # self.other_utility = self.revenue * income_statement_characters[ +# # 'other_utility_percent'] +# # self.non_utility_expense = self.revenue * income_statement_characters[ +# # 'non_utility_expense_percent'] +# # self.utility_expense = self.energy_opex + self.other_utility +# # self.net_non_energy_opex = self.other_utility + self.non_utility_expense +# # self.total_opex = self.energy_opex + self.net_non_energy_opex +# # self.noi = self.revenue - self.total_opex + + +# def convert_income_statement_class(income_statement_class): +# """ +# Convert single year income statement object into a dictionary format +# Args: +# income_statement_class (object): single year income statement object +# Return: +# income_statement_dict (dictionary) +# """ +# income_statement_dict = { +# 'year': income_statement_class.year, +# 'revenue': income_statement_class.revenue, +# 'utility_expense': income_statement_class.utility_expense, +# 'energy_opex': income_statement_class.energy_opex, +# 'electricity_opex': income_statement_class.electricity_opex, +# 'gas_opex': income_statement_class.gas_opex, +# 'oil_opex': income_statement_class.oil_opex, +# 'water_opex': income_statement_class.water_opex, +# 'other_utility': income_statement_class.other_utility, +# 'non_utility_expense': income_statement_class.non_utility_expense, +# 'net_non_energy_opex': income_statement_class.net_non_energy_opex, +# 'total_opex': income_statement_class.total_opex, +# 'noi': income_statement_class.noi +# } +# return income_statement_dict + + +# class Income_Statement_Next(): +# """ +# Create single year income_statement object, +# with input of last year data, income statement characters, and projected annual bill +# """ + +# def __init__(self, year, last_revenue, growth_rate_flag, characters, +# annual_bill_table): +# """ +# Calculation is done in initiation. +# Args: +# year (int): the year that will be projected on +# last_revenue (float): last year revenue +# growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average +# characters (dictionary): 6 characters calculated from historical income statements +# annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types +# Final instance is a single year income_statement object + +# Description: +# characters = { +# 'start_year': (int) 2014, +# 'end_year': (int) 2016, +# 'cagr': (float) 0.054, +# 'other_utility_percent': (float) 0.020 == 2.0% +# 'non_utility_expense_percent': (float) 0.035 == 3.5% +# 'revenue_average': (float) 3000.00 +# } +# annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} +# electricity_bill = {2014: 100, 2015:200, ...} +# """ +# self.year = year + +# if growth_rate_flag == -1.0: # growth_rate_flag == average +# current_revenue = characters['revenue_average'] +# else: +# if growth_rate_flag == -2.0: # growth_rate_flag == cagr +# growth_rate = characters['cagr'] +# else: +# growth_rate = growth_rate_flag # growth_rate_flag == 0.00, 0.01, ... +# current_revenue = last_revenue * (1 + growth_rate) +# self.revenue = current_revenue +# self.electricity_opex = annual_bill_table['electricity'][self.year] +# self.gas_opex = annual_bill_table['gas'][self.year] +# self.oil_opex = annual_bill_table['oil'][self.year] +# self.water_opex = annual_bill_table['water'][self.year] +# self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex +# self.other_utility = self.revenue * characters['other_utility_percent'] +# self.non_utility_expense = self.revenue * characters[ +# 'non_utility_expense_percent'] +# self.utility_expense = self.energy_opex + self.other_utility +# self.net_non_energy_opex = self.other_utility + self.non_utility_expense +# self.total_opex = self.energy_opex + self.net_non_energy_opex +# self.noi = self.revenue - self.total_opex + + +# class Income_Statement_Table(): +# """ +# Create a income statement table, containing multiple years, including historical and projected data +# Key Attributes are: +# hist_table (list): list of single year income_statement objects, containing historical data +# table(list): list of single year income_statement objects, containing historical and projected data +# """ + +# def __init__(self, raw_income_input, annual_bill_table): +# """ +# Create hist_table to store historical income statement data, and calculate some key characters. +# Args: +# raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year +# annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + +# Key Attributes: +# cagr (float): compound annual growth rate +# other_utility_percent (float): percentage, (average other_utility / average revenue) +# non_utility_expense_percent (float): percentage, (average non_utility_expense_percent / average revenue) +# hist_table (list): list of single year income_statement objects, containing historical data +# table(list): list of single year income_statement objects, containing historical and projected data +# characters (dictionary): contains key characters determined from historical data. Is used to project + +# Description: +# raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} +# annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} +# electricity_bill = {2014: 100, 2015:200, ...} +# characters = { +# 'start_year': (int) 2014, +# 'end_year': (int) 2016, +# 'cagr': (float) 0.054, +# 'other_utility_percent': (float) 0.02, +# 'non_utility_expense_percent': (float) 0.03, +# 'revenue_average': (float) 3000.00 +# } +# """ +# self.hist_start_year = None +# self.hist_end_year = None +# self.cagr = 0.00 +# self.other_utility_percent = 0.00 +# self.non_utility_expense_percent = 0.00 +# self.revenue_average = 0.00 # average revenue +# self.hist_table = [] +# self.table = [] +# self.characters = {} + +# for year in raw_income_input: +# current_income_statement = Income_Statement() +# current_income_statement.put_hist(year, raw_income_input[year], +# annual_bill_table) +# self.hist_table.append(current_income_statement) + +# sorted_income_hist_year = sorted(raw_income_input) +# self.hist_start_year = sorted_income_hist_year[0] +# self.hist_end_year = sorted_income_hist_year[-1] +# # if start_year == None: return None +# self.cagr = cal_cagr( +# raw_income_input[self.hist_start_year]['revenue'], +# raw_income_input[self.hist_end_year]['revenue'], +# self.hist_end_year - self.hist_start_year) + +# revenue_sum = 0 +# other_utility_sum = 0 +# non_utility_expense_sum = 0 +# for current_income_statement in self.hist_table: +# revenue_sum += current_income_statement.revenue +# other_utility_sum += current_income_statement.other_utility +# non_utility_expense_sum += current_income_statement.non_utility_expense + +# self.other_utility_percent = other_utility_sum / revenue_sum +# self.non_utility_expense_percent = non_utility_expense_sum / revenue_sum +# self.revenue_average = revenue_sum / ( +# self.hist_end_year - self.hist_start_year + 1) + +# self.table = self.hist_table +# self.characters = { +# 'start_year': self.hist_start_year, +# 'end_year': self.hist_end_year, +# 'cagr': self.cagr, +# 'other_utility_percent': self.other_utility_percent, +# 'non_utility_expense_percent': self.non_utility_expense_percent, +# 'revenue_average': self.revenue_average +# } + +# def project(self, growth_rate_flag, analysis_date, annual_bill_table): +# """ +# Project future income statement. Append multiple single year income_statement objects to self.table +# Args: +# growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average +# analysis_date (dictionary): proforma's starting date and the years of proforma +# annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types +# Return: +# list: list of single year income_statement objects, containing historical and projection +# Note: +# project() overwrites existing projection data +# """ +# # characters = copy.deepcopy(self.characters) +# proforma_year = form_bill_year(analysis_date['proforma_start'], +# analysis_date['proforma_duration']) +# current_table = copy.deepcopy(self.hist_table) +# for year in proforma_year: +# last_revenue = current_table[-1].revenue +# if year <= current_table[-1].year: +# continue +# current_income_statement = Income_Statement_Next( +# year, last_revenue, growth_rate_flag, self.characters, +# annual_bill_table) +# current_table.append(current_income_statement) +# self.table = current_table +# # return current_table + +# def get_hist_table(self): +# """ +# Get historical table, in dictionary formatting +# Return: +# dictionary: dict of dict of income statement. Key is year +# Description: +# hist_table_dict = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} +# """ +# hist_table_dict = {} +# for current_income_statement in self.hist_table: +# hist_table_dict[current_income_statement.year] = { +# 'year': +# current_income_statement.year, +# 'revenue': +# current_income_statement.revenue, +# 'utility_expense': +# current_income_statement.utility_expense, +# 'energy_opex': +# current_income_statement.energy_opex, +# 'electricity_opex': +# current_income_statement.electricity_opex, +# 'gas_opex': +# current_income_statement.gas_opex, +# 'oil_opex': +# current_income_statement.oil_opex, +# 'water_opex': +# current_income_statement.water_opex, +# 'other_utility': +# current_income_statement.other_utility, +# 'non_utility_expense': +# current_income_statement.non_utility_expense, +# 'net_non_energy_opex': +# current_income_statement.net_non_energy_opex, +# 'total_opex': +# current_income_statement.total_opex, +# 'noi': +# current_income_statement.noi +# } +# return hist_table_dict + +# def get_cagr(self): +# """ +# Get compound annual growth rate +# Return: +# float: compound annual growth rate +# """ +# return copy.deepcopy(self.cagr) + +# def get_average(self): +# """ +# Get average income statement. Only need initialized Income_Statement_Table +# Return: +# dictionary: dict of average income statement, Keys are income statement items. +# Description: +# output_dict = {'year': None, 'revenue': 95000.0, ... ,'noi': 35166.666666666657} +# """ +# # current_year = 'Average' +# current_income_statement = Income_Statement() +# average_revenue = mean(list(i_s.revenue for i_s in self.hist_table)) +# annual_bills = { +# 'electricity': +# mean(list(i_s.electricity_opex for i_s in self.hist_table)), +# 'gas': +# mean(list(i_s.gas_opex for i_s in self.hist_table)), +# 'oil': +# mean(list(i_s.oil_opex for i_s in self.hist_table)), +# 'water': +# mean(list(i_s.water_opex for i_s in self.hist_table)) +# } +# current_income_statement.put_average(average_revenue, annual_bills, +# self.characters) + +# return convert_income_statement_class(current_income_statement) + +# def get_single_year(self, year): +# """ +# Get single year income statement from self.table. Table can either contain or not contain projection +# Args: +# year (int): the year that need to be extracted +# Return: +# dict: single year income statement. Keys are income statement items. +# Note: +# if the target year is not in self.table, then return None. +# """ +# for current_income_statement in self.table: +# if current_income_statement.year == year: +# return { +# 'year': +# current_income_statement.year, +# 'revenue': +# current_income_statement.revenue, +# 'utility_expense': +# current_income_statement.utility_expense, +# 'energy_opex': +# current_income_statement.energy_opex, +# 'electricity_opex': +# current_income_statement.electricity_opex, +# 'gas_opex': +# current_income_statement.gas_opex, +# 'oil_opex': +# current_income_statement.oil_opex, +# 'water_opex': +# current_income_statement.water_opex, +# 'other_utility': +# current_income_statement.other_utility, +# 'non_utility_expense': +# current_income_statement.non_utility_expense, +# 'net_non_energy_opex': +# current_income_statement.net_non_energy_opex, +# 'total_opex': +# current_income_statement.total_opex, +# 'noi': +# current_income_statement.noi +# } +# return None + +# def get_noi_dict(self): +# """ +# Get a dictionary of net operating income. +# Return: +# dictionary: net operating income for each year in income statement table. Key is year +# """ +# noi_dict = {} +# for current_income_statement in self.table: +# noi_dict[ +# current_income_statement.year] = current_income_statement.noi +# return noi_dict + +# def get_first_year_noi(self, commission_date): +# """ +# Get first year noi after commissioning year, from post_saving income statement +# Args: +# commission_date (date): construction finishing date. Saving starts NEXT month/year +# Return: +# float: noi of the next year after commission date +# """ +# first_year = commission_date.year + 1 +# for current_income_statement in self.table: +# if current_income_statement.year == first_year: +# return current_income_statement.noi + +# def get_total_energy_dict(self): +# total_energy_dict = {} +# for current_income_statement in self.table: +# total_energy_dict[ +# current_income_statement.year] = current_income_statement.energy_opex +# return total_energy_dict diff --git a/bpfin/financials/financial_lib.py b/bpfin/financials/financial_lib.py index 796c5ed..46fde65 100644 --- a/bpfin/financials/financial_lib.py +++ b/bpfin/financials/financial_lib.py @@ -30,28 +30,29 @@ def organize_bill_overview(bill_overview, analysis_date): bill_dict[4] = bill_overview['water'] # water_bill average_bill_dict = {} - for i in range(4): - if bill_dict[i + 1][1]: + for i in range(1, 5): + # the whole function needs re-write, not only i + 1 stuff + if bill_dict[i][1]: sum_bill = 0 year_number = 0 - for year in bill_dict[i + 1][0]: - if bill_dict[i + 1][0][year] != 0: - sum_bill += bill_dict[i + 1][0][year] + for year in bill_dict[i][0]: + if bill_dict[i][0][year] != 0: + sum_bill += bill_dict[i][0][year] year_number += 1 if year_number != 0: - average_bill_dict[i + 1] = float(sum_bill) / year_number + average_bill_dict[i] = float(sum_bill) / year_number else: - average_bill_dict[i + 1] = 0 - # average_bill_dict[i + 1] = float(sum(bill_dict[i + 1][0][year] for year in bill_dict[i + 1][0])) / len(bill_dict[i + 1][0]) + average_bill_dict[i] = 0 + # average_bill_dict[i] = float(sum(bill_dict[i][0][year] for year in bill_dict[i][0])) / len(bill_dict[i][0]) for year in proforma_year: - for i in range(4): - if year in bill_dict[i + 1][0]: + for i in range(1, 5): + if year in bill_dict[i][0]: pass else: - bill_dict[i + 1][0][year] = average_bill_dict[i + 1] - if bill_dict[i + 1][0][year] == 0: - bill_dict[i + 1][0][year] = average_bill_dict[i + 1] + bill_dict[i][0][year] = average_bill_dict[i] + if bill_dict[i][0][year] == 0: + bill_dict[i][0][year] = average_bill_dict[i] bill_overview_organized = { 'electricity': bill_dict[1][0], 'gas': bill_dict[2][0], diff --git a/bpfin/financials/financial_saving.py b/bpfin/financials/financial_saving.py index a3d78ad..72c769c 100644 --- a/bpfin/financials/financial_saving.py +++ b/bpfin/financials/financial_saving.py @@ -1,375 +1,375 @@ -""" -this is replacement for saving.py -The inputs would incorporate bill_list = [Bill objects] -""" -import datetime -import copy -from bpfin.utilbills.bill_lib import cal_last_day -from bpfin.utilbills.bill_lib import annualizing_projection -from bpfin.utilbills.bill_lib import form_year_calendar -from bpfin.utilbills.bill_lib import form_bill_calendar -from bpfin.utilbills.bill_post_proj_rough import bill_post_proj_rough -from bpfin.tests.testdata import sample_data as db -from bpfin.tests.testdata import feature_data as fdb - - -UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] - - -class Saving(): - """ - Calculate savings and project bills with commissioning date considered. For one utility type - Attributes: - is_manual_input (boolean): label indicating is bill input by scraper or manual. Yes == manual input - commission_date (date): the date that construction work finished, saving starts at NEXT month - proforma_date (list): list of dates, months of pro-forma time period - proforma_usage (list) : list of monthly usage, for pro-forma period - calculated by put_monthly_proforma() - Data before commissioning date == historical - Data after commissioning date == post saving data - proforma_charge (list) : list of monthly charge, for pro-forma period - calculated by put_monthly_proforma() - Data before commissioning date == historical - Data after commissioning date == post saving data - proforma_saving_usage (list) : monthly savings on usage, for pro-forma period - calculated by put_monthly_proforma() - Data before commissioning date == historical - Data after commissioning date == post saving data - proforma_saving_charge (list) : monthly savings on usage, for pro-forma period - calculated by put_monthly_proforma() - Data before commissioning date == historical - Data after commissioning date == post saving data - first_year_saving_usage (float) : first 12 months saving on usage. - first_year_saving_charge (float) : first 12 months saving on charge. If data is annual, saving is on NEXT year - annual_proforma_usage (dictionary) : key is year, value is float, annual usage - annual_proforma_charge (dictionary) : key is year, value is float, annual charge - first_year_prior_charge (float): first 12 months prior_saving charge. If data is annual, charge is for NEXT year - percent_saving (float): percentage saving on first year charge. 0.2 == 20% - """ - def __init__(self, is_manual_input, commission_date, proforma_date): - """ - Initiate Saving object. - Args: - is_manual_input (boolean): label indicating is bill input by scraper or manual. Yes == manual input - commission_date (date): the date that construction work finished, saving starts at NEXT month - proforma_date (list): list of dates, months of pro-forma time period - """ - self.is_manual_input = is_manual_input - self.commission_date = commission_date - self.proforma_date = proforma_date - self.proforma_usage = None - self.proforma_charge = None - self.proforma_saving_usage = None - self.proforma_saving_charge = None - self.first_year_saving_charge = None - self.first_year_saving_usage = None - self.annual_proforma_usage = None - self.annual_proforma_charge = None - self.first_year_prior_charge = None - self.percent_saving = 0 - - def put_monthly_proforma(self, prior_saving_usage, post_saving_usage, prior_saving_charge, post_saving_charge): - """ - Put monthly prior_saving and post_saving, generate: monthly usage and charge for pro-forma purpose. - Also generate first year savings, and annual usage and annual charge - - Args: - prior_saving_usage (list): list of float, usage without (prior to) savings - post_saving_usage (list): list of float, usage with (post to) savings - prior_saving_charge (list): list of float, charge without (prior to) savings - post_saving_charge (list): list of float, charge with (post to) savings - """ - if not self.is_manual_input: - saving_start_date = datetime.date( - self.commission_date.year, - self.commission_date.month, - cal_last_day(self.commission_date.year, self.commission_date.month)) - - saving_usage_dict = {} - saving_charge_dict = {} - proforma_usage_dict = {} - proforma_charge_dict = {} - first_year_saving_usage = 0 - first_year_saving_charge = 0 - first_yr_counter = 0 - first_year_prior_charge = 0 - for date, prior_usage, post_usage, prior_charge, post_charge in zip( - self.proforma_date, - prior_saving_usage, - post_saving_usage, - prior_saving_charge, - post_saving_charge): - if date > saving_start_date: - saving_usage_dict[date] = prior_usage - post_usage - saving_charge_dict[date] = prior_charge - post_charge - proforma_usage_dict[date] = post_usage - proforma_charge_dict[date] = post_charge - if first_yr_counter <= 12: - first_year_saving_usage += prior_usage - post_usage - first_year_saving_charge += prior_charge - post_charge - first_year_prior_charge += prior_charge - first_yr_counter += 1 - else: - saving_usage_dict[date] = 0 - saving_charge_dict[date] = 0 - proforma_usage_dict[date] = prior_usage - proforma_charge_dict[date] = prior_charge - - self.proforma_usage = list(proforma_usage_dict.values()) - self.proforma_charge = list(proforma_charge_dict.values()) - self.proforma_saving_usage = list(saving_usage_dict.values()) - self.proforma_saving_charge = list(saving_charge_dict.values()) - self.first_year_saving_usage = first_year_saving_usage - self.first_year_saving_charge = first_year_saving_charge - self.annual_proforma_usage = annualizing_projection(self.proforma_date, self.proforma_charge) - self.annual_proforma_charge = annualizing_projection(self.proforma_date, self.proforma_charge) - self.first_year_prior_charge = first_year_prior_charge - self.percent_saving = first_year_saving_charge / first_year_prior_charge - else: - raise ValueError('Put_monthly_saving, please check: manual_input ==', self.is_manual_input) - - def put_annual_proforma(self, annual_charge, percent_saving): - """ - Put annual prior_saving charge, calculate saving on charge and overall percentage saving - - Args: - annual_charge (dictionary): dict of annual prior saving charge, {2014: 100, ...}, with future estimated - percent_saving (float): percent of saving, 0.030 == 3.0% - """ - if self.is_manual_input: - annual_saving_dict = {} - annual_charge_dict = {} - proforma_year = form_year_calendar(self.proforma_date[0], self.proforma_date[-1]) - for year in proforma_year: - if year < self.commission_date.year: - annual_saving_dict[year] = 0 - annual_charge_dict[year] = annual_charge[year] - if year > self.commission_date.year: - annual_saving_dict[year] = annual_charge[year] * percent_saving - annual_charge_dict[year] = annual_charge[year] * (1 - percent_saving) - if year == self.commission_date.year: - effective_saving = (12 - self.commission_date.month) / 12 * percent_saving # partial year saving - annual_saving_dict[year] = annual_charge[year] * effective_saving - annual_charge_dict[year] = annual_charge[year] * (1 - effective_saving) - - first_year = self.commission_date.year + 1 - self.first_year_saving_charge = annual_saving_dict[first_year] - self.annual_proforma_charge = annual_charge_dict - self.first_year_prior_charge = annual_charge[first_year] - self.percent_saving = ( - annual_saving_dict[first_year] / annual_charge[first_year] if annual_charge[first_year] != 0 else 0) - else: - raise ValueError('Put_annual_saving, please check: manual_input ==', self.is_manual_input) - - def get_proforma_charge(self): - """ - Get proforma_charge, for monthly pro-forma, not availabel for annual - Return: - list: proforma_charge - """ - return self.proforma_charge - - def get_proforma_saving_charge(self): - """ - Get proforma_saving_charge, for monthly pro-forma, not availabel for annual - Return: - list: proforma_saving_charge - """ - return self.proforma_saving_charge - - def get_percent_saving(self): - """ - Get percentage saving on charge - Return: - float: overall percentage saving on charge - """ - return self.percent_saving - - def get_first_year_saving_charge(self): - """ - Get first year saving - Return: - float: sum of first 12 months of savings - """ - return self.first_year_saving_charge - - def get_first_year_prior_charge(self): - """ - Get prior charge for first year after commissioning - Return: - float: prior charge for first year after commissioning - """ - return self.first_year_prior_charge - - def get_annual_proforma_charge(self): - """ - Calculate and return annual pro-forma charge - Return: - dictionary: dict of annual item, {year: annual charge} - """ - return self.annual_proforma_charge - - -class Saving_Overview(): - """ - Generate saving schedule, annual usages and charges for 4 utility types - Also calculate percentage saving on usages and charges. - If rate plan applies, usage % saving can be different from charge % saving - Saving_Overview should be scenario based, it is an "attribute" of a scenario - - Attributes: - bill_list (list): list of Bill objects, each element is a Bill class object. Contains 4 utilities - # manual_input_dict(dictionary): key is utility type, value is boolean. True == mannual_input - prior_annual_bill_table (dictionary): annual bill, for 4 utility_types. See description for detail - proforma_date (list): list of dates, months of pro-forma time period - commission_date (date): the date that construction work finished, saving starts at NEXT month - utility_saving_dict (dictionary): dict of objects, each element is a Saving class object. Contains 4 utilities - saving_percent_charge (dictionary): key is utility type, value is float {'electricity': float} - total_first_year_saving (float): dollar saving for the first 12 month after commissioning - total_saving_percent (float): percentage saving on charge for the first 12 month after commissioning - - Description: - prior_annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, ...} - electricity_bill = {2014: 100, 2015:200, ...} - """ - - def __init__( - self, - bill_list, - analysis_date, # consider to replace it with proforma_date - commission_date): - """ - Initiate Saving_Overview. - Take in manual_input_dict, annual and monthly bills, commission_date and percentage savings - Generate Saving objects, pro-forma date period - - Args: - bill_list (list): list of Bill objects - analysis_date (dictionary): proforma's starting date and the years of proforma - commission_date (date): the date that construction work finished, saving starts at NEXT month - - To Do: validate bill_list - """ - self.prior_annual_bill_table = {} - self.utility_saving_dict = {'electricity': None, 'gas': None, 'oil': None, 'water': None} - self.saving_percent_charge = {'electricity': None, 'gas': None, 'oil': None, 'water': None} - - self.bill_list = bill_list - for bill in bill_list: - self.prior_annual_bill_table[bill.utility_type] = bill.get_annual_bill() - self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] - self.commission_date = commission_date - self.total_first_year_saving = 0 # dollar - self.total_saving_percent = 0 # dollar percentage - - def put_saving(self, percent_saving_dict, full_saving_dict): - """ - Put inputs to generage saving data. - Also calculate overall saving on charge, and percentage saving - - Args: - percent_saving_dict (dictionary): dict of float, percentage saving for 4 utility types - full_saving_dict (dictionary): dict of undefined saving, from engineering saving analysis. can be None - """ - total_first_year_saving = 0 - total_first_year_prior_charge = 0 - for bill in self.bill_list: - utility = bill.utility_type - is_manual_input = bill.is_manual_input - - prior_month_bill = bill.get_prior_proj_rough() - utility_saving = Saving(is_manual_input, self.commission_date, self.proforma_date) - - if utility_saving.is_manual_input: # bill is from manual input, monthly bill not availabel - utility_saving.put_annual_proforma(bill.get_annual_bill(), percent_saving_dict[utility]) - - if not utility_saving.is_manual_input: # bill is from scraper, monthly bill is availabel - if full_saving_dict[utility] is None: # engineering saving is not availabel, use percentage saving - post_month_bill = bill_post_proj_rough(prior_month_bill, percent_saving_dict[utility]) - # else: # engineering saving is availabel. Project monthly_bill with regression. not finished yet - # post_month_bill = bill_proj_reg(prior_utility, full_saving_dict[utility], HDD, CDD, occupancy) - - utility_saving.put_monthly_proforma( - prior_month_bill['usage'], - post_month_bill['usage'], - prior_month_bill['charge'], - post_month_bill['charge']) - - total_first_year_saving += utility_saving.get_first_year_saving_charge() - total_first_year_prior_charge += utility_saving.get_first_year_prior_charge() - self.saving_percent_charge[utility] = utility_saving.get_percent_saving() - self.utility_saving_dict[utility] = utility_saving - - self.total_first_year_saving = total_first_year_saving - self.total_saving_percent = ( - total_first_year_saving / total_first_year_prior_charge if total_first_year_prior_charge != 0 else 0) - - def get_total_first_year_saving(self): - """ - Get first year total dollar saving - This is used for: loan allocation, simple_payback calculation - Retrun: - float: a dollar number - """ - return self.total_first_year_saving - - def get_simple_payback(self, cost): - """ - Get the simple payback for a given project cost. cost / first year dollar saving - Args: - cost (float): project cost, estimated or quoted - Return: - float: simple paback, in year - """ - return float(cost / self.total_first_year_saving if self.total_first_year_saving != 0 else 0) - - def get_total_saving_percent(self): - """ - Get the total saving percentage. First dollar saving / First year prior_saving charge - Return: - float: total dollar saving percentage - """ - return self.total_saving_percent - - def get_post_annual_bill_table(self): - """ - Generate annual bill table for pro-forma, consiering commission date. - Return: - dictionary: dict of dict of annual charges, for 4 utility types - Description: - output = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} - electricity_bill = {2014: 100, 2015:200, ...} - } - """ - post_annual_bill_table = {} - for utility_type in UTILITY_TYPE_LIST: - post_annual_bill_table[utility_type] = copy.deepcopy( - self.utility_saving_dict[utility_type].get_annual_proforma_charge()) - return post_annual_bill_table - - def get_utility_annual_saving_charge(self, utility_type): - """ - Get the proforma annual saving on charge for a utility_type - Args: - utility_type (string): 'electricity', 'gas', 'oil', 'water' - Return: - dictionary: {year: saving on charge} - """ - prior_charge_dict = copy.deepcopy(self.prior_annual_bill_table[utility_type]) - post_charge_dict = copy.deepcopy(self.utility_saving_dict[utility_type].get_annual_proforma_charge()) - saving_dict = {} - for year in prior_charge_dict: - saving_dict[year] = prior_charge_dict[year] - post_charge_dict[year] - return saving_dict - - -# **** ugly test **** -# sav_o = Saving_Overview(fdb.bill_list, fdb.analysis_date, db.commission_date) -# sav_o.put_saving(db.percent_saving_dict, db.full_saving_dict) - -# print('\n first_year_dollar_saving =', sav_o.get_total_first_year_saving()) -# print('\n simple payback =', sav_o.get_simple_payback(150000)) -# print('\n total saving percent =', sav_o.get_total_saving_percent()) -# print('\n electricity_annual_saving_charge', sav_o.get_utility_annual_saving_charge('electricity')) -# print(sav_o.get_post_annual_bill_table()) +# """ +# this is replacement for saving.py +# The inputs would incorporate bill_list = [Bill objects] +# """ +# import datetime +# import copy +# from bpfin.utilbills.bill_lib import cal_last_day +# from bpfin.utilbills.bill_lib import annualizing_projection +# from bpfin.utilbills.bill_lib import form_year_calendar +# from bpfin.utilbills.bill_lib import form_bill_calendar +# from bpfin.utilbills.bill_post_proj_rough import bill_post_proj_rough +# from bpfin.tests.testdata import sample_data as db +# from bpfin.tests.testdata import feature_data as fdb + + +# UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] + + +# class Saving(): +# """ +# Calculate savings and project bills with commissioning date considered. For one utility type +# Attributes: +# is_manual_input (boolean): label indicating is bill input by scraper or manual. Yes == manual input +# commission_date (date): the date that construction work finished, saving starts at NEXT month +# proforma_date (list): list of dates, months of pro-forma time period +# proforma_usage (list) : list of monthly usage, for pro-forma period +# calculated by put_monthly_proforma() +# Data before commissioning date == historical +# Data after commissioning date == post saving data +# proforma_charge (list) : list of monthly charge, for pro-forma period +# calculated by put_monthly_proforma() +# Data before commissioning date == historical +# Data after commissioning date == post saving data +# proforma_saving_usage (list) : monthly savings on usage, for pro-forma period +# calculated by put_monthly_proforma() +# Data before commissioning date == historical +# Data after commissioning date == post saving data +# proforma_saving_charge (list) : monthly savings on usage, for pro-forma period +# calculated by put_monthly_proforma() +# Data before commissioning date == historical +# Data after commissioning date == post saving data +# first_year_saving_usage (float) : first 12 months saving on usage. +# first_year_saving_charge (float) : first 12 months saving on charge. If data is annual, saving is on NEXT year +# annual_proforma_usage (dictionary) : key is year, value is float, annual usage +# annual_proforma_charge (dictionary) : key is year, value is float, annual charge +# first_year_prior_charge (float): first 12 months prior_saving charge. If data is annual, charge is for NEXT year +# percent_saving (float): percentage saving on first year charge. 0.2 == 20% +# """ +# def __init__(self, is_manual_input, commission_date, proforma_date): +# """ +# Initiate Saving object. +# Args: +# is_manual_input (boolean): label indicating is bill input by scraper or manual. Yes == manual input +# commission_date (date): the date that construction work finished, saving starts at NEXT month +# proforma_date (list): list of dates, months of pro-forma time period +# """ +# self.is_manual_input = is_manual_input +# self.commission_date = commission_date +# self.proforma_date = proforma_date +# self.proforma_usage = None +# self.proforma_charge = None +# self.proforma_saving_usage = None +# self.proforma_saving_charge = None +# self.first_year_saving_charge = None +# self.first_year_saving_usage = None +# self.annual_proforma_usage = None +# self.annual_proforma_charge = None +# self.first_year_prior_charge = None +# self.percent_saving = 0 + +# def put_monthly_proforma(self, prior_saving_usage, post_saving_usage, prior_saving_charge, post_saving_charge): +# """ +# Put monthly prior_saving and post_saving, generate: monthly usage and charge for pro-forma purpose. +# Also generate first year savings, and annual usage and annual charge + +# Args: +# prior_saving_usage (list): list of float, usage without (prior to) savings +# post_saving_usage (list): list of float, usage with (post to) savings +# prior_saving_charge (list): list of float, charge without (prior to) savings +# post_saving_charge (list): list of float, charge with (post to) savings +# """ +# if not self.is_manual_input: +# saving_start_date = datetime.date( +# self.commission_date.year, +# self.commission_date.month, +# cal_last_day(self.commission_date.year, self.commission_date.month)) + +# saving_usage_dict = {} +# saving_charge_dict = {} +# proforma_usage_dict = {} +# proforma_charge_dict = {} +# first_year_saving_usage = 0 +# first_year_saving_charge = 0 +# first_yr_counter = 0 +# first_year_prior_charge = 0 +# for date, prior_usage, post_usage, prior_charge, post_charge in zip( +# self.proforma_date, +# prior_saving_usage, +# post_saving_usage, +# prior_saving_charge, +# post_saving_charge): +# if date > saving_start_date: +# saving_usage_dict[date] = prior_usage - post_usage +# saving_charge_dict[date] = prior_charge - post_charge +# proforma_usage_dict[date] = post_usage +# proforma_charge_dict[date] = post_charge +# if first_yr_counter <= 12: +# first_year_saving_usage += prior_usage - post_usage +# first_year_saving_charge += prior_charge - post_charge +# first_year_prior_charge += prior_charge +# first_yr_counter += 1 +# else: +# saving_usage_dict[date] = 0 +# saving_charge_dict[date] = 0 +# proforma_usage_dict[date] = prior_usage +# proforma_charge_dict[date] = prior_charge + +# self.proforma_usage = list(proforma_usage_dict.values()) +# self.proforma_charge = list(proforma_charge_dict.values()) +# self.proforma_saving_usage = list(saving_usage_dict.values()) +# self.proforma_saving_charge = list(saving_charge_dict.values()) +# self.first_year_saving_usage = first_year_saving_usage +# self.first_year_saving_charge = first_year_saving_charge +# self.annual_proforma_usage = annualizing_projection(self.proforma_date, self.proforma_charge) +# self.annual_proforma_charge = annualizing_projection(self.proforma_date, self.proforma_charge) +# self.first_year_prior_charge = first_year_prior_charge +# self.percent_saving = first_year_saving_charge / first_year_prior_charge +# else: +# raise ValueError('Put_monthly_saving, please check: manual_input ==', self.is_manual_input) + +# def put_annual_proforma(self, annual_charge, percent_saving): +# """ +# Put annual prior_saving charge, calculate saving on charge and overall percentage saving + +# Args: +# annual_charge (dictionary): dict of annual prior saving charge, {2014: 100, ...}, with future estimated +# percent_saving (float): percent of saving, 0.030 == 3.0% +# """ +# if self.is_manual_input: +# annual_saving_dict = {} +# annual_charge_dict = {} +# proforma_year = form_year_calendar(self.proforma_date[0], self.proforma_date[-1]) +# for year in proforma_year: +# if year < self.commission_date.year: +# annual_saving_dict[year] = 0 +# annual_charge_dict[year] = annual_charge[year] +# if year > self.commission_date.year: +# annual_saving_dict[year] = annual_charge[year] * percent_saving +# annual_charge_dict[year] = annual_charge[year] * (1 - percent_saving) +# if year == self.commission_date.year: +# effective_saving = (12 - self.commission_date.month) / 12 * percent_saving # partial year saving +# annual_saving_dict[year] = annual_charge[year] * effective_saving +# annual_charge_dict[year] = annual_charge[year] * (1 - effective_saving) + +# first_year = self.commission_date.year + 1 +# self.first_year_saving_charge = annual_saving_dict[first_year] +# self.annual_proforma_charge = annual_charge_dict +# self.first_year_prior_charge = annual_charge[first_year] +# self.percent_saving = ( +# annual_saving_dict[first_year] / annual_charge[first_year] if annual_charge[first_year] != 0 else 0) +# else: +# raise ValueError('Put_annual_saving, please check: manual_input ==', self.is_manual_input) + +# def get_proforma_charge(self): +# """ +# Get proforma_charge, for monthly pro-forma, not availabel for annual +# Return: +# list: proforma_charge +# """ +# return self.proforma_charge + +# def get_proforma_saving_charge(self): +# """ +# Get proforma_saving_charge, for monthly pro-forma, not availabel for annual +# Return: +# list: proforma_saving_charge +# """ +# return self.proforma_saving_charge + +# def get_percent_saving(self): +# """ +# Get percentage saving on charge +# Return: +# float: overall percentage saving on charge +# """ +# return self.percent_saving + +# def get_first_year_saving_charge(self): +# """ +# Get first year saving +# Return: +# float: sum of first 12 months of savings +# """ +# return self.first_year_saving_charge + +# def get_first_year_prior_charge(self): +# """ +# Get prior charge for first year after commissioning +# Return: +# float: prior charge for first year after commissioning +# """ +# return self.first_year_prior_charge + +# def get_annual_proforma_charge(self): +# """ +# Calculate and return annual pro-forma charge +# Return: +# dictionary: dict of annual item, {year: annual charge} +# """ +# return self.annual_proforma_charge + + +# class Saving_Overview(): +# """ +# Generate saving schedule, annual usages and charges for 4 utility types +# Also calculate percentage saving on usages and charges. +# If rate plan applies, usage % saving can be different from charge % saving +# Saving_Overview should be scenario based, it is an "attribute" of a scenario + +# Attributes: +# bill_list (list): list of Bill objects, each element is a Bill class object. Contains 4 utilities +# # manual_input_dict(dictionary): key is utility type, value is boolean. True == mannual_input +# prior_annual_bill_table (dictionary): annual bill, for 4 utility_types. See description for detail +# proforma_date (list): list of dates, months of pro-forma time period +# commission_date (date): the date that construction work finished, saving starts at NEXT month +# utility_saving_dict (dictionary): dict of objects, each element is a Saving class object. Contains 4 utilities +# saving_percent_charge (dictionary): key is utility type, value is float {'electricity': float} +# total_first_year_saving (float): dollar saving for the first 12 month after commissioning +# total_saving_percent (float): percentage saving on charge for the first 12 month after commissioning + +# Description: +# prior_annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, ...} +# electricity_bill = {2014: 100, 2015:200, ...} +# """ + +# def __init__( +# self, +# bill_list, +# analysis_date, # consider to replace it with proforma_date +# commission_date): +# """ +# Initiate Saving_Overview. +# Take in manual_input_dict, annual and monthly bills, commission_date and percentage savings +# Generate Saving objects, pro-forma date period + +# Args: +# bill_list (list): list of Bill objects +# analysis_date (dictionary): proforma's starting date and the years of proforma +# commission_date (date): the date that construction work finished, saving starts at NEXT month + +# To Do: validate bill_list +# """ +# self.prior_annual_bill_table = {} +# self.utility_saving_dict = {'electricity': None, 'gas': None, 'oil': None, 'water': None} +# self.saving_percent_charge = {'electricity': None, 'gas': None, 'oil': None, 'water': None} + +# self.bill_list = bill_list +# for bill in bill_list: +# self.prior_annual_bill_table[bill.utility_type] = bill.get_annual_bill() +# self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] +# self.commission_date = commission_date +# self.total_first_year_saving = 0 # dollar +# self.total_saving_percent = 0 # dollar percentage + +# def put_saving(self, percent_saving_dict, full_saving_dict): +# """ +# Put inputs to generage saving data. +# Also calculate overall saving on charge, and percentage saving + +# Args: +# percent_saving_dict (dictionary): dict of float, percentage saving for 4 utility types +# full_saving_dict (dictionary): dict of undefined saving, from engineering saving analysis. can be None +# """ +# total_first_year_saving = 0 +# total_first_year_prior_charge = 0 +# for bill in self.bill_list: +# utility = bill.utility_type +# is_manual_input = bill.is_manual_input + +# prior_month_bill = bill.get_prior_proj_rough() +# utility_saving = Saving(is_manual_input, self.commission_date, self.proforma_date) + +# if utility_saving.is_manual_input: # bill is from manual input, monthly bill not availabel +# utility_saving.put_annual_proforma(bill.get_annual_bill(), percent_saving_dict[utility]) + +# if not utility_saving.is_manual_input: # bill is from scraper, monthly bill is availabel +# if full_saving_dict[utility] is None: # engineering saving is not availabel, use percentage saving +# post_month_bill = bill_post_proj_rough(prior_month_bill, percent_saving_dict[utility]) +# # else: # engineering saving is availabel. Project monthly_bill with regression. not finished yet +# # post_month_bill = bill_proj_reg(prior_utility, full_saving_dict[utility], HDD, CDD, occupancy) + +# utility_saving.put_monthly_proforma( +# prior_month_bill['usage'], +# post_month_bill['usage'], +# prior_month_bill['charge'], +# post_month_bill['charge']) + +# total_first_year_saving += utility_saving.get_first_year_saving_charge() +# total_first_year_prior_charge += utility_saving.get_first_year_prior_charge() +# self.saving_percent_charge[utility] = utility_saving.get_percent_saving() +# self.utility_saving_dict[utility] = utility_saving + +# self.total_first_year_saving = total_first_year_saving +# self.total_saving_percent = ( +# total_first_year_saving / total_first_year_prior_charge if total_first_year_prior_charge != 0 else 0) + +# def get_total_first_year_saving(self): +# """ +# Get first year total dollar saving +# This is used for: loan allocation, simple_payback calculation +# Retrun: +# float: a dollar number +# """ +# return self.total_first_year_saving + +# def get_simple_payback(self, cost): +# """ +# Get the simple payback for a given project cost. cost / first year dollar saving +# Args: +# cost (float): project cost, estimated or quoted +# Return: +# float: simple paback, in year +# """ +# return float(cost / self.total_first_year_saving if self.total_first_year_saving != 0 else 0) + +# def get_total_saving_percent(self): +# """ +# Get the total saving percentage. First dollar saving / First year prior_saving charge +# Return: +# float: total dollar saving percentage +# """ +# return self.total_saving_percent + +# def get_post_annual_bill_table(self): +# """ +# Generate annual bill table for pro-forma, consiering commission date. +# Return: +# dictionary: dict of dict of annual charges, for 4 utility types +# Description: +# output = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} +# electricity_bill = {2014: 100, 2015:200, ...} +# } +# """ +# post_annual_bill_table = {} +# for utility_type in UTILITY_TYPE_LIST: +# post_annual_bill_table[utility_type] = copy.deepcopy( +# self.utility_saving_dict[utility_type].get_annual_proforma_charge()) +# return post_annual_bill_table + +# def get_utility_annual_saving_charge(self, utility_type): +# """ +# Get the proforma annual saving on charge for a utility_type +# Args: +# utility_type (string): 'electricity', 'gas', 'oil', 'water' +# Return: +# dictionary: {year: saving on charge} +# """ +# prior_charge_dict = copy.deepcopy(self.prior_annual_bill_table[utility_type]) +# post_charge_dict = copy.deepcopy(self.utility_saving_dict[utility_type].get_annual_proforma_charge()) +# saving_dict = {} +# for year in prior_charge_dict: +# saving_dict[year] = prior_charge_dict[year] - post_charge_dict[year] +# return saving_dict + + +# # **** ugly test **** +# # sav_o = Saving_Overview(fdb.bill_list, fdb.analysis_date, db.commission_date) +# # sav_o.put_saving(db.percent_saving_dict, db.full_saving_dict) + +# # print('\n first_year_dollar_saving =', sav_o.get_total_first_year_saving()) +# # print('\n simple payback =', sav_o.get_simple_payback(150000)) +# # print('\n total saving percent =', sav_o.get_total_saving_percent()) +# # print('\n electricity_annual_saving_charge', sav_o.get_utility_annual_saving_charge('electricity')) +# # print(sav_o.get_post_annual_bill_table()) -- GitLab From 19842d404e8061df4b0b5548d22442ef00a49af6 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 18 May 2017 14:26:05 -0400 Subject: [PATCH 27/30] Rewrite income statement to incorporate Bill class. Formed backend calls for Bill Overview, Income Statement, and the test files, test data --- bpfin/financials/financial_lib.py | 90 ++-- bpfin/lib/back_end_call.py | 57 ++- bpfin/lib/data_generation.py | 0 .../test_financials/test_financial_lib.py | 8 +- .../test_utilbills/test_back_end_call.py | 23 +- bpfin/tests/testdata/data_generation.py | 84 ---- bpfin/tests/testdata/feature_data.py | 406 +++++++++++++++++- bpfin/tests/testdata/sample_data.py | 61 ++- bpfin/utilbills/bill_backend_call.py | 20 - data_generation.py | 1 + 10 files changed, 580 insertions(+), 170 deletions(-) create mode 100644 bpfin/lib/data_generation.py delete mode 100644 bpfin/utilbills/bill_backend_call.py create mode 100644 data_generation.py diff --git a/bpfin/financials/financial_lib.py b/bpfin/financials/financial_lib.py index 46fde65..253c1ec 100644 --- a/bpfin/financials/financial_lib.py +++ b/bpfin/financials/financial_lib.py @@ -259,12 +259,13 @@ class Income_Statement_Table(): table(list): list of single year income_statement objects, containing historical and projected data """ - def __init__(self, raw_income_input, annual_bill_table): + def __init__(self, raw_income_input, annual_bill_table, analysis_date): """ Create hist_table to store historical income statement data, and calculate some key characters. Args: raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + analysis_date (dictionary): proforma's starting date and the years of proforma Key Attributes: cagr (float): compound annual growth rate @@ -286,6 +287,14 @@ class Income_Statement_Table(): 'non_utility_expense_percent': (float) 0.03, 'revenue_average': (float) 3000.00 } + + Error Validation: + raw_income_input: + year should not be empty. + year should be covered by analysis date. Raise: change analysis date + any year should contain 4 raw inputs. + annual_bill_table: + annual_bill_table years should cover proforma years """ self.hist_start_year = None self.hist_end_year = None @@ -296,6 +305,7 @@ class Income_Statement_Table(): self.hist_table = [] self.table = [] self.characters = {} + self.analysis_date = analysis_date for year in raw_income_input: current_income_statement = Income_Statement() @@ -335,12 +345,11 @@ class Income_Statement_Table(): 'revenue_average': self.revenue_average } - def project(self, growth_rate_flag, analysis_date, annual_bill_table): + def project(self, growth_rate_flag, annual_bill_table): """ Project future income statement. Append multiple single year income_statement objects to self.table Args: growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average - analysis_date (dictionary): proforma's starting date and the years of proforma annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types Return: list: list of single year income_statement objects, containing historical and projection @@ -348,8 +357,8 @@ class Income_Statement_Table(): project() overwrites existing projection data """ # characters = copy.deepcopy(self.characters) - proforma_year = form_bill_year(analysis_date['proforma_start'], - analysis_date['proforma_duration']) + proforma_year = form_bill_year(self.analysis_date['proforma_start'], + self.analysis_date['proforma_duration']) current_table = copy.deepcopy(self.hist_table) for year in proforma_year: last_revenue = current_table[-1].revenue @@ -373,32 +382,19 @@ class Income_Statement_Table(): hist_table_dict = {} for current_income_statement in self.hist_table: hist_table_dict[current_income_statement.year] = { - 'year': - current_income_statement.year, - 'revenue': - current_income_statement.revenue, - 'utility_expense': - current_income_statement.utility_expense, - 'energy_opex': - current_income_statement.energy_opex, - 'electricity_opex': - current_income_statement.electricity_opex, - 'gas_opex': - current_income_statement.gas_opex, - 'oil_opex': - current_income_statement.oil_opex, - 'water_opex': - current_income_statement.water_opex, - 'other_utility': - current_income_statement.other_utility, - 'non_utility_expense': - current_income_statement.non_utility_expense, - 'net_non_energy_opex': - current_income_statement.net_non_energy_opex, - 'total_opex': - current_income_statement.total_opex, - 'noi': - current_income_statement.noi + 'year': current_income_statement.year, + 'revenue': current_income_statement.revenue, + 'utility_expense': current_income_statement.utility_expense, + 'energy_opex': current_income_statement.energy_opex, + 'electricity_opex': current_income_statement.electricity_opex, + 'gas_opex': current_income_statement.gas_opex, + 'oil_opex': current_income_statement.oil_opex, + 'water_opex': current_income_statement.water_opex, + 'other_utility': current_income_statement.other_utility, + 'non_utility_expense': current_income_statement.non_utility_expense, + 'net_non_energy_opex': current_income_statement.net_non_energy_opex, + 'total_opex': current_income_statement.total_opex, + 'noi': current_income_statement.noi } return hist_table_dict @@ -431,9 +427,7 @@ class Income_Statement_Table(): 'water': mean(list(i_s.water_opex for i_s in self.hist_table)) } - current_income_statement.put_average(average_revenue, annual_bills, - self.characters) - + current_income_statement.put_average(average_revenue, annual_bills, self.characters) return convert_income_statement_class(current_income_statement) def get_single_year(self, year): @@ -503,6 +497,34 @@ class Income_Statement_Table(): if current_income_statement.year == first_year: return current_income_statement.noi + def get_full_income_table(self): + """ + Get full projected income statement table + Return: + dictionary: dict of dict of income statement. Key is year + """ + # validate projection availability + if self.table[-1].year <= self.hist_end_year: + raise ValueError('income statement is not projected properly') + table_dict = {} + for current_income_statement in self.table: + table_dict[current_income_statement.year] = { + 'year': current_income_statement.year, + 'revenue': current_income_statement.revenue, + 'utility_expense': current_income_statement.utility_expense, + 'energy_opex': current_income_statement.energy_opex, + 'electricity_opex': current_income_statement.electricity_opex, + 'gas_opex': current_income_statement.gas_opex, + 'oil_opex': current_income_statement.oil_opex, + 'water_opex': current_income_statement.water_opex, + 'other_utility': current_income_statement.other_utility, + 'non_utility_expense': current_income_statement.non_utility_expense, + 'net_non_energy_opex': current_income_statement.net_non_energy_opex, + 'total_opex': current_income_statement.total_opex, + 'noi': current_income_statement.noi + } + return table_dict + def get_total_energy_dict(self): total_energy_dict = {} for current_income_statement in self.table: diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index 3f49f75..58824e7 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -1,5 +1,7 @@ from bpfin.utilbills.bill import Bill -from bpfin.tests.testdata import feature_data as db +from bpfin.financials.financial_lib import Income_Statement_Table +# from bpfin.tests.testdata import feature_data as db +# import pprint UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] @@ -117,6 +119,59 @@ def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): return annual_bill_table +def prior_income_statement(raw_income_input, annual_bill_table, analysis_date, growth_rate_flag): + """ + Take in prior-annual_bill_table, and raw inputs of income statements + Generate historical income statement, calculate its characters including CAGR and other percent ratios + Generate prior_saving income statement. Store characters in database + + Args: + raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year + annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + analysis_date (dictionary): proforma's starting date and the years of proforma + growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average + Returns: + prior_income_statement (dictionary): dict of dict of prior_saving income statement. Key is year + next_year_income (dictionary): next year income statement. Keys are income statement items. + average_income (dictionary): single year income statement for historical average. Keys = income statement items + historical_cagr (float): compound annual growth rate + + Description: + raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} + annual_bill_table = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} + + prior_income_statement = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} + + """ + income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) + income_table.project(growth_rate_flag, annual_bill_table) + + prior_income = income_table.get_full_income_table() + next_year_income = income_table.get_single_year(income_table.hist_end_year + 1) + average_income = income_table.get_average() + historical_cagr = income_table.get_cagr() + return prior_income, next_year_income, average_income, historical_cagr + + # **** ugly test **** # print('\nmonthly_bill =', monthly_bill(db.raw_bill_table, db.analysis_date)) # print('\nannual_bill =', annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)) +# print('\nprior_income_statement =', prior_income_statement( +# db.raw_income_input, +# db.prior_annual_bill, +# db.analysis_date, +# -2.0)) + + + +# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) +# pp.pprint(result) + +# writein = str(result) +# f = open('data_generation.py', 'w') +# f.write(writein) +# f.close() diff --git a/bpfin/lib/data_generation.py b/bpfin/lib/data_generation.py new file mode 100644 index 0000000..e69de29 diff --git a/bpfin/tests/test_financials/test_financial_lib.py b/bpfin/tests/test_financials/test_financial_lib.py index 0799fdb..e24e1b6 100644 --- a/bpfin/tests/test_financials/test_financial_lib.py +++ b/bpfin/tests/test_financials/test_financial_lib.py @@ -24,9 +24,11 @@ def test_Income_Statement_Table(): output_single_year = db.income_statement_2017_avg output_first_year_noi = 33165.377210606544 - IS_table = Income_Statement_Table(input_raw_income_input, - input_annual_bill_table) - IS_table.project(-1.0, db.analysis_date, db.bill_overview_organized) + IS_table = Income_Statement_Table( + input_raw_income_input, + input_annual_bill_table, + db.analysis_date) + IS_table.project(-1.0, db.bill_overview_organized) assert IS_table.get_cagr() == output_cagr # test get_cagr assert IS_table.get_hist_table() == output_hist_table # test get_hist_table diff --git a/bpfin/tests/test_utilbills/test_back_end_call.py b/bpfin/tests/test_utilbills/test_back_end_call.py index c6d7c06..38cc215 100644 --- a/bpfin/tests/test_utilbills/test_back_end_call.py +++ b/bpfin/tests/test_utilbills/test_back_end_call.py @@ -1,5 +1,6 @@ -from bpfin.lib.back_end_call import monthly_bill, annual_bill +from bpfin.lib.back_end_call import monthly_bill, annual_bill, prior_income_statement from bpfin.tests.testdata import feature_data as db +from bpfin.tests.testdata import sample_data as sdb def test_monthly_bill(): @@ -7,9 +8,7 @@ def test_monthly_bill(): 'electricity': db.prior_annual_bill['electricity'], 'gas': None, 'oil': None, - 'water': None - } - + 'water': None} result_dict = monthly_bill(db.raw_bill_table, db.analysis_date) assert output_dict == result_dict @@ -18,3 +17,19 @@ def test_annual_bill(): output_dict = db.prior_annual_bill result_dict = annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date) assert output_dict == result_dict + + +def test_prior_income_statement(): + output_income_statement = db.prior_income_statement_cagr + output_next_year_income = db.next_year_income + output_average_income = db.average_income + output_historical_cagr = db.historical_cagr + prior_income, next_year_income, average_income, historical_cagr = prior_income_statement( + db.raw_income_input, + db.prior_annual_bill, + db.analysis_date, + -2.0) + assert output_income_statement == prior_income + assert output_next_year_income == next_year_income + assert output_average_income == average_income + assert output_historical_cagr == historical_cagr diff --git a/bpfin/tests/testdata/data_generation.py b/bpfin/tests/testdata/data_generation.py index 71343ae..e69de29 100644 --- a/bpfin/tests/testdata/data_generation.py +++ b/bpfin/tests/testdata/data_generation.py @@ -1,84 +0,0 @@ -{ - 'electricity': None, - 'gas': { - 2014: 0, - 2015: 1020, - 2016: 1220, - 2017: 1520, - 2012: 1253.3333333333333, - 2013: 1253.3333333333333, - 2018: 1253.3333333333333, - 2019: 1253.3333333333333, - 2020: 1253.3333333333333, - 2021: 1253.3333333333333, - 2022: 1253.3333333333333, - 2023: 1253.3333333333333, - 2024: 1253.3333333333333, - 2025: 1253.3333333333333, - 2026: 1253.3333333333333, - 2027: 1253.3333333333333, - 2028: 1253.3333333333333, - 2029: 1253.3333333333333, - 2030: 1253.3333333333333, - 2031: 1253.3333333333333, - 2032: 1253.3333333333333, - 2033: 1253.3333333333333, - 2034: 1253.3333333333333, - 2035: 1253.3333333333333, - 2036: 1253.3333333333333 - }, - 'oil': { - 2015: 1010, - 2016: 1210, - 2017: 1510, - 2012: 1243.3333333333333, - 2013: 1243.3333333333333, - 2014: 1243.3333333333333, - 2018: 1243.3333333333333, - 2019: 1243.3333333333333, - 2020: 1243.3333333333333, - 2021: 1243.3333333333333, - 2022: 1243.3333333333333, - 2023: 1243.3333333333333, - 2024: 1243.3333333333333, - 2025: 1243.3333333333333, - 2026: 1243.3333333333333, - 2027: 1243.3333333333333, - 2028: 1243.3333333333333, - 2029: 1243.3333333333333, - 2030: 1243.3333333333333, - 2031: 1243.3333333333333, - 2032: 1243.3333333333333, - 2033: 1243.3333333333333, - 2034: 1243.3333333333333, - 2035: 1243.3333333333333, - 2036: 1243.3333333333333 - }, - 'water': { - 2015: 0, - 2016: 0, - 2017: 0, - 2012: 0, - 2013: 0, - 2014: 0, - 2018: 0, - 2019: 0, - 2020: 0, - 2021: 0, - 2022: 0, - 2023: 0, - 2024: 0, - 2025: 0, - 2026: 0, - 2027: 0, - 2028: 0, - 2029: 0, - 2030: 0, - 2031: 0, - 2032: 0, - 2033: 0, - 2034: 0, - 2035: 0, - 2036: 0 - } -} diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 61450d6..d9895af 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -104,12 +104,24 @@ raw_annual_bill_table = { 'water': annual_bill_water } - -# bill_table = Bill_Table(analysis_date) -# bill_table.put_month_bill_table(raw_bill_table) -# bill_table.put_annual_bill_table(raw_annual_bill_table) -# bill_list = bill_table.get_bill_list() - +# raw income_statement +raw_income_input = { + 2014: { + 'revenue': 90000, + 'utility_expense': 55000, + 'non_utility_expense': 3500 + }, + 2015: { + 'revenue': 95000, + 'utility_expense': 55000, + 'non_utility_expense': 3000 + }, + 2016: { + 'revenue': 100000, + 'utility_expense': 60000, + 'non_utility_expense': 3000 + } +} # saving scenarios percent_saving_dict = { @@ -237,6 +249,388 @@ prior_annual_bill = { } } +prior_income_statement_cagr = { + 2014: { + 'year': 2014, + 'revenue': 90000, + 'utility_expense': 55000, + 'energy_opex': 2589.41283438579, + 'electricity_opex': 1346.079501052457, + 'gas_opex': 0, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 52410.58716561421, + 'non_utility_expense': 3500, + 'net_non_energy_opex': 55910.58716561421, + 'total_opex': 58500.0, + 'noi': 31500.0 + }, + 2015: { + 'year': 2015, + 'revenue': 95000, + 'utility_expense': 55000, + 'energy_opex': 3387.4370596899435, + 'electricity_opex': 1357.4370596899435, + 'gas_opex': 1020, + 'oil_opex': 1010, + 'water_opex': 0, + 'other_utility': 51612.562940310054, + 'non_utility_expense': 3000, + 'net_non_energy_opex': 54612.562940310054, + 'total_opex': 58000.0, + 'noi': 37000.0 + }, + 2016: { + 'year': 2016, + 'revenue': 100000, + 'utility_expense': 60000, + 'energy_opex': 3798.4645719731475, + 'electricity_opex': 1368.4645719731475, + 'gas_opex': 1220, + 'oil_opex': 1210, + 'water_opex': 0, + 'other_utility': 56201.53542802685, + 'non_utility_expense': 3000, + 'net_non_energy_opex': 59201.53542802685, + 'total_opex': 63000.0, + 'noi': 37000.0 + }, + 2017: { + 'year': 2017, + 'revenue': 105409.25533894598, + 'utility_expense': 63685.47076980913, + 'energy_opex': 4425.243439807112, + 'electricity_opex': 1395.2434398071116, + 'gas_opex': 1520, + 'oil_opex': 1510, + 'water_opex': 0, + 'other_utility': 59260.22733000202, + 'non_utility_expense': 3513.6418446315324, + 'net_non_energy_opex': 62773.869174633546, + 'total_opex': 67199.11261444066, + 'noi': 38210.14272450532 + }, + 2018: { + 'year': 2018, + 'revenue': 111111.11111111112, + 'utility_expense': 66392.37014575081, + 'energy_opex': 3926.605805029146, + 'electricity_opex': 1429.9391383624793, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 62465.764340721675, + 'non_utility_expense': 3703.703703703704, + 'net_non_energy_opex': 66169.46804442538, + 'total_opex': 70096.07384945452, + 'noi': 41015.0372616566 + }, + 2019: { + 'year': 2019, + 'revenue': 117121.3948210511, + 'utility_expense': 69806.48568426503, + 'energy_opex': 3961.788650929455, + 'electricity_opex': 1465.1219842627888, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 65844.69703333559, + 'non_utility_expense': 3904.0464940350366, + 'net_non_energy_opex': 69748.74352737062, + 'total_opex': 73710.53217830008, + 'noi': 43410.86264275102 + }, + 2020: { + 'year': 2020, + 'revenue': 123456.79012345681, + 'utility_expense': 73402.51446668015, + 'energy_opex': 3996.1096436560692, + 'electricity_opex': 1499.442976989403, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 69406.40482302409, + 'non_utility_expense': 4115.22633744856, + 'net_non_energy_opex': 73521.63116047265, + 'total_opex': 77517.74080412873, + 'noi': 45939.049319328085 + }, + 2021: { + 'year': 2021, + 'revenue': 130134.88313450124, + 'utility_expense': 77188.98559899243, + 'energy_opex': 4028.211117508439, + 'electricity_opex': 1531.5444508417731, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 73160.77448148398, + 'non_utility_expense': 4337.829437816708, + 'net_non_energy_opex': 77498.60391930069, + 'total_opex': 81526.81503680913, + 'noi': 48608.068097692114 + }, + 2022: { + 'year': 2022, + 'revenue': 137174.21124828537, + 'utility_expense': 81175.51065176747, + 'energy_opex': 4057.283070629577, + 'electricity_opex': 1560.6164039629107, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 77118.2275811379, + 'non_utility_expense': 4572.4737082761785, + 'net_non_energy_opex': 81690.70128941408, + 'total_opex': 85747.98436004366, + 'noi': 51426.22688824171 + }, + 2023: { + 'year': 2023, + 'revenue': 144594.3145938903, + 'utility_expense': 85375.52089355137, + 'energy_opex': 4085.771469680246, + 'electricity_opex': 1589.1048030135796, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 81289.74942387112, + 'non_utility_expense': 4819.81048646301, + 'net_non_energy_opex': 86109.55991033414, + 'total_opex': 90195.33138001438, + 'noi': 54398.98321387592 + }, + 2024: { + 'year': 2024, + 'revenue': 152415.79027587266, + 'utility_expense': 89801.7755864785, + 'energy_opex': 4114.856051880816, + 'electricity_opex': 1618.1893852141502, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 85686.91953459768, + 'non_utility_expense': 5080.526342529089, + 'net_non_energy_opex': 90767.44587712677, + 'total_opex': 94882.30192900759, + 'noi': 57533.488346865066 + }, + 2025: { + 'year': 2025, + 'revenue': 160660.349548767, + 'utility_expense': 94466.87160244759, + 'energy_opex': 4144.927798146355, + 'electricity_opex': 1648.2611314796882, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 90321.94380430123, + 'non_utility_expense': 5355.3449849589, + 'net_non_energy_opex': 95677.28878926013, + 'total_opex': 99822.21658740648, + 'noi': 60838.13296136052 + }, + 2026: { + 'year': 2026, + 'revenue': 169350.87808430297, + 'utility_expense': 99384.92941996356, + 'energy_opex': 4177.241048188369, + 'electricity_opex': 1680.5743815217027, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 95207.68837177519, + 'non_utility_expense': 5645.029269476766, + 'net_non_energy_opex': 100852.71764125196, + 'total_opex': 105029.95868944033, + 'noi': 64320.919394862634 + }, + 2027: { + 'year': 2027, + 'revenue': 178511.49949863003, + 'utility_expense': 104569.84110763346, + 'energy_opex': 4212.125769520951, + 'electricity_opex': 1715.4591028542839, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 100357.71533811251, + 'non_utility_expense': 5950.383316621001, + 'net_non_energy_opex': 106308.0986547335, + 'total_opex': 110520.22442425445, + 'noi': 67991.27507437558 + }, + 2028: { + 'year': 2028, + 'revenue': 188167.6423158922, + 'utility_expense': 110034.25881989759, + 'energy_opex': 4247.938406814023, + 'electricity_opex': 1751.2717401473571, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 105786.32041308357, + 'non_utility_expense': 6272.254743863074, + 'net_non_energy_opex': 112058.57515694664, + 'total_opex': 116306.51356376066, + 'noi': 71861.12875213155 + }, + 2029: { + 'year': 2029, + 'revenue': 198346.1105540334, + 'utility_expense': 115792.37389037732, + 'energy_opex': 4283.8012924745335, + 'electricity_opex': 1787.1346258078672, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 111508.57259790279, + 'non_utility_expense': 6611.5370184677795, + 'net_non_energy_opex': 118120.10961637057, + 'total_opex': 122403.9109088451, + 'noi': 75942.1996451883 + }, + 2030: { + 'year': 2030, + 'revenue': 209075.15812876914, + 'utility_expense': 121860.01856938066, + 'energy_opex': 4319.662554843346, + 'electricity_opex': 1822.9958881766795, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 117540.3560145373, + 'non_utility_expense': 6969.171937625638, + 'net_non_energy_opex': 124509.52795216294, + 'total_opex': 128829.1905070063, + 'noi': 80245.96762176284 + }, + 2031: { + 'year': 2031, + 'revenue': 220384.56728225935, + 'utility_expense': 128254.04609337838, + 'energy_opex': 4355.63209570858, + 'electricity_opex': 1858.9654290419135, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 123898.41399766979, + 'non_utility_expense': 7346.152242741978, + 'net_non_energy_opex': 131244.56624041178, + 'total_opex': 135600.19833612035, + 'noi': 84784.368946139 + }, + 2032: { + 'year': 2032, + 'revenue': 232305.73125418797, + 'utility_expense': 134992.39514124338, + 'energy_opex': 4391.999569535249, + 'electricity_opex': 1895.3329028685823, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 130600.39557170814, + 'non_utility_expense': 7743.524375139598, + 'net_non_energy_opex': 138343.91994684775, + 'total_opex': 142735.919516383, + 'noi': 89569.81173780496 + }, + 2033: { + 'year': 2033, + 'revenue': 244871.74142473264, + 'utility_expense': 142094.09314422478, + 'energy_opex': 4429.188702369451, + 'electricity_opex': 1932.5220357027845, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 137664.90444185532, + 'non_utility_expense': 8162.3913808244215, + 'net_non_energy_opex': 145827.29582267973, + 'total_opex': 150256.4845250492, + 'noi': 94615.25689968345 + }, + 2034: { + 'year': 2034, + 'revenue': 258117.47917131998, + 'utility_expense': 149578.9225071101, + 'energy_opex': 4467.371871878835, + 'electricity_opex': 1970.705205212169, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 145111.55063523128, + 'non_utility_expense': 8603.915972377332, + 'net_non_energy_opex': 153715.4666076086, + 'total_opex': 158182.83847948743, + 'noi': 99934.64069183255 + }, + 2035: { + 'year': 2035, + 'revenue': 272079.71269414737, + 'utility_expense': 157467.601487427, + 'energy_opex': 4506.596552032184, + 'electricity_opex': 2009.9298853655182, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 152961.0049353948, + 'non_utility_expense': 9069.323756471578, + 'net_non_energy_opex': 162030.32869186637, + 'total_opex': 166536.92524389856, + 'noi': 105542.78745024881 + }, + 2036: { + 'year': 2036, + 'revenue': 286797.19907924446, + 'utility_expense': 165781.9237598475, + 'energy_opex': 4546.867498479411, + 'electricity_opex': 2050.2008318127446, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 161235.0562613681, + 'non_utility_expense': 9559.906635974816, + 'net_non_energy_opex': 170794.96289734292, + 'total_opex': 175341.83039582233, + 'noi': 111455.36868342213 + } +} + +next_year_income = { + 'year': 2017, + 'revenue': 105409.25533894598, + 'utility_expense': 63685.47076980913, + 'energy_opex': 4425.243439807112, + 'electricity_opex': 1395.2434398071116, + 'gas_opex': 1520, + 'oil_opex': 1510, + 'water_opex': 0, + 'other_utility': 59260.22733000202, + 'non_utility_expense': 3513.6418446315324, + 'net_non_energy_opex': 62773.869174633546, + 'total_opex': 67199.11261444066, + 'noi': 38210.14272450532 +} + +average_income = { + 'year': None, + 'revenue': 95000.0, + 'utility_expense': 56666.666666666657, + 'energy_opex': 3258.4381553496269, + 'electricity_opex': 1357.327044238516, + 'gas_opex': 746.66666666666663, + 'oil_opex': 1154.4444444444443, + 'water_opex': 0.0, + 'other_utility': 53408.228511317029, + 'non_utility_expense': 3166.6666666666665, + 'net_non_energy_opex': 56574.895177983693, + 'total_opex': 59833.333333333321, + 'noi': 35166.666666666679 +} + +historical_cagr = 0.05409255338945984 + post_annual_bill_table = { 'electricity': { 2012: 1302.3894189123205, diff --git a/bpfin/tests/testdata/sample_data.py b/bpfin/tests/testdata/sample_data.py index 392ff56..01d2f20 100644 --- a/bpfin/tests/testdata/sample_data.py +++ b/bpfin/tests/testdata/sample_data.py @@ -725,21 +725,53 @@ cash_balance = { 2014: 5000.0, 2015: 6000, 2016: 4500.0 } -raw_balance_sheet = {'cash': cash_balance, 'other_debt_service': liability_dictionary, - 'net_income': noi_dictionary} +raw_balance_sheet = { + 'cash': cash_balance, + 'other_debt_service': liability_dictionary, + 'net_income': noi_dictionary +} -hist_balance_sheet = {2014: {'cash': 5000, 'other_debt_service': 100, 'net_income': 31500}, - 2015: {'cash': 6000, 'other_debt_service': 6000, 'net_income': 37000}, - 2016: {'cash': 4500, 'other_debt_service': 15000, 'net_income': 37000}} +hist_balance_sheet = { + 2014: { + 'cash': 5000, + 'other_debt_service': 100, + 'net_income': 31500 + }, + 2015: { + 'cash': 6000, + 'other_debt_service': 6000, + 'net_income': 37000 + }, + 2016: { + 'cash': 4500, + 'other_debt_service': 15000, + 'net_income': 37000 + } +} -balance_sheet_2018 = {2018: {'cash': 75992.72229401008,'other_debt_service': 5000, -'net_income': 45214.28536645262}} +balance_sheet_2018 = { + 2018: { + 'cash': 75992.72229401008, + 'other_debt_service': 5000, + 'net_income': 45214.28536645262 + } +} -balance_sheet_2025 = {2025: {'cash': 510670.2474431231,'other_debt_service': 0.00, -'net_income': 76882.87761572453}} +balance_sheet_2025 = { + 2025: { + 'cash': 510670.2474431231, + 'other_debt_service': 0.00, + 'net_income': 76882.87761572453 + } +} -balance_sheet_2030 = {2030: {'cash': 987109.6873789529,'other_debt_service': 0.00, -'net_income': 108765.29589285178}} +balance_sheet_2030 = { + 2030: { + 'cash': 987109.6873789529, + 'other_debt_service': 0.00, + 'net_income': 108765.29589285178 + } +} # pro-forma date and bill projection - @@ -2867,10 +2899,3 @@ inflation_coeff_dict = { datetime.date(2070, 10, 31): 2.920832971888047, datetime.date(2070, 11, 30): 2.9256359158230265, datetime.date(2070, 12, 31): 2.9304467575976516} - - - - - - - diff --git a/bpfin/utilbills/bill_backend_call.py b/bpfin/utilbills/bill_backend_call.py deleted file mode 100644 index 5661bda..0000000 --- a/bpfin/utilbills/bill_backend_call.py +++ /dev/null @@ -1,20 +0,0 @@ -from bpfin.utilbills.bill_month_normalize_rough import bill_month_normalize_rough -from bpfin.utilbills.bill_prior_proj_rough import bill_prior_proj_rough -from bpfin.utilbills import bill_lib as bl -from bpfin.tests.testdata import sample_data as db - - -def bill_prior_proj_rough_annual(raw_bill, analysis_date): - norm_bill = bill_month_normalize_rough(raw_bill) - prior_bill_rough = bill_prior_proj_rough(norm_bill, raw_bill, analysis_date, db.inflation_coeff_dict) - annual_bill_rough = bl.annualizing_projection( - prior_bill_rough['date_to'], - prior_bill_rough['charge']) - return annual_bill_rough - - -def prior_proj_rough_month(raw_bill, analysis_date): - norm_bill = bill_month_normalize_rough(raw_bill) - prior_bill_utility = bill_prior_proj_rough(norm_bill, raw_bill, analysis_date, db.inflation_coeff_dict) - # annual_bill_utility = bl.annualizing_projection(prior_bill_utility['date_to'], prior_bill_utility['charge']) - return prior_bill_utility diff --git a/data_generation.py b/data_generation.py new file mode 100644 index 0000000..0c5c1ce --- /dev/null +++ b/data_generation.py @@ -0,0 +1 @@ +({2014: {'year': 2014, 'revenue': 90000, 'utility_expense': 55000, 'energy_opex': 2589.41283438579, 'electricity_opex': 1346.079501052457, 'gas_opex': 0, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 52410.58716561421, 'non_utility_expense': 3500, 'net_non_energy_opex': 55910.58716561421, 'total_opex': 58500.0, 'noi': 31500.0}, 2015: {'year': 2015, 'revenue': 95000, 'utility_expense': 55000, 'energy_opex': 3387.4370596899435, 'electricity_opex': 1357.4370596899435, 'gas_opex': 1020, 'oil_opex': 1010, 'water_opex': 0, 'other_utility': 51612.562940310054, 'non_utility_expense': 3000, 'net_non_energy_opex': 54612.562940310054, 'total_opex': 58000.0, 'noi': 37000.0}, 2016: {'year': 2016, 'revenue': 100000, 'utility_expense': 60000, 'energy_opex': 3798.4645719731475, 'electricity_opex': 1368.4645719731475, 'gas_opex': 1220, 'oil_opex': 1210, 'water_opex': 0, 'other_utility': 56201.53542802685, 'non_utility_expense': 3000, 'net_non_energy_opex': 59201.53542802685, 'total_opex': 63000.0, 'noi': 37000.0}, 2017: {'year': 2017, 'revenue': 105409.25533894598, 'utility_expense': 63685.47076980913, 'energy_opex': 4425.243439807112, 'electricity_opex': 1395.2434398071116, 'gas_opex': 1520, 'oil_opex': 1510, 'water_opex': 0, 'other_utility': 59260.22733000202, 'non_utility_expense': 3513.6418446315324, 'net_non_energy_opex': 62773.869174633546, 'total_opex': 67199.11261444066, 'noi': 38210.14272450532}, 2018: {'year': 2018, 'revenue': 111111.11111111112, 'utility_expense': 66392.37014575081, 'energy_opex': 3926.605805029146, 'electricity_opex': 1429.9391383624793, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 62465.764340721675, 'non_utility_expense': 3703.703703703704, 'net_non_energy_opex': 66169.46804442538, 'total_opex': 70096.07384945452, 'noi': 41015.0372616566}, 2019: {'year': 2019, 'revenue': 117121.3948210511, 'utility_expense': 69806.48568426503, 'energy_opex': 3961.788650929455, 'electricity_opex': 1465.1219842627888, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 65844.69703333559, 'non_utility_expense': 3904.0464940350366, 'net_non_energy_opex': 69748.74352737062, 'total_opex': 73710.53217830008, 'noi': 43410.86264275102}, 2020: {'year': 2020, 'revenue': 123456.79012345681, 'utility_expense': 73402.51446668015, 'energy_opex': 3996.1096436560692, 'electricity_opex': 1499.442976989403, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 69406.40482302409, 'non_utility_expense': 4115.22633744856, 'net_non_energy_opex': 73521.63116047265, 'total_opex': 77517.74080412873, 'noi': 45939.049319328085}, 2021: {'year': 2021, 'revenue': 130134.88313450124, 'utility_expense': 77188.98559899243, 'energy_opex': 4028.211117508439, 'electricity_opex': 1531.5444508417731, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 73160.77448148398, 'non_utility_expense': 4337.829437816708, 'net_non_energy_opex': 77498.60391930069, 'total_opex': 81526.81503680913, 'noi': 48608.068097692114}, 2022: {'year': 2022, 'revenue': 137174.21124828537, 'utility_expense': 81175.51065176747, 'energy_opex': 4057.283070629577, 'electricity_opex': 1560.6164039629107, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 77118.2275811379, 'non_utility_expense': 4572.4737082761785, 'net_non_energy_opex': 81690.70128941408, 'total_opex': 85747.98436004366, 'noi': 51426.22688824171}, 2023: {'year': 2023, 'revenue': 144594.3145938903, 'utility_expense': 85375.52089355137, 'energy_opex': 4085.771469680246, 'electricity_opex': 1589.1048030135796, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 81289.74942387112, 'non_utility_expense': 4819.81048646301, 'net_non_energy_opex': 86109.55991033414, 'total_opex': 90195.33138001438, 'noi': 54398.98321387592}, 2024: {'year': 2024, 'revenue': 152415.79027587266, 'utility_expense': 89801.7755864785, 'energy_opex': 4114.856051880816, 'electricity_opex': 1618.1893852141502, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 85686.91953459768, 'non_utility_expense': 5080.526342529089, 'net_non_energy_opex': 90767.44587712677, 'total_opex': 94882.30192900759, 'noi': 57533.488346865066}, 2025: {'year': 2025, 'revenue': 160660.349548767, 'utility_expense': 94466.87160244759, 'energy_opex': 4144.927798146355, 'electricity_opex': 1648.2611314796882, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 90321.94380430123, 'non_utility_expense': 5355.3449849589, 'net_non_energy_opex': 95677.28878926013, 'total_opex': 99822.21658740648, 'noi': 60838.13296136052}, 2026: {'year': 2026, 'revenue': 169350.87808430297, 'utility_expense': 99384.92941996356, 'energy_opex': 4177.241048188369, 'electricity_opex': 1680.5743815217027, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 95207.68837177519, 'non_utility_expense': 5645.029269476766, 'net_non_energy_opex': 100852.71764125196, 'total_opex': 105029.95868944033, 'noi': 64320.919394862634}, 2027: {'year': 2027, 'revenue': 178511.49949863003, 'utility_expense': 104569.84110763346, 'energy_opex': 4212.125769520951, 'electricity_opex': 1715.4591028542839, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 100357.71533811251, 'non_utility_expense': 5950.383316621001, 'net_non_energy_opex': 106308.0986547335, 'total_opex': 110520.22442425445, 'noi': 67991.27507437558}, 2028: {'year': 2028, 'revenue': 188167.6423158922, 'utility_expense': 110034.25881989759, 'energy_opex': 4247.938406814023, 'electricity_opex': 1751.2717401473571, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 105786.32041308357, 'non_utility_expense': 6272.254743863074, 'net_non_energy_opex': 112058.57515694664, 'total_opex': 116306.51356376066, 'noi': 71861.12875213155}, 2029: {'year': 2029, 'revenue': 198346.1105540334, 'utility_expense': 115792.37389037732, 'energy_opex': 4283.8012924745335, 'electricity_opex': 1787.1346258078672, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 111508.57259790279, 'non_utility_expense': 6611.5370184677795, 'net_non_energy_opex': 118120.10961637057, 'total_opex': 122403.9109088451, 'noi': 75942.1996451883}, 2030: {'year': 2030, 'revenue': 209075.15812876914, 'utility_expense': 121860.01856938066, 'energy_opex': 4319.662554843346, 'electricity_opex': 1822.9958881766795, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 117540.3560145373, 'non_utility_expense': 6969.171937625638, 'net_non_energy_opex': 124509.52795216294, 'total_opex': 128829.1905070063, 'noi': 80245.96762176284}, 2031: {'year': 2031, 'revenue': 220384.56728225935, 'utility_expense': 128254.04609337838, 'energy_opex': 4355.63209570858, 'electricity_opex': 1858.9654290419135, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 123898.41399766979, 'non_utility_expense': 7346.152242741978, 'net_non_energy_opex': 131244.56624041178, 'total_opex': 135600.19833612035, 'noi': 84784.368946139}, 2032: {'year': 2032, 'revenue': 232305.73125418797, 'utility_expense': 134992.39514124338, 'energy_opex': 4391.999569535249, 'electricity_opex': 1895.3329028685823, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 130600.39557170814, 'non_utility_expense': 7743.524375139598, 'net_non_energy_opex': 138343.91994684775, 'total_opex': 142735.919516383, 'noi': 89569.81173780496}, 2033: {'year': 2033, 'revenue': 244871.74142473264, 'utility_expense': 142094.09314422478, 'energy_opex': 4429.188702369451, 'electricity_opex': 1932.5220357027845, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 137664.90444185532, 'non_utility_expense': 8162.3913808244215, 'net_non_energy_opex': 145827.29582267973, 'total_opex': 150256.4845250492, 'noi': 94615.25689968345}, 2034: {'year': 2034, 'revenue': 258117.47917131998, 'utility_expense': 149578.9225071101, 'energy_opex': 4467.371871878835, 'electricity_opex': 1970.705205212169, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 145111.55063523128, 'non_utility_expense': 8603.915972377332, 'net_non_energy_opex': 153715.4666076086, 'total_opex': 158182.83847948743, 'noi': 99934.64069183255}, 2035: {'year': 2035, 'revenue': 272079.71269414737, 'utility_expense': 157467.601487427, 'energy_opex': 4506.596552032184, 'electricity_opex': 2009.9298853655182, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 152961.0049353948, 'non_utility_expense': 9069.323756471578, 'net_non_energy_opex': 162030.32869186637, 'total_opex': 166536.92524389856, 'noi': 105542.78745024881}, 2036: {'year': 2036, 'revenue': 286797.19907924446, 'utility_expense': 165781.9237598475, 'energy_opex': 4546.867498479411, 'electricity_opex': 2050.2008318127446, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 161235.0562613681, 'non_utility_expense': 9559.906635974816, 'net_non_energy_opex': 170794.96289734292, 'total_opex': 175341.83039582233, 'noi': 111455.36868342213}}, {'year': 2017, 'revenue': 105409.25533894598, 'utility_expense': 63685.47076980913, 'energy_opex': 4425.243439807112, 'electricity_opex': 1395.2434398071116, 'gas_opex': 1520, 'oil_opex': 1510, 'water_opex': 0, 'other_utility': 59260.22733000202, 'non_utility_expense': 3513.6418446315324, 'net_non_energy_opex': 62773.869174633546, 'total_opex': 67199.11261444066, 'noi': 38210.14272450532}, {'year': None, 'revenue': 95000.0, 'utility_expense': 56666.666666666657, 'energy_opex': 3258.4381553496269, 'electricity_opex': 1357.327044238516, 'gas_opex': 746.66666666666663, 'oil_opex': 1154.4444444444443, 'water_opex': 0.0, 'other_utility': 53408.228511317029, 'non_utility_expense': 3166.6666666666665, 'net_non_energy_opex': 56574.895177983693, 'total_opex': 59833.333333333321, 'noi': 35166.666666666679}, 0.05409255338945984) \ No newline at end of file -- GitLab From b8232b38755160e7d2c51f067373f90904e09f5f Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 18 May 2017 17:28:26 -0400 Subject: [PATCH 28/30] Replace backend call annual_bill_table with annual_bill_table's inputs --- bpfin/lib/back_end_call.py | 3 ++- .../test_utilbills/test_back_end_call.py | 4 +++- bpfin/utilbills/bill_backend_call.py | 20 +++++++++++++++++++ data_generation.py | 1 - 4 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 bpfin/utilbills/bill_backend_call.py delete mode 100644 data_generation.py diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index 58824e7..55ab673 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -119,7 +119,7 @@ def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): return annual_bill_table -def prior_income_statement(raw_income_input, annual_bill_table, analysis_date, growth_rate_flag): +def prior_income_statement(raw_income_input, raw_bill_table, raw_annual_bill_table, analysis_date, growth_rate_flag): """ Take in prior-annual_bill_table, and raw inputs of income statements Generate historical income statement, calculate its characters including CAGR and other percent ratios @@ -147,6 +147,7 @@ def prior_income_statement(raw_income_input, annual_bill_table, analysis_date, g prior_income_statement = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} """ + annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date) income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) income_table.project(growth_rate_flag, annual_bill_table) diff --git a/bpfin/tests/test_utilbills/test_back_end_call.py b/bpfin/tests/test_utilbills/test_back_end_call.py index 38cc215..b991a09 100644 --- a/bpfin/tests/test_utilbills/test_back_end_call.py +++ b/bpfin/tests/test_utilbills/test_back_end_call.py @@ -26,7 +26,9 @@ def test_prior_income_statement(): output_historical_cagr = db.historical_cagr prior_income, next_year_income, average_income, historical_cagr = prior_income_statement( db.raw_income_input, - db.prior_annual_bill, + # db.prior_annual_bill, + db.raw_bill_table, + db.raw_annual_bill_table, db.analysis_date, -2.0) assert output_income_statement == prior_income diff --git a/bpfin/utilbills/bill_backend_call.py b/bpfin/utilbills/bill_backend_call.py new file mode 100644 index 0000000..970ed9d --- /dev/null +++ b/bpfin/utilbills/bill_backend_call.py @@ -0,0 +1,20 @@ +from bpfin.utilbills.bill_month_normalize_rough import bill_month_normalize_rough +from bpfin.utilbills.bill_prior_proj_rough import bill_prior_proj_rough +from bpfin.utilbills import bill_lib as bl +from bpfin.tests.testdata import sample_data as db + + +def bill_prior_proj_rough_annual(utility_type, raw_bill, analysis_date): + norm_bill = bill_month_normalize_rough(utility_type, raw_bill) + prior_bill_rough = bill_prior_proj_rough(norm_bill, raw_bill, analysis_date, db.inflation_coeff_dict) + annual_bill_rough = bl.annualizing_projection( + prior_bill_rough['date_to'], + prior_bill_rough['charge']) + return annual_bill_rough + + +def prior_proj_rough_month(utility_type, raw_bill, analysis_date): + norm_bill = bill_month_normalize_rough(utility_type, raw_bill) + prior_bill_utility = bill_prior_proj_rough(norm_bill, raw_bill, analysis_date, db.inflation_coeff_dict) + # annual_bill_utility = bl.annualizing_projection(prior_bill_utility['date_to'], prior_bill_utility['charge']) + return prior_bill_utility diff --git a/data_generation.py b/data_generation.py deleted file mode 100644 index 0c5c1ce..0000000 --- a/data_generation.py +++ /dev/null @@ -1 +0,0 @@ -({2014: {'year': 2014, 'revenue': 90000, 'utility_expense': 55000, 'energy_opex': 2589.41283438579, 'electricity_opex': 1346.079501052457, 'gas_opex': 0, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 52410.58716561421, 'non_utility_expense': 3500, 'net_non_energy_opex': 55910.58716561421, 'total_opex': 58500.0, 'noi': 31500.0}, 2015: {'year': 2015, 'revenue': 95000, 'utility_expense': 55000, 'energy_opex': 3387.4370596899435, 'electricity_opex': 1357.4370596899435, 'gas_opex': 1020, 'oil_opex': 1010, 'water_opex': 0, 'other_utility': 51612.562940310054, 'non_utility_expense': 3000, 'net_non_energy_opex': 54612.562940310054, 'total_opex': 58000.0, 'noi': 37000.0}, 2016: {'year': 2016, 'revenue': 100000, 'utility_expense': 60000, 'energy_opex': 3798.4645719731475, 'electricity_opex': 1368.4645719731475, 'gas_opex': 1220, 'oil_opex': 1210, 'water_opex': 0, 'other_utility': 56201.53542802685, 'non_utility_expense': 3000, 'net_non_energy_opex': 59201.53542802685, 'total_opex': 63000.0, 'noi': 37000.0}, 2017: {'year': 2017, 'revenue': 105409.25533894598, 'utility_expense': 63685.47076980913, 'energy_opex': 4425.243439807112, 'electricity_opex': 1395.2434398071116, 'gas_opex': 1520, 'oil_opex': 1510, 'water_opex': 0, 'other_utility': 59260.22733000202, 'non_utility_expense': 3513.6418446315324, 'net_non_energy_opex': 62773.869174633546, 'total_opex': 67199.11261444066, 'noi': 38210.14272450532}, 2018: {'year': 2018, 'revenue': 111111.11111111112, 'utility_expense': 66392.37014575081, 'energy_opex': 3926.605805029146, 'electricity_opex': 1429.9391383624793, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 62465.764340721675, 'non_utility_expense': 3703.703703703704, 'net_non_energy_opex': 66169.46804442538, 'total_opex': 70096.07384945452, 'noi': 41015.0372616566}, 2019: {'year': 2019, 'revenue': 117121.3948210511, 'utility_expense': 69806.48568426503, 'energy_opex': 3961.788650929455, 'electricity_opex': 1465.1219842627888, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 65844.69703333559, 'non_utility_expense': 3904.0464940350366, 'net_non_energy_opex': 69748.74352737062, 'total_opex': 73710.53217830008, 'noi': 43410.86264275102}, 2020: {'year': 2020, 'revenue': 123456.79012345681, 'utility_expense': 73402.51446668015, 'energy_opex': 3996.1096436560692, 'electricity_opex': 1499.442976989403, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 69406.40482302409, 'non_utility_expense': 4115.22633744856, 'net_non_energy_opex': 73521.63116047265, 'total_opex': 77517.74080412873, 'noi': 45939.049319328085}, 2021: {'year': 2021, 'revenue': 130134.88313450124, 'utility_expense': 77188.98559899243, 'energy_opex': 4028.211117508439, 'electricity_opex': 1531.5444508417731, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 73160.77448148398, 'non_utility_expense': 4337.829437816708, 'net_non_energy_opex': 77498.60391930069, 'total_opex': 81526.81503680913, 'noi': 48608.068097692114}, 2022: {'year': 2022, 'revenue': 137174.21124828537, 'utility_expense': 81175.51065176747, 'energy_opex': 4057.283070629577, 'electricity_opex': 1560.6164039629107, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 77118.2275811379, 'non_utility_expense': 4572.4737082761785, 'net_non_energy_opex': 81690.70128941408, 'total_opex': 85747.98436004366, 'noi': 51426.22688824171}, 2023: {'year': 2023, 'revenue': 144594.3145938903, 'utility_expense': 85375.52089355137, 'energy_opex': 4085.771469680246, 'electricity_opex': 1589.1048030135796, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 81289.74942387112, 'non_utility_expense': 4819.81048646301, 'net_non_energy_opex': 86109.55991033414, 'total_opex': 90195.33138001438, 'noi': 54398.98321387592}, 2024: {'year': 2024, 'revenue': 152415.79027587266, 'utility_expense': 89801.7755864785, 'energy_opex': 4114.856051880816, 'electricity_opex': 1618.1893852141502, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 85686.91953459768, 'non_utility_expense': 5080.526342529089, 'net_non_energy_opex': 90767.44587712677, 'total_opex': 94882.30192900759, 'noi': 57533.488346865066}, 2025: {'year': 2025, 'revenue': 160660.349548767, 'utility_expense': 94466.87160244759, 'energy_opex': 4144.927798146355, 'electricity_opex': 1648.2611314796882, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 90321.94380430123, 'non_utility_expense': 5355.3449849589, 'net_non_energy_opex': 95677.28878926013, 'total_opex': 99822.21658740648, 'noi': 60838.13296136052}, 2026: {'year': 2026, 'revenue': 169350.87808430297, 'utility_expense': 99384.92941996356, 'energy_opex': 4177.241048188369, 'electricity_opex': 1680.5743815217027, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 95207.68837177519, 'non_utility_expense': 5645.029269476766, 'net_non_energy_opex': 100852.71764125196, 'total_opex': 105029.95868944033, 'noi': 64320.919394862634}, 2027: {'year': 2027, 'revenue': 178511.49949863003, 'utility_expense': 104569.84110763346, 'energy_opex': 4212.125769520951, 'electricity_opex': 1715.4591028542839, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 100357.71533811251, 'non_utility_expense': 5950.383316621001, 'net_non_energy_opex': 106308.0986547335, 'total_opex': 110520.22442425445, 'noi': 67991.27507437558}, 2028: {'year': 2028, 'revenue': 188167.6423158922, 'utility_expense': 110034.25881989759, 'energy_opex': 4247.938406814023, 'electricity_opex': 1751.2717401473571, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 105786.32041308357, 'non_utility_expense': 6272.254743863074, 'net_non_energy_opex': 112058.57515694664, 'total_opex': 116306.51356376066, 'noi': 71861.12875213155}, 2029: {'year': 2029, 'revenue': 198346.1105540334, 'utility_expense': 115792.37389037732, 'energy_opex': 4283.8012924745335, 'electricity_opex': 1787.1346258078672, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 111508.57259790279, 'non_utility_expense': 6611.5370184677795, 'net_non_energy_opex': 118120.10961637057, 'total_opex': 122403.9109088451, 'noi': 75942.1996451883}, 2030: {'year': 2030, 'revenue': 209075.15812876914, 'utility_expense': 121860.01856938066, 'energy_opex': 4319.662554843346, 'electricity_opex': 1822.9958881766795, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 117540.3560145373, 'non_utility_expense': 6969.171937625638, 'net_non_energy_opex': 124509.52795216294, 'total_opex': 128829.1905070063, 'noi': 80245.96762176284}, 2031: {'year': 2031, 'revenue': 220384.56728225935, 'utility_expense': 128254.04609337838, 'energy_opex': 4355.63209570858, 'electricity_opex': 1858.9654290419135, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 123898.41399766979, 'non_utility_expense': 7346.152242741978, 'net_non_energy_opex': 131244.56624041178, 'total_opex': 135600.19833612035, 'noi': 84784.368946139}, 2032: {'year': 2032, 'revenue': 232305.73125418797, 'utility_expense': 134992.39514124338, 'energy_opex': 4391.999569535249, 'electricity_opex': 1895.3329028685823, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 130600.39557170814, 'non_utility_expense': 7743.524375139598, 'net_non_energy_opex': 138343.91994684775, 'total_opex': 142735.919516383, 'noi': 89569.81173780496}, 2033: {'year': 2033, 'revenue': 244871.74142473264, 'utility_expense': 142094.09314422478, 'energy_opex': 4429.188702369451, 'electricity_opex': 1932.5220357027845, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 137664.90444185532, 'non_utility_expense': 8162.3913808244215, 'net_non_energy_opex': 145827.29582267973, 'total_opex': 150256.4845250492, 'noi': 94615.25689968345}, 2034: {'year': 2034, 'revenue': 258117.47917131998, 'utility_expense': 149578.9225071101, 'energy_opex': 4467.371871878835, 'electricity_opex': 1970.705205212169, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 145111.55063523128, 'non_utility_expense': 8603.915972377332, 'net_non_energy_opex': 153715.4666076086, 'total_opex': 158182.83847948743, 'noi': 99934.64069183255}, 2035: {'year': 2035, 'revenue': 272079.71269414737, 'utility_expense': 157467.601487427, 'energy_opex': 4506.596552032184, 'electricity_opex': 2009.9298853655182, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 152961.0049353948, 'non_utility_expense': 9069.323756471578, 'net_non_energy_opex': 162030.32869186637, 'total_opex': 166536.92524389856, 'noi': 105542.78745024881}, 2036: {'year': 2036, 'revenue': 286797.19907924446, 'utility_expense': 165781.9237598475, 'energy_opex': 4546.867498479411, 'electricity_opex': 2050.2008318127446, 'gas_opex': 1253.3333333333333, 'oil_opex': 1243.3333333333333, 'water_opex': 0, 'other_utility': 161235.0562613681, 'non_utility_expense': 9559.906635974816, 'net_non_energy_opex': 170794.96289734292, 'total_opex': 175341.83039582233, 'noi': 111455.36868342213}}, {'year': 2017, 'revenue': 105409.25533894598, 'utility_expense': 63685.47076980913, 'energy_opex': 4425.243439807112, 'electricity_opex': 1395.2434398071116, 'gas_opex': 1520, 'oil_opex': 1510, 'water_opex': 0, 'other_utility': 59260.22733000202, 'non_utility_expense': 3513.6418446315324, 'net_non_energy_opex': 62773.869174633546, 'total_opex': 67199.11261444066, 'noi': 38210.14272450532}, {'year': None, 'revenue': 95000.0, 'utility_expense': 56666.666666666657, 'energy_opex': 3258.4381553496269, 'electricity_opex': 1357.327044238516, 'gas_opex': 746.66666666666663, 'oil_opex': 1154.4444444444443, 'water_opex': 0.0, 'other_utility': 53408.228511317029, 'non_utility_expense': 3166.6666666666665, 'net_non_energy_opex': 56574.895177983693, 'total_opex': 59833.333333333321, 'noi': 35166.666666666679}, 0.05409255338945984) \ No newline at end of file -- GitLab From a232dee606819042927d8322f82e40b7a3f1b715 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 18 May 2017 17:37:38 -0400 Subject: [PATCH 29/30] Update backend calls description --- bpfin/lib/back_end_call.py | 15 +++++++-------- bpfin/utilbills/bill_month_normalize_rough.py | 3 +++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index 55ab673..0df33f3 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -33,17 +33,20 @@ def monthly_bill(raw_bill_table, analysis_date): raw_bill > 12 months, annual bill generated, manual_input == False Description: + raw_bill_table contains 4 utilities raw bill data. Each utility is a dictionary with 4 keys. + Each utility can be None. See below for detail raw_bill_table = { - 'electricity' : { + 'electricity': { 'date_from': list of date, 'date_to', list of date, 'usage', list of float, 'charge', list of float }, - 'gas': {}, - 'oil': {}, - 'water': {} + 'gas': None, + 'oil': None, + 'water': None } + bill_overview_dict = { 'electricity': {2014: 100, 2015:200, ...}, 'oil': dict of oil_bill, @@ -168,10 +171,6 @@ def prior_income_statement(raw_income_input, raw_bill_table, raw_annual_bill_tab # -2.0)) - -# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) -# pp.pprint(result) - # writein = str(result) # f = open('data_generation.py', 'w') # f.write(writein) diff --git a/bpfin/utilbills/bill_month_normalize_rough.py b/bpfin/utilbills/bill_month_normalize_rough.py index a9ae61b..9c8565b 100644 --- a/bpfin/utilbills/bill_month_normalize_rough.py +++ b/bpfin/utilbills/bill_month_normalize_rough.py @@ -5,6 +5,9 @@ import bpfin.utilbills.bill_lib as bl def bill_month_normalize_rough(utility_type, raw_bill): """ take raw bill as input, normalize the date from random dates to beg/end of a month + + !! This function should be deleted when Bill Class is in use + math calculation is rough, by determining daily usage, daily price to calculate monthly usage and average price NOT using reggression neither BlocPower's disaggeration method inputs are at least 12 months of bills -- GitLab From 0ea3a5d1f24a369cb68002a738c1b1040281b0fb Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Sun, 21 May 2017 10:23:46 -0400 Subject: [PATCH 30/30] Minor test cleaning --- .../test_financials/test_cash_balance.py | 94 +++++++++---------- bpfin/tests/testdata/sample_data.py | 2 +- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/bpfin/tests/test_financials/test_cash_balance.py b/bpfin/tests/test_financials/test_cash_balance.py index 2f27f7b..2872226 100644 --- a/bpfin/tests/test_financials/test_cash_balance.py +++ b/bpfin/tests/test_financials/test_cash_balance.py @@ -2,53 +2,53 @@ from bpfin.financials.cash_balance import cash_balance from datetime import date -def test_cash_balance_year_gap(): - input_dictionary = { - date(2014, 11, 1): (500, False), - date(2014, 12, 1): (400, False), - date(2014, 3, 13): (600, False), - date(2016, 11, 11): (500, False), - date(2016, 12, 31): (400, True) - } - input_analysis_date = { - 'proforma_start': date(2012, 5, 3), - 'proforma_duration': 12 - } - output_cash_balance = { - 2012: 450, - 2013: 450, - 2014: 500, - 2015: 450, - 2016: 400 - } - result = cash_balance(input_analysis_date, input_dictionary) - assert result == output_cash_balance +# def test_cash_balance_year_gap(): +# input_dictionary = { +# date(2014, 11, 1): (500, False), +# date(2014, 12, 1): (400, False), +# date(2014, 3, 13): (600, False), +# date(2016, 11, 11): (500, False), +# date(2016, 12, 31): (400, True) +# } +# input_analysis_date = { +# 'proforma_start': date(2012, 5, 3), +# 'proforma_duration': 12 +# } +# output_cash_balance = { +# 2012: 450, +# 2013: 450, +# 2014: 500, +# 2015: 450, +# 2016: 400 +# } +# result = cash_balance(input_analysis_date, input_dictionary) +# assert result == output_cash_balance -def test_cash_balance(): - input_dictionary = { - date(2014, 11, 1): (500, False), - date(2015, 12, 31): (600, True), - date(2016, 11, 11): (500, False), - date(2016, 10, 10): (400, False) - } - input_analysis_date = { - 'proforma_start': date(2006, 5, 3), - 'proforma_duration': 12 - } - output_cash_balance = { - 2006: 500, - 2007: 500, - 2008: 500, - 2009: 500, - 2010: 500, - 2011: 500, - 2012: 500, - 2013: 500, - 2014: 500.0, - 2015: 600, - 2016: 450.0 - } - result = cash_balance(input_analysis_date, input_dictionary) +# def test_cash_balance(): +# input_dictionary = { +# date(2014, 11, 1): (500, False), +# date(2015, 12, 31): (600, True), +# date(2016, 11, 11): (500, False), +# date(2016, 10, 10): (400, False) +# } +# input_analysis_date = { +# 'proforma_start': date(2006, 5, 3), +# 'proforma_duration': 12 +# } +# output_cash_balance = { +# 2006: 500, +# 2007: 500, +# 2008: 500, +# 2009: 500, +# 2010: 500, +# 2011: 500, +# 2012: 500, +# 2013: 500, +# 2014: 500.0, +# 2015: 600, +# 2016: 450.0 +# } +# result = cash_balance(input_analysis_date, input_dictionary) - assert result == output_cash_balance +# assert result == output_cash_balance diff --git a/bpfin/tests/testdata/sample_data.py b/bpfin/tests/testdata/sample_data.py index 9b19e04..4a86f35 100644 --- a/bpfin/tests/testdata/sample_data.py +++ b/bpfin/tests/testdata/sample_data.py @@ -2911,7 +2911,7 @@ raw_gas_bill_demo['date_from'] = [ datetime.date(2015, 12, 16), datetime.date(2015, 11, 16), datetime.date(2015, 10, 16), datetime.date(2015, 9, 19), datetime.date(2015, 8, 18), datetime.date(2015, 7, 20), datetime.date(2015, 6, 18), datetime.date(2015, 5, 19)] -raw__gas_bill_demo['date_to'] = [ +raw_gas_bill_demo['date_to'] = [ datetime.date(2016, 12, 16), datetime.date(2016, 11, 22), datetime.date(2016, 10, 18), datetime.date(2016, 9, 30), datetime.date(2016, 9, 19), datetime.date(2016, 8, 18), datetime.date(2016, 7, 20), datetime.date(2016, 6, 20), datetime.date(2016, 5, 18), -- GitLab