From f25250ab96862ee63e4442d8b1ca51508be52823 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 2 Aug 2017 12:22:58 -0400 Subject: [PATCH 1/6] Update backend call for inputs, especially bill overview part. Now monthly and annual bill projection are in same backend call function. It also returns displaying list of lists for prior annual bill (previously called bill overview) --- bpfin/back_end_call/back_end_inputs.py | 133 ++++++--------- bpfin/tests/testdata/feature_data.py | 2 +- bpfin/utilbills/bill.py | 220 +++++++------------------ bpfin/utilbills/bill_lib.py | 29 ++++ 4 files changed, 138 insertions(+), 246 deletions(-) diff --git a/bpfin/back_end_call/back_end_inputs.py b/bpfin/back_end_call/back_end_inputs.py index bdd422c..9bae39f 100644 --- a/bpfin/back_end_call/back_end_inputs.py +++ b/bpfin/back_end_call/back_end_inputs.py @@ -1,28 +1,29 @@ from bpfin.lib.other import UTILITY_TYPE_LIST from bpfin.utilbills.bill import Bill -from bpfin.financials.cash_balance import cash_balance -from bpfin.financials.liability import final_liability_dict +from bpfin.utilbills.bill_lib import sum_energy_opex from bpfin.financials.financial_income import Income_Statement_Table -from bpfin.financials.financial_balance import Balance_Sheet_Table -# Bill Overview Monthly Input -def monthly_bill(raw_bill_table, analysis_date): +def annual_bill(raw_monthly_bill_table, raw_annual_bill_table, analysis_date): """ - Take in raw_bill_table - Generate annual bill for utility type with available bill + Annual bill overview Calculation + Take in raw_monthly_bill_table and raw_annual_bill_table + Generate prior annual bill for utility type with available bill Generate manual_input dictionary indicating manual input label for 4 utilities + Return formatted annual bill table Args: - raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types + raw_monthly_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: + list: list of lists, display prior_annual_bill and manual_input indicators in displaying formatting 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 + raw_monthly_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: @@ -32,9 +33,9 @@ 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. + raw_monthly_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 = { + raw_monthly_bill_table = { 'electricity': { 'date_from': list of date, 'date_to', list of date, @@ -45,7 +46,11 @@ def monthly_bill(raw_bill_table, analysis_date): 'oil': None, 'water': None } - + raw_annual_bill_table = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} bill_overview_dict = { 'electricity': {2014: 100, 2015:200, ...}, 'oil': dict of oil_bill, @@ -55,23 +60,20 @@ def monthly_bill(raw_bill_table, analysis_date): To Do: from utilbills.bill.py merge form_prior_month_bill(), and delete that one * this work is done. need all units test and then can delete that """ - # 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') + raise ValueError('Bill_Overview - bill has empty analysis_date') + bill_overview_dict = {} manual_input_dict = {} prior_month_bill_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() + current_bill.put_raw_bill(raw_monthly_bill_table[utility], raw_annual_bill_table[utility]) + + bill_overview_dict[utility] = current_bill.get_prior_annual_bill() manual_input_dict[utility] = current_bill.get_manual_input() prior_month_bill_dict[utility] = current_bill.get_prior_proj_rough() + if len(bill_overview_dict.keys()) != len(UTILITY_TYPE_LIST): raise ValueError( 'Bill_Overview - monthly_bill has incomplete result in bill_overview_dict' @@ -80,69 +82,30 @@ def monthly_bill(raw_bill_table, analysis_date): raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') if len(prior_month_bill_dict.keys()) != len(UTILITY_TYPE_LIST): raise ValueError('Bill_Overview - monthly_bill has incomplete result of monthly_proj') - return bill_overview_dict, manual_input_dict, prior_month_bill_dict - # return bill_overview_dict + # formatting annual bill (bill overview on front end) + proforma_year = sorted(bill_overview_dict[UTILITY_TYPE_LIST[0]]) + bill_overview_front_end_list = [] -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 + bill_overview_front_end_list.append(['Data Source', 'Utility/Year']) + bill_overview_front_end_list[0] += proforma_year - 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 + for utility in UTILITY_TYPE_LIST: + utility_expense_list = [] + utility_expense_list.append('Annual Estimate' if manual_input_dict[utility] else 'Monthly Bill') + utility_expense_list.append(utility) + utility_expense_list += [bill_overview_dict[utility][year] for year in proforma_year] + bill_overview_front_end_list.append(utility_expense_list) - Returns: - annual_bill_table (dictionary): dictionary of dictionary of annual bills, see description for detail + bill_overview_front_end_list.append(['', 'Total Energy Expense']) + bill_overview_front_end_list[-1] += sum_energy_opex(bill_overview_dict)[1] - 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 = {} - 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) - 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() - manual_input_dict[utility] = current_bill.get_manual_input() - if len(annual_bill_table.keys()) != len(UTILITY_TYPE_LIST): - raise ValueError( - 'Bill_Overview - annual_bill has incomplete result in annual_bill_table' - ) - if len(manual_input_dict.keys()) != len(UTILITY_TYPE_LIST): - raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') - return annual_bill_table, manual_input_dict - # return annual_bill_table + return bill_overview_dict, manual_input_dict, prior_month_bill_dict, bill_overview_front_end_list def form_prior_income_table( raw_income_input, - raw_bill_table, + raw_monthly_bill_table, raw_annual_bill_table, analysis_date, growth_rate_flag): @@ -153,7 +116,7 @@ def form_prior_income_table( Args: raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year - raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types + raw_monthly_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required 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 @@ -167,7 +130,7 @@ def form_prior_income_table( Description: raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} - raw_bill_table = { + raw_monthly_bill_table = { 'electricity': { 'date_from': list of date, 'date_to', list of date, @@ -188,7 +151,7 @@ def form_prior_income_table( prior_income = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} """ - annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date)[0] + annual_bill_table = annual_bill(raw_monthly_bill_table, raw_annual_bill_table, analysis_date)[0] income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) income_table.project(growth_rate_flag, annual_bill_table) @@ -201,19 +164,25 @@ def form_prior_income_table( # **** ugly test **** # import pprint +# from bpfin.financials.cash_balance import cash_balance +# from bpfin.financials.liability import final_liability_dict +# from bpfin.financials.financial_balance import Balance_Sheet_Table # from bpfin.tests.testdata import feature_data as db -# 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('\nannual_bill =', annual_bill(db.raw_monthly_bill_table, db.raw_annual_bill_table, db.analysis_date)[-1]) + +# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) +# pp.pprint(annual_bill(db.raw_monthly_bill_table, db.raw_annual_bill_table, db.analysis_date)[-1]) + # print('\nprior_income_statement =', form_prior_income_table( # db.raw_income_input, -# db.raw_bill_table, +# db.raw_monthly_bill_table, # db.raw_annual_bill_table, # db.analysis_date, # -2.0)) # result = form_prior_income_table( # db.raw_income_input, -# db.raw_bill_table, +# db.raw_monthly_bill_table, # db.raw_annual_bill_table, # db.analysis_date, # -2.0)[0] diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 1ec4290..abd739a 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -172,7 +172,7 @@ raw_water_bill_demo['charge'] = [ raw_water_bill_demo['usage'] = [ 3500000, 3600000, 3300000] -raw_bill_table = { +raw_monthly_bill_table = { 'gas': raw_gas_bill_demo, 'electricity': raw_elec_bill_demo, 'oil': raw_oil_bill_demo, diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 0f4593f..2ba4606 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -15,11 +15,11 @@ from bpfin.lib.other import average_list_with_none # from bpfin.tests.testdata import feature_data as db -def validate_raw_bill(raw_bill): +def validate_raw_monthly_bill(raw_monthly_bill): """ - Raw bill validation. A valid raw_bill is not None, and it covers at least 12 months + Raw bill validation. A valid raw_monthly_bill is not None, and it covers at least 12 months Args: - raw_bill (dictionary): dictionary of lists. Keys are + raw_monthly_bill (dictionary): dictionary of lists. Keys are 'date_from': list of datetime, 'date_to', list of datetime, 'usage', list of float values, @@ -27,10 +27,10 @@ def validate_raw_bill(raw_bill): Return: Bollean: True == data validated. False == data is not validated """ - if raw_bill: + if raw_monthly_bill: month_list = form_date_calendar( - raw_bill['date_from'][0], - raw_bill['date_to'][-1])[1] + raw_monthly_bill['date_from'][0], + raw_monthly_bill['date_to'][-1])[1] if len(month_list) >= 12: return True else: @@ -76,17 +76,23 @@ class Bill(): 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 + raw_monthly_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, + raw_annual_bill (dictionary): {year: float}. annual bill allowing some years data missing + 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} Description: monthly_normalized_bill_rough = { @@ -107,12 +113,17 @@ class Bill(): Add in rate plan feature to replace/supplement current rough monthly pricing method """ + utility_type = None + analysis_date = None + proforma_date = None + is_manual_input = None + raw_monthly_bill = None + raw_annual_bill = None monthly_normalized_bill_rough = None prior_bill_rough = None monthly_normailzed_bill_reg = None prior_bill_reg = None - annual_bill = None - utility_type = None + prior_annual_bill = None def __init__(self, utility_type, analysis_date): """ @@ -126,7 +137,7 @@ class Bill(): self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] self.is_manual_input = True - def put_month_bill(self, raw_bill): + def put_raw_bill(self, raw_monthly_bill, raw_annual_bill): """ Put raw monthly bill. Validate input data (Not None and time coverage >=12 months) @@ -134,39 +145,51 @@ class Bill(): Determine annual bill with prior_saving bill Args: - raw_bill (dictionary): dictionary of lists. Keys are + raw_monthly_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, + raw_annual_bill (dictionary): {year: float}. annual bill allowing some years data missing """ - validation = validate_raw_bill(raw_bill) + self.raw_monthly_bill = raw_monthly_bill + self.raw_annual_bill = raw_annual_bill + + validation = validate_raw_monthly_bill(raw_monthly_bill) if validation: - self.monthly_normalized_bill_rough = bill_month_normalize_rough(self.utility_type, copy.deepcopy(raw_bill)) + self.monthly_normalized_bill_rough = bill_month_normalize_rough( + self.utility_type, + copy.deepcopy(raw_monthly_bill) + ) self.is_manual_input = False self.prior_bill_rough = bill_prior_proj_rough( copy.deepcopy(self.monthly_normalized_bill_rough), - copy.deepcopy(raw_bill), + copy.deepcopy(raw_monthly_bill), self.analysis_date, inflation_coeff_dict ) - self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) + self.prior_annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) else: + if self.raw_annual_bill: + self.prior_annual_bill = estimate_annual_bill( + copy.deepcopy(self.raw_annual_bill), + self.analysis_date + ) if validation == -1: raise ValueError('monthly bill input not valid for ', self.utility_type) else: pass - 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 + # 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: - self.annual_bill = estimate_annual_bill(raw_annual_bill, self.analysis_date) + # Args: + # raw_annual_bill (dictionary): {year: float}. annual bill allowing some years data missing + # """ + # if self.is_manual_input: + # self.annual_bill = estimate_annual_bill(raw_annual_bill, self.analysis_date) def get_prior_proj_rough(self): """ @@ -189,13 +212,13 @@ class Bill(): """ return self.prior_bill_reg - def get_annual_bill(self): + def get_prior_annual_bill(self): """ Get annual bill in dictionary format Return: dictionary: {year, float} """ - return self.annual_bill + return self.prior_annual_bill def get_manual_input(self): """ @@ -206,150 +229,21 @@ class Bill(): return self.is_manual_input -# def form_prior_month_bill(raw_bill_table, analysis_date, is_rough): -# """ -# Calculate and generate monthly energy bill for utilities that has available bills -# !!! Should be merged to back_end_call.monthly_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 -# is_rough (boolean): True == apply rough daily estimation, False == apply regression estimation - -# Return: -# prior_month_bill: dictionary, dict of dict of lists, projected energy bill for prior_saving - -# 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': { -# 'date_from': list of date, -# 'date_to', list of date, -# 'usage', list of float, -# 'charge', list of float}, -# 'gas': None, -# 'oil': None, -# 'water': None -# } - -# prior_month_bill = { -# 'electricity': { -# 'date_from': list, -# 'date_to': list, -# 'usage': list, -# 'charge': list, -# 'price': list}, -# 'gas': None, -# 'oil': None, -# 'water': None} -# """ -# if not raw_bill_table: -# raise ValueError('form_prior_month_bill - has empty raw_bill_table') -# if not analysis_date: -# raise ValueError('form_prior_month_bill - has empty analysis_date') - -# prior_month_bill_dict = {} -# for utility in UTILITY_TYPE_LIST: -# if is_rough: -# 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) -# prior_month_bill_dict[utility] = current_bill.get_prior_proj_rough() -# else: -# pass -# if len(prior_month_bill_dict.keys()) != len(UTILITY_TYPE_LIST): -# raise ValueError('form_prior_month_bill - has incomplete result') -# return prior_month_bill_dict - -# 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 - # **** ugly test **** # e_bill = Bill('electricity', db.analysis_date) -# e_bill.put_month_bill(db.raw_bill_table['electricity']) +# e_bill.put_month_bill(db.raw_monthly_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 = Bill(db.raw_monthly_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) +# bill_table = Bill_Table(db.raw_monthly_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(form_prior_month_bill(db.raw_bill_table, db.analysis_date, True)) +# print(form_prior_month_bill(db.raw_monthly_bill_table, db.analysis_date, True)) diff --git a/bpfin/utilbills/bill_lib.py b/bpfin/utilbills/bill_lib.py index 69d21ca..07e3a9f 100644 --- a/bpfin/utilbills/bill_lib.py +++ b/bpfin/utilbills/bill_lib.py @@ -3,6 +3,35 @@ import calendar import pandas as pd import copy from bpfin.lib.other import month_shift +from bpfin.lib.other import UTILITY_TYPE_LIST + + +def sum_energy_opex(bill_overview_dict): + """Calculate total energy opex + Args: + bill_overview_dict = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} + Return: + dictionary: total energy expense with year as key + list: total energy expense in list + """ + proforma_year = sorted(bill_overview_dict[UTILITY_TYPE_LIST[0]]) + total_opex_dict = {} + total_opex_list = [] + for year in proforma_year: + opex = 0 + for utility in UTILITY_TYPE_LIST: + if year in bill_overview_dict[utility]: + opex += bill_overview_dict[utility][year] + else: + raise ValueError('Key error: year not in bill_overview_dict when cal total bill. Year:', year) + total_opex_dict[year] = opex + total_opex_list.append(opex) + + return total_opex_dict, total_opex_list def add_list(obj_list, number): -- GitLab From 389d91cf3d28cd422d2be456a8e9d2d5ef7e09f2 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 2 Aug 2017 12:26:50 -0400 Subject: [PATCH 2/6] Modify function name --- bpfin/back_end_call/back_end_inputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bpfin/back_end_call/back_end_inputs.py b/bpfin/back_end_call/back_end_inputs.py index 9bae39f..3c89430 100644 --- a/bpfin/back_end_call/back_end_inputs.py +++ b/bpfin/back_end_call/back_end_inputs.py @@ -4,7 +4,7 @@ from bpfin.utilbills.bill_lib import sum_energy_opex from bpfin.financials.financial_income import Income_Statement_Table -def annual_bill(raw_monthly_bill_table, raw_annual_bill_table, analysis_date): +def form_annual_bill_table(raw_monthly_bill_table, raw_annual_bill_table, analysis_date): """ Annual bill overview Calculation Take in raw_monthly_bill_table and raw_annual_bill_table -- GitLab From 69f75bdbd6be5a117747ae217593e6b76dc72d4d Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 2 Aug 2017 12:31:55 -0400 Subject: [PATCH 3/6] Remove put annual_bill from Bill class. Raw bill put takes only one step now. --- bpfin/utilbills/bill.py | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 2ba4606..b704150 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -139,10 +139,13 @@ class Bill(): def put_raw_bill(self, raw_monthly_bill, raw_annual_bill): """ - 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 + Put raw monthly bill and raw annual bill. + Validate input data + If monthly bill is Not None and time coverage >=12 months: + Roughly month_normalize the bill and roughly project prior_saving bill + Else: + Project annual energy bill and project prior_saving bill only for annual + Determine all annual bill with prior_saving bill Args: raw_monthly_bill (dictionary): dictionary of lists. Keys are @@ -180,17 +183,6 @@ class Bill(): else: pass - # 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: - # 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 -- GitLab From ac7abf3382366af1fa1cb1866b952b8cdba3d25a Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 2 Aug 2017 15:41:41 -0400 Subject: [PATCH 4/6] Update files to incorporate Bill class and back_end_call change --- bpfin/back_end_call/back_end_budget.py | 6 +-- bpfin/back_end_call/back_end_inputs.py | 2 +- bpfin/back_end_call/back_end_prelim.py | 18 +++---- .../test_back_end_budget.py | 2 +- .../test_back_end_inputs.py | 18 ++----- .../test_financials/test_financial_saving.py | 4 +- bpfin/tests/testdata/feature_data.py | 48 +++++++++---------- 7 files changed, 44 insertions(+), 54 deletions(-) diff --git a/bpfin/back_end_call/back_end_budget.py b/bpfin/back_end_call/back_end_budget.py index 8fc3136..b004567 100644 --- a/bpfin/back_end_call/back_end_budget.py +++ b/bpfin/back_end_call/back_end_budget.py @@ -1,5 +1,5 @@ import copy -from bpfin.back_end_call.back_end_inputs import annual_bill +from bpfin.back_end_call.back_end_inputs import form_annual_bill_table from bpfin.financials.financial_income import Income_Statement_Table from bpfin.financials.financial_balance import Balance_Sheet_Table from bpfin.financials.cash_balance import cash_balance @@ -78,7 +78,7 @@ def budget_simulation( # function variable building up preference_list = ['loan_only', 'loan_first', 'sf_first', 'sf_max'] - annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date)[0] + annual_bill_table = form_annual_bill_table(raw_bill_table, raw_annual_bill_table, analysis_date)[0] income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) income_table.project(growth_rate_flag, annual_bill_table) @@ -149,7 +149,7 @@ def budget_simulation( # db.commission_date, # db.customer_preference, # db.req_dscr, -# db.raw_bill_table, +# db.raw_monthly_bill_table, # db.raw_annual_bill_table, # db.raw_income_input, # db.growth_rate_flag, diff --git a/bpfin/back_end_call/back_end_inputs.py b/bpfin/back_end_call/back_end_inputs.py index 3c89430..0dce36c 100644 --- a/bpfin/back_end_call/back_end_inputs.py +++ b/bpfin/back_end_call/back_end_inputs.py @@ -151,7 +151,7 @@ def form_prior_income_table( prior_income = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} """ - annual_bill_table = annual_bill(raw_monthly_bill_table, raw_annual_bill_table, analysis_date)[0] + annual_bill_table = form_annual_bill_table(raw_monthly_bill_table, raw_annual_bill_table, analysis_date)[0] 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/back_end_call/back_end_prelim.py b/bpfin/back_end_call/back_end_prelim.py index 0d22179..bfabaac 100644 --- a/bpfin/back_end_call/back_end_prelim.py +++ b/bpfin/back_end_call/back_end_prelim.py @@ -3,14 +3,14 @@ from bpfin.financials.liability import final_liability_dict from bpfin.financials.financial_income import Income_Statement_Table from bpfin.financials.financial_balance import Balance_Sheet_Table from bpfin.financials.scenario import Scenario -from bpfin.back_end_call.back_end_inputs import monthly_bill, annual_bill +from bpfin.back_end_call.back_end_inputs import form_annual_bill_table # delete following imports when online # from bpfin.tests.testdata import feature_data as db # import pprint -def prelim_scenario(raw_bill_table, raw_annual_bill_table, raw_income_input, +def prelim_scenario(raw_monthly_bill_table, raw_annual_bill_table, raw_income_input, growth_rate_flag, raw_liability_input, raw_cash_balance, raw_loan_input_list, analysis_date, commission_date, construction_cost, percent_saving_dict, full_saving_dict, @@ -19,7 +19,7 @@ def prelim_scenario(raw_bill_table, raw_annual_bill_table, raw_income_input, Conduct Preliminary Financial Analysis Args: - raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types + raw_monthly_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average @@ -44,7 +44,7 @@ def prelim_scenario(raw_bill_table, raw_annual_bill_table, raw_income_input, Description: raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} - raw_bill_table = { + raw_monthly_bill_table = { 'electricity': { 'date_from': list of date, 'date_to', list of date, @@ -111,8 +111,8 @@ def prelim_scenario(raw_bill_table, raw_annual_bill_table, raw_income_input, monthly_financial_projection_table write unit test file for scenario class and for prelim_scenario back_end_call """ - prior_annual_bill_table, manual_input_dict = annual_bill( - raw_bill_table, raw_annual_bill_table, analysis_date + prior_annual_bill_table, manual_input_dict, prior_month_bill, unsued_display_table = form_annual_bill_table( + raw_monthly_bill_table, raw_annual_bill_table, analysis_date ) prior_income_table = Income_Statement_Table( @@ -156,14 +156,14 @@ def prelim_scenario(raw_bill_table, raw_annual_bill_table, raw_income_input, loan_input_list=raw_loan_input_list) scenario_ob.prelim_analysis( - prior_month_bill=monthly_bill(raw_bill_table, analysis_date)[2], + prior_month_bill=prior_month_bill, percent_saving_dict=percent_saving_dict, full_saving_dict=full_saving_dict, req_dscr=req_dscr, customer_preference=customer_preference) # Define raw_annual_bill_table as a "get" of prior_bill - # Define raw_bill_table as a "get" of prior_bill + # Define raw_monthly_bill_table as a "get" of prior_bill # Delete prior_annual_bill_table # the following is front-end formatting building: loan_showcase_list = [ @@ -211,7 +211,7 @@ def prelim_scenario(raw_bill_table, raw_annual_bill_table, raw_income_input, # print( # prelim_scenario( -# raw_bill_table=db.raw_bill_table, +# raw_monthly_bill_table=db.raw_monthly_bill_table, # raw_annual_bill_table=db.raw_annual_bill_table, # raw_income_input=db.raw_income_input, # growth_rate_flag=-2.0, diff --git a/bpfin/tests/test_back_end_call/test_back_end_budget.py b/bpfin/tests/test_back_end_call/test_back_end_budget.py index d05d722..6b539ac 100644 --- a/bpfin/tests/test_back_end_call/test_back_end_budget.py +++ b/bpfin/tests/test_back_end_call/test_back_end_budget.py @@ -9,7 +9,7 @@ def test_budget_simulation(): db.commission_date, db.customer_preference, db.req_dscr, - db.raw_bill_table, + db.raw_monthly_bill_table, db.raw_annual_bill_table, db.raw_income_input, db.growth_rate_flag, diff --git a/bpfin/tests/test_back_end_call/test_back_end_inputs.py b/bpfin/tests/test_back_end_call/test_back_end_inputs.py index 8033219..63ed41a 100644 --- a/bpfin/tests/test_back_end_call/test_back_end_inputs.py +++ b/bpfin/tests/test_back_end_call/test_back_end_inputs.py @@ -1,21 +1,11 @@ # from bpfin.lib.back_end_call import budget_simulation -from bpfin.back_end_call.back_end_inputs import monthly_bill, annual_bill, form_prior_income_table +from bpfin.back_end_call.back_end_inputs import form_annual_bill_table, form_prior_income_table from bpfin.tests.testdata import feature_data as db -def test_monthly_bill(): - output_dict = { - 'electricity': db.prior_annual_bill['electricity'], - 'gas': db.prior_annual_bill['gas'], - 'oil': db.prior_annual_bill['oil'], - 'water': None} - result_dict = monthly_bill(db.raw_bill_table, db.analysis_date)[0] - assert output_dict == result_dict - - -def test_annual_bill(): +def test_form_annual_bill_table(): output_dict = db.prior_annual_bill - result_dict = annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)[0] + result_dict = form_annual_bill_table(db.raw_monthly_bill_table, db.raw_annual_bill_table, db.analysis_date)[0] assert output_dict == result_dict @@ -26,7 +16,7 @@ def test_form_prior_income_table(): output_historical_cagr = db.historical_cagr prior_income, next_year_income, average_income, historical_cagr = form_prior_income_table( db.raw_income_input, - db.raw_bill_table, + db.raw_monthly_bill_table, db.raw_annual_bill_table, db.analysis_date, -2.0) diff --git a/bpfin/tests/test_financials/test_financial_saving.py b/bpfin/tests/test_financials/test_financial_saving.py index fa921e6..62c924f 100644 --- a/bpfin/tests/test_financials/test_financial_saving.py +++ b/bpfin/tests/test_financials/test_financial_saving.py @@ -2,7 +2,7 @@ import datetime 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 bpfin.back_end_call.back_end_inputs import monthly_bill +from bpfin.back_end_call.back_end_inputs import form_annual_bill_table def test_Saving_put_monthly_proforma(): @@ -91,7 +91,7 @@ def test_Saving_Overview(): input_analysis_date = db.analysis_date input_commissioning_date = datetime.date(2017, 3, 14) - prior_monthly_bill = monthly_bill(db.raw_bill_table, input_analysis_date)[2] + prior_monthly_bill = form_annual_bill_table(db.raw_monthly_bill_table, db. raw_annual_bill_table, input_analysis_date)[2] input_prior_month_bill = prior_monthly_bill input_percent_saving_dict = db.percent_saving_dict input_full_saving_dict = db.full_saving_dict diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index abd739a..6497dd1 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -710,30 +710,30 @@ historical_cagr = 0.01242283656582921 growth_rate_flag = -2.0 budget_simulation_result = [ - [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], - ['Budget', 37723.783022506694, 68031.962268808478, 68031.962268808478, 68031.962268808478, 68031.962268808478], - ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0], - ['Joe Fund', 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], - ['Tooraj Capital', 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486, 18031.962268808486], - ['Self Finance', 0.0, 0.0, 0.0, 0.0, 0.0]], - [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], - ['Budget', 37723.783022506694, 77213.635268808473, 118031.96226880848, 118031.96226880848, 118031.96226880848], - ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0], - ['Joe Fund', 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], - ['Tooraj Capital', 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486, 18031.962268808486], - ['Self Finance', 0.0, 9181.6730000000007, 50000.0, 50000.0, 50000.0]], - [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], - ['Budget', 52184.67464428641, 89908.457666793111, 118031.96226880848, 118031.96226880848, 118031.96226880848], - ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0], - ['Joe Fund', 2184.6746442864069, 39908.457666793111, 50000.0, 50000.0, 50000.0], - ['Tooraj Capital', 0.0, 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486], - ['Self Finance', 50000.0, 50000.0, 50000.0, 50000.0, 50000.0]], - [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], - ['Budget', 87723.783022506686, 118031.96226880848, 118031.96226880848, 118031.96226880848, 118031.96226880848], - ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0], - ['Joe Fund', 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], - ['Tooraj Capital', 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486, 18031.962268808486], - ['Self Finance', 50000.0, 50000.0, 50000.0, 50000.0, 50000.0]] + [['saving_percentage', 0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], + ['Budget', 0.0, 37723.783022506694, 68031.962268808478, 68031.962268808478, 68031.962268808478, 68031.962268808478], + ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ['Joe Fund', 0.0, 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], + ['Tooraj Capital', 0.0, 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486, 18031.962268808486], + ['Self Finance', 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], + [['saving_percentage', 0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], + ['Budget', 0.0, 37723.783022506694, 77213.635268808473, 118031.96226880848, 118031.96226880848, 118031.96226880848], + ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ['Joe Fund', 0.0, 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], + ['Tooraj Capital', 0.0, 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486, 18031.962268808486], + ['Self Finance', 0.0, 0.0, 9181.6730000000007, 50000.0, 50000.0, 50000.0]], + [['saving_percentage', 0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], + ['Budget', 0.0, 52184.67464428641, 89908.457666793111, 118031.96226880848, 118031.96226880848, 118031.96226880848], + ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ['Joe Fund', 0.0, 2184.6746442864069, 39908.457666793111, 50000.0, 50000.0, 50000.0], + ['Tooraj Capital', 0.0, 0.0, 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486], + ['Self Finance', 0.0, 50000.0, 50000.0, 50000.0, 50000.0, 50000.0]], + [['saving_percentage', 0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], + ['Budget', 50000.0, 87723.783022506686, 118031.96226880848, 118031.96226880848, 118031.96226880848, 118031.96226880848], + ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ['Joe Fund', 0.0, 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], + ['Tooraj Capital', 0.0, 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486, 18031.962268808486], + ['Self Finance', 50000.0, 50000.0, 50000.0, 50000.0, 50000.0, 50000.0]] ] post_annual_bill = { -- GitLab From ab3807d63921e35acac7e2441ea70a3d32e816e5 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 3 Aug 2017 11:18:30 -0400 Subject: [PATCH 5/6] Fix Bill input mechanism, now can proceed if both raw_month_bill and raw_annual_bill are not available. --- bpfin/back_end_call/back_end_inputs.py | 4 ++-- bpfin/utilbills/bill.py | 10 +++++----- bpfin/utilbills/bill_lib.py | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/bpfin/back_end_call/back_end_inputs.py b/bpfin/back_end_call/back_end_inputs.py index 0dce36c..30f08b2 100644 --- a/bpfin/back_end_call/back_end_inputs.py +++ b/bpfin/back_end_call/back_end_inputs.py @@ -168,10 +168,10 @@ def form_prior_income_table( # from bpfin.financials.liability import final_liability_dict # from bpfin.financials.financial_balance import Balance_Sheet_Table # from bpfin.tests.testdata import feature_data as db -# print('\nannual_bill =', annual_bill(db.raw_monthly_bill_table, db.raw_annual_bill_table, db.analysis_date)[-1]) +# print('\nannual_bill =', form_annual_bill_table(db.raw_monthly_bill_table, db.raw_annual_bill_table, db.analysis_date)[-1]) # pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) -# pp.pprint(annual_bill(db.raw_monthly_bill_table, db.raw_annual_bill_table, db.analysis_date)[-1]) +# pp.pprint(form_annual_bill_table(db.raw_monthly_bill_table, db.raw_annual_bill_table, db.analysis_date)[-1]) # print('\nprior_income_statement =', form_prior_income_table( # db.raw_income_input, diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index b704150..93616fa 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -173,11 +173,11 @@ class Bill(): ) self.prior_annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) else: - if self.raw_annual_bill: - self.prior_annual_bill = estimate_annual_bill( - copy.deepcopy(self.raw_annual_bill), - self.analysis_date - ) + # if self.raw_annual_bill: + self.prior_annual_bill = estimate_annual_bill( + copy.deepcopy(self.raw_annual_bill), + self.analysis_date + ) if validation == -1: raise ValueError('monthly bill input not valid for ', self.utility_type) else: diff --git a/bpfin/utilbills/bill_lib.py b/bpfin/utilbills/bill_lib.py index 07e3a9f..5885f91 100644 --- a/bpfin/utilbills/bill_lib.py +++ b/bpfin/utilbills/bill_lib.py @@ -27,6 +27,7 @@ def sum_energy_opex(bill_overview_dict): if year in bill_overview_dict[utility]: opex += bill_overview_dict[utility][year] else: + # opex +=0 raise ValueError('Key error: year not in bill_overview_dict when cal total bill. Year:', year) total_opex_dict[year] = opex total_opex_list.append(opex) -- GitLab From 3b7139a056bfabf079a381e40e463e1a02e0b96f Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 3 Aug 2017 14:07:43 -0400 Subject: [PATCH 6/6] Updated version to 0.1.8 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 37eca03..0f42646 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ reqs = [str(req.req) for req in install_reqs] setup( name='bpfin', - version='0.1.0', + version='0.1.8', description='Finance models and utilites', author='BlocPower', author_email='admin@blocpower.org', -- GitLab