diff --git a/bpfin/financials/financial_balance.py b/bpfin/financials/financial_balance.py index b8643c91badb40bb58b49b52819dc14051614cc8..a56472bccc1de7e2467a60d83518370fcef04f92 100644 --- a/bpfin/financials/financial_balance.py +++ b/bpfin/financials/financial_balance.py @@ -81,7 +81,6 @@ class Balance_Sheet_Table(): self.hist_balance_sheet_table = [] # entire table, not just historical self.bs_table = [] - for year in sorted(cash_balance_dictionary): current_balance_sheet = Balance_Sheet() if year in net_income_dictionary: diff --git a/bpfin/financials/financial_income.py b/bpfin/financials/financial_income.py index 10781b4d5f378c9d3e89a231da9ceca29c1a67cf..93532e4185ad38254828714d85b91ffbdcac920c 100644 --- a/bpfin/financials/financial_income.py +++ b/bpfin/financials/financial_income.py @@ -1,465 +1,561 @@ -# """ -# 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 -# from bpfin.lib.other import average_list_with_none - - -# 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.lib import other as lib +from bpfin.utilbills.bill_lib import form_bill_year +from numpy import mean +from bpfin.lib.other import UTILITY_TYPE_LIST + + +class Income_Statement(): + """ + Generate Income Statement for single year, with standard accounting line items + Will be called in Income_Statement_Table() class + """ + 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 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, ...} + """ + if not validate_growth_rate_flag(growth_rate_flag): + raise ValueError('growth_rate input is not valid') + 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, 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 + 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 + } + + 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 + """ + if not validate_empty(raw_income_input): + raise ValueError( + 'Income_Statement_Table-init() raw_income_input is empty') + if not validate_empty(analysis_date): + raise ValueError( + 'Income_Statement_Table-init() analysis_date is empty') + # validate annual_bill_table + if not validate_annual_bill_table(annual_bill_table): + raise ValueError( + 'Income_Statement_Table-init() annual_bill_table is invalid') + # validate raw_income_input + + 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 = {} + self.analysis_date = analysis_date + + for year in sorted(raw_income_input.keys()): + 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] + self.cagr = lib.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 = copy.deepcopy(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, 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 + 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 + """ + if not validate_growth_rate_flag(growth_rate_flag): + raise ValueError('growth_rate input is not valid') + 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 + 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_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: + total_energy_dict[current_income_statement. + year] = current_income_statement.energy_opex + return total_energy_dict + + def get_utility_opex_dict(self, energy_utility_opex): + bill_dict = {} + for current_income_statement in self.table: + bill_dict[current_income_statement. + year] = current_income_statement.energy_utility_opex + + return bill_dict + + +def validate_growth_rate_flag(growth_rate_flag): + """ + Validate growth_rate_flag + the input is either -2, -1 or >= 0 + """ + if growth_rate_flag >= 0: + return True + if growth_rate_flag == -1: + return True + if growth_rate_flag == -2: + return True + return False + + +def validate_annual_bill_table(annual_bill_table): + """ + check annual bill table data + variable should have 4 keys, which are utility types + All utilities should have same year range (same starting year and length) + bill charge values are not None + """ + if not len(annual_bill_table.keys()) == len(UTILITY_TYPE_LIST): + return False + + start_year = sorted(annual_bill_table['electricity'].keys())[0] + duration = len(annual_bill_table['electricity'].keys()) + + for utility in UTILITY_TYPE_LIST: + if sorted(annual_bill_table[utility].keys())[0] != start_year: + return False + if len(annual_bill_table[utility].keys()) != duration: + return False + for charge in annual_bill_table[utility].values(): + if not charge: + return False + return True + + +def validate_empty(parameter): + """ + check empty for input parameter + """ + if not parameter: + return False + else: + return True diff --git a/bpfin/financials/financial_lib.py b/bpfin/financials/financial_lib.py index 726fa09674ca13a23397e0534425c9a51174fdac..236fa0faba2f05391b87546d0b723d5af0f842bd 100644 --- a/bpfin/financials/financial_lib.py +++ b/bpfin/financials/financial_lib.py @@ -9,6 +9,8 @@ from numpy import mean def organize_bill_overview(bill_overview, analysis_date): """take bill_overview as inputs, fill in the blank annual bill with average numbers + !! this func should be deleted when financial_income.py and back_end_call.py is online + Args: bill_overview (dictionary): original annual bill input, with blank cells, for 4 utility_types analysis_date (dictionary): proforma's starting date and the years of proforma @@ -43,7 +45,7 @@ def organize_bill_overview(bill_overview, analysis_date): average_bill_dict[i] = float(sum_bill) / year_number else: 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]) + # 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(1, 5): @@ -62,475 +64,441 @@ def organize_bill_overview(bill_overview, analysis_date): return bill_overview_organized -class Income_Statement(): - 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, 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 - 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 - } - - 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 - 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 = {} - self.analysis_date = analysis_date - - 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 = lib.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, 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 - 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(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 - 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_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: - total_energy_dict[ - current_income_statement.year] = current_income_statement.energy_opex - return total_energy_dict +# class Income_Statement(): +# 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 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, 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 +# 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 +# } + +# 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 +# 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 = {} +# self.analysis_date = analysis_date + +# 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 = lib.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, 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 +# 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(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 +# 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_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: +# total_energy_dict[ +# current_income_statement.year] = current_income_statement.energy_opex +# return total_energy_dict diff --git a/bpfin/financials/financial_saving.py b/bpfin/financials/financial_saving.py index 72c769c18ecce4423c786014abd7315516e3ded8..82bd4f7a96bf002eec311659650228ab77ef8044 100644 --- a/bpfin/financials/financial_saving.py +++ b/bpfin/financials/financial_saving.py @@ -1,366 +1,393 @@ -# """ -# 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 +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.lib.other import UTILITY_TYPE_LIST + + +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, + manual_input_dict, + 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: + manual_input_dict (dictionary): dict of boolean, indicating if bill is manually input + 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: + manual_input_dict = { + 'electricity': True, + 'oil': False, + 'gas': False, + 'water': False} + + prior_annual_bill_table = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} + """ + # 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.manual_input_dict = manual_input_dict + 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} + """ + 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 = {} + 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()) + # # **** ugly test **** diff --git a/bpfin/financials/saving.py b/bpfin/financials/saving.py deleted file mode 100644 index 96d80c82ffecd531c4df78d14cbe5cfb20a4e7c7..0000000000000000000000000000000000000000 --- a/bpfin/financials/saving.py +++ /dev/null @@ -1,384 +0,0 @@ -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/scenario.py b/bpfin/financials/scenario.py index bdce9e1433300ed264f91569290c8d0b8ed9edb7..3e4405bb803d2ca5c277c70aae76016252db5bb3 100644 --- a/bpfin/financials/scenario.py +++ b/bpfin/financials/scenario.py @@ -1,11 +1,17 @@ import copy -from bpfin.financials.saving import Saving_Overview +from bpfin.financials.financial_saving import Saving_Overview from bpfin.financials.loan import Loan_List from bpfin.lib.other import add_year_dictionary, divide_dscr_dict, min_none_list from bpfin.utilbills.bill_lib import form_bill_year, annualizing_projection +from bpfin.lib.other import UTILITY_TYPE_LIST # may delete from here after sample built -# from bpfin.financials.financial_lib import Income_Statement_Table, Balance_Sheet_Table -# from bpfin.tests.testdata import sample_data as db +# from bpfin.tests.testdata import feature_data as db +# from bpfin.tests.testdata import sample_data as sdb +# from bpfin.lib.back_end_call import monthly_bill +# from bpfin.financials.liability import final_liability_dict +# from bpfin.financials.balance_sheet_projection import balance_sheet_projection +# from bpfin.financials.financial_income import Income_Statement_Table +# from bpfin.financials.financial_balance import Balance_Sheet_Table class Scenario(): @@ -29,11 +35,13 @@ class Scenario(): loan_list (object): Loan_List object, used to store loan options, and result of financing planning saving_overview (object): Saving_Overview object, calculate and store scenario based saving results scheduled_loan_list (list): list of Loan objects. Store financing plans for each loan option + """ + def __init__(self, analysis_date, commission_date, construction_cost, - bill_overview, prior_annual_bill_table, other_debt_service, - prior_income_statement_table, prior_balance_sheet_table, - loan_input_list): + manual_input_dict, prior_income_statement_table, + other_debt_service, prior_annual_bill_table, growth_rate_flag, + prior_balance_sheet_table, loan_input_list): """ Initiate Scenario object. Pass in historical bill, income statement and balance sheet data @@ -45,9 +53,8 @@ class Scenario(): 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 construction_cost (float): project cost, estimated or quoted - bill_overview (dictionary): original annual bill input, with blank cells, for 4 utility_types + manual_input_dict (dictionary): dict of boolean values, indicating manual_input status of each utility prior_annual_bill_table (dictionary): annual bill, for 4 utility_types, proir to saving - other_debt_service (dict): dictionary of debt service values per year prior_income_statement_table (object): complete Income_Statement_Table, with prior_saving projection prior_balance_sheet_table (object): complete Balance_Sheet_Table, with prior_saving projection loan_input_list (list): list of dictionary of loan basic terms. @@ -61,23 +68,28 @@ class Scenario(): self.commission_date = copy.deepcopy(commission_date) self.construction_cost = copy.deepcopy(construction_cost) - self.prior_annual_bill_table = copy.deepcopy(prior_annual_bill_table) self.other_debt_service = copy.deepcopy(other_debt_service) + self.manual_input_dict = copy.deepcopy(manual_input_dict) + + self.prior_annual_bill_table = copy.deepcopy(prior_annual_bill_table) + self.prior_income_statement_table = copy.deepcopy(prior_income_statement_table) self.prior_balance_sheet_table = copy.deepcopy(prior_balance_sheet_table) + self.post_income_statement_table = None self.post_balance_sheet_table = None self.loan_list = Loan_List(loan_input_list) + self.saving_overview = Saving_Overview( - bill_overview=copy.deepcopy(bill_overview), + manual_input_dict=copy.deepcopy(manual_input_dict), prior_annual_bill_table=copy.deepcopy(prior_annual_bill_table), analysis_date=copy.deepcopy(analysis_date), commissioning_date=copy.deepcopy(commission_date)) self.scheduled_loan_list = None - def prelim_anlaysis(self, prior_month_bill, percent_saving_dict, + def prelim_analysis(self, prior_month_bill, percent_saving_dict, full_saving_dict, growth_rate_flag, req_dscr, customer_preference): """ @@ -110,7 +122,9 @@ class Scenario(): 'cust_saving_dscr': float, customer required lower limit of (saving / debt service) ratio} To Do: clarify commission date and loan start date - balance sheet is not considering in energy loan. Which is should. And it changes get_graph_dict + balance sheet is not considering in energy loan. Which should. And it changes get_graph_dict + + prior_month_bill -> self.prior_month_bill """ # calculate saving, by generating saving_overview object self.saving_overview.put_saving( @@ -119,23 +133,23 @@ class Scenario(): full_saving_dict=full_saving_dict) # generate post_saving income_statement_table object - post_annual_bill_table = copy.deepcopy(self.saving_overview.get_post_annual_bill_table()) - post_income_statement_table = copy.deepcopy(self.prior_income_statement_table) - post_income_statement_table.project(growth_rate_flag, self.analysis_date, post_annual_bill_table) + post_annual_bill_table = copy.deepcopy( + self.saving_overview.get_post_annual_bill_table()) + post_income_statement_table = copy.deepcopy( + self.prior_income_statement_table) + post_income_statement_table.project( + growth_rate_flag, post_annual_bill_table) self.post_income_statement_table = post_income_statement_table # generate post_saving balance_sheet_table object - post_saving_noi_dict = copy.deepcopy( - self.post_income_statement_table.get_noi_dict()) - post_balance_sheet_table = copy.deepcopy( - self.prior_balance_sheet_table) + post_saving_noi_dict = copy.deepcopy(self.post_income_statement_table.get_noi_dict()) + post_balance_sheet_table = copy.deepcopy(self.prior_balance_sheet_table) post_balance_sheet_table.project_balance_sheet( self.analysis_date, self.other_debt_service, post_saving_noi_dict) self.post_balance_sheet_table = post_balance_sheet_table # allocate loan and get loan schedules - first_year_saving = copy.deepcopy( - self.saving_overview.get_total_first_year_saving()) + first_year_saving = copy.deepcopy(self.saving_overview.get_total_first_year_saving()) first_year_noi = copy.deepcopy( self.post_income_statement_table.get_first_year_noi( self.commission_date)) @@ -165,13 +179,13 @@ class Scenario(): proforma_year = form_bill_year(self.analysis_date['proforma_start'], self.analysis_date['proforma_duration']) post_annual_bill_table = copy.deepcopy(self.saving_overview.get_post_annual_bill_table()) - utility_type_list = ['electricity', 'gas', 'oil', 'water'] post_energy_bill = {} for year in proforma_year: current_total_energy_bill = 0 - for utility_type in utility_type_list: - current_total_energy_bill += post_annual_bill_table[utility_type][year] + for utility_type in UTILITY_TYPE_LIST: + current_total_energy_bill += post_annual_bill_table[ + utility_type][year] post_energy_bill[year] = current_total_energy_bill return post_energy_bill @@ -188,7 +202,8 @@ class Scenario(): annual_debt_service_dict[year] = 0 for current_loan in copy.deepcopy(self.scheduled_loan_list): - current_loan_annual = annualizing_projection(current_loan.terms, current_loan.debt_service_due) + current_loan_annual = annualizing_projection( + current_loan.terms, current_loan.debt_service_due) for year in proforma_year: if year in current_loan_annual: annual_debt_service_dict[year] += current_loan_annual[year] @@ -211,7 +226,8 @@ class Scenario(): prior_annual_bill = copy.deepcopy(self.prior_income_statement_table.get_total_energy_dict()) net_saving_dict = {} for year in prior_annual_bill: - net_saving_dict[year] = prior_annual_bill[year] - proforma_bill[year] - annual_debt_service[year] + net_saving_dict[ + year] = prior_annual_bill[year] - proforma_bill[year] - annual_debt_service[year] graph_dict = { 'energy_expenses': proforma_bill, 'total_loan': annual_debt_service, @@ -233,10 +249,11 @@ class Scenario(): debt_dict = copy.deepcopy(self.get_annual_debt_service()) savings_dict = {} - utility_type_list = ['electricity', 'gas', 'oil', 'water'] - for utility_type in utility_type_list: + for utility_type in UTILITY_TYPE_LIST: savings_dict = add_year_dictionary( - savings_dict, copy.deepcopy(self.saving_overview.get_utility_annual_saving_charge(utility_type))) + savings_dict, + copy.deepcopy(self.saving_overview.get_utility_annual_saving_charge(utility_type)) + ) dscr_dict = {} dscr_dict['noi_dscr'] = divide_dscr_dict(noi_dict, debt_dict) @@ -271,14 +288,13 @@ class Scenario(): # # ***** ugly test that can be a guide for front end dev ***** - -# # liability_dictionary = cash_balance('dict of tuples, date: (float, boolean)') # growth_toggle = 0.01 # prior_IS_table = Income_Statement_Table( # db.raw_income_input, -# db.bill_overview_organized) -# prior_IS_table.project(growth_toggle, db.analysis_date, db.bill_overview_organized) -# prior_BS_table = Balance_Sheet_Table(db.raw_balance_sheet) +# db.prior_annual_bill, +# db.analysis_date) +# prior_IS_table.project(growth_toggle, db.prior_annual_bill) +# prior_BS_table = Balance_Sheet_Table(sdb.raw_balance_sheet) # prior_BS_table.project_balance_sheet( # db.analysis_date, # db.liability_dictionary, @@ -287,17 +303,19 @@ class Scenario(): # scenario = Scenario( # analysis_date=db.analysis_date, # commission_date=db.commission_date, -# construction_cost=60000, -# bill_overview=db.bill_overview, -# prior_annual_bill_table=db.bill_overview_organized, -# other_debt_service=db.liability_dictionary, +# construction_cost=db.cost_estimation, +# manual_input_dict=db.manual_input_dict, +# prior_annual_bill_table=db.prior_annual_bill, # prior_income_statement_table=prior_IS_table, +# other_debt_service=db.liability_dictionary, +# growth_rate_flag=growth_toggle, # prior_balance_sheet_table=prior_BS_table, -# loan_input_list=db.loan_input_list +# loan_input_list=db.raw_loan_input_list # ) -# scenario.prelim_anlaysis( -# prior_month_bill=db.prior_month_bill, +# prior_month_bill = monthly_bill(db.raw_bill_table, db.analysis_date)[2] +# scenario.prelim_analysis( +# prior_month_bill=prior_month_bill, # percent_saving_dict=db.percent_saving_dict, # full_saving_dict=db.full_saving_dict, # growth_rate_flag=growth_toggle, diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index 6e20df58a4436a1231d78b8c122d4556675a2143..74c02a695b570dd12eb339d3953409b0b046695e 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -1,12 +1,13 @@ +from bpfin.lib.other import UTILITY_TYPE_LIST from bpfin.utilbills.bill import Bill -from bpfin.financials.financial_lib import Income_Statement_Table +from bpfin.financials.cash_balance import cash_balance +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.tests.testdata import feature_data as db # import pprint -UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] - - # Bill Overview Monthly Input def monthly_bill(raw_bill_table, analysis_date): """ @@ -52,13 +53,18 @@ def monthly_bill(raw_bill_table, analysis_date): 'oil': dict of oil_bill, 'gas': dict of gas_bill, 'water': dict of water_bill} + + 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 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 = {} + prior_month_bill_dict = {} for utility in UTILITY_TYPE_LIST: current_bill = Bill(utility, analysis_date) if utility in raw_bill_table: @@ -67,12 +73,17 @@ def monthly_bill(raw_bill_table, analysis_date): current_bill.put_month_bill(None) bill_overview_dict[utility] = current_bill.get_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') - # 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 + 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') + 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 def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): @@ -100,14 +111,17 @@ def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): '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 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: @@ -117,12 +131,18 @@ def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): 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') - return annual_bill_table + 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 -def prior_income_statement_table( +def form_prior_income_table( raw_income_input, raw_bill_table, raw_annual_bill_table, @@ -167,10 +187,10 @@ def prior_income_statement_table( 'oil': {2014: 100, 2015:200, ...}, 'water': dict of water_bill} - prior_income_statement = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} + 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) + annual_bill_table = annual_bill(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) @@ -181,16 +201,76 @@ def prior_income_statement_table( return prior_income, next_year_income, average_income, historical_cagr +def prelim_scenario(raw_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, + req_dscr, customer_preference): + from bpfin.financials.scenario import Scenario + prior_annual_bill_table, manual_input_dict = annual_bill( + raw_bill_table, raw_annual_bill_table, analysis_date + ) + + prior_income_table = Income_Statement_Table(raw_income_input, prior_annual_bill_table, analysis_date) + + cash_balance_dict = cash_balance(analysis_date, raw_cash_balance) + liability_dict = final_liability_dict(analysis_date, raw_liability_input) + + balance_sheet_input = { + 'cash_dictionary': cash_balance_dict, + 'other_debt_service_dictionary': liability_dict, + 'net_income_dictionary': prior_income_table.get_noi_dict() + } + + prior_income_table.project(growth_rate_flag, prior_annual_bill_table) + + prior_bs = Balance_Sheet_Table(cash_balance_dict, liability_dict, prior_income_table.get_noi_dict()) + + prior_bs.project_balance_sheet(analysis_date, liability_dict, prior_income_table.get_noi_dict()) + + scenario_ob = Scenario( + analysis_date=analysis_date, + commission_date=commission_date, + construction_cost=construction_cost, + manual_input_dict=manual_input_dict, + prior_annual_bill_table=prior_annual_bill_table, + prior_income_statement_table=prior_income_table, + other_debt_service=liability_dict, + growth_rate_flag=growth_rate_flag, + prior_balance_sheet_table=prior_bs, + loan_input_list=raw_loan_input_list) + + scenario_ob.prelim_analysis( + prior_month_bill=monthly_bill(raw_bill_table, analysis_date)[2], + percent_saving_dict=percent_saving_dict, + full_saving_dict=full_saving_dict, + growth_rate_flag=growth_rate_flag, + 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 + # Delete prior_annual_bill_table + + return scenario_ob.get_graph_dict() + + # **** ugly test **** +# print(db.raw_bill_table) # 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)) +# prior_income_statement_result = prior_income_statement( # print('\nprior_income_statement =', prior_income_statement_table( # db.raw_income_input, # db.raw_bill_table, # db.raw_annual_bill_table, # db.analysis_date, -# -2.0)) +# -2.0) +# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) +# pp.pprint(prior_income_statement_result) # result = prior_income_statement_table( # db.raw_income_input, # db.raw_bill_table, @@ -202,3 +282,20 @@ def prior_income_statement_table( # f = open('data_generation.py', 'w') # f.write(writein) # f.close() + +# print(prelim_scenario( +# raw_bill_table=db.raw_bill_table, +# raw_annual_bill_table=db.raw_annual_bill_table, +# raw_income_input=db.raw_income_input, +# growth_rate_flag=-2.0, +# raw_liability_input=db.raw_liability_input, +# raw_cash_balance=db.raw_cash_balance, +# raw_loan_input_list=db.raw_loan_input_list, +# analysis_date=db.analysis_date, +# commission_date=db.commission_date, +# construction_cost=db.cost_estimation, +# percent_saving_dict=db.percent_saving_dict, +# full_saving_dict=db.full_saving_dict, +# req_dscr=db.req_dscr, +# customer_preference=db.customer_preference +# )) diff --git a/bpfin/lib/other.py b/bpfin/lib/other.py index 65e34f93d100a2b8f7c16bb16522c91de03a4e3e..a643c73cbc6166f77415bc136716570b8d62e74a 100644 --- a/bpfin/lib/other.py +++ b/bpfin/lib/other.py @@ -4,6 +4,9 @@ import numpy as np from scipy.optimize import linprog +UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] + + def add_year_dictionary(dict_1, dict_2): summed_dict = {x: dict_1.get(x, 0) + dict_2.get(x, 0) for x in set(dict_1).union(dict_2)} return summed_dict diff --git a/bpfin/tests/test_financials/test_balance_sheet_projection.py b/bpfin/tests/test_financials/test_balance_sheet_projection.py index fc0cc9c3f7d4915c887259d6dabb86090ee77e13..fdf781335d14e5dfe9d53d532adf69e8390909e1 100644 --- a/bpfin/tests/test_financials/test_balance_sheet_projection.py +++ b/bpfin/tests/test_financials/test_balance_sheet_projection.py @@ -1,5 +1,4 @@ from bpfin.financials.balance_sheet_projection import balance_sheet_projection -from bpfin.financials.financial_lib import Income_Statement_Table from bpfin.tests.testdata import sample_data as db import datetime diff --git a/bpfin/tests/test_financials/test_cash_balance.py b/bpfin/tests/test_financials/test_cash_balance.py index 7037a665e45b16400d3f0f9ce746639bb9fae243..880f499ec47aaa7900e54a2bfc1ef3e4da433ba4 100644 --- a/bpfin/tests/test_financials/test_cash_balance.py +++ b/bpfin/tests/test_financials/test_cash_balance.py @@ -30,6 +30,7 @@ def test_cash_balance(): date(2014, 11, 1): (500, False), date(2015, 12, 31): (600, True), date(2016, 11, 11): (300, False), + date(2016, 10, 11): (400, False), date(2016, 10, 10): (500, False) } input_analysis_date = { @@ -50,4 +51,5 @@ def test_cash_balance(): 2016: 400.0 } result = cash_balance(input_analysis_date, input_dictionary) + assert result == output_cash_balance diff --git a/bpfin/tests/test_financials/test_financial_income.py b/bpfin/tests/test_financials/test_financial_income.py new file mode 100644 index 0000000000000000000000000000000000000000..f92f4279aa0abd2492f9a3a577ccbf5761816b36 --- /dev/null +++ b/bpfin/tests/test_financials/test_financial_income.py @@ -0,0 +1,37 @@ +from bpfin.lib.other import UTILITY_TYPE_LIST +from bpfin.tests.testdata import feature_data as db +from bpfin.financials.financial_income import validate_growth_rate_flag + + +def test_validate_growth(): + input_list = [0.02, 0.01, 0, -0.5, -1, -1.5, -2, -2.5, -3] + output_list = [True, True, True, False, True, False, True, False, False] + for (growth, validation) in zip(input_list, output_list): + assert validation == validate_growth_rate_flag(growth) + +# Income_Statement_Table is tested in test_back_end_call.py +# def test_Income_Statement_Table(): +# input_raw_income_input = db.raw_income_input +# input_annual_bill_table = db.bill_overview_organized +# output_cagr = 0.05409255338945984 +# output_hist_table = db.income_statement_full +# output_average_table = db.income_statement_average +# output_noi_dict = db.noi_dict_average +# 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, +# 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 +# assert IS_table.get_average() == output_average_table # test get_average +# assert IS_table.get_noi_dict() == output_noi_dict # test get_noi_dict +# assert IS_table.get_single_year( +# 2017) == output_single_year # test get_single_year +# print(IS_table.get_single_year(2018)) +# assert IS_table.get_first_year_noi(datetime.date(2017, 3, 14)) == output_first_year_noi + diff --git a/bpfin/tests/test_financials/test_financial_lib.py b/bpfin/tests/test_financials/test_financial_lib.py index 8d5f6ec582af48fdc6c90379e976b57fac5ec27f..89823bfe990c4360c6b0f8baa661965776de99eb 100644 --- a/bpfin/tests/test_financials/test_financial_lib.py +++ b/bpfin/tests/test_financials/test_financial_lib.py @@ -1,9 +1,6 @@ import datetime import bpfin.financials.financial_lib as fl from bpfin.tests.testdata import sample_data as db -from bpfin.financials.financial_lib import Income_Statement_Table - - def test_organize_bill_overview(): input_bill_overview = db.bill_overview input_analysis_date = db.analysis_date @@ -12,28 +9,3 @@ def test_organize_bill_overview(): input_analysis_date) assert output_dict == result_dict - -def test_Income_Statement_Table(): - input_raw_income_input = db.raw_income_input - input_annual_bill_table = db.bill_overview_organized - output_cagr = 0.05409255338945984 - output_hist_table = db.income_statement_full - output_average_table = db.income_statement_average - output_noi_dict = db.noi_dict_average - 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, - 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 - assert IS_table.get_average() == output_average_table # test get_average - assert IS_table.get_noi_dict() == output_noi_dict # test get_noi_dict - assert IS_table.get_single_year( - 2017) == output_single_year # test get_single_year - print(IS_table.get_single_year(2018)) - assert IS_table.get_first_year_noi(datetime.date(2017, 3, 14)) == output_first_year_noi diff --git a/bpfin/tests/test_financials/test_financial_saving.py b/bpfin/tests/test_financials/test_financial_saving.py index 49e6fe1344258041e33c309fd879bfa528872b22..8c6f4699d4e96fb18fddf219f1f8a85389a96970 100644 --- a/bpfin/tests/test_financials/test_financial_saving.py +++ b/bpfin/tests/test_financials/test_financial_saving.py @@ -1,142 +1,145 @@ -# 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 - -# 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 = 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 - -# 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() - -# 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.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 = [ + 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(): + from bpfin.lib.back_end_call import monthly_bill + input_manual_input_dict = db.manual_input_dict + input_prior_annual_bill_table = db.prior_annual_bill + 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] + input_prior_month_bill = prior_monthly_bill + input_percent_saving_dict = db.percent_saving_dict + input_full_saving_dict = db.full_saving_dict + + output_first_year_saving = 28667.507248011731 + output_simple_payback = 1.744134903932668 + output_total_saving_percent = 0.46592312148565274 + output_electricity_annual_saving_charge = { + 2012: 0.0, + 2013: 0.0, + 2014: 0.0, + 2015: 0.0, + 2016: 0.0, + 2017: 162.81610514235683, + 2018: 217.5358446704256, + 2019: 222.88552019426197, + 2020: 228.10636614744124, + 2021: 232.98501809818731, + 2022: 237.40591301201312, + 2023: 241.73930274248983, + 2024: 246.16421306765812, + 2025: 250.73914770469878, + 2026: 255.65734234845252, + 2027: 260.96499723521435, + 2028: 266.41256515482269, + 2029: 271.86744883912024, + 2030: 277.3223130868306, + 2031: 282.79361546555913, + 2032: 288.32598130207771, + 2033: 293.98354473703466, + 2034: 299.79239684503989, + 2035: 305.75966632361155, + 2036: 311.88609209170363 + } + output_post_annual_bill_table = db.post_annual_bill + + saving_overview = Saving_Overview( + input_manual_input_dict, 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_financials/test_saving.py b/bpfin/tests/test_financials/test_saving.py deleted file mode 100644 index 1681c81dd42fced76d257d9b9bb802a7d2e7a72e..0000000000000000000000000000000000000000 --- a/bpfin/tests/test_financials/test_saving.py +++ /dev/null @@ -1,143 +0,0 @@ -# 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_lib/test_back_end_call.py similarity index 79% rename from bpfin/tests/test_utilbills/test_back_end_call.py rename to bpfin/tests/test_lib/test_back_end_call.py index 8a8e695a6d3a793ba122ee1724e76927d404ad27..b23b975c9f7f668e1ffdb57970c6ed3ff6295217 100644 --- a/bpfin/tests/test_utilbills/test_back_end_call.py +++ b/bpfin/tests/test_lib/test_back_end_call.py @@ -1,4 +1,4 @@ -from bpfin.lib.back_end_call import monthly_bill, annual_bill, prior_income_statement_table +from bpfin.lib.back_end_call import monthly_bill, annual_bill, form_prior_income_table from bpfin.tests.testdata import feature_data as db from bpfin.tests.testdata import sample_data as sdb @@ -6,27 +6,26 @@ from bpfin.tests.testdata import sample_data as sdb def test_monthly_bill(): output_dict = { 'electricity': db.prior_annual_bill['electricity'], - 'gas': None, - 'oil': None, + '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) + result_dict = monthly_bill(db.raw_bill_table, db.analysis_date)[0] 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) + result_dict = annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)[0] assert output_dict == result_dict -def test_prior_income_statement_table(): +def test_form_prior_income_table(): 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_table( + prior_income, next_year_income, average_income, historical_cagr = form_prior_income_table( db.raw_income_input, - # db.prior_annual_bill, db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date, diff --git a/bpfin/tests/test_utilbills/test_bill.py b/bpfin/tests/test_utilbills/test_bill.py index 5e27f234efc5c669d663487d862b39ab61bd9a45..02ebf740ddc9e5091a6222f6fd277db901a85320 100644 --- a/bpfin/tests/test_utilbills/test_bill.py +++ b/bpfin/tests/test_utilbills/test_bill.py @@ -1,8 +1,42 @@ -# 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 +from bpfin.tests.testdata import feature_data as db +UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] + + +def test_Bill_put_month(): + """ + test Bill with utility inputs + """ + analysis_date = db.analysis_date + + for utility in UTILITY_TYPE_LIST: + if db.raw_bill_table[utility]: + input_raw_bill = db.raw_bill_table[utility] + current_bill = Bill(utility, analysis_date) + current_bill.put_month_bill(input_raw_bill) + output_annual_bill = db.prior_annual_bill[utility] + # print('\n', current_bill.get_annual_bill()) + assert output_annual_bill == current_bill.get_annual_bill() + + +def test_Bill_put_annual(): + """ + test Bill with utility inputs + """ + analysis_date = db.analysis_date + + for utility in UTILITY_TYPE_LIST: + if not db.raw_bill_table[utility]: + input_raw_annual_bill = db.raw_annual_bill_table[utility] + current_bill = Bill(utility, analysis_date) + current_bill.put_annual_bill(input_raw_annual_bill) + output_annual_bill = db.prior_annual_bill[utility] + # print('\n', current_bill.get_annual_bill()) + assert output_annual_bill == current_bill.get_annual_bill() + # def test_Bill_Table_put_month(): # """ # test Bill_Table with only inputing month bill diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 5c825986eaa25c23cae227008cd8ddfda343abc3..3ca96fb5aad34c05e9e9249958c9981156b2b301 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -1,7 +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 # from bpfin.utilbills.bill import form_annual_bill_table @@ -14,13 +13,13 @@ import pprint # pro-forma projection time base analysis_date = { - 'proforma_start': datetime.date(2012, 1, 15), + 'proforma_start': date(2012, 1, 15), 'proforma_duration': 25} # Engineering Scenarios and project economics cost_estimation = 150000.00 -commission_date = datetime.date(2017, 3, 14) +commission_date = date(2017, 3, 14) # required debt service coverage ratio @@ -55,74 +54,185 @@ full_saving_dict = { } # dict of engineering analysis, {'date_from': [], 'date_to': [], 'heating': [], 'cooling': [], 'other': []} +raw_elec_bill_demo = {} +raw_elec_bill_demo['utility_type'] = 'electricity' +raw_elec_bill_demo['date_from'] = [ + date(2016, 11, 28), date(2016, 10, 26), date(2016, 9, 26), + date(2016, 8, 25), date(2016, 7, 27), date(2016, 6, 27), + date(2016, 5, 26), date(2016, 4, 27), date(2016, 3, 29), + date(2016, 2, 29), date(2016, 1, 28), date(2016, 12, 29), + date(2015, 11, 25), date(2015, 10, 27), date(2015, 9, 25), + date(2015, 8, 26), date(2015, 7, 28), date(2015, 6, 26), + date(2015, 5, 28), date(2015, 4, 28), date(2015, 3, 30), + date(2015, 2, 27), date(2015, 1, 28)] +raw_elec_bill_demo['date_to'] = [ + date(2016, 12, 28), date(2016, 11, 28), date(2016, 10, 26), + date(2016, 9, 26), date(2016, 8, 25), date(2016, 7, 27), + date(2016, 6, 27), date(2016, 5, 26), date(2016, 4, 27), + date(2016, 3, 29), date(2016, 2, 29), date(2016, 1, 28), + date(2016, 12, 29), date(2015, 11, 25), date(2015, 10, 27), + date(2015, 9, 25), date(2015, 8, 26), date(2015, 7, 28), + date(2015, 6, 26), date(2015, 5, 28), date(2015, 4, 28), + date(2015, 3, 30), date(2015, 2, 27)] +raw_elec_bill_demo['charge'] = [ + 56.01, 39.62, 67.42, 63.6, + 57.25, 56.21, 71.66, 68.28, + 63.57, 68.12, 71.73, 145.44, + 172.37, 130.65, 136.1, 137.14, + 126.16, 152.56, 169.68, 196.46, + 163.49, 163.88, 172.33] +raw_elec_bill_demo['usage'] = [ + 117.88, 91.93, 89.07, 98.17, + 86.88, 87.05, 101.05, 88.19, + 90.05, 104.19, 98.13, 182.55, + 208.85, 144.06, 136.16, 162.13, + 154.95, 179.64, 189.02, 168.92, + 190.8, 189.42, 214.66] -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_gas_bill_demo = {} +raw_gas_bill_demo['utility_type'] = 'gas' +raw_gas_bill_demo['date_from'] = [ + date(2016, 11, 22), date(2016, 10, 18), date(2016, 9, 30), + date(2016, 9, 19), date(2016, 8, 18), date(2016, 7, 20), + date(2016, 6, 20), date(2016, 5, 18), date(2016, 4, 19), + date(2016, 3, 18), date(2016, 2, 17), date(2016, 1, 19), + date(2015, 12, 16), date(2015, 11, 16), date(2015, 10, 16), + date(2015, 9, 19), date(2015, 8, 18), date(2015, 7, 20), + date(2015, 6, 18), date(2015, 5, 19)] +raw_gas_bill_demo['date_to'] = [ + date(2016, 12, 16), date(2016, 11, 22), date(2016, 10, 18), + date(2016, 9, 30), date(2016, 9, 19), date(2016, 8, 18), + date(2016, 7, 20), date(2016, 6, 20), date(2016, 5, 18), + date(2016, 4, 19), date(2016, 3, 18), date(2016, 2, 17), + date(2016, 1, 19), date(2015, 12, 16), date(2015, 11, 16), + date(2015, 10, 16), date(2015, 9, 19), date(2015, 8, 18), + date(2015, 7, 20), date(2015, 6, 18)] +raw_gas_bill_demo['charge'] = [ + 304.79, 456.54, 232.75, 372.16, + 192.29, 379.36, 385.14, 400.74, + 343.49, 377.99, 378.44, 361.68, + 454.84, 397.33, 401.29, 354.76, + 412.32, 378.43, 414.33, 390.65] +raw_gas_bill_demo['usage'] = [ + 322.00, 470.00, 241.00, 148.00, + 429.00, 389.00, 402.00, 443.00, + 389.00, 429.00, 402.00, 389.00, + 456.00, 402.00, 416.00, 362.00, + 429.00, 389.00, 429.00, 402.00] -raw_bill_table = { - 'electricity': e_raw_bill, - # 'gas': None, - # 'oil': None, - # 'water': None, -} +raw_oil_bill_demo = {} +raw_oil_bill_demo['utility_type'] = 'oil' +raw_oil_bill_demo['date_from'] = [ + date(2016, 11, 28), date(2016, 10, 26), date(2016, 9, 26), + date(2016, 8, 25), date(2016, 7, 27), date(2016, 6, 27), + date(2016, 5, 26), date(2016, 4, 27), date(2016, 3, 29), + date(2016, 2, 29), date(2016, 1, 28), date(2016, 12, 29), + date(2015, 11, 25), date(2015, 10, 27), date(2015, 9, 25), + date(2015, 8, 26), date(2015, 7, 28), date(2015, 6, 26), + date(2015, 5, 28), date(2015, 4, 28), date(2015, 3, 30), + date(2015, 2, 27), date(2015, 1, 28)] +raw_oil_bill_demo['date_to'] = [ + date(2016, 12, 28), date(2016, 11, 28), date(2016, 10, 26), + date(2016, 9, 26), date(2016, 8, 25), date(2016, 7, 27), + date(2016, 6, 27), date(2016, 5, 26), date(2016, 4, 27), + date(2016, 3, 29), date(2016, 2, 29), date(2016, 1, 28), + date(2016, 12, 29), date(2015, 11, 25), date(2015, 10, 27), + date(2015, 9, 25), date(2015, 8, 26), date(2015, 7, 28), + date(2015, 6, 26), date(2015, 5, 28), date(2015, 4, 28), + date(2015, 3, 30), date(2015, 2, 27)] +raw_oil_bill_demo['charge'] = [ + 3100, 2400, 3380, 1850, + 3000, 3200, 6700, 5300, + 4850, 5130, 5700, 3120, + 2420, 3400, 1870, 3020, + 3220, 6720, 5320, 4870, + 5150, 5720, 3680] +raw_oil_bill_demo['usage'] = [ + 1200, 700, 1000, 500, + 800, 900, 1700, 1400, + 1200, 1300, 1500, 1200, + 750, 1050, 550, 850, + 950, 1750, 1450, 1250, + 1350, 1550, 1450] +raw_water_bill_demo = {} +raw_water_bill_demo['utility_type'] = 'water' +raw_water_bill_demo['date_from'] = [ + date(2016, 1, 3), date(2015, 1, 1), date(2014, 1, 2)] +raw_water_bill_demo['date_to'] = [ + date(2016, 12, 28), date(2016, 1, 2), date(2014, 12, 31)] +raw_water_bill_demo['charge'] = [ + 20000, 20500, 21000] +raw_water_bill_demo['usage'] = [ + 3500000, 3600000, 3300000] + +raw_bill_table = { + 'gas': raw_gas_bill_demo, + 'electricity': raw_elec_bill_demo, + 'oil': raw_oil_bill_demo, + 'water': None} # raw annual_bill -annual_bill_gas = {2013: None, 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} +# 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} +annual_bill_water = {2014: 20000, 2015: 20500, 2016: 21000} +# annual_bill_gas = {2013: None, 2014: 0, 2015: 1020, 2016: 1220, 2017: 1520} +# annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} + raw_annual_bill_table = { - # 'electricity': None, # True == Mannual Input - 'gas': annual_bill_gas, - 'oil': annual_bill_oil, + 'electricity': None, # True == Mannual Input + 'gas': None, + 'oil': None, 'water': annual_bill_water } +manual_input_dict = { + 'electricity': False, + 'gas': False, + 'oil': False, + 'water': True +} + # 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 +# } +# } + + raw_income_input = { 2014: { - 'revenue': 90000, - 'utility_expense': 55000, - 'non_utility_expense': 3500 + 'revenue': 200000, + 'utility_expense': 77000, + 'non_utility_expense': 105000, }, 2015: { - 'revenue': 95000, - 'utility_expense': 55000, - 'non_utility_expense': 3000 + 'revenue': 196000, + 'utility_expense': 77400, + 'non_utility_expense': 104000, }, 2016: { - 'revenue': 100000, - 'utility_expense': 60000, - 'non_utility_expense': 3000 + 'revenue': 205000, + 'utility_expense': 78100, + 'non_utility_expense': 130000, } } + # saving scenarios percent_saving_dict = { 'electricity': 0.25, @@ -132,613 +242,588 @@ 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': []} + +raw_liability_input = { + 'debt1': (150, 'NYSERDA', 10, date(2012, 12, 31)), + 'debt2': (100, 'NYCEEC', 20, date(2012, 8, 31)) + } +raw_cash_balance = { + date(2014, 3, 14): (60000, False), + date(2015, 12, 31): (62000, True), + date(2016, 12, 31): (52000, True) +} + + +loan_term_1 = {'institute': 'NYSERDA', 'max_amount': 500000, 'interest': 0.08, 'duration': 120} +loan_term_2 = {'institute': 'Joe Fund', 'max_amount': 50000, 'interest': 0.05, 'duration': 108} +loan_term_3 = {'institute': 'Tooraj Capital', 'max_amount': 75000, 'interest': 0.07, 'duration': 114} +loan_term_dict = {1: loan_term_1, 2: loan_term_2, 3: loan_term_3} +raw_loan_input_list = [loan_term_1, loan_term_2, loan_term_3] + + +# ****** result ***** 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 + 2012: 792.50603354980865, 2013: 806.56436875723125, 2014: 819.06930386346062, 2015: 825.90552345154924, + 2016: 832.68423357284746, 2017: 849.02230670593224, 2018: 870.14337868170207, 2019: 891.54208077704823, + 2020: 912.4254645897646, 2021: 931.94007239274924, 2022: 949.62365204805212, 2023: 966.95721096995919, + 2024: 984.65685227063227, 2025: 1002.9565908187957, 2026: 1022.6293693938102, 2027: 1043.8599889408579, + 2028: 1065.650260619291, 2029: 1087.4697953564814, 2030: 1109.2892523473229, 2031: 1131.1744618622365, + 2032: 1153.3039252083101, 2033: 1175.9341789481377, 2034: 1199.1695873801598, 2035: 1223.0386652944458, + 2036: 1247.5443683668141 }, 'gas': { - 2014: 1253.3333333333333, - 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 + 2012: 4475.7823032289816, 2013: 4554.23035844273, 2014: 4625.0674743716318, 2015: 4661.2913287497577, + 2016: 4701.7495360589701, 2017: 4795.4503391884955, 2018: 4915.0282767633271, 2019: 5035.5379855566225, + 2020: 5153.4461029776512, 2021: 5263.0160958060724, 2022: 5362.6629176416373, 2023: 5460.5010514877649, + 2024: 5560.5157868837869, 2025: 5663.9000896622329, 2026: 5775.3403422790016, 2027: 5895.3500983195381, + 2028: 6018.354334038997, 2029: 6141.4801983648376, 2030: 6264.6360349883253, 2031: 6388.1588176948035, + 2032: 6513.1311498178793, 2033: 6640.9586307853224, 2034: 6772.2131729990733, 2035: 6907.0434291160136, + 2036: 7045.4681428808135 }, '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 + 2012: 29234.099384206329, 2013: 29755.35937994433, 2014: 30216.03963037725, 2015: 30474.979346602693, + 2016: 30718.862565999072, 2017: 31317.518165038713, 2018: 32095.810899238011, 2019: 32886.130778177096, + 2020: 33656.574871338729, 2021: 34378.237209910454, 2022: 35031.181929600622, 2023: 35670.741459090197, + 2024: 36323.497554619345, 2025: 36998.445300671876, 2026: 37723.192824355341, 2027: 38506.050354491614, + 2028: 39310.02024337896, 2029: 40115.192469629976, 2030: 40920.276277032368, 2031: 41727.798007431789, + 2032: 42544.132418124602, 2033: 43378.864396965881, 2034: 44235.89304442647, 2035: 45116.305108602697, + 2036: 46020.203841190028 }, - '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 + 'water': { + 2014: 20000, 2015: 20500, 2016: 21000, 2012: 20500.0, 2013: 20500.0, 2017: 20500.0, 2018: 20500.0, + 2019: 20500.0, 2020: 20500.0, 2021: 20500.0, 2022: 20500.0, 2023: 20500.0, 2024: 20500.0, 2025: 20500.0, + 2026: 20500.0, 2027: 20500.0, 2028: 20500.0, 2029: 20500.0, 2030: 20500.0, 2031: 20500.0, 2032: 20500.0, + 2033: 20500.0, 2034: 20500.0, 2035: 20500.0, 2036: 20500.0 } } + prior_income_statement_cagr = { 2014: { 'year': 2014, - 'revenue': 90000, - 'utility_expense': 55000, - 'energy_opex': 3842.7461677191232, - 'electricity_opex': 1346.0795010524571, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 51157.253832280876, - 'non_utility_expense': 3500, - 'net_non_energy_opex': 54657.253832280876, - 'total_opex': 58500.0, - 'noi': 31500.0 + 'revenue': 200000, + 'utility_expense': 77000, + 'energy_opex': 55660.176408612344, + 'electricity_opex': 819.06930386346062, + 'gas_opex': 4625.0674743716318, + 'oil_opex': 30216.03963037725, + 'water_opex': 20000, + 'other_utility': 21339.823591387656, + 'non_utility_expense': 105000, + 'net_non_energy_opex': 126339.82359138766, + 'total_opex': 182000.0, + 'noi': 18000.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 + 'revenue': 196000, + 'utility_expense': 77400, + 'energy_opex': 56462.176198804002, + 'electricity_opex': 825.90552345154924, + 'gas_opex': 4661.2913287497577, + 'oil_opex': 30474.979346602693, + 'water_opex': 20500, + 'other_utility': 20937.823801195998, + 'non_utility_expense': 104000, + 'net_non_energy_opex': 124937.82380119601, + 'total_opex': 181400.0, + 'noi': 14600.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.535428026851, - 'non_utility_expense': 3000, - 'net_non_energy_opex': 59201.535428026851, - 'total_opex': 63000.0, - 'noi': 37000.0 + 'revenue': 205000, + 'utility_expense': 78100, + 'energy_opex': 57253.29633563089, + 'electricity_opex': 832.68423357284746, + 'gas_opex': 4701.7495360589701, + 'oil_opex': 30718.862565999072, + 'water_opex': 21000, + 'other_utility': 20846.70366436911, + 'non_utility_expense': 130000, + 'net_non_energy_opex': 150846.70366436912, + 'total_opex': 208100.0, + 'noi': -3100.0 }, 2017: { 'year': 2017, - 'revenue': 105409.25533894598, - 'utility_expense': 63221.916617675291, - 'energy_opex': 4425.2434398071118, - 'electricity_opex': 1395.2434398071116, - 'gas_opex': 1520, - 'oil_opex': 1510, - 'water_opex': 0, - 'other_utility': 58796.673177868179, - 'non_utility_expense': 3513.6418446315324, - 'net_non_energy_opex': 62310.315022499708, - 'total_opex': 66735.558462306813, - 'noi': 38673.696876639166 + 'revenue': 207546.681495995, + 'utility_expense': 79261.074976421922, + 'energy_opex': 57461.990810933145, + 'electricity_opex': 849.02230670593224, + 'gas_opex': 4795.4503391884955, + 'oil_opex': 31317.518165038713, + 'water_opex': 20500.0, + 'other_utility': 21799.08416548877, + 'non_utility_expense': 117068.76044449637, + 'net_non_energy_opex': 138867.84460998513, + 'total_opex': 196329.83542091827, + 'noi': 11216.846075076726 }, 2018: { 'year': 2018, - 'revenue': 111111.11111111112, - 'utility_expense': 65903.741165893778, - 'energy_opex': 3926.6058050291458, - 'electricity_opex': 1429.9391383624793, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 61977.135360864631, - 'non_utility_expense': 3703.703703703704, - 'net_non_energy_opex': 65680.839064568339, - 'total_opex': 69607.444869597486, - 'noi': 41503.666241513638 + 'revenue': 210124.99999999994, + 'utility_expense': 80450.873180044437, + 'energy_opex': 58380.98255468304, + 'electricity_opex': 870.14337868170207, + 'gas_opex': 4915.0282767633271, + 'oil_opex': 32095.810899238011, + 'water_opex': 20500.0, + 'other_utility': 22069.890625361393, + 'non_utility_expense': 118523.08652246254, + 'net_non_energy_opex': 140592.97714782393, + 'total_opex': 198973.95970250695, + 'noi': 11151.040297492989 }, 2019: { 'year': 2019, - 'revenue': 117121.3948210511, - 'utility_expense': 69291.425515227427, - 'energy_opex': 3961.7886509294549, - 'electricity_opex': 1465.1219842627888, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 65329.63686429798, - 'non_utility_expense': 3904.0464940350366, - 'net_non_energy_opex': 69233.683358333015, - 'total_opex': 73195.472009262478, - 'noi': 43925.922811788623 + 'revenue': 212735.3485333948, + 'utility_expense': 81657.272114136751, + 'energy_opex': 59313.210844510766, + 'electricity_opex': 891.54208077704823, + 'gas_opex': 5035.5379855566225, + 'oil_opex': 32886.130778177096, + 'water_opex': 20500.0, + 'other_utility': 22344.061269625981, + 'non_utility_expense': 119995.47945560873, + 'net_non_energy_opex': 142339.54072523472, + 'total_opex': 201652.75156974548, + 'noi': 11082.596963649325 }, 2020: { 'year': 2020, - 'revenue': 123456.79012345681, - 'utility_expense': 72859.593377950107, - 'energy_opex': 3996.1096436560692, - 'electricity_opex': 1499.442976989403, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 68863.483734294045, - 'non_utility_expense': 4115.22633744856, - 'net_non_energy_opex': 72978.710071742607, - 'total_opex': 76974.819715398684, - 'noi': 46481.970408058129 + 'revenue': 215378.12499999988, + 'utility_expense': 82844.084329901569, + 'energy_opex': 60222.446438906147, + 'electricity_opex': 912.4254645897646, + 'gas_opex': 5153.4461029776512, + 'oil_opex': 33656.574871338729, + 'water_opex': 20500.0, + 'other_utility': 22621.637890995422, + 'non_utility_expense': 121486.16368552408, + 'net_non_energy_opex': 144107.8015765195, + 'total_opex': 204330.24801542563, + 'noi': 11047.87698457425 }, 2021: { 'year': 2021, - 'revenue': 130134.88313450124, - 'utility_expense': 76616.696522283994, - 'energy_opex': 4028.2111175084392, - 'electricity_opex': 1531.5444508417731, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 72588.485404775551, - 'non_utility_expense': 4337.829437816708, - 'net_non_energy_opex': 76926.314842592255, - 'total_opex': 80954.525960100698, - 'noi': 49180.357174400546 + 'revenue': 218053.7322467296, + 'utility_expense': 83975.856179475901, + 'energy_opex': 61073.193378109274, + 'electricity_opex': 931.94007239274924, + 'gas_opex': 5263.0160958060724, + 'oil_opex': 34378.237209910454, + 'water_opex': 20500.0, + 'other_utility': 22902.662801366623, + 'non_utility_expense': 122995.3664419989, + 'net_non_energy_opex': 145898.02924336554, + 'total_opex': 206971.22262147482, + 'noi': 11082.509625254781 }, 2022: { 'year': 2022, - 'revenue': 137174.21124828537, - 'utility_expense': 80572.264997622973, - 'energy_opex': 4057.2830706295772, - 'electricity_opex': 1560.6164039629107, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 76514.981926993394, - 'non_utility_expense': 4572.4737082761785, - 'net_non_energy_opex': 81087.45563526958, - 'total_opex': 85144.738705899159, - 'noi': 52029.472542386211 + 'revenue': 220762.5781249998, + 'utility_expense': 85030.647337560615, + 'energy_opex': 61843.468499290313, + 'electricity_opex': 949.62365204805212, + 'gas_opex': 5362.6629176416373, + 'oil_opex': 35031.181929600622, + 'water_opex': 20500.0, + 'other_utility': 23187.178838270298, + 'non_utility_expense': 124523.31777766213, + 'net_non_energy_opex': 147710.49661593244, + 'total_opex': 209553.96511522276, + 'noi': 11208.613009777036 }, 2023: { 'year': 2023, - 'revenue': 144594.3145938903, - 'utility_expense': 84739.644141653087, - 'energy_opex': 4085.7714696802459, - 'electricity_opex': 1589.1048030135796, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 80653.872671972844, - 'non_utility_expense': 4819.81048646301, - 'net_non_energy_opex': 85473.683158435859, - 'total_opex': 89559.454628116102, - 'noi': 55034.859965774202 + 'revenue': 223505.07555289776, + 'utility_expense': 86073.429092948703, + 'energy_opex': 62598.19972154792, + 'electricity_opex': 966.95721096995919, + 'gas_opex': 5460.5010514877649, + 'oil_opex': 35670.741459090197, + 'water_opex': 20500.0, + 'other_utility': 23475.229371400783, + 'non_utility_expense': 126070.25060304883, + 'net_non_energy_opex': 149545.47997444961, + 'total_opex': 212143.67969599753, + 'noi': 11361.395856900228 }, 2024: { 'year': 2024, - 'revenue': 152415.79027587266, - 'utility_expense': 89131.502637429046, - 'energy_opex': 4114.8560518808163, - 'electricity_opex': 1618.1893852141502, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 85016.646585548224, - 'non_utility_expense': 5080.526342529089, - 'net_non_energy_opex': 90097.172928077314, - 'total_opex': 94212.028979958137, - 'noi': 58203.761295914519 + 'revenue': 226281.6425781247, + 'utility_expense': 87135.528503000809, + 'energy_opex': 63368.670193773767, + 'electricity_opex': 984.65685227063227, + 'gas_opex': 5560.5157868837869, + 'oil_opex': 36323.497554619345, + 'water_opex': 20500.0, + 'other_utility': 23766.858309227046, + 'non_utility_expense': 127636.40072210363, + 'net_non_energy_opex': 151403.25903133067, + 'total_opex': 214771.92922510445, + 'noi': 11509.713353020255 }, 2025: { 'year': 2025, - 'revenue': 160660.349548767, - 'utility_expense': 93760.341878116174, - 'energy_opex': 4144.9277981463547, - 'electricity_opex': 1648.2611314796882, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 89615.414079969822, - 'non_utility_expense': 5355.3449849589, - 'net_non_energy_opex': 94970.759064928716, - 'total_opex': 99115.686863075069, - 'noi': 61544.662685691932 + 'revenue': 229092.70244172015, + 'utility_expense': 88227.412086838711, + 'energy_opex': 64165.301981152908, + 'electricity_opex': 1002.9565908187957, + 'gas_opex': 5663.9000896622329, + 'oil_opex': 36998.445300671876, + 'water_opex': 20500.0, + 'other_utility': 24062.110105685795, + 'non_utility_expense': 129222.00686812503, + 'net_non_energy_opex': 153284.11697381083, + 'total_opex': 217449.41895496374, + 'noi': 11643.283486756409 }, 2026: { 'year': 2026, - 'revenue': 169350.87808430297, - 'utility_expense': 98640.181698797518, - 'energy_opex': 4177.2410481883689, - 'electricity_opex': 1680.5743815217027, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 94462.940650609147, - 'non_utility_expense': 5645.029269476766, - 'net_non_energy_opex': 100107.96992008592, - 'total_opex': 104285.21096827429, - 'noi': 65065.667116028679 + 'revenue': 231938.6836425778, + 'utility_expense': 89382.192302985874, + 'energy_opex': 65021.162536028154, + 'electricity_opex': 1022.6293693938102, + 'gas_opex': 5775.3403422790016, + 'oil_opex': 37723.192824355341, + 'water_opex': 20500.0, + 'other_utility': 24361.029766957719, + 'non_utility_expense': 130827.3107401562, + 'net_non_energy_opex': 155188.34050711393, + 'total_opex': 220209.50304314209, + 'noi': 11729.180599435698 }, 2027: { 'year': 2027, - 'revenue': 178511.49949863003, - 'utility_expense': 103784.80808059855, - 'energy_opex': 4212.1257695209506, - 'electricity_opex': 1715.4591028542839, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 99572.682311077602, - 'non_utility_expense': 5950.383316621001, - 'net_non_energy_opex': 105523.0656276986, - 'total_opex': 109735.19139721955, - 'noi': 68776.308101410483 + 'revenue': 234820.0200027631, + 'utility_expense': 90608.923300079943, + 'energy_opex': 65945.260441752005, + 'electricity_opex': 1043.8599889408579, + 'gas_opex': 5895.3500983195381, + 'oil_opex': 38506.050354491614, + 'water_opex': 20500.0, + 'other_utility': 24663.662858327934, + 'non_utility_expense': 132452.55703982813, + 'net_non_energy_opex': 157116.21989815607, + 'total_opex': 223061.48033990807, + 'noi': 11758.539662855037 }, 2028: { 'year': 2028, - 'revenue': 188167.6423158922, - 'utility_expense': 109206.76135193532, - 'energy_opex': 4247.9384068140234, - 'electricity_opex': 1751.2717401473571, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 104958.8229451213, - 'non_utility_expense': 6272.254743863074, - 'net_non_energy_opex': 111231.07768898437, - 'total_opex': 115479.01609579839, - 'noi': 72688.626220093822 + 'revenue': 237737.1507336422, + 'utility_expense': 91864.080349168915, + 'energy_opex': 66894.02483803725, + 'electricity_opex': 1065.650260619291, + 'gas_opex': 6018.354334038997, + 'oil_opex': 39310.02024337896, + 'water_opex': 20500.0, + 'other_utility': 24970.055511131657, + 'non_utility_expense': 134097.9935086601, + 'net_non_energy_opex': 159068.04901979174, + 'total_opex': 225962.07385782897, + 'noi': 11775.076875813218 }, 2029: { 'year': 2029, - 'revenue': 198346.1105540334, - 'utility_expense': 114920.11497144966, - 'energy_opex': 4283.8012924745335, - 'electricity_opex': 1787.1346258078672, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 110636.31367897513, - 'non_utility_expense': 6611.5370184677795, - 'net_non_energy_opex': 117247.8506974429, - 'total_opex': 121531.65198991743, - 'noi': 76814.458564115965 + 'revenue': 240690.52050283214, + 'utility_expense': 93124.396893137426, + 'energy_opex': 67844.142463351294, + 'electricity_opex': 1087.4697953564814, + 'gas_opex': 6141.4801983648376, + 'oil_opex': 40115.192469629976, + 'water_opex': 20500.0, + 'other_utility': 25280.254429786128, + 'non_utility_expense': 135763.8709658238, + 'net_non_energy_opex': 161044.12539560991, + 'total_opex': 228888.26785896119, + 'noi': 11802.252643870946 }, 2030: { 'year': 2030, - 'revenue': 209075.15812876914, - 'utility_expense': 120940.57693831145, - 'energy_opex': 4319.6625548433458, - 'electricity_opex': 1822.9958881766795, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 116620.9143834681, - 'non_utility_expense': 6969.171937625638, - 'net_non_energy_opex': 123590.08632109374, - 'total_opex': 127909.74887593709, - 'noi': 81165.409252832047 + 'revenue': 243680.5795019832, + 'utility_expense': 94388.508463277947, + 'energy_opex': 68794.201564368006, + 'electricity_opex': 1109.2892523473229, + 'gas_opex': 6264.6360349883253, + 'oil_opex': 40920.276277032368, + 'water_opex': 20500.0, + 'other_utility': 25594.306898909941, + 'non_utility_expense': 137450.44334637656, + 'net_non_energy_opex': 163044.7502452865, + 'total_opex': 231838.95180965451, + 'noi': 11841.627692328679 }, 2031: { 'year': 2031, - 'revenue': 220384.56728225935, - 'utility_expense': 127284.86951679207, - 'energy_opex': 4355.63209570858, - 'electricity_opex': 1858.9654290419135, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 122929.23742108348, - 'non_utility_expense': 7346.152242741978, - 'net_non_energy_opex': 130275.38966382547, - 'total_opex': 134631.02175953405, - 'noi': 85753.545522725297 + 'revenue': 246707.78351540287, + 'utility_expense': 95659.392077519587, + 'energy_opex': 69747.131286988821, + 'electricity_opex': 1131.1744618622365, + 'gas_opex': 6388.1588176948035, + 'oil_opex': 41727.798007431789, + 'water_opex': 20500.0, + 'other_utility': 25912.260790530774, + 'non_utility_expense': 139157.96773996935, + 'net_non_energy_opex': 165070.22853050011, + 'total_opex': 234817.35981748893, + 'noi': 11890.423697913939 }, 2032: { 'year': 2032, - 'revenue': 232305.73125418797, - 'utility_expense': 133970.79332894427, - 'energy_opex': 4391.9995695352491, - 'electricity_opex': 1895.3329028685823, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 129578.79375940903, - 'non_utility_expense': 7743.524375139598, - 'net_non_energy_opex': 137322.31813454864, - 'total_opex': 141714.31770408389, - 'noi': 90591.413550104073 + 'revenue': 249772.5939895327, + 'utility_expense': 96944.732064533484, + 'energy_opex': 70710.567493150797, + 'electricity_opex': 1153.3039252083101, + 'gas_opex': 6513.1311498178793, + 'oil_opex': 42544.132418124602, + 'water_opex': 20500.0, + 'other_utility': 26234.164571382684, + 'non_utility_expense': 140886.70443003593, + 'net_non_energy_opex': 167120.86900141861, + 'total_opex': 237831.43649456941, + 'noi': 11941.157494963292 }, 2033: { 'year': 2033, - 'revenue': 244871.74142473264, - 'utility_expense': 141017.23028135113, - 'energy_opex': 4429.1887023694508, - 'electricity_opex': 1932.5220357027845, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 136588.04157898168, - 'non_utility_expense': 8162.3913808244215, - 'net_non_energy_opex': 144750.43295980609, - 'total_opex': 149179.62166217554, - 'noi': 95692.119762557093 + 'revenue': 252875.4781032879, + 'utility_expense': 98255.82451699338, + 'energy_opex': 71695.757206699345, + 'electricity_opex': 1175.9341789481377, + 'gas_opex': 6640.9586307853224, + 'oil_opex': 43378.864396965881, + 'water_opex': 20500.0, + 'other_utility': 26560.067310294038, + 'non_utility_expense': 142636.91693346857, + 'net_non_energy_opex': 169196.98424376262, + 'total_opex': 240892.74145046197, + 'noi': 11982.73665282593 }, 2034: { 'year': 2034, - 'revenue': 258117.47917131998, - 'utility_expense': 148443.8093823333, - 'energy_opex': 4467.371871878835, - 'electricity_opex': 1970.7052052121689, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 143976.43751045447, - 'non_utility_expense': 8603.915972377332, - 'net_non_energy_opex': 152580.35348283179, - 'total_opex': 157047.72535471062, - 'noi': 101069.75381660936 + 'revenue': 256016.90883927097, + 'utility_expense': 99597.294490472937, + 'energy_opex': 72707.2758048057, + 'electricity_opex': 1199.1695873801598, + 'gas_opex': 6772.2131729990733, + 'oil_opex': 44235.89304442647, + 'water_opex': 20500.0, + 'other_utility': 26890.018685667244, + 'non_utility_expense': 144408.8720407868, + 'net_non_energy_opex': 171298.89072645406, + 'total_opex': 244006.16653125978, + 'noi': 12010.742308011191 }, 2035: { 'year': 2035, - 'revenue': 272079.71269414737, - 'utility_expense': 156271.08719534514, - 'energy_opex': 4506.5965520321843, - 'electricity_opex': 2009.9298853655182, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 151764.49064331295, - 'non_utility_expense': 9069.323756471578, - 'net_non_energy_opex': 160833.81439978452, - 'total_opex': 165340.41095181671, - 'noi': 106739.30174233066 + 'revenue': 259197.36505587003, + 'utility_expense': 100970.45619606454, + 'energy_opex': 73746.387203013161, + 'electricity_opex': 1223.0386652944458, + 'gas_opex': 6907.0434291160136, + 'oil_opex': 45116.305108602697, + 'water_opex': 20500.0, + 'other_utility': 27224.068993051384, + 'non_utility_expense': 146202.83985680525, + 'net_non_energy_opex': 173426.90884985664, + 'total_opex': 247173.29605286982, + 'noi': 12024.069003000215 }, 2036: { 'year': 2036, - 'revenue': 286797.19907924446, - 'utility_expense': 164520.68695453997, - 'energy_opex': 4546.8674984794106, - 'electricity_opex': 2050.2008318127446, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 159973.81945606056, - 'non_utility_expense': 9559.906635974816, - 'net_non_energy_opex': 169533.72609203539, - 'total_opex': 174080.59359051479, - 'noi': 112716.60548872966 + 'revenue': 262417.3315602527, + 'utility_expense': 102375.48550524657, + 'energy_opex': 74813.216352437652, + 'electricity_opex': 1247.5443683668141, + 'gas_opex': 7045.4681428808135, + 'oil_opex': 46020.203841190028, + 'water_opex': 20500.0, + 'other_utility': 27562.269152808924, + 'non_utility_expense': 148019.09384180643, + 'net_non_energy_opex': 175581.36299461537, + 'total_opex': 250394.57934705302, + 'noi': 12022.752213199681 } } next_year_income = { 'year': 2017, - 'revenue': 105409.25533894598, - 'utility_expense': 63221.916617675291, - 'energy_opex': 4425.2434398071118, - 'electricity_opex': 1395.2434398071116, - 'gas_opex': 1520, - 'oil_opex': 1510, - 'water_opex': 0, - 'other_utility': 58796.673177868179, - 'non_utility_expense': 3513.6418446315324, - 'net_non_energy_opex': 62310.315022499708, - 'total_opex': 66735.558462306813, - 'noi': 38673.696876639166 + 'revenue': 207546.681495995, + 'utility_expense': 79261.074976421922, + 'energy_opex': 57461.990810933145, + 'electricity_opex': 849.02230670593224, + 'gas_opex': 4795.4503391884955, + 'oil_opex': 31317.518165038713, + 'water_opex': 20500.0, + 'other_utility': 21799.08416548877, + 'non_utility_expense': 117068.76044449637, + 'net_non_energy_opex': 138867.84460998513, + 'total_opex': 196329.83542091827, + 'noi': 11216.846075076726 } average_income = { 'year': None, - 'revenue': 95000.0, - 'utility_expense': 56666.666666666657, - 'energy_opex': 3676.2159331274047, - 'electricity_opex': 1357.327044238516, - 'gas_opex': 1164.4444444444443, - 'oil_opex': 1154.4444444444443, - 'water_opex': 0.0, - 'other_utility': 52990.450733539255, - 'non_utility_expense': 3166.6666666666665, - 'net_non_energy_opex': 56157.11740020592, - 'total_opex': 59833.333333333321, - 'noi': 35166.666666666679 + 'revenue': 200333.33333333334, + 'utility_expense': 77500.0, + 'energy_opex': 56458.549647682405, + 'electricity_opex': 825.88635362928574, + 'gas_opex': 4662.7027797267865, + 'oil_opex': 30469.960514326336, + 'water_opex': 20500.0, + 'other_utility': 21041.450352317588, + 'non_utility_expense': 113000.00000000001, + 'net_non_energy_opex': 134041.4503523176, + 'total_opex': 190500.0, + 'noi': 9833.333333333343 } +historical_cagr = 0.01242283656582921 -historical_cagr = 0.05409255338945984 - -post_annual_bill_table = { +post_annual_bill = { '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 + 2012: 792.50603354980865, + 2013: 806.56436875723125, + 2014: 819.06930386346062, + 2015: 825.90552345154924, + 2016: 832.68423357284746, + 2017: 686.20620156357541, + 2018: 652.60753401127647, + 2019: 668.65656058278626, + 2020: 684.31909844232337, + 2021: 698.95505429456193, + 2022: 712.21773903603901, + 2023: 725.21790822746937, + 2024: 738.49263920297415, + 2025: 752.21744311409691, + 2026: 766.97202704535766, + 2027: 782.89499170564352, + 2028: 799.23769546446829, + 2029: 815.60234651736118, + 2030: 831.96693926049227, + 2031: 848.38084639667738, + 2032: 864.97794390623244, + 2033: 881.95063421110308, + 2034: 899.37719053511989, + 2035: 917.27899897083421, + 2036: 935.65827627511044 }, '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 + 2012: 4475.7823032289816, + 2013: 4554.23035844273, + 2014: 4625.0674743716318, + 2015: 4661.2913287497577, + 2016: 4701.7495360589701, + 2017: 4432.0076232344945, + 2018: 4423.5254490869947, + 2019: 4531.9841870009604, + 2020: 4638.1014926798862, + 2021: 4736.7144862254645, + 2022: 4826.3966258774726, + 2023: 4914.4509463389886, + 2024: 5004.464208195408, + 2025: 5097.5100806960108, + 2026: 5197.806308051101, + 2027: 5305.815088487584, + 2028: 5416.5189006350965, + 2029: 5527.3321785283542, + 2030: 5638.1724314894936, + 2031: 5749.3429359253232, + 2032: 5861.8180348360929, + 2033: 5976.8627677067898, + 2034: 6094.9918556991643, + 2035: 6216.3390862044125, + 2036: 6340.9213285927335 }, '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 + 2012: 29234.099384206329, + 2013: 29755.35937994433, + 2014: 30216.03963037725, + 2015: 30474.979346602693, + 2016: 30718.862565999072, + 2017: 12627.09017823708, + 2018: 6419.1621798475999, + 2019: 6577.2261556354169, + 2020: 6731.3149742677442, + 2021: 6875.647441982087, + 2022: 7006.2363859201232, + 2023: 7134.1482918180382, + 2024: 7264.6995109238669, + 2025: 7399.6890601343712, + 2026: 7544.6385648710675, + 2027: 7701.2100708983235, + 2028: 7862.0040486757889, + 2029: 8023.0384939259939, + 2030: 8184.0552554064707, + 2031: 8345.5596014863549, + 2032: 8508.8264836249164, + 2033: 8675.7728793931728, + 2034: 8847.1786088852914, + 2035: 9023.2610217205365, + 2036: 9204.0407682380028 }, 'water': { - 2012: 0, - 2013: 0, - 2014: 0, + 2012: 20500.0, + 2013: 20500.0, + 2014: 20000, + 2015: 20500, + 2016: 21000, + 2017: 20500.0, + 2018: 20500.0, + 2019: 20500.0, + 2020: 20500.0, + 2021: 20500.0, + 2022: 20500.0, + 2023: 20500.0, + 2024: 20500.0, + 2025: 20500.0, + 2026: 20500.0, + 2027: 20500.0, + 2028: 20500.0, + 2029: 20500.0, + 2030: 20500.0, + 2031: 20500.0, + 2032: 20500.0, + 2033: 20500.0, + 2034: 20500.0, + 2035: 20500.0, + 2036: 20500.0 + } +} + + +liability_dictionary = { + 2012: 650, + 2013: 2550, + 2014: 300, 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 + 2017: 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/sample_data.py b/bpfin/tests/testdata/sample_data.py index 25ddfe01031a6b755c93ed553c70a75e585e62ee..8f15a0e75bf0d1a9e5ac114657b8243f99c82f9f 100644 --- a/bpfin/tests/testdata/sample_data.py +++ b/bpfin/tests/testdata/sample_data.py @@ -1,6 +1,5 @@ import datetime import pprint -# from bpfin.utilbills import bill_lib as bl # pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) # pp.pprint(inflation_coeff_dict) @@ -627,15 +626,20 @@ income_statement_projection_cagr = { 'water_opex': 0.0, 'year': 2036}} -noi_dictionary = {2014: 31500, 2015: 37000, 2016: 37000, 2017: 41278.43692755746, 2018 : 45214.28536645262, 2019 : 48842.631285961135, -2020: 52731.934093183474,2021: 56939.349890971425,2022:61487.1217724919, 2023 : 66332.18430708232, -2024: 71461.4261836982, 2025: 76882.87761572453, 2026: 82578.96693651963, 2027: 88565.60757013515, -2028: 94901.76519041706, 2029: 101627.80434590613, 2030: 108765.29589285178, 2031: 116334.19001298171, 2032: 124349.95052275498, -2033: 132827.33443479502, 2034: 141788.36292608615, 2035: 151259.5001164715, 2036: 161269.59000112678} +noi_dictionary = { + 2014: 31500, 2015: 37000, 2016: 37000, 2017: 41278.43692755746, 2018: 45214.28536645262, 2019: 48842.631285961135, + 2020: 52731.934093183474, 2021: 56939.349890971425, 2022: 61487.1217724919, 2023: 66332.18430708232, + 2024: 71461.4261836982, 2025: 76882.87761572453, 2026: 82578.96693651963, 2027: 88565.60757013515, + 2028: 94901.76519041706, 2029: 101627.80434590613, 2030: 108765.29589285178, 2031: 116334.19001298171, + 2032: 124349.95052275498, + 2033: 132827.33443479502, 2034: 141788.36292608615, 2035: 151259.5001164715, 2036: 161269.59000112678 +} -liability_input = {'debt1': (150, 'NYSERDA', 10, datetime.date(2012, 12, 31)), - 'debt2': (100, 'NYCEEC', 20, datetime.date(2012, 8, 31))} +liability_input = { + 'debt1': (150, 'NYSERDA', 10, datetime.date(2012, 12, 31)), + 'debt2': (100, 'NYCEEC', 20, datetime.date(2012, 8, 31)) +} liability_dictionary = { @@ -3014,13 +3018,41 @@ raw_water_bill_demo['charge'] = [ raw_water_bill_demo['usage'] = [ 3500000, 3600000, 3300000] -bill_demo = {'gas_bill': raw_gas_bill_demo, 'electricity_bill': raw_elec_bill_demo, - 'oil_bill': raw_oil_bill_demo, 'water_bill': raw_water_bill_demo} +bill_demo = { + 'gas_bill': raw_gas_bill_demo, + 'electricity_bill': raw_elec_bill_demo, + 'oil_bill': raw_oil_bill_demo, + 'water_bill': raw_water_bill_demo} -raw_income_input_demo = {2014: {'total_revenue': 200000, 'other_utility_expenses': 10000, 'energy_expenses': 67000, 'utility_expenses': 77000, 'non_utility_operating_expenses': 105000, -'total_expenses': 182000, 'noi': 18000}, 2015: {'total_revenue': 196000, 'other_utility_expenses': 8400, 'energy_expenses': 69000, 'utility_expenses': 77400, 'non_utility_operating_expenses': 104000, -'total_expenses': 181400, 'noi': 14600}, 2016: {'total_revenue': 205000, 'other_utility_expenses': 8100, 'energy_expenses': 70000, 'utility_expenses': 78100, 'non_utility_operating_expenses': 130000, -'total_expenses': 208100, 'noi': -3100}} +raw_income_input_demo = { + 2014: { + 'total_revenue': 200000, + 'other_utility_expenses': 10000, + 'energy_expenses': 67000, + 'utility_expenses': 77000, + 'non_utility_operating_expenses': 105000, + 'total_expenses': 182000, + 'noi': 18000 + }, + 2015: { + 'total_revenue': 196000, + 'other_utility_expenses': 8400, + 'energy_expenses': 69000, + 'utility_expenses': 77400, + 'non_utility_operating_expenses': 104000, + 'total_expenses': 181400, + 'noi': 14600 + }, + 2016: { + 'total_revenue': 205000, + 'other_utility_expenses': 8100, + 'energy_expenses': 70000, + 'utility_expenses': 78100, + 'non_utility_operating_expenses': 130000, + 'total_expenses': 208100, + 'noi': -3100 + } +} raw_balance_sheet_demo = {2014: {'cash': 60000, 'other_debt_service': 1000, 'noi': 18000}, diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py index 535e04278b28ed4d987923697f5231e0f312b2bb..0f4593f067e35a6c4354eca4b075bb64c6ecc8df 100644 --- a/bpfin/utilbills/bill.py +++ b/bpfin/utilbills/bill.py @@ -4,18 +4,17 @@ 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 -from bpfin.lib.other import average_nonzero_list +# 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.lib.other import UTILITY_TYPE_LIST +from bpfin.tests.testdata import feature_data as db from bpfin.lib.other import average_list_with_none # from bpfin.tests.testdata import feature_data as db -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 @@ -207,6 +206,64 @@ 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(): # """ # """ @@ -279,7 +336,6 @@ class Bill(): # return bill_list - # **** ugly test **** # e_bill = Bill('electricity', db.analysis_date) # e_bill.put_month_bill(db.raw_bill_table['electricity']) @@ -294,3 +350,6 @@ class Bill(): # 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))