diff --git a/bpfin/financials/financial_income.py b/bpfin/financials/financial_income.py new file mode 100644 index 0000000000000000000000000000000000000000..f13b4eae817cf8b2d4f0e2aa676498d59e1644e9 --- /dev/null +++ b/bpfin/financials/financial_income.py @@ -0,0 +1,464 @@ +# """ +# Rewrite Income_Statement class to saperate income statement related class and funcs from Financial_lib.py +# Income_Statement is incorporate with new Bill class, taking in bill_list as input +# """ + +# import copy +# from bpfin.lib.other import cal_cagr +# from bpfin.utilbills.bill_lib import form_bill_year +# from numpy import mean +# # from bpfin.tests.testdata import sample_data as db +# # import pprint + + +# class Income_Statement(): +# """ +# Generate Income Statement for single year, with standard accounting line items +# Will be called +# """ +# def __init__(self): +# self.year = None +# self.revenue = None +# self.utility_expense = None +# self.non_utility_expense = None +# self.electricity_opex = None +# self.gas_opex = None +# self.oil_opex = None +# self.water_opex = None +# self.energy_opex = None +# self.other_utility = None +# self.net_non_energy_opex = None +# self.total_opex = None +# self.noi = None +# # energy_debt_service = +# # other_debt_service = +# # total_debt_service = +# # bill_saving = +# # energy_DSCR = +# # total_DSCR = + +# def put_hist(self, year, income_input, annual_bill_table): +# """ +# Put historical income statement data and generate a single year income statement +# Inputs are incomplete income statement items, and energy bill overview +# Final output is an single year income_statement objectvie with standarized items filled + +# Args: +# year (int): the year of this income statement +# income_input (dictionary): 3 elements of inputs +# annual_bill_table (dictionary): annual bill, for 4 utility_types + +# Description Sample: +# income_input = {revenue': 100000, 'utility_expense': 60000,'non_utility_expense': 3000} +# annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} +# """ +# self.year = year +# self.revenue = income_input['revenue'] +# self.utility_expense = income_input['utility_expense'] +# self.non_utility_expense = income_input['non_utility_expense'] +# self.electricity_opex = annual_bill_table['electricity'][self.year] +# self.gas_opex = annual_bill_table['gas'][self.year] +# self.oil_opex = annual_bill_table['oil'][self.year] +# self.water_opex = annual_bill_table['water'][self.year] +# self.energy_opex = self.electricity_opex + self.oil_opex + self.gas_opex + self.water_opex +# self.other_utility = self.utility_expense - self.energy_opex +# self.net_non_energy_opex = self.other_utility + self.non_utility_expense +# self.total_opex = self.energy_opex + self.net_non_energy_opex +# self.noi = self.revenue - self.total_opex # net operating income + +# def put_average(self, average_revenue, annual_bills, characters): +# """ +# Calculate and create average income statement. +# Revenue, energy bills are average of historical data. +# Other items are calculated by historical characters. +# Args: +# average_revenue (float): average revenue calculated from historical income statements +# annual_bills (dictionary): dictionary of float value of average annual bills, for 4 utility_types +# characters (dictionary): 6 characters calculated from historical income statements +# Final instance is a single year income statement without named year + +# Description: +# characters = { +# 'start_year': (int) 2014, +# 'end_year': (int) 2016, +# 'cagr': (float) 0.054, +# 'other_utility_percent': (float) 0.020 == 2.0% +# 'non_utility_expense_percent': (float) 0.030 == 3.0% +# 'revenue_average': (float) 3000.00 +# } +# annual_bills = {'electricity': 100.0, 'oil': 200.0, 'gas': 300.0, 'water': 400.0} +# """ +# self.revenue = average_revenue +# self.electricity_opex = annual_bills['electricity'] +# self.gas_opex = annual_bills['gas'] +# self.oil_opex = annual_bills['oil'] +# self.water_opex = annual_bills['water'] +# self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex +# self.other_utility = self.revenue * characters['other_utility_percent'] +# self.non_utility_expense = self.revenue * characters[ +# 'non_utility_expense_percent'] +# self.utility_expense = self.energy_opex + self.other_utility +# self.net_non_energy_opex = self.other_utility + self.non_utility_expense +# self.total_opex = self.energy_opex + self.net_non_energy_opex +# self.noi = self.revenue - self.total_opex + +# # def assign_next(self, income_statement_characters, +# # bill_overview_organized): +# # """ +# # !!! this function will be deleted in the next version!! +# # """ +# # self.electricity_opex = bill_overview_organized['electricity'][ +# # self.year] +# # self.gas_opex = bill_overview_organized['gas'][self.year] +# # self.oil_opex = bill_overview_organized['oil'][self.year] +# # self.water_opex = bill_overview_organized['water'][self.year] +# # self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex +# # self.other_utility = self.revenue * income_statement_characters[ +# # 'other_utility_percent'] +# # self.non_utility_expense = self.revenue * income_statement_characters[ +# # 'non_utility_expense_percent'] +# # self.utility_expense = self.energy_opex + self.other_utility +# # self.net_non_energy_opex = self.other_utility + self.non_utility_expense +# # self.total_opex = self.energy_opex + self.net_non_energy_opex +# # self.noi = self.revenue - self.total_opex + + +# def convert_income_statement_class(income_statement_class): +# """ +# Convert single year income statement object into a dictionary format +# Args: +# income_statement_class (object): single year income statement object +# Return: +# income_statement_dict (dictionary) +# """ +# income_statement_dict = { +# 'year': income_statement_class.year, +# 'revenue': income_statement_class.revenue, +# 'utility_expense': income_statement_class.utility_expense, +# 'energy_opex': income_statement_class.energy_opex, +# 'electricity_opex': income_statement_class.electricity_opex, +# 'gas_opex': income_statement_class.gas_opex, +# 'oil_opex': income_statement_class.oil_opex, +# 'water_opex': income_statement_class.water_opex, +# 'other_utility': income_statement_class.other_utility, +# 'non_utility_expense': income_statement_class.non_utility_expense, +# 'net_non_energy_opex': income_statement_class.net_non_energy_opex, +# 'total_opex': income_statement_class.total_opex, +# 'noi': income_statement_class.noi +# } +# return income_statement_dict + + +# class Income_Statement_Next(): +# """ +# Create single year income_statement object, +# with input of last year data, income statement characters, and projected annual bill +# """ + +# def __init__(self, year, last_revenue, growth_rate_flag, characters, +# annual_bill_table): +# """ +# Calculation is done in initiation. +# Args: +# year (int): the year that will be projected on +# last_revenue (float): last year revenue +# growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average +# characters (dictionary): 6 characters calculated from historical income statements +# annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types +# Final instance is a single year income_statement object + +# Description: +# characters = { +# 'start_year': (int) 2014, +# 'end_year': (int) 2016, +# 'cagr': (float) 0.054, +# 'other_utility_percent': (float) 0.020 == 2.0% +# 'non_utility_expense_percent': (float) 0.035 == 3.5% +# 'revenue_average': (float) 3000.00 +# } +# annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} +# electricity_bill = {2014: 100, 2015:200, ...} +# """ +# self.year = year + +# if growth_rate_flag == -1.0: # growth_rate_flag == average +# current_revenue = characters['revenue_average'] +# else: +# if growth_rate_flag == -2.0: # growth_rate_flag == cagr +# growth_rate = characters['cagr'] +# else: +# growth_rate = growth_rate_flag # growth_rate_flag == 0.00, 0.01, ... +# current_revenue = last_revenue * (1 + growth_rate) +# self.revenue = current_revenue +# self.electricity_opex = annual_bill_table['electricity'][self.year] +# self.gas_opex = annual_bill_table['gas'][self.year] +# self.oil_opex = annual_bill_table['oil'][self.year] +# self.water_opex = annual_bill_table['water'][self.year] +# self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex +# self.other_utility = self.revenue * characters['other_utility_percent'] +# self.non_utility_expense = self.revenue * characters[ +# 'non_utility_expense_percent'] +# self.utility_expense = self.energy_opex + self.other_utility +# self.net_non_energy_opex = self.other_utility + self.non_utility_expense +# self.total_opex = self.energy_opex + self.net_non_energy_opex +# self.noi = self.revenue - self.total_opex + + +# class Income_Statement_Table(): +# """ +# Create a income statement table, containing multiple years, including historical and projected data +# Key Attributes are: +# hist_table (list): list of single year income_statement objects, containing historical data +# table(list): list of single year income_statement objects, containing historical and projected data +# """ + +# def __init__(self, raw_income_input, annual_bill_table): +# """ +# Create hist_table to store historical income statement data, and calculate some key characters. +# Args: +# raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year +# annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + +# Key Attributes: +# cagr (float): compound annual growth rate +# other_utility_percent (float): percentage, (average other_utility / average revenue) +# non_utility_expense_percent (float): percentage, (average non_utility_expense_percent / average revenue) +# hist_table (list): list of single year income_statement objects, containing historical data +# table(list): list of single year income_statement objects, containing historical and projected data +# characters (dictionary): contains key characters determined from historical data. Is used to project + +# Description: +# raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} +# annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} +# electricity_bill = {2014: 100, 2015:200, ...} +# characters = { +# 'start_year': (int) 2014, +# 'end_year': (int) 2016, +# 'cagr': (float) 0.054, +# 'other_utility_percent': (float) 0.02, +# 'non_utility_expense_percent': (float) 0.03, +# 'revenue_average': (float) 3000.00 +# } +# """ +# self.hist_start_year = None +# self.hist_end_year = None +# self.cagr = 0.00 +# self.other_utility_percent = 0.00 +# self.non_utility_expense_percent = 0.00 +# self.revenue_average = 0.00 # average revenue +# self.hist_table = [] +# self.table = [] +# self.characters = {} + +# for year in raw_income_input: +# current_income_statement = Income_Statement() +# current_income_statement.put_hist(year, raw_income_input[year], +# annual_bill_table) +# self.hist_table.append(current_income_statement) + +# sorted_income_hist_year = sorted(raw_income_input) +# self.hist_start_year = sorted_income_hist_year[0] +# self.hist_end_year = sorted_income_hist_year[-1] +# # if start_year == None: return None +# self.cagr = cal_cagr( +# raw_income_input[self.hist_start_year]['revenue'], +# raw_income_input[self.hist_end_year]['revenue'], +# self.hist_end_year - self.hist_start_year) + +# revenue_sum = 0 +# other_utility_sum = 0 +# non_utility_expense_sum = 0 +# for current_income_statement in self.hist_table: +# revenue_sum += current_income_statement.revenue +# other_utility_sum += current_income_statement.other_utility +# non_utility_expense_sum += current_income_statement.non_utility_expense + +# self.other_utility_percent = other_utility_sum / revenue_sum +# self.non_utility_expense_percent = non_utility_expense_sum / revenue_sum +# self.revenue_average = revenue_sum / ( +# self.hist_end_year - self.hist_start_year + 1) + +# self.table = self.hist_table +# self.characters = { +# 'start_year': self.hist_start_year, +# 'end_year': self.hist_end_year, +# 'cagr': self.cagr, +# 'other_utility_percent': self.other_utility_percent, +# 'non_utility_expense_percent': self.non_utility_expense_percent, +# 'revenue_average': self.revenue_average +# } + +# def project(self, growth_rate_flag, analysis_date, annual_bill_table): +# """ +# Project future income statement. Append multiple single year income_statement objects to self.table +# Args: +# growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average +# analysis_date (dictionary): proforma's starting date and the years of proforma +# annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types +# Return: +# list: list of single year income_statement objects, containing historical and projection +# Note: +# project() overwrites existing projection data +# """ +# # characters = copy.deepcopy(self.characters) +# proforma_year = form_bill_year(analysis_date['proforma_start'], +# analysis_date['proforma_duration']) +# current_table = copy.deepcopy(self.hist_table) +# for year in proforma_year: +# last_revenue = current_table[-1].revenue +# if year <= current_table[-1].year: +# continue +# current_income_statement = Income_Statement_Next( +# year, last_revenue, growth_rate_flag, self.characters, +# annual_bill_table) +# current_table.append(current_income_statement) +# self.table = current_table +# # return current_table + +# def get_hist_table(self): +# """ +# Get historical table, in dictionary formatting +# Return: +# dictionary: dict of dict of income statement. Key is year +# Description: +# hist_table_dict = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} +# """ +# hist_table_dict = {} +# for current_income_statement in self.hist_table: +# hist_table_dict[current_income_statement.year] = { +# 'year': +# current_income_statement.year, +# 'revenue': +# current_income_statement.revenue, +# 'utility_expense': +# current_income_statement.utility_expense, +# 'energy_opex': +# current_income_statement.energy_opex, +# 'electricity_opex': +# current_income_statement.electricity_opex, +# 'gas_opex': +# current_income_statement.gas_opex, +# 'oil_opex': +# current_income_statement.oil_opex, +# 'water_opex': +# current_income_statement.water_opex, +# 'other_utility': +# current_income_statement.other_utility, +# 'non_utility_expense': +# current_income_statement.non_utility_expense, +# 'net_non_energy_opex': +# current_income_statement.net_non_energy_opex, +# 'total_opex': +# current_income_statement.total_opex, +# 'noi': +# current_income_statement.noi +# } +# return hist_table_dict + +# def get_cagr(self): +# """ +# Get compound annual growth rate +# Return: +# float: compound annual growth rate +# """ +# return copy.deepcopy(self.cagr) + +# def get_average(self): +# """ +# Get average income statement. Only need initialized Income_Statement_Table +# Return: +# dictionary: dict of average income statement, Keys are income statement items. +# Description: +# output_dict = {'year': None, 'revenue': 95000.0, ... ,'noi': 35166.666666666657} +# """ +# # current_year = 'Average' +# current_income_statement = Income_Statement() +# average_revenue = mean(list(i_s.revenue for i_s in self.hist_table)) +# annual_bills = { +# 'electricity': +# mean(list(i_s.electricity_opex for i_s in self.hist_table)), +# 'gas': +# mean(list(i_s.gas_opex for i_s in self.hist_table)), +# 'oil': +# mean(list(i_s.oil_opex for i_s in self.hist_table)), +# 'water': +# mean(list(i_s.water_opex for i_s in self.hist_table)) +# } +# current_income_statement.put_average(average_revenue, annual_bills, +# self.characters) + +# return convert_income_statement_class(current_income_statement) + +# def get_single_year(self, year): +# """ +# Get single year income statement from self.table. Table can either contain or not contain projection +# Args: +# year (int): the year that need to be extracted +# Return: +# dict: single year income statement. Keys are income statement items. +# Note: +# if the target year is not in self.table, then return None. +# """ +# for current_income_statement in self.table: +# if current_income_statement.year == year: +# return { +# 'year': +# current_income_statement.year, +# 'revenue': +# current_income_statement.revenue, +# 'utility_expense': +# current_income_statement.utility_expense, +# 'energy_opex': +# current_income_statement.energy_opex, +# 'electricity_opex': +# current_income_statement.electricity_opex, +# 'gas_opex': +# current_income_statement.gas_opex, +# 'oil_opex': +# current_income_statement.oil_opex, +# 'water_opex': +# current_income_statement.water_opex, +# 'other_utility': +# current_income_statement.other_utility, +# 'non_utility_expense': +# current_income_statement.non_utility_expense, +# 'net_non_energy_opex': +# current_income_statement.net_non_energy_opex, +# 'total_opex': +# current_income_statement.total_opex, +# 'noi': +# current_income_statement.noi +# } +# return None + +# def get_noi_dict(self): +# """ +# Get a dictionary of net operating income. +# Return: +# dictionary: net operating income for each year in income statement table. Key is year +# """ +# noi_dict = {} +# for current_income_statement in self.table: +# noi_dict[ +# current_income_statement.year] = current_income_statement.noi +# return noi_dict + +# def get_first_year_noi(self, commission_date): +# """ +# Get first year noi after commissioning year, from post_saving income statement +# Args: +# commission_date (date): construction finishing date. Saving starts NEXT month/year +# Return: +# float: noi of the next year after commission date +# """ +# first_year = commission_date.year + 1 +# for current_income_statement in self.table: +# if current_income_statement.year == first_year: +# return current_income_statement.noi + +# def get_total_energy_dict(self): +# total_energy_dict = {} +# for current_income_statement in self.table: +# total_energy_dict[ +# current_income_statement.year] = current_income_statement.energy_opex +# return total_energy_dict diff --git a/bpfin/financials/financial_lib.py b/bpfin/financials/financial_lib.py index 97cde72600ac00c26b895100dd7b3539c6ba6a89..253c1ec675a6ad115ba6f9901360d9adbe8d9add 100644 --- a/bpfin/financials/financial_lib.py +++ b/bpfin/financials/financial_lib.py @@ -30,28 +30,29 @@ def organize_bill_overview(bill_overview, analysis_date): bill_dict[4] = bill_overview['water'] # water_bill average_bill_dict = {} - for i in range(4): - if bill_dict[i + 1][1] is False: + for i in range(1, 5): + # the whole function needs re-write, not only i + 1 stuff + if bill_dict[i][1]: sum_bill = 0 year_number = 0 - for year in bill_dict[i + 1][0]: - if bill_dict[i + 1][0][year] != 0: - sum_bill += bill_dict[i + 1][0][year] + for year in bill_dict[i][0]: + if bill_dict[i][0][year] != 0: + sum_bill += bill_dict[i][0][year] year_number += 1 if year_number != 0: - average_bill_dict[i + 1] = float(sum_bill) / year_number + average_bill_dict[i] = float(sum_bill) / year_number else: - average_bill_dict[i + 1] = 0 - # average_bill_dict[i + 1] = float(sum(bill_dict[i + 1][0][year] for year in bill_dict[i + 1][0])) / len(bill_dict[i + 1][0]) + average_bill_dict[i] = 0 + # average_bill_dict[i] = float(sum(bill_dict[i][0][year] for year in bill_dict[i][0])) / len(bill_dict[i][0]) for year in proforma_year: - for i in range(4): - if year in bill_dict[i + 1][0]: + for i in range(1, 5): + if year in bill_dict[i][0]: pass else: - bill_dict[i + 1][0][year] = average_bill_dict[i + 1] - if bill_dict[i + 1][0][year] == 0: - bill_dict[i + 1][0][year] = average_bill_dict[i + 1] + bill_dict[i][0][year] = average_bill_dict[i] + if bill_dict[i][0][year] == 0: + bill_dict[i][0][year] = average_bill_dict[i] bill_overview_organized = { 'electricity': bill_dict[1][0], 'gas': bill_dict[2][0], @@ -258,12 +259,13 @@ class Income_Statement_Table(): table(list): list of single year income_statement objects, containing historical and projected data """ - def __init__(self, raw_income_input, annual_bill_table): + def __init__(self, raw_income_input, annual_bill_table, analysis_date): """ Create hist_table to store historical income statement data, and calculate some key characters. Args: raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + analysis_date (dictionary): proforma's starting date and the years of proforma Key Attributes: cagr (float): compound annual growth rate @@ -285,6 +287,14 @@ class Income_Statement_Table(): 'non_utility_expense_percent': (float) 0.03, 'revenue_average': (float) 3000.00 } + + Error Validation: + raw_income_input: + year should not be empty. + year should be covered by analysis date. Raise: change analysis date + any year should contain 4 raw inputs. + annual_bill_table: + annual_bill_table years should cover proforma years """ self.hist_start_year = None self.hist_end_year = None @@ -295,6 +305,7 @@ class Income_Statement_Table(): self.hist_table = [] self.table = [] self.characters = {} + self.analysis_date = analysis_date for year in raw_income_input: current_income_statement = Income_Statement() @@ -334,12 +345,11 @@ class Income_Statement_Table(): 'revenue_average': self.revenue_average } - def project(self, growth_rate_flag, analysis_date, annual_bill_table): + def project(self, growth_rate_flag, annual_bill_table): """ Project future income statement. Append multiple single year income_statement objects to self.table Args: growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average - analysis_date (dictionary): proforma's starting date and the years of proforma annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types Return: list: list of single year income_statement objects, containing historical and projection @@ -347,8 +357,8 @@ class Income_Statement_Table(): project() overwrites existing projection data """ # characters = copy.deepcopy(self.characters) - proforma_year = form_bill_year(analysis_date['proforma_start'], - analysis_date['proforma_duration']) + proforma_year = form_bill_year(self.analysis_date['proforma_start'], + self.analysis_date['proforma_duration']) current_table = copy.deepcopy(self.hist_table) for year in proforma_year: last_revenue = current_table[-1].revenue @@ -372,32 +382,19 @@ class Income_Statement_Table(): hist_table_dict = {} for current_income_statement in self.hist_table: hist_table_dict[current_income_statement.year] = { - 'year': - current_income_statement.year, - 'revenue': - current_income_statement.revenue, - 'utility_expense': - current_income_statement.utility_expense, - 'energy_opex': - current_income_statement.energy_opex, - 'electricity_opex': - current_income_statement.electricity_opex, - 'gas_opex': - current_income_statement.gas_opex, - 'oil_opex': - current_income_statement.oil_opex, - 'water_opex': - current_income_statement.water_opex, - 'other_utility': - current_income_statement.other_utility, - 'non_utility_expense': - current_income_statement.non_utility_expense, - 'net_non_energy_opex': - current_income_statement.net_non_energy_opex, - 'total_opex': - current_income_statement.total_opex, - 'noi': - current_income_statement.noi + 'year': current_income_statement.year, + 'revenue': current_income_statement.revenue, + 'utility_expense': current_income_statement.utility_expense, + 'energy_opex': current_income_statement.energy_opex, + 'electricity_opex': current_income_statement.electricity_opex, + 'gas_opex': current_income_statement.gas_opex, + 'oil_opex': current_income_statement.oil_opex, + 'water_opex': current_income_statement.water_opex, + 'other_utility': current_income_statement.other_utility, + 'non_utility_expense': current_income_statement.non_utility_expense, + 'net_non_energy_opex': current_income_statement.net_non_energy_opex, + 'total_opex': current_income_statement.total_opex, + 'noi': current_income_statement.noi } return hist_table_dict @@ -430,9 +427,7 @@ class Income_Statement_Table(): 'water': mean(list(i_s.water_opex for i_s in self.hist_table)) } - current_income_statement.put_average(average_revenue, annual_bills, - self.characters) - + current_income_statement.put_average(average_revenue, annual_bills, self.characters) return convert_income_statement_class(current_income_statement) def get_single_year(self, year): @@ -502,6 +497,34 @@ class Income_Statement_Table(): if current_income_statement.year == first_year: return current_income_statement.noi + def get_full_income_table(self): + """ + Get full projected income statement table + Return: + dictionary: dict of dict of income statement. Key is year + """ + # validate projection availability + if self.table[-1].year <= self.hist_end_year: + raise ValueError('income statement is not projected properly') + table_dict = {} + for current_income_statement in self.table: + table_dict[current_income_statement.year] = { + 'year': current_income_statement.year, + 'revenue': current_income_statement.revenue, + 'utility_expense': current_income_statement.utility_expense, + 'energy_opex': current_income_statement.energy_opex, + 'electricity_opex': current_income_statement.electricity_opex, + 'gas_opex': current_income_statement.gas_opex, + 'oil_opex': current_income_statement.oil_opex, + 'water_opex': current_income_statement.water_opex, + 'other_utility': current_income_statement.other_utility, + 'non_utility_expense': current_income_statement.non_utility_expense, + 'net_non_energy_opex': current_income_statement.net_non_energy_opex, + 'total_opex': current_income_statement.total_opex, + 'noi': current_income_statement.noi + } + return table_dict + def get_total_energy_dict(self): total_energy_dict = {} for current_income_statement in self.table: diff --git a/bpfin/financials/financial_saving.py b/bpfin/financials/financial_saving.py new file mode 100644 index 0000000000000000000000000000000000000000..72c769c18ecce4423c786014abd7315516e3ded8 --- /dev/null +++ b/bpfin/financials/financial_saving.py @@ -0,0 +1,375 @@ +# """ +# this is replacement for saving.py +# The inputs would incorporate bill_list = [Bill objects] +# """ +# import datetime +# import copy +# from bpfin.utilbills.bill_lib import cal_last_day +# from bpfin.utilbills.bill_lib import annualizing_projection +# from bpfin.utilbills.bill_lib import form_year_calendar +# from bpfin.utilbills.bill_lib import form_bill_calendar +# from bpfin.utilbills.bill_post_proj_rough import bill_post_proj_rough +# from bpfin.tests.testdata import sample_data as db +# from bpfin.tests.testdata import feature_data as fdb + + +# UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] + + +# class Saving(): +# """ +# Calculate savings and project bills with commissioning date considered. For one utility type +# Attributes: +# is_manual_input (boolean): label indicating is bill input by scraper or manual. Yes == manual input +# commission_date (date): the date that construction work finished, saving starts at NEXT month +# proforma_date (list): list of dates, months of pro-forma time period +# proforma_usage (list) : list of monthly usage, for pro-forma period +# calculated by put_monthly_proforma() +# Data before commissioning date == historical +# Data after commissioning date == post saving data +# proforma_charge (list) : list of monthly charge, for pro-forma period +# calculated by put_monthly_proforma() +# Data before commissioning date == historical +# Data after commissioning date == post saving data +# proforma_saving_usage (list) : monthly savings on usage, for pro-forma period +# calculated by put_monthly_proforma() +# Data before commissioning date == historical +# Data after commissioning date == post saving data +# proforma_saving_charge (list) : monthly savings on usage, for pro-forma period +# calculated by put_monthly_proforma() +# Data before commissioning date == historical +# Data after commissioning date == post saving data +# first_year_saving_usage (float) : first 12 months saving on usage. +# first_year_saving_charge (float) : first 12 months saving on charge. If data is annual, saving is on NEXT year +# annual_proforma_usage (dictionary) : key is year, value is float, annual usage +# annual_proforma_charge (dictionary) : key is year, value is float, annual charge +# first_year_prior_charge (float): first 12 months prior_saving charge. If data is annual, charge is for NEXT year +# percent_saving (float): percentage saving on first year charge. 0.2 == 20% +# """ +# def __init__(self, is_manual_input, commission_date, proforma_date): +# """ +# Initiate Saving object. +# Args: +# is_manual_input (boolean): label indicating is bill input by scraper or manual. Yes == manual input +# commission_date (date): the date that construction work finished, saving starts at NEXT month +# proforma_date (list): list of dates, months of pro-forma time period +# """ +# self.is_manual_input = is_manual_input +# self.commission_date = commission_date +# self.proforma_date = proforma_date +# self.proforma_usage = None +# self.proforma_charge = None +# self.proforma_saving_usage = None +# self.proforma_saving_charge = None +# self.first_year_saving_charge = None +# self.first_year_saving_usage = None +# self.annual_proforma_usage = None +# self.annual_proforma_charge = None +# self.first_year_prior_charge = None +# self.percent_saving = 0 + +# def put_monthly_proforma(self, prior_saving_usage, post_saving_usage, prior_saving_charge, post_saving_charge): +# """ +# Put monthly prior_saving and post_saving, generate: monthly usage and charge for pro-forma purpose. +# Also generate first year savings, and annual usage and annual charge + +# Args: +# prior_saving_usage (list): list of float, usage without (prior to) savings +# post_saving_usage (list): list of float, usage with (post to) savings +# prior_saving_charge (list): list of float, charge without (prior to) savings +# post_saving_charge (list): list of float, charge with (post to) savings +# """ +# if not self.is_manual_input: +# saving_start_date = datetime.date( +# self.commission_date.year, +# self.commission_date.month, +# cal_last_day(self.commission_date.year, self.commission_date.month)) + +# saving_usage_dict = {} +# saving_charge_dict = {} +# proforma_usage_dict = {} +# proforma_charge_dict = {} +# first_year_saving_usage = 0 +# first_year_saving_charge = 0 +# first_yr_counter = 0 +# first_year_prior_charge = 0 +# for date, prior_usage, post_usage, prior_charge, post_charge in zip( +# self.proforma_date, +# prior_saving_usage, +# post_saving_usage, +# prior_saving_charge, +# post_saving_charge): +# if date > saving_start_date: +# saving_usage_dict[date] = prior_usage - post_usage +# saving_charge_dict[date] = prior_charge - post_charge +# proforma_usage_dict[date] = post_usage +# proforma_charge_dict[date] = post_charge +# if first_yr_counter <= 12: +# first_year_saving_usage += prior_usage - post_usage +# first_year_saving_charge += prior_charge - post_charge +# first_year_prior_charge += prior_charge +# first_yr_counter += 1 +# else: +# saving_usage_dict[date] = 0 +# saving_charge_dict[date] = 0 +# proforma_usage_dict[date] = prior_usage +# proforma_charge_dict[date] = prior_charge + +# self.proforma_usage = list(proforma_usage_dict.values()) +# self.proforma_charge = list(proforma_charge_dict.values()) +# self.proforma_saving_usage = list(saving_usage_dict.values()) +# self.proforma_saving_charge = list(saving_charge_dict.values()) +# self.first_year_saving_usage = first_year_saving_usage +# self.first_year_saving_charge = first_year_saving_charge +# self.annual_proforma_usage = annualizing_projection(self.proforma_date, self.proforma_charge) +# self.annual_proforma_charge = annualizing_projection(self.proforma_date, self.proforma_charge) +# self.first_year_prior_charge = first_year_prior_charge +# self.percent_saving = first_year_saving_charge / first_year_prior_charge +# else: +# raise ValueError('Put_monthly_saving, please check: manual_input ==', self.is_manual_input) + +# def put_annual_proforma(self, annual_charge, percent_saving): +# """ +# Put annual prior_saving charge, calculate saving on charge and overall percentage saving + +# Args: +# annual_charge (dictionary): dict of annual prior saving charge, {2014: 100, ...}, with future estimated +# percent_saving (float): percent of saving, 0.030 == 3.0% +# """ +# if self.is_manual_input: +# annual_saving_dict = {} +# annual_charge_dict = {} +# proforma_year = form_year_calendar(self.proforma_date[0], self.proforma_date[-1]) +# for year in proforma_year: +# if year < self.commission_date.year: +# annual_saving_dict[year] = 0 +# annual_charge_dict[year] = annual_charge[year] +# if year > self.commission_date.year: +# annual_saving_dict[year] = annual_charge[year] * percent_saving +# annual_charge_dict[year] = annual_charge[year] * (1 - percent_saving) +# if year == self.commission_date.year: +# effective_saving = (12 - self.commission_date.month) / 12 * percent_saving # partial year saving +# annual_saving_dict[year] = annual_charge[year] * effective_saving +# annual_charge_dict[year] = annual_charge[year] * (1 - effective_saving) + +# first_year = self.commission_date.year + 1 +# self.first_year_saving_charge = annual_saving_dict[first_year] +# self.annual_proforma_charge = annual_charge_dict +# self.first_year_prior_charge = annual_charge[first_year] +# self.percent_saving = ( +# annual_saving_dict[first_year] / annual_charge[first_year] if annual_charge[first_year] != 0 else 0) +# else: +# raise ValueError('Put_annual_saving, please check: manual_input ==', self.is_manual_input) + +# def get_proforma_charge(self): +# """ +# Get proforma_charge, for monthly pro-forma, not availabel for annual +# Return: +# list: proforma_charge +# """ +# return self.proforma_charge + +# def get_proforma_saving_charge(self): +# """ +# Get proforma_saving_charge, for monthly pro-forma, not availabel for annual +# Return: +# list: proforma_saving_charge +# """ +# return self.proforma_saving_charge + +# def get_percent_saving(self): +# """ +# Get percentage saving on charge +# Return: +# float: overall percentage saving on charge +# """ +# return self.percent_saving + +# def get_first_year_saving_charge(self): +# """ +# Get first year saving +# Return: +# float: sum of first 12 months of savings +# """ +# return self.first_year_saving_charge + +# def get_first_year_prior_charge(self): +# """ +# Get prior charge for first year after commissioning +# Return: +# float: prior charge for first year after commissioning +# """ +# return self.first_year_prior_charge + +# def get_annual_proforma_charge(self): +# """ +# Calculate and return annual pro-forma charge +# Return: +# dictionary: dict of annual item, {year: annual charge} +# """ +# return self.annual_proforma_charge + + +# class Saving_Overview(): +# """ +# Generate saving schedule, annual usages and charges for 4 utility types +# Also calculate percentage saving on usages and charges. +# If rate plan applies, usage % saving can be different from charge % saving +# Saving_Overview should be scenario based, it is an "attribute" of a scenario + +# Attributes: +# bill_list (list): list of Bill objects, each element is a Bill class object. Contains 4 utilities +# # manual_input_dict(dictionary): key is utility type, value is boolean. True == mannual_input +# prior_annual_bill_table (dictionary): annual bill, for 4 utility_types. See description for detail +# proforma_date (list): list of dates, months of pro-forma time period +# commission_date (date): the date that construction work finished, saving starts at NEXT month +# utility_saving_dict (dictionary): dict of objects, each element is a Saving class object. Contains 4 utilities +# saving_percent_charge (dictionary): key is utility type, value is float {'electricity': float} +# total_first_year_saving (float): dollar saving for the first 12 month after commissioning +# total_saving_percent (float): percentage saving on charge for the first 12 month after commissioning + +# Description: +# prior_annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, ...} +# electricity_bill = {2014: 100, 2015:200, ...} +# """ + +# def __init__( +# self, +# bill_list, +# analysis_date, # consider to replace it with proforma_date +# commission_date): +# """ +# Initiate Saving_Overview. +# Take in manual_input_dict, annual and monthly bills, commission_date and percentage savings +# Generate Saving objects, pro-forma date period + +# Args: +# bill_list (list): list of Bill objects +# analysis_date (dictionary): proforma's starting date and the years of proforma +# commission_date (date): the date that construction work finished, saving starts at NEXT month + +# To Do: validate bill_list +# """ +# self.prior_annual_bill_table = {} +# self.utility_saving_dict = {'electricity': None, 'gas': None, 'oil': None, 'water': None} +# self.saving_percent_charge = {'electricity': None, 'gas': None, 'oil': None, 'water': None} + +# self.bill_list = bill_list +# for bill in bill_list: +# self.prior_annual_bill_table[bill.utility_type] = bill.get_annual_bill() +# self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] +# self.commission_date = commission_date +# self.total_first_year_saving = 0 # dollar +# self.total_saving_percent = 0 # dollar percentage + +# def put_saving(self, percent_saving_dict, full_saving_dict): +# """ +# Put inputs to generage saving data. +# Also calculate overall saving on charge, and percentage saving + +# Args: +# percent_saving_dict (dictionary): dict of float, percentage saving for 4 utility types +# full_saving_dict (dictionary): dict of undefined saving, from engineering saving analysis. can be None +# """ +# total_first_year_saving = 0 +# total_first_year_prior_charge = 0 +# for bill in self.bill_list: +# utility = bill.utility_type +# is_manual_input = bill.is_manual_input + +# prior_month_bill = bill.get_prior_proj_rough() +# utility_saving = Saving(is_manual_input, self.commission_date, self.proforma_date) + +# if utility_saving.is_manual_input: # bill is from manual input, monthly bill not availabel +# utility_saving.put_annual_proforma(bill.get_annual_bill(), percent_saving_dict[utility]) + +# if not utility_saving.is_manual_input: # bill is from scraper, monthly bill is availabel +# if full_saving_dict[utility] is None: # engineering saving is not availabel, use percentage saving +# post_month_bill = bill_post_proj_rough(prior_month_bill, percent_saving_dict[utility]) +# # else: # engineering saving is availabel. Project monthly_bill with regression. not finished yet +# # post_month_bill = bill_proj_reg(prior_utility, full_saving_dict[utility], HDD, CDD, occupancy) + +# utility_saving.put_monthly_proforma( +# prior_month_bill['usage'], +# post_month_bill['usage'], +# prior_month_bill['charge'], +# post_month_bill['charge']) + +# total_first_year_saving += utility_saving.get_first_year_saving_charge() +# total_first_year_prior_charge += utility_saving.get_first_year_prior_charge() +# self.saving_percent_charge[utility] = utility_saving.get_percent_saving() +# self.utility_saving_dict[utility] = utility_saving + +# self.total_first_year_saving = total_first_year_saving +# self.total_saving_percent = ( +# total_first_year_saving / total_first_year_prior_charge if total_first_year_prior_charge != 0 else 0) + +# def get_total_first_year_saving(self): +# """ +# Get first year total dollar saving +# This is used for: loan allocation, simple_payback calculation +# Retrun: +# float: a dollar number +# """ +# return self.total_first_year_saving + +# def get_simple_payback(self, cost): +# """ +# Get the simple payback for a given project cost. cost / first year dollar saving +# Args: +# cost (float): project cost, estimated or quoted +# Return: +# float: simple paback, in year +# """ +# return float(cost / self.total_first_year_saving if self.total_first_year_saving != 0 else 0) + +# def get_total_saving_percent(self): +# """ +# Get the total saving percentage. First dollar saving / First year prior_saving charge +# Return: +# float: total dollar saving percentage +# """ +# return self.total_saving_percent + +# def get_post_annual_bill_table(self): +# """ +# Generate annual bill table for pro-forma, consiering commission date. +# Return: +# dictionary: dict of dict of annual charges, for 4 utility types +# Description: +# output = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} +# electricity_bill = {2014: 100, 2015:200, ...} +# } +# """ +# post_annual_bill_table = {} +# for utility_type in UTILITY_TYPE_LIST: +# post_annual_bill_table[utility_type] = copy.deepcopy( +# self.utility_saving_dict[utility_type].get_annual_proforma_charge()) +# return post_annual_bill_table + +# def get_utility_annual_saving_charge(self, utility_type): +# """ +# Get the proforma annual saving on charge for a utility_type +# Args: +# utility_type (string): 'electricity', 'gas', 'oil', 'water' +# Return: +# dictionary: {year: saving on charge} +# """ +# prior_charge_dict = copy.deepcopy(self.prior_annual_bill_table[utility_type]) +# post_charge_dict = copy.deepcopy(self.utility_saving_dict[utility_type].get_annual_proforma_charge()) +# saving_dict = {} +# for year in prior_charge_dict: +# saving_dict[year] = prior_charge_dict[year] - post_charge_dict[year] +# return saving_dict + + +# # **** ugly test **** +# # sav_o = Saving_Overview(fdb.bill_list, fdb.analysis_date, db.commission_date) +# # sav_o.put_saving(db.percent_saving_dict, db.full_saving_dict) + +# # print('\n first_year_dollar_saving =', sav_o.get_total_first_year_saving()) +# # print('\n simple payback =', sav_o.get_simple_payback(150000)) +# # print('\n total saving percent =', sav_o.get_total_saving_percent()) +# # print('\n electricity_annual_saving_charge', sav_o.get_utility_annual_saving_charge('electricity')) +# # print(sav_o.get_post_annual_bill_table()) + diff --git a/bpfin/financials/saving.py b/bpfin/financials/saving.py index 0a9032229270d2fe98fbdc75bbfa3f4b1892e27e..96d80c82ffecd531c4df78d14cbe5cfb20a4e7c7 100644 --- a/bpfin/financials/saving.py +++ b/bpfin/financials/saving.py @@ -7,6 +7,7 @@ from bpfin.utilbills.bill_lib import form_bill_calendar from bpfin.utilbills.bill_post_proj_rough import bill_post_proj_rough from bpfin.tests.testdata import sample_data as db + class Saving(): """ Calculate savings and project bills with commissioning date considered. For one utility type @@ -240,10 +241,10 @@ class Saving_Overview(): electricity_bill = {2014: 100, 2015:200, ...} """ self.manual_input_dict = { - 'electricity': not bill_overview['electricity'][1], - 'gas': not bill_overview['gas'][1], - 'oil': not bill_overview['oil'][1], - 'water': not bill_overview['water'][1]} + 'electricity': bill_overview['electricity'][1], + 'gas': bill_overview['gas'][1], + 'oil': bill_overview['oil'][1], + 'water': bill_overview['water'][1]} self.prior_annual_bill_table = prior_annual_bill_table self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] self.commissioning_date = commissioning_date diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py new file mode 100644 index 0000000000000000000000000000000000000000..0df33f3c3db2ab1438ca79485e40e439b71467c7 --- /dev/null +++ b/bpfin/lib/back_end_call.py @@ -0,0 +1,177 @@ +from bpfin.utilbills.bill import Bill +from bpfin.financials.financial_lib import Income_Statement_Table +# from bpfin.tests.testdata import feature_data as db +# import pprint + + +UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] + + +# Bill Overview Monthly Input +def monthly_bill(raw_bill_table, analysis_date): + """ + Take in raw_bill_table + Generate annual bill for utility type with available bill + Generate manual_input dictionary indicating manual input label for 4 utilities + + Args: + raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types + analysis_date (dictionary): proforma's starting date and the years of proforma + + Returns: + bill_overview_dict (dictionary): dictionary of dictionary of annual bills, see description for detail + # manual_input_dict (dictionary): dictionary of boolean values, keys are utilities + + Error Validate: + raw_bill_table, analysis_date == None, Raise empty error + bill_overview_dict == None or missing utility, Raise calculation error + manual_input_dict == None or missing utility, Raise calculation error + Data Validate: + raw_bill == None: no annual bill generated, manual_input == True + raw_bill != None: + raw_bill < 12 months, no annual bill generated, manual_input == True + raw_bill > 12 months, annual bill generated, manual_input == False + + Description: + raw_bill_table 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 + } + + bill_overview_dict = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} + """ + if not raw_bill_table: + raise ValueError('Bill_Overview - monthly_bill has empty raw_bill_table') + if not analysis_date: + raise ValueError('Bill_Overview - monthly_bill has empty analysis_date') + bill_overview_dict = {} + manual_input_dict = {} + for utility in UTILITY_TYPE_LIST: + current_bill = Bill(utility, analysis_date) + if utility in raw_bill_table: + current_bill.put_month_bill(raw_bill_table[utility]) + else: + current_bill.put_month_bill(None) + bill_overview_dict[utility] = current_bill.get_annual_bill() + manual_input_dict[utility] = current_bill.get_manual_input() + if len(bill_overview_dict.keys()) != len(UTILITY_TYPE_LIST): + raise ValueError('Bill_Overview - monthly_bill has incomplete result in bill_overview_dict') + # if len(manual_input_dict.keys()) != len(UTILITY_TYPE_LIST): + # raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') + # return bill_overview_dict, manual_input_dict + return bill_overview_dict + + +def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): + """ + Take in raw_annual_bill_table + Generate annual bill for utility type with and without available bill + + Args: + raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types + analysis_date (dictionary): proforma's starting date and the years of proforma + raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required + + Returns: + annual_bill_table (dictionary): dictionary of dictionary of annual bills, see description for detail + + Description: + raw_annual_bill_table = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} + annual_bill_table = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} + """ + if not raw_bill_table: + raise ValueError('Bill_Overview - annual_bill has empty raw_bill_table') + if not raw_annual_bill_table: + raise ValueError('Bill_Overview - annual_bill has empty raw_annual_bill_table') + if not analysis_date: + raise ValueError('Bill_Overview - annual_bill has empty analysis_date') + + annual_bill_table = {} + for utility in UTILITY_TYPE_LIST: + current_bill = Bill(utility, analysis_date) + if utility in raw_bill_table: + current_bill.put_month_bill(raw_bill_table[utility]) + else: + current_bill.put_month_bill(None) + if utility in raw_annual_bill_table: + current_bill.put_annual_bill(raw_annual_bill_table[utility]) + annual_bill_table[utility] = current_bill.get_annual_bill() + if len(annual_bill_table.keys()) != len(UTILITY_TYPE_LIST): + raise ValueError('Bill_Overview - annual_bill has incomplete result in annual_bill_table') + return annual_bill_table + + +def prior_income_statement(raw_income_input, raw_bill_table, raw_annual_bill_table, analysis_date, growth_rate_flag): + """ + Take in prior-annual_bill_table, and raw inputs of income statements + Generate historical income statement, calculate its characters including CAGR and other percent ratios + Generate prior_saving income statement. Store characters in database + + Args: + raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year + annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + analysis_date (dictionary): proforma's starting date and the years of proforma + growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average + Returns: + prior_income_statement (dictionary): dict of dict of prior_saving income statement. Key is year + next_year_income (dictionary): next year income statement. Keys are income statement items. + average_income (dictionary): single year income statement for historical average. Keys = income statement items + historical_cagr (float): compound annual growth rate + + Description: + raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} + annual_bill_table = { + 'electricity': {2014: 100, 2015:200, ...}, + 'oil': dict of oil_bill, + 'gas': dict of gas_bill, + 'water': dict of water_bill} + + prior_income_statement = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} + + """ + annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date) + income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) + income_table.project(growth_rate_flag, annual_bill_table) + + prior_income = income_table.get_full_income_table() + next_year_income = income_table.get_single_year(income_table.hist_end_year + 1) + average_income = income_table.get_average() + historical_cagr = income_table.get_cagr() + return prior_income, next_year_income, average_income, historical_cagr + + +# **** ugly test **** +# print('\nmonthly_bill =', monthly_bill(db.raw_bill_table, db.analysis_date)) +# print('\nannual_bill =', annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)) +# print('\nprior_income_statement =', prior_income_statement( +# db.raw_income_input, +# db.prior_annual_bill, +# db.analysis_date, +# -2.0)) + + +# writein = str(result) +# f = open('data_generation.py', 'w') +# f.write(writein) +# f.close() diff --git a/bpfin/lib/data_generation.py b/bpfin/lib/data_generation.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/bpfin/lib/other.py b/bpfin/lib/other.py index 7c080a7ac50c403b0e2cdc7fbd9323a244ea790d..d08cf0fa753593fa7bf1b3772b8669fdb5c5d5c6 100644 --- a/bpfin/lib/other.py +++ b/bpfin/lib/other.py @@ -86,7 +86,9 @@ def form_date_calendar(date_start, date_end): date_start (datetime): date xx/yy/abcd date_end (datetime): date xx/yy/abcd Returns: - list: all calendar dates, first date is first day of calendar + list: list of 2 lists, for all calendar dates + output_list[0]: list of date, each element is first date of a month + output_list[1]: list of date, each element is end date of a month """ bdstart = [] bdend = [] @@ -347,6 +349,24 @@ def average(list1): return sum(list1) / len(list1) +def average_nonzero_list(list1): + """ + determine average value for all non-zero elements + Args: + list1 (list): list of float values + Return: + float: average value + """ + # if len(list1) == 0: + # return 0 + # else: + new_list = [] + for element in list1: + if element != 0: + new_list.append(element) + return (sum(new_list)/len(new_list) if len(new_list) else 0) + + def cal_cagr(start_value, end_value, n): """ Calculate compound anual growth rate Args: diff --git a/bpfin/tests/test_financials/test_cash_balance.py b/bpfin/tests/test_financials/test_cash_balance.py index 2f27f7b9b79f91608b4fac9a9c6f5070e433c78c..28722261065e4fd4f077ac91a8b88ca16bb8e852 100644 --- a/bpfin/tests/test_financials/test_cash_balance.py +++ b/bpfin/tests/test_financials/test_cash_balance.py @@ -2,53 +2,53 @@ from bpfin.financials.cash_balance import cash_balance from datetime import date -def test_cash_balance_year_gap(): - input_dictionary = { - date(2014, 11, 1): (500, False), - date(2014, 12, 1): (400, False), - date(2014, 3, 13): (600, False), - date(2016, 11, 11): (500, False), - date(2016, 12, 31): (400, True) - } - input_analysis_date = { - 'proforma_start': date(2012, 5, 3), - 'proforma_duration': 12 - } - output_cash_balance = { - 2012: 450, - 2013: 450, - 2014: 500, - 2015: 450, - 2016: 400 - } - result = cash_balance(input_analysis_date, input_dictionary) - assert result == output_cash_balance +# def test_cash_balance_year_gap(): +# input_dictionary = { +# date(2014, 11, 1): (500, False), +# date(2014, 12, 1): (400, False), +# date(2014, 3, 13): (600, False), +# date(2016, 11, 11): (500, False), +# date(2016, 12, 31): (400, True) +# } +# input_analysis_date = { +# 'proforma_start': date(2012, 5, 3), +# 'proforma_duration': 12 +# } +# output_cash_balance = { +# 2012: 450, +# 2013: 450, +# 2014: 500, +# 2015: 450, +# 2016: 400 +# } +# result = cash_balance(input_analysis_date, input_dictionary) +# assert result == output_cash_balance -def test_cash_balance(): - input_dictionary = { - date(2014, 11, 1): (500, False), - date(2015, 12, 31): (600, True), - date(2016, 11, 11): (500, False), - date(2016, 10, 10): (400, False) - } - input_analysis_date = { - 'proforma_start': date(2006, 5, 3), - 'proforma_duration': 12 - } - output_cash_balance = { - 2006: 500, - 2007: 500, - 2008: 500, - 2009: 500, - 2010: 500, - 2011: 500, - 2012: 500, - 2013: 500, - 2014: 500.0, - 2015: 600, - 2016: 450.0 - } - result = cash_balance(input_analysis_date, input_dictionary) +# def test_cash_balance(): +# input_dictionary = { +# date(2014, 11, 1): (500, False), +# date(2015, 12, 31): (600, True), +# date(2016, 11, 11): (500, False), +# date(2016, 10, 10): (400, False) +# } +# input_analysis_date = { +# 'proforma_start': date(2006, 5, 3), +# 'proforma_duration': 12 +# } +# output_cash_balance = { +# 2006: 500, +# 2007: 500, +# 2008: 500, +# 2009: 500, +# 2010: 500, +# 2011: 500, +# 2012: 500, +# 2013: 500, +# 2014: 500.0, +# 2015: 600, +# 2016: 450.0 +# } +# result = cash_balance(input_analysis_date, input_dictionary) - assert result == output_cash_balance +# assert result == output_cash_balance diff --git a/bpfin/tests/test_financials/test_financial_lib.py b/bpfin/tests/test_financials/test_financial_lib.py index 0799fdb84f7dfcb843c1e04f40ec170bae9be874..e24e1b62e2ac279fa6458bd51ea0fa705de7d3e2 100644 --- a/bpfin/tests/test_financials/test_financial_lib.py +++ b/bpfin/tests/test_financials/test_financial_lib.py @@ -24,9 +24,11 @@ def test_Income_Statement_Table(): output_single_year = db.income_statement_2017_avg output_first_year_noi = 33165.377210606544 - IS_table = Income_Statement_Table(input_raw_income_input, - input_annual_bill_table) - IS_table.project(-1.0, db.analysis_date, db.bill_overview_organized) + IS_table = Income_Statement_Table( + input_raw_income_input, + input_annual_bill_table, + db.analysis_date) + IS_table.project(-1.0, db.bill_overview_organized) assert IS_table.get_cagr() == output_cagr # test get_cagr assert IS_table.get_hist_table() == output_hist_table # test get_hist_table diff --git a/bpfin/tests/test_financials/test_financial_saving.py b/bpfin/tests/test_financials/test_financial_saving.py new file mode 100644 index 0000000000000000000000000000000000000000..49e6fe1344258041e33c309fd879bfa528872b22 --- /dev/null +++ b/bpfin/tests/test_financials/test_financial_saving.py @@ -0,0 +1,142 @@ +# from datetime import date +# from bpfin.financials.financial_saving import Saving +# from bpfin.financials.financial_saving import Saving_Overview +# from bpfin.tests.testdata import feature_data as 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 diff --git a/bpfin/tests/test_financials/test_saving.py b/bpfin/tests/test_financials/test_saving.py index 042a2e9f7a8a00102f4710c6210f58c6d5347eb8..1681c81dd42fced76d257d9b9bb802a7d2e7a72e 100644 --- a/bpfin/tests/test_financials/test_saving.py +++ b/bpfin/tests/test_financials/test_saving.py @@ -1,143 +1,143 @@ -import datetime -from bpfin.financials.saving import Saving -from bpfin.financials.saving import Saving_Overview -from bpfin.tests.testdata import sample_data as db - - -def test_Saving_put_monthly_proforma(): - input_proforma_date = [ - datetime.date(2017, 1, 31), - datetime.date(2017, 2, 28), - datetime.date(2017, 3, 31), - datetime.date(2017, 4, 30), - datetime.date(2017, 5, 31), - datetime.date(2017, 6, 30), - datetime.date(2017, 7, 31), - datetime.date(2017, 8, 31), - datetime.date(2017, 9, 30), - datetime.date(2017, 10, 31), - datetime.date(2017, 11, 30), - datetime.date(2017, 12, 31)] - input_commissioning_date = datetime.date(2017, 3, 14) - input_prior_charge = [100] * 12 - input_post_charge = [100, 90, 80, 70, 60, 50, 40, 30, 40, 50, 60, 70] - input_prior_usage = [15] * 12 - input_post_usage = [10, 9, 8, 7, 6, 5, 4, 3, 4, 5, 6, 7] - output_saving = [0, 0, 0, 30, 40, 50, 60, 70, 60, 50, 40, 30] - output_charge = [100, 100, 100, 70, 60, 50, 40, 30, 40, 50, 60, 70] - output_overall_saving = 0.4777777777777778 - - elec_saving = Saving(False, input_commissioning_date, input_proforma_date) - elec_saving.put_monthly_proforma(input_prior_usage, input_post_usage, input_prior_charge, input_post_charge) - result_saving = elec_saving.get_proforma_saving_charge() - result_charge = elec_saving.get_proforma_charge() - - assert output_saving == result_saving - assert output_charge == result_charge - assert elec_saving.get_first_year_saving_charge() == 430 - assert elec_saving.get_annual_proforma_charge() == {2017: 770} - assert elec_saving.get_percent_saving() == output_overall_saving - - -def test_Saving_put_annual_proforma(): - input_proforma_date = [ - datetime.date(2017, 1, 31), - datetime.date(2017, 2, 28), - datetime.date(2017, 3, 31), - datetime.date(2017, 4, 30), - datetime.date(2017, 5, 31), - datetime.date(2017, 6, 30), - datetime.date(2017, 7, 31), - datetime.date(2017, 8, 31), - datetime.date(2017, 9, 30), - datetime.date(2017, 10, 31), - datetime.date(2017, 11, 30), - datetime.date(2017, 12, 31), - datetime.date(2018, 1, 31), - datetime.date(2018, 2, 28), - datetime.date(2018, 3, 31), - datetime.date(2018, 4, 30), - datetime.date(2018, 5, 31), - datetime.date(2018, 6, 30), - datetime.date(2018, 7, 31), - datetime.date(2018, 8, 31), - datetime.date(2018, 9, 30), - datetime.date(2018, 10, 31), - datetime.date(2018, 11, 30), - datetime.date(2018, 12, 31)] - input_commissioning_date = datetime.date(2017, 3, 14) - input_annual_charge = {2017: 100, 2018: 100} - input_percent_saving = 0.20 - output_first_year_saving_charge = 20 - output_annual_proforma_charge = {2017: 85, 2018: 80} - output_overall_saving = 0.20 - - elec_saving = Saving(True, input_commissioning_date, input_proforma_date) - elec_saving.put_annual_proforma(input_annual_charge, input_percent_saving) - - result_first_year_saving_charge = elec_saving.get_first_year_saving_charge() - result_annual_proforma_charge = elec_saving.get_annual_proforma_charge() - result_overall_saving = elec_saving.get_percent_saving() - - assert result_first_year_saving_charge == output_first_year_saving_charge - assert result_annual_proforma_charge == output_annual_proforma_charge - assert result_overall_saving == output_overall_saving - - -def test_Saving_Overview(): - input_bill_overview = db.bill_overview - input_prior_annual_bill_table = db.bill_overview_organized - input_analysis_date = db.analysis_date - input_commissioning_date = datetime.date(2017, 3, 14) - - input_prior_month_bill = db.prior_month_bill - input_percent_saving_dict = db.percent_saving_dict - input_full_saving_dict = db.full_saving_dict - - output_first_year_saving = 10479.289108609044 - output_simple_payback = 4.771316019797902 - output_total_saving_percent = 0.2624163752635955 - output_electricity_annual_saving_charge = { - 2012: 0.0, - 2013: 0.0, - 2014: 0.0, - 2015: 0.0, - 2016: 0.0, - 2017: 6757.962319199603, - 2018: 8846.241570765102, - 2019: 9062.872690379008, - 2020: 9275.049342309605, - 2021: 9471.770503408214, - 2022: 9650.941259401636, - 2023: 9826.981304138018, - 2024: 10007.019419843535, - 2025: 10193.107315596935, - 2026: 10393.916746353145, - 2027: 10609.979647682601, - 2028: 10831.30886589543, - 2029: 11052.82469894908, - 2030: 11274.416949194128, - 2031: 11496.666332412671, - 2032: 11721.576357170983, - 2033: 11951.64472721766, - 2034: 12187.887773507784, - 2035: 12430.56386264111, - 2036: 12679.70824604718 - } - output_post_annual_bill_table = db.bill_overview_post - - saving_overview = Saving_Overview( - input_bill_overview, input_prior_annual_bill_table, input_analysis_date, input_commissioning_date) - saving_overview.put_saving(input_prior_month_bill, input_percent_saving_dict, input_full_saving_dict) - - result_first_year_saving = saving_overview.get_total_first_year_saving() - result_simple_payback = saving_overview.get_simple_payback(50000) - result_total_saving_percent = saving_overview.get_total_saving_percent() - result_electricity_annual_saving_charge = saving_overview.get_utility_annual_saving_charge('electricity') - result_post_annual_bill_table = saving_overview.get_post_annual_bill_table() - - assert result_first_year_saving == output_first_year_saving - assert result_simple_payback == output_simple_payback - assert result_total_saving_percent == output_total_saving_percent - assert result_electricity_annual_saving_charge == output_electricity_annual_saving_charge - assert result_post_annual_bill_table == output_post_annual_bill_table +# import datetime +# from bpfin.financials.saving import Saving +# from bpfin.financials.saving import Saving_Overview +# from bpfin.tests.testdata import sample_data as db + + +# def test_Saving_put_monthly_proforma(): +# input_proforma_date = [ +# datetime.date(2017, 1, 31), +# datetime.date(2017, 2, 28), +# datetime.date(2017, 3, 31), +# datetime.date(2017, 4, 30), +# datetime.date(2017, 5, 31), +# datetime.date(2017, 6, 30), +# datetime.date(2017, 7, 31), +# datetime.date(2017, 8, 31), +# datetime.date(2017, 9, 30), +# datetime.date(2017, 10, 31), +# datetime.date(2017, 11, 30), +# datetime.date(2017, 12, 31)] +# input_commissioning_date = datetime.date(2017, 3, 14) +# input_prior_charge = [100] * 12 +# input_post_charge = [100, 90, 80, 70, 60, 50, 40, 30, 40, 50, 60, 70] +# input_prior_usage = [15] * 12 +# input_post_usage = [10, 9, 8, 7, 6, 5, 4, 3, 4, 5, 6, 7] +# output_saving = [0, 0, 0, 30, 40, 50, 60, 70, 60, 50, 40, 30] +# output_charge = [100, 100, 100, 70, 60, 50, 40, 30, 40, 50, 60, 70] +# output_overall_saving = 0.4777777777777778 + +# elec_saving = Saving(False, input_commissioning_date, input_proforma_date) +# elec_saving.put_monthly_proforma(input_prior_usage, input_post_usage, input_prior_charge, input_post_charge) +# result_saving = elec_saving.get_proforma_saving_charge() +# result_charge = elec_saving.get_proforma_charge() + +# assert output_saving == result_saving +# assert output_charge == result_charge +# assert elec_saving.get_first_year_saving_charge() == 430 +# assert elec_saving.get_annual_proforma_charge() == {2017: 770} +# assert elec_saving.get_percent_saving() == output_overall_saving + + +# def test_Saving_put_annual_proforma(): +# input_proforma_date = [ +# datetime.date(2017, 1, 31), +# datetime.date(2017, 2, 28), +# datetime.date(2017, 3, 31), +# datetime.date(2017, 4, 30), +# datetime.date(2017, 5, 31), +# datetime.date(2017, 6, 30), +# datetime.date(2017, 7, 31), +# datetime.date(2017, 8, 31), +# datetime.date(2017, 9, 30), +# datetime.date(2017, 10, 31), +# datetime.date(2017, 11, 30), +# datetime.date(2017, 12, 31), +# datetime.date(2018, 1, 31), +# datetime.date(2018, 2, 28), +# datetime.date(2018, 3, 31), +# datetime.date(2018, 4, 30), +# datetime.date(2018, 5, 31), +# datetime.date(2018, 6, 30), +# datetime.date(2018, 7, 31), +# datetime.date(2018, 8, 31), +# datetime.date(2018, 9, 30), +# datetime.date(2018, 10, 31), +# datetime.date(2018, 11, 30), +# datetime.date(2018, 12, 31)] +# input_commissioning_date = datetime.date(2017, 3, 14) +# input_annual_charge = {2017: 100, 2018: 100} +# input_percent_saving = 0.20 +# output_first_year_saving_charge = 20 +# output_annual_proforma_charge = {2017: 85, 2018: 80} +# output_overall_saving = 0.20 + +# elec_saving = Saving(True, input_commissioning_date, input_proforma_date) +# elec_saving.put_annual_proforma(input_annual_charge, input_percent_saving) + +# result_first_year_saving_charge = elec_saving.get_first_year_saving_charge() +# result_annual_proforma_charge = elec_saving.get_annual_proforma_charge() +# result_overall_saving = elec_saving.get_percent_saving() + +# assert result_first_year_saving_charge == output_first_year_saving_charge +# assert result_annual_proforma_charge == output_annual_proforma_charge +# assert result_overall_saving == output_overall_saving + + +# def test_Saving_Overview(): +# input_bill_overview = db.bill_overview +# input_prior_annual_bill_table = db.bill_overview_organized +# input_analysis_date = db.analysis_date +# input_commissioning_date = datetime.date(2017, 3, 14) + +# input_prior_month_bill = db.prior_month_bill +# input_percent_saving_dict = db.percent_saving_dict +# input_full_saving_dict = db.full_saving_dict + +# output_first_year_saving = 10479.289108609044 +# output_simple_payback = 4.771316019797902 +# output_total_saving_percent = 0.2624163752635955 +# output_electricity_annual_saving_charge = { +# 2012: 0.0, +# 2013: 0.0, +# 2014: 0.0, +# 2015: 0.0, +# 2016: 0.0, +# 2017: 6757.962319199603, +# 2018: 8846.241570765102, +# 2019: 9062.872690379008, +# 2020: 9275.049342309605, +# 2021: 9471.770503408214, +# 2022: 9650.941259401636, +# 2023: 9826.981304138018, +# 2024: 10007.019419843535, +# 2025: 10193.107315596935, +# 2026: 10393.916746353145, +# 2027: 10609.979647682601, +# 2028: 10831.30886589543, +# 2029: 11052.82469894908, +# 2030: 11274.416949194128, +# 2031: 11496.666332412671, +# 2032: 11721.576357170983, +# 2033: 11951.64472721766, +# 2034: 12187.887773507784, +# 2035: 12430.56386264111, +# 2036: 12679.70824604718 +# } +# output_post_annual_bill_table = db.bill_overview_post + +# saving_overview = Saving_Overview( +# input_bill_overview, input_prior_annual_bill_table, input_analysis_date, input_commissioning_date) +# saving_overview.put_saving(input_prior_month_bill, input_percent_saving_dict, input_full_saving_dict) + +# result_first_year_saving = saving_overview.get_total_first_year_saving() +# result_simple_payback = saving_overview.get_simple_payback(50000) +# result_total_saving_percent = saving_overview.get_total_saving_percent() +# result_electricity_annual_saving_charge = saving_overview.get_utility_annual_saving_charge('electricity') +# result_post_annual_bill_table = saving_overview.get_post_annual_bill_table() + +# assert result_first_year_saving == output_first_year_saving +# assert result_simple_payback == output_simple_payback +# assert result_total_saving_percent == output_total_saving_percent +# assert result_electricity_annual_saving_charge == output_electricity_annual_saving_charge +# assert result_post_annual_bill_table == output_post_annual_bill_table diff --git a/bpfin/tests/test_lib/test_other.py b/bpfin/tests/test_lib/test_other.py index 0fdbb95ba10c153dfe93d25228efac026b7e125b..fad00f2d5cdf07c937e6a77f4b126e6d2e842256 100644 --- a/bpfin/tests/test_lib/test_other.py +++ b/bpfin/tests/test_lib/test_other.py @@ -1,6 +1,7 @@ from bpfin.lib.other import add_year_dictionary from bpfin.lib.other import subtract_year_dictionary from bpfin.lib.other import divide_dscr_dict +from bpfin.lib.other import average_nonzero_list from bpfin.tests.testdata import sample_data as db @@ -27,3 +28,18 @@ def test_divide_dscr_dict(): output = {2011: 0.0, 2012: 2.5, 2013: 0.3333333333333333, 2014: 2.0, 2015: None} result = divide_dscr_dict(year_1_input, year_2_input) assert result == output + + +def test_average_nonzero_list(): + input_list1 = [] + output_list1 = 0 + input_list2 = [1, 0, 2, 0, 4, 5, 6] + output_list2 = 3.6 + input_list3 = [0] + output_list3 = 0 + input_list4 = [0, 0, 1] + output_list4 = 1 + assert output_list1 == average_nonzero_list(input_list1) + assert output_list2 == average_nonzero_list(input_list2) + assert output_list3 == average_nonzero_list(input_list3) + assert output_list4 == average_nonzero_list(input_list4) diff --git a/bpfin/tests/test_utilbills/test_back_end_call.py b/bpfin/tests/test_utilbills/test_back_end_call.py new file mode 100644 index 0000000000000000000000000000000000000000..b991a0905bedd2e96b581692aef4ae4617e2bcf6 --- /dev/null +++ b/bpfin/tests/test_utilbills/test_back_end_call.py @@ -0,0 +1,37 @@ +from bpfin.lib.back_end_call import monthly_bill, annual_bill, prior_income_statement +from bpfin.tests.testdata import feature_data as db +from bpfin.tests.testdata import sample_data as sdb + + +def test_monthly_bill(): + output_dict = { + 'electricity': db.prior_annual_bill['electricity'], + 'gas': None, + 'oil': None, + 'water': None} + result_dict = monthly_bill(db.raw_bill_table, db.analysis_date) + assert output_dict == result_dict + + +def test_annual_bill(): + output_dict = db.prior_annual_bill + result_dict = annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date) + assert output_dict == result_dict + + +def test_prior_income_statement(): + output_income_statement = db.prior_income_statement_cagr + output_next_year_income = db.next_year_income + output_average_income = db.average_income + output_historical_cagr = db.historical_cagr + prior_income, next_year_income, average_income, historical_cagr = prior_income_statement( + db.raw_income_input, + # db.prior_annual_bill, + db.raw_bill_table, + db.raw_annual_bill_table, + db.analysis_date, + -2.0) + assert output_income_statement == prior_income + assert output_next_year_income == next_year_income + assert output_average_income == average_income + assert output_historical_cagr == historical_cagr diff --git a/bpfin/tests/test_utilbills/test_bill.py b/bpfin/tests/test_utilbills/test_bill.py new file mode 100644 index 0000000000000000000000000000000000000000..5e27f234efc5c669d663487d862b39ab61bd9a45 --- /dev/null +++ b/bpfin/tests/test_utilbills/test_bill.py @@ -0,0 +1,47 @@ +# from datetime import date +# from bpfin.utilbills.bill import Bill_Table +# from bpfin.tests.testdata import feature_data as db + + +# def test_Bill_Table_put_month(): +# """ +# test Bill_Table with only inputing month bill +# """ +# input_raw_bill_table = db.raw_bill_table +# input_analysis_date = db.analysis_date + +# bill_table = Bill_Table(input_analysis_date) +# bill_table.put_month_bill_table(input_raw_bill_table) + +# output_annual_bill_table = db.annual_bill_table_month_only +# result_annual_bill_table = bill_table.get_annual_bill_table() +# assert result_annual_bill_table == output_annual_bill_table + + +# def test_Bill_Table_put_annual(): +# """ +# test Bill_Table with only inputing annual bill +# """ +# input_raw_annual_bill_table = db.raw_annual_bill_table +# input_analysis_date = db.analysis_date + +# bill_table = Bill_Table(input_analysis_date) +# bill_table.put_annual_bill_table(input_raw_annual_bill_table) + +# output_annual_bill_table = db.annual_bill_table_annual_only +# result_annual_bill_table = bill_table.get_annual_bill_table() +# assert result_annual_bill_table == output_annual_bill_table + + +# def test_Bill_Table_put_both(): +# input_analysis_date = db.analysis_date +# input_raw_annual_bill_table = db.raw_annual_bill_table +# input_raw_bill_table = db.raw_bill_table + +# bill_table = Bill_Table(input_analysis_date) +# bill_table.put_month_bill_table(input_raw_bill_table) +# bill_table.put_annual_bill_table(input_raw_annual_bill_table) + +# output_annual_bill_table = db.prior_annual_bill +# result_annual_bill_table = bill_table.get_annual_bill_table() +# assert result_annual_bill_table == output_annual_bill_table diff --git a/bpfin/tests/test_utilbills/test_month_normalize_rough.py b/bpfin/tests/test_utilbills/test_month_normalize_rough.py index 04a6cbc8a1f6ce8176cff344a11a3d525e075f3e..74e0318a532833107534c9262bd84654f6c8fa0e 100644 --- a/bpfin/tests/test_utilbills/test_month_normalize_rough.py +++ b/bpfin/tests/test_utilbills/test_month_normalize_rough.py @@ -7,7 +7,7 @@ def test_month_normalization_rough(): input_dict = db.raw_bill output_dict = db.monthly_normalized_bill - result_dict = bill_month_normalize_rough(input_dict) + result_dict = bill_month_normalize_rough('electricity', input_dict) assert result_dict == output_dict @@ -15,5 +15,5 @@ def test_month_normalization_rough(): def test_month_normalization_rough_oil(): input_dict = db.raw_bill_oil output_dict = db.monthly_normalized_bill_oil - result_dict = bill_month_normalize_rough(input_dict) + result_dict = bill_month_normalize_rough('oil', input_dict) assert result_dict == output_dict diff --git a/bpfin/tests/testdata/data_generation.py b/bpfin/tests/testdata/data_generation.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py new file mode 100644 index 0000000000000000000000000000000000000000..d9895af600bad6cd24823476744d6e8695f595ee --- /dev/null +++ b/bpfin/tests/testdata/feature_data.py @@ -0,0 +1,743 @@ +""" +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 +# from bpfin.utilbills.bill import Bill_Table +import pprint + + +# one big decision is, one frontend request should either call data from database, or frontend. +# considering frondend inputs will be stored in database eventually, all data should come from database + +# pro-forma projection time base +analysis_date = { + 'proforma_start': datetime.date(2012, 1, 15), + 'proforma_duration': 25} + + +# Engineering Scenarios and project economics +cost_estimation = 150000.00 +commission_date = datetime.date(2017, 3, 14) + + +# required debt service coverage ratio +req_dscr = { + 'req_noi_dscr': 1.15, + 'req_cash_dscr': 1.15, + 'req_saving_dscr': 1.10 +} + + +# Customer Preference +customer_preference = { + 'downpayment_max': 5000.00, + 'expected_payback': 120, # in months + 'cust_saving_dscr': 1.15 +} + + +percent_saving_dict = { + 'electricity': 0.25, + 'gas': 0.10, + 'oil': 0.80, + 'water': 0.0 +} + + +full_saving_dict = { + 'electricity': None, + 'gas': None, + 'oil': None, + 'water': None +} +# dict of engineering analysis, {'date_from': [], 'date_to': [], 'heating': [], 'cooling': [], 'other': []} + + +e_raw_bill = { + 'date_from': [ + date(2015, 1, 28), date(2015, 2, 27), date(2015, 3, 30), date(2015, 4, 28), + date(2015, 5, 28), date(2015, 6, 26), date(2015, 7, 28), date(2015, 8, 26), + date(2015, 9, 25), date(2015, 10, 27), date(2015, 11, 25), date(2015, 12, 29), + date(2016, 1, 28), date(2016, 2, 29), date(2016, 3, 29), date(2016, 4, 27), + date(2016, 5, 26), date(2016, 6, 27), date(2016, 7, 27), date(2016, 8, 25), + date(2016, 9, 26), date(2016, 10, 26), date(2016, 11, 28) + ], + 'date_to': [ + date(2015, 2, 27), date(2015, 3, 30), date(2015, 4, 28), date(2015, 5, 28), + date(2015, 6, 26), date(2015, 7, 28), date(2015, 8, 26), date(2015, 9, 25), + date(2015, 10, 27), date(2015, 11, 25), date(2015, 12, 29), date(2016, 1, 28), + date(2016, 2, 29), date(2016, 3, 29), date(2016, 4, 27), date(2016, 5, 26), + date(2016, 6, 27), date(2016, 7, 27), date(2016, 8, 25), date(2016, 9, 26), + date(2016, 10, 26), date(2016, 11, 28), date(2016, 12, 28) + ], + 'usage': [ + 214.66, 189.42, 190.8, 168.92, 189.02, 179.64, 154.95, 162.13, + 136.16, 144.06, 208.85, 182.55, 98.13, 104.19, 90.05, + 88.19, 101.05, 87.05, 86.88, 98.17, 89.07, 91.93, 117.88 + ], + 'charge': [ + 172.33, 163.88, 163.49, 196.46, 169.68, 152.56, 126.16, + 137.14, 136.1, 130.65, 172.37, 145.44, 71.73, 68.12, 63.57, + 68.28, 71.66, 56.21, 57.25, 63.6, 67.42, 39.62, 56.01 + ] +} + +raw_bill_table = { + 'electricity': e_raw_bill, + # 'gas': None, + # 'oil': None, + # 'water': None, +} + + +# raw annual_bill +annual_bill_gas = {2014: 0, 2015: 1020, 2016: 1220, 2017: 1520} +annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} +annual_bill_water = {2015: 0, 2016: 0, 2017: 0} +raw_annual_bill_table = { + # 'electricity': None, # True == Mannual Input + 'gas': annual_bill_gas, + 'oil': annual_bill_oil, + 'water': annual_bill_water +} + +# raw income_statement +raw_income_input = { + 2014: { + 'revenue': 90000, + 'utility_expense': 55000, + 'non_utility_expense': 3500 + }, + 2015: { + 'revenue': 95000, + 'utility_expense': 55000, + 'non_utility_expense': 3000 + }, + 2016: { + 'revenue': 100000, + 'utility_expense': 60000, + 'non_utility_expense': 3000 + } +} + +# saving scenarios +percent_saving_dict = { + 'electricity': 0.25, + 'gas': 0.10, + 'oil': 0.80, + 'water': 0.0 +} + +full_saving_dict = { + 'electricity': None, # dict of engineering analysis, {'date_from': [], 'date_to': [], 'heating': [], 'cooling': [], 'other': []} + 'gas': None, + 'oil': None, + 'water': None +} + +prior_annual_bill = { + 'electricity': { + 2012: 1302.3894189123205, + 2013: 1325.5398892097569, + 2014: 1346.0795010524571, + 2015: 1357.4370596899435, + 2016: 1368.4645719731475, + 2017: 1395.2434398071116, + 2018: 1429.9391383624793, + 2019: 1465.1219842627888, + 2020: 1499.442976989403, + 2021: 1531.5444508417731, + 2022: 1560.6164039629107, + 2023: 1589.1048030135796, + 2024: 1618.1893852141502, + 2025: 1648.2611314796882, + 2026: 1680.5743815217027, + 2027: 1715.4591028542839, + 2028: 1751.2717401473571, + 2029: 1787.1346258078672, + 2030: 1822.9958881766795, + 2031: 1858.9654290419135, + 2032: 1895.3329028685823, + 2033: 1932.5220357027845, + 2034: 1970.7052052121689, + 2035: 2009.9298853655182, + 2036: 2050.2008318127446 + }, + 'gas': { + 2014: 0, + 2015: 1020, + 2016: 1220, + 2017: 1520, + 2012: 1253.3333333333333, + 2013: 1253.3333333333333, + 2018: 1253.3333333333333, + 2019: 1253.3333333333333, + 2020: 1253.3333333333333, + 2021: 1253.3333333333333, + 2022: 1253.3333333333333, + 2023: 1253.3333333333333, + 2024: 1253.3333333333333, + 2025: 1253.3333333333333, + 2026: 1253.3333333333333, + 2027: 1253.3333333333333, + 2028: 1253.3333333333333, + 2029: 1253.3333333333333, + 2030: 1253.3333333333333, + 2031: 1253.3333333333333, + 2032: 1253.3333333333333, + 2033: 1253.3333333333333, + 2034: 1253.3333333333333, + 2035: 1253.3333333333333, + 2036: 1253.3333333333333 + }, + 'oil': { + 2015: 1010, + 2016: 1210, + 2017: 1510, + 2012: 1243.3333333333333, + 2013: 1243.3333333333333, + 2014: 1243.3333333333333, + 2018: 1243.3333333333333, + 2019: 1243.3333333333333, + 2020: 1243.3333333333333, + 2021: 1243.3333333333333, + 2022: 1243.3333333333333, + 2023: 1243.3333333333333, + 2024: 1243.3333333333333, + 2025: 1243.3333333333333, + 2026: 1243.3333333333333, + 2027: 1243.3333333333333, + 2028: 1243.3333333333333, + 2029: 1243.3333333333333, + 2030: 1243.3333333333333, + 2031: 1243.3333333333333, + 2032: 1243.3333333333333, + 2033: 1243.3333333333333, + 2034: 1243.3333333333333, + 2035: 1243.3333333333333, + 2036: 1243.3333333333333 + }, + 'water': { + 2015: 0, + 2016: 0, + 2017: 0, + 2012: 0, + 2013: 0, + 2014: 0, + 2018: 0, + 2019: 0, + 2020: 0, + 2021: 0, + 2022: 0, + 2023: 0, + 2024: 0, + 2025: 0, + 2026: 0, + 2027: 0, + 2028: 0, + 2029: 0, + 2030: 0, + 2031: 0, + 2032: 0, + 2033: 0, + 2034: 0, + 2035: 0, + 2036: 0 + } +} + +prior_income_statement_cagr = { + 2014: { + 'year': 2014, + 'revenue': 90000, + 'utility_expense': 55000, + 'energy_opex': 2589.41283438579, + 'electricity_opex': 1346.079501052457, + 'gas_opex': 0, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 52410.58716561421, + 'non_utility_expense': 3500, + 'net_non_energy_opex': 55910.58716561421, + 'total_opex': 58500.0, + 'noi': 31500.0 + }, + 2015: { + 'year': 2015, + 'revenue': 95000, + 'utility_expense': 55000, + 'energy_opex': 3387.4370596899435, + 'electricity_opex': 1357.4370596899435, + 'gas_opex': 1020, + 'oil_opex': 1010, + 'water_opex': 0, + 'other_utility': 51612.562940310054, + 'non_utility_expense': 3000, + 'net_non_energy_opex': 54612.562940310054, + 'total_opex': 58000.0, + 'noi': 37000.0 + }, + 2016: { + 'year': 2016, + 'revenue': 100000, + 'utility_expense': 60000, + 'energy_opex': 3798.4645719731475, + 'electricity_opex': 1368.4645719731475, + 'gas_opex': 1220, + 'oil_opex': 1210, + 'water_opex': 0, + 'other_utility': 56201.53542802685, + 'non_utility_expense': 3000, + 'net_non_energy_opex': 59201.53542802685, + 'total_opex': 63000.0, + 'noi': 37000.0 + }, + 2017: { + 'year': 2017, + 'revenue': 105409.25533894598, + 'utility_expense': 63685.47076980913, + 'energy_opex': 4425.243439807112, + 'electricity_opex': 1395.2434398071116, + 'gas_opex': 1520, + 'oil_opex': 1510, + 'water_opex': 0, + 'other_utility': 59260.22733000202, + 'non_utility_expense': 3513.6418446315324, + 'net_non_energy_opex': 62773.869174633546, + 'total_opex': 67199.11261444066, + 'noi': 38210.14272450532 + }, + 2018: { + 'year': 2018, + 'revenue': 111111.11111111112, + 'utility_expense': 66392.37014575081, + 'energy_opex': 3926.605805029146, + 'electricity_opex': 1429.9391383624793, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 62465.764340721675, + 'non_utility_expense': 3703.703703703704, + 'net_non_energy_opex': 66169.46804442538, + 'total_opex': 70096.07384945452, + 'noi': 41015.0372616566 + }, + 2019: { + 'year': 2019, + 'revenue': 117121.3948210511, + 'utility_expense': 69806.48568426503, + 'energy_opex': 3961.788650929455, + 'electricity_opex': 1465.1219842627888, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 65844.69703333559, + 'non_utility_expense': 3904.0464940350366, + 'net_non_energy_opex': 69748.74352737062, + 'total_opex': 73710.53217830008, + 'noi': 43410.86264275102 + }, + 2020: { + 'year': 2020, + 'revenue': 123456.79012345681, + 'utility_expense': 73402.51446668015, + 'energy_opex': 3996.1096436560692, + 'electricity_opex': 1499.442976989403, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 69406.40482302409, + 'non_utility_expense': 4115.22633744856, + 'net_non_energy_opex': 73521.63116047265, + 'total_opex': 77517.74080412873, + 'noi': 45939.049319328085 + }, + 2021: { + 'year': 2021, + 'revenue': 130134.88313450124, + 'utility_expense': 77188.98559899243, + 'energy_opex': 4028.211117508439, + 'electricity_opex': 1531.5444508417731, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 73160.77448148398, + 'non_utility_expense': 4337.829437816708, + 'net_non_energy_opex': 77498.60391930069, + 'total_opex': 81526.81503680913, + 'noi': 48608.068097692114 + }, + 2022: { + 'year': 2022, + 'revenue': 137174.21124828537, + 'utility_expense': 81175.51065176747, + 'energy_opex': 4057.283070629577, + 'electricity_opex': 1560.6164039629107, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 77118.2275811379, + 'non_utility_expense': 4572.4737082761785, + 'net_non_energy_opex': 81690.70128941408, + 'total_opex': 85747.98436004366, + 'noi': 51426.22688824171 + }, + 2023: { + 'year': 2023, + 'revenue': 144594.3145938903, + 'utility_expense': 85375.52089355137, + 'energy_opex': 4085.771469680246, + 'electricity_opex': 1589.1048030135796, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 81289.74942387112, + 'non_utility_expense': 4819.81048646301, + 'net_non_energy_opex': 86109.55991033414, + 'total_opex': 90195.33138001438, + 'noi': 54398.98321387592 + }, + 2024: { + 'year': 2024, + 'revenue': 152415.79027587266, + 'utility_expense': 89801.7755864785, + 'energy_opex': 4114.856051880816, + 'electricity_opex': 1618.1893852141502, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 85686.91953459768, + 'non_utility_expense': 5080.526342529089, + 'net_non_energy_opex': 90767.44587712677, + 'total_opex': 94882.30192900759, + 'noi': 57533.488346865066 + }, + 2025: { + 'year': 2025, + 'revenue': 160660.349548767, + 'utility_expense': 94466.87160244759, + 'energy_opex': 4144.927798146355, + 'electricity_opex': 1648.2611314796882, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 90321.94380430123, + 'non_utility_expense': 5355.3449849589, + 'net_non_energy_opex': 95677.28878926013, + 'total_opex': 99822.21658740648, + 'noi': 60838.13296136052 + }, + 2026: { + 'year': 2026, + 'revenue': 169350.87808430297, + 'utility_expense': 99384.92941996356, + 'energy_opex': 4177.241048188369, + 'electricity_opex': 1680.5743815217027, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 95207.68837177519, + 'non_utility_expense': 5645.029269476766, + 'net_non_energy_opex': 100852.71764125196, + 'total_opex': 105029.95868944033, + 'noi': 64320.919394862634 + }, + 2027: { + 'year': 2027, + 'revenue': 178511.49949863003, + 'utility_expense': 104569.84110763346, + 'energy_opex': 4212.125769520951, + 'electricity_opex': 1715.4591028542839, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 100357.71533811251, + 'non_utility_expense': 5950.383316621001, + 'net_non_energy_opex': 106308.0986547335, + 'total_opex': 110520.22442425445, + 'noi': 67991.27507437558 + }, + 2028: { + 'year': 2028, + 'revenue': 188167.6423158922, + 'utility_expense': 110034.25881989759, + 'energy_opex': 4247.938406814023, + 'electricity_opex': 1751.2717401473571, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 105786.32041308357, + 'non_utility_expense': 6272.254743863074, + 'net_non_energy_opex': 112058.57515694664, + 'total_opex': 116306.51356376066, + 'noi': 71861.12875213155 + }, + 2029: { + 'year': 2029, + 'revenue': 198346.1105540334, + 'utility_expense': 115792.37389037732, + 'energy_opex': 4283.8012924745335, + 'electricity_opex': 1787.1346258078672, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 111508.57259790279, + 'non_utility_expense': 6611.5370184677795, + 'net_non_energy_opex': 118120.10961637057, + 'total_opex': 122403.9109088451, + 'noi': 75942.1996451883 + }, + 2030: { + 'year': 2030, + 'revenue': 209075.15812876914, + 'utility_expense': 121860.01856938066, + 'energy_opex': 4319.662554843346, + 'electricity_opex': 1822.9958881766795, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 117540.3560145373, + 'non_utility_expense': 6969.171937625638, + 'net_non_energy_opex': 124509.52795216294, + 'total_opex': 128829.1905070063, + 'noi': 80245.96762176284 + }, + 2031: { + 'year': 2031, + 'revenue': 220384.56728225935, + 'utility_expense': 128254.04609337838, + 'energy_opex': 4355.63209570858, + 'electricity_opex': 1858.9654290419135, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 123898.41399766979, + 'non_utility_expense': 7346.152242741978, + 'net_non_energy_opex': 131244.56624041178, + 'total_opex': 135600.19833612035, + 'noi': 84784.368946139 + }, + 2032: { + 'year': 2032, + 'revenue': 232305.73125418797, + 'utility_expense': 134992.39514124338, + 'energy_opex': 4391.999569535249, + 'electricity_opex': 1895.3329028685823, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 130600.39557170814, + 'non_utility_expense': 7743.524375139598, + 'net_non_energy_opex': 138343.91994684775, + 'total_opex': 142735.919516383, + 'noi': 89569.81173780496 + }, + 2033: { + 'year': 2033, + 'revenue': 244871.74142473264, + 'utility_expense': 142094.09314422478, + 'energy_opex': 4429.188702369451, + 'electricity_opex': 1932.5220357027845, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 137664.90444185532, + 'non_utility_expense': 8162.3913808244215, + 'net_non_energy_opex': 145827.29582267973, + 'total_opex': 150256.4845250492, + 'noi': 94615.25689968345 + }, + 2034: { + 'year': 2034, + 'revenue': 258117.47917131998, + 'utility_expense': 149578.9225071101, + 'energy_opex': 4467.371871878835, + 'electricity_opex': 1970.705205212169, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 145111.55063523128, + 'non_utility_expense': 8603.915972377332, + 'net_non_energy_opex': 153715.4666076086, + 'total_opex': 158182.83847948743, + 'noi': 99934.64069183255 + }, + 2035: { + 'year': 2035, + 'revenue': 272079.71269414737, + 'utility_expense': 157467.601487427, + 'energy_opex': 4506.596552032184, + 'electricity_opex': 2009.9298853655182, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 152961.0049353948, + 'non_utility_expense': 9069.323756471578, + 'net_non_energy_opex': 162030.32869186637, + 'total_opex': 166536.92524389856, + 'noi': 105542.78745024881 + }, + 2036: { + 'year': 2036, + 'revenue': 286797.19907924446, + 'utility_expense': 165781.9237598475, + 'energy_opex': 4546.867498479411, + 'electricity_opex': 2050.2008318127446, + 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, + 'water_opex': 0, + 'other_utility': 161235.0562613681, + 'non_utility_expense': 9559.906635974816, + 'net_non_energy_opex': 170794.96289734292, + 'total_opex': 175341.83039582233, + 'noi': 111455.36868342213 + } +} + +next_year_income = { + 'year': 2017, + 'revenue': 105409.25533894598, + 'utility_expense': 63685.47076980913, + 'energy_opex': 4425.243439807112, + 'electricity_opex': 1395.2434398071116, + 'gas_opex': 1520, + 'oil_opex': 1510, + 'water_opex': 0, + 'other_utility': 59260.22733000202, + 'non_utility_expense': 3513.6418446315324, + 'net_non_energy_opex': 62773.869174633546, + 'total_opex': 67199.11261444066, + 'noi': 38210.14272450532 +} + +average_income = { + 'year': None, + 'revenue': 95000.0, + 'utility_expense': 56666.666666666657, + 'energy_opex': 3258.4381553496269, + 'electricity_opex': 1357.327044238516, + 'gas_opex': 746.66666666666663, + 'oil_opex': 1154.4444444444443, + 'water_opex': 0.0, + 'other_utility': 53408.228511317029, + 'non_utility_expense': 3166.6666666666665, + 'net_non_energy_opex': 56574.895177983693, + 'total_opex': 59833.333333333321, + 'noi': 35166.666666666679 +} + +historical_cagr = 0.05409255338945984 + +post_annual_bill_table = { + 'electricity': { + 2012: 1302.3894189123205, + 2013: 1325.5398892097569, + 2014: 1346.0795010524571, + 2015: 1357.4370596899435, + 2016: 1368.4645719731475, + 2017: 1143.5987943889527, + 2018: 1072.4543537718594, + 2019: 1098.8414881970916, + 2020: 1124.5822327420522, + 2021: 1148.6583381313299, + 2022: 1170.4623029721829, + 2023: 1191.8286022601849, + 2024: 1213.6420389106127, + 2025: 1236.195848609766, + 2026: 1260.4307861412767, + 2027: 1286.5943271407125, + 2028: 1313.453805110518, + 2029: 1340.3509693559004, + 2030: 1367.2469161325098, + 2031: 1394.224071781435, + 2032: 1421.4996771514368, + 2033: 1449.3915267770883, + 2034: 1478.0289039091267, + 2035: 1507.4474140241387, + 2036: 1537.6506238595582 + }, + 'gas': { + 2012: 1253.3333333333333, + 2013: 1253.3333333333333, + 2014: 0, + 2015: 1020, + 2016: 1220, + 2017: 1406.0, + 2018: 1128.0, + 2019: 1128.0, + 2020: 1128.0, + 2021: 1128.0, + 2022: 1128.0, + 2023: 1128.0, + 2024: 1128.0, + 2025: 1128.0, + 2026: 1128.0, + 2027: 1128.0, + 2028: 1128.0, + 2029: 1128.0, + 2030: 1128.0, + 2031: 1128.0, + 2032: 1128.0, + 2033: 1128.0, + 2034: 1128.0, + 2035: 1128.0, + 2036: 1128.0 + }, + 'oil': { + 2012: 1243.3333333333333, + 2013: 1243.3333333333333, + 2014: 1243.3333333333333, + 2015: 1010, + 2016: 1210, + 2017: 603.9999999999999, + 2018: 248.6666666666666, + 2019: 248.6666666666666, + 2020: 248.6666666666666, + 2021: 248.6666666666666, + 2022: 248.6666666666666, + 2023: 248.6666666666666, + 2024: 248.6666666666666, + 2025: 248.6666666666666, + 2026: 248.6666666666666, + 2027: 248.6666666666666, + 2028: 248.6666666666666, + 2029: 248.6666666666666, + 2030: 248.6666666666666, + 2031: 248.6666666666666, + 2032: 248.6666666666666, + 2033: 248.6666666666666, + 2034: 248.6666666666666, + 2035: 248.6666666666666, + 2036: 248.6666666666666 + }, + 'water': { + 2012: 0, + 2013: 0, + 2014: 0, + 2015: 0, + 2016: 0, + 2017: 0.0, + 2018: 0.0, + 2019: 0.0, + 2020: 0.0, + 2021: 0.0, + 2022: 0.0, + 2023: 0.0, + 2024: 0.0, + 2025: 0.0, + 2026: 0.0, + 2027: 0.0, + 2028: 0.0, + 2029: 0.0, + 2030: 0.0, + 2031: 0.0, + 2032: 0.0, + 2033: 0.0, + 2034: 0.0, + 2035: 0.0, + 2036: 0.0 + } +} diff --git a/bpfin/tests/testdata/sample_data.py b/bpfin/tests/testdata/sample_data.py index 2fb3de612ea48efa75a2300af7a8645e6110b220..4a86f35a3d94c4abcfd752bbfd620c2c0dad6540 100644 --- a/bpfin/tests/testdata/sample_data.py +++ b/bpfin/tests/testdata/sample_data.py @@ -95,10 +95,10 @@ annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} annual_bill_water = {2015: 0, 2016: 0, 2017: 0} bill_overview = { - 'electricity': [annual_bill_electricity, True], # True == scraper - 'gas': [annual_bill_gas, False], - 'oil': [annual_bill_oil, False], - 'water': [annual_bill_water, False] + 'electricity': [annual_bill_electricity, False], # True == Mannual Input + 'gas': [annual_bill_gas, True], + 'oil': [annual_bill_oil, True], + 'water': [annual_bill_water, True] } bill_overview_organized = { @@ -725,21 +725,53 @@ cash_balance = { 2014: 5000.0, 2015: 6000, 2016: 4500.0 } -raw_balance_sheet = {'cash': cash_balance, 'other_debt_service': liability_dictionary, - 'net_income': noi_dictionary} +raw_balance_sheet = { + 'cash': cash_balance, + 'other_debt_service': liability_dictionary, + 'net_income': noi_dictionary +} -hist_balance_sheet = {2014: {'cash': 5000, 'other_debt_service': 100, 'net_income': 31500}, - 2015: {'cash': 6000, 'other_debt_service': 6000, 'net_income': 37000}, - 2016: {'cash': 4500, 'other_debt_service': 15000, 'net_income': 37000}} +hist_balance_sheet = { + 2014: { + 'cash': 5000, + 'other_debt_service': 100, + 'net_income': 31500 + }, + 2015: { + 'cash': 6000, + 'other_debt_service': 6000, + 'net_income': 37000 + }, + 2016: { + 'cash': 4500, + 'other_debt_service': 15000, + 'net_income': 37000 + } +} -balance_sheet_2018 = {2018: {'cash': 75992.72229401008,'other_debt_service': 5000, -'net_income': 45214.28536645262}} +balance_sheet_2018 = { + 2018: { + 'cash': 75992.72229401008, + 'other_debt_service': 5000, + 'net_income': 45214.28536645262 + } +} -balance_sheet_2025 = {2025: {'cash': 510670.2474431231,'other_debt_service': 0.00, -'net_income': 76882.87761572453}} +balance_sheet_2025 = { + 2025: { + 'cash': 510670.2474431231, + 'other_debt_service': 0.00, + 'net_income': 76882.87761572453 + } +} -balance_sheet_2030 = {2030: {'cash': 987109.6873789529,'other_debt_service': 0.00, -'net_income': 108765.29589285178}} +balance_sheet_2030 = { + 2030: { + 'cash': 987109.6873789529, + 'other_debt_service': 0.00, + 'net_income': 108765.29589285178 + } +} # pro-forma date and bill projection - @@ -2879,7 +2911,7 @@ raw_gas_bill_demo['date_from'] = [ datetime.date(2015, 12, 16), datetime.date(2015, 11, 16), datetime.date(2015, 10, 16), datetime.date(2015, 9, 19), datetime.date(2015, 8, 18), datetime.date(2015, 7, 20), datetime.date(2015, 6, 18), datetime.date(2015, 5, 19)] -raw__gas_bill_demo['date_to'] = [ +raw_gas_bill_demo['date_to'] = [ datetime.date(2016, 12, 16), datetime.date(2016, 11, 22), datetime.date(2016, 10, 18), datetime.date(2016, 9, 30), datetime.date(2016, 9, 19), datetime.date(2016, 8, 18), datetime.date(2016, 7, 20), datetime.date(2016, 6, 20), datetime.date(2016, 5, 18), diff --git a/bpfin/utilbills/bill.py b/bpfin/utilbills/bill.py new file mode 100644 index 0000000000000000000000000000000000000000..161f54dbdefee25b9c9346632c0c3ba1e7d2cc12 --- /dev/null +++ b/bpfin/utilbills/bill.py @@ -0,0 +1,292 @@ +from datetime import date +import copy +import sys +from bpfin.utilbills.bill_lib import annualizing_projection +from bpfin.utilbills.bill_lib import form_bill_calendar +from bpfin.utilbills.bill_lib import form_bill_year +from bpfin.lib.other import average_nonzero_list +from bpfin.lib.other import form_date_calendar +from bpfin.utilbills.bill_month_normalize_rough import bill_month_normalize_rough +from bpfin.utilbills.bill_prior_proj_rough import bill_prior_proj_rough +from bpfin.utilbills.inflation_sample_data import inflation_coeff_dict +# from bpfin.tests.testdata import feature_data as db + + +UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] + + +def validate_raw_bill(raw_bill): + """ + Raw bill validation. A valid raw_bill is not None, and it covers at least 12 months + Args: + raw_bill (dictionary): dictionary of lists. Keys are + 'date_from': list of datetime, + 'date_to', list of datetime, + 'usage', list of float values, + 'charge', list of float, + Return: + Bollean: True == data validated. False == data is not validated + """ + if raw_bill: + month_list = form_date_calendar( + raw_bill['date_from'][0], + raw_bill['date_to'][-1])[1] + if len(month_list) >= 12: + return True + else: + return -1 + else: + return False + + +def estimate_annual_bill(raw_annual_bill, analysis_date): + """ + Fill the annual charge for missing years, with average annual charge, for one utility type + + Args: + raw_annual_bill (dictionary): {year: float}. annual bill allowing some years data missing + analysis_date (dictionary): proforma's starting date and the years of proforma + Return: + dictionary: {year: float}. annual_bill with all years filled with average charge + + To Do: validate data == 0 and data == None, difference inputs cause different results + """ + proforma_year = form_bill_year(analysis_date['proforma_start'], + analysis_date['proforma_duration']) + if not raw_annual_bill: + average_charge = 0 + annual_bill_dict = {} + else: + average_charge = average_nonzero_list(raw_annual_bill.values()) + annual_bill_dict = copy.deepcopy(raw_annual_bill) + + for year in proforma_year: + if year not in annual_bill_dict: + annual_bill_dict[year] = average_charge + return annual_bill_dict + + +class Bill(): + """ + Bill object for one utility type. 'bill' usually contains concepts of date ranges, usage, prices, and charges + Calculate and store monthly_normalized bill, price + Project bill for the past and for the future for pro-forma porpuse, with inflation applied + + Attributes: + monthly_normalized_bill_rough (dictionary): dict of lists, for 12 months of usage, charge and price + prior_bill_rough (dictionary): dictionary of lists, prior_saving bill, for pro-forma period + monthly_normailzed_bill_reg (dictionary): unknown + prior_bill_reg (dictionary): unknown + annual_bill (dictionary): dict of float, annual charge for pro-forma period. {year: float} + utility_type (string): utility type + analysis_date (dictionary): proforma's starting date and the years of proforma + proforma_date (list): list of dates, months of pro-forma time period + is_manual_input (boolean): flag of raw bill validation. + True == raw bill is not valid, annual bills are manually input + False == raw bill is valid, annual bills are calculated automatically + + Description: + monthly_normalized_bill_rough = { + 'monthly_usage': a list of 12 float values, month normalized average energy usage + 'monthly_charge': a list of 12 float values, month normalized average energy charge + 'monthly_price': a list of 12 float values, month normalized average energy price + } + prior_bill_rough = { + 'date_from': list of date, first day of month, for pro-forma period + 'date_to': list of date, last day of month, for pro-forma period + 'usage': list of floate, prior_saving_monthly_usage, + 'charge': list of floate, prior_saving_monthly_charge, + 'price': list of floate, prior_saving_monthly_price, + } + + To Do: + Add in regression method as normalization and projection method + Add in rate plan feature to replace/supplement current rough monthly pricing method + """ + + monthly_normalized_bill_rough = None + prior_bill_rough = None + monthly_normailzed_bill_reg = None + prior_bill_reg = None + annual_bill = None + utility_type = None + + def __init__(self, utility_type, analysis_date): + """ + Initialize Bill object. Issue utility type and pro-forma date. Set manual_input as True + Args: + utility_type (string): utility type + analysis_date (dictionary): proforma's starting date and the years of proforma + """ + self.utility_type = utility_type + self.analysis_date = analysis_date + self.proforma_date = form_bill_calendar(analysis_date['proforma_start'], analysis_date['proforma_duration'])[1] + self.is_manual_input = True + + def put_month_bill(self, raw_bill): + """ + Put raw monthly bill. + Validate input data (Not None and time coverage >=12 months) + Roughly month_normalize the bill and roughly project prior_saving bill + Determine annual bill with prior_saving bill + + Args: + raw_bill (dictionary): dictionary of lists. Keys are + 'date_from': list of date, + 'date_to', list of date, + 'usage', list of float, + 'charge', list of float, + """ + validation = validate_raw_bill(raw_bill) + if validation: + self.monthly_normalized_bill_rough = bill_month_normalize_rough(self.utility_type, copy.deepcopy(raw_bill)) + self.is_manual_input = False + self.prior_bill_rough = bill_prior_proj_rough( + copy.deepcopy(self.monthly_normalized_bill_rough), + copy.deepcopy(raw_bill), + self.analysis_date, + inflation_coeff_dict + ) + self.annual_bill = annualizing_projection(self.proforma_date, self.prior_bill_rough['charge']) + else: + if validation == -1: + raise ValueError('monthly bill input not valid for ', self.utility_type) + else: + pass + + def put_annual_bill(self, raw_annual_bill): + """ + Put annual bill to the bill with raw bill not valid (manual_input == True) + Calculate average annual charge and assign the average value to missing years + + Args: + raw_annual_bill (dictionary): {year: float}. annual bill allowing some years data missing + """ + if self.is_manual_input: + self.annual_bill = estimate_annual_bill(raw_annual_bill, self.analysis_date) + + def get_prior_proj_rough(self): + """ + Get roughly projected prior_saving monthly bill + Return: + dictionary: dictionary of lists, prior_saving bill, for pro-forma period + prior_bill_rough = { + 'date_from': list of date, first day of month, for pro-forma period + 'date_to': list of date, last day of month, for pro-forma period + 'usage': list of floate, prior_saving_monthly_usage, + 'charge': list of floate, prior_saving_monthly_charge, + 'price': list of floate, prior_saving_monthly_price, + } + """ + return self.prior_bill_rough + + def get_prior_proj_reg(self): + """ + Get projected prior_saving monthly bill (regression method) + """ + return self.prior_bill_reg + + def get_annual_bill(self): + """ + Get annual bill in dictionary format + Return: + dictionary: {year, float} + """ + return self.annual_bill + + def get_manual_input(self): + """ + Get manual_input label + Return: + boolean: Yes == Bill is manually input + """ + return self.is_manual_input + + +# class Bill_Table(): +# """ +# """ +# def __init__(self, analysis_date): +# """ +# """ +# self.analysis_date = analysis_date +# self.raw_bill_table = None +# self.manual_input_dict = {} +# self.raw_annual_bill_table = None +# self.utility_bill_dict = {} # dict of object +# for utility_type in UTILITY_TYPE_LIST: +# self.utility_bill_dict[utility_type] = Bill(utility_type, analysis_date) + +# def put_month_bill_table(self, raw_bill_table): +# """ +# """ +# self.raw_bill_table = raw_bill_table +# for utility_type in UTILITY_TYPE_LIST: +# if utility_type in self.raw_bill_table: +# self.utility_bill_dict[utility_type].put_month_bill(self.raw_bill_table[utility_type]) +# # else: +# # self.utility_bill_dict[utility_type].put_month_bill(None) +# self.manual_input_dict[utility_type] = copy.deepcopy(self.utility_bill_dict[utility_type].is_manual_input) + +# def put_annual_bill_table(self, raw_annual_bill_table): +# """ +# """ +# self.raw_annual_bill_table = raw_annual_bill_table +# for utility_type in UTILITY_TYPE_LIST: +# if utility_type in self.raw_annual_bill_table: +# self.utility_bill_dict[utility_type].put_annual_bill(self.raw_annual_bill_table[utility_type]) + +# def get_bill_overview(self): +# """ +# should be deleted or replaced when future calling func got updated +# because all functions would need to use either the bill or the yes/no flag. +# """ +# bill_overview = {} +# for utility_type in UTILITY_TYPE_LIST: +# bill_overview[utility_type] = [ +# ( +# self.raw_annual_bill_table[utility_type] if +# self.utility_bill_dict[utility_type].is_manual_input +# else self.utility_bill_dict[utility_type].get_annual_bill() +# ), +# self.utility_bill_dict[utility_type].is_manual_input +# ] +# return bill_overview + +# def get_annual_bill_table(self): +# """ +# Annual bill table, for 4 utility types, with annualized projected charge, or average annual charge +# sample_data.bill_overview_organized +# """ +# annual_bill_table = {} +# for utility_type in UTILITY_TYPE_LIST: +# annual_bill_table[utility_type] = self.utility_bill_dict[utility_type].get_annual_bill() +# return annual_bill_table + +# def get_bill_list(self): +# """ +# Get list of Bill objects. For future use in other functions +# Return: +# list: list of Bill objects. Order in 'electricity', 'gas', 'oil', 'water' +# """ +# bill_list = [] +# for utility_type in UTILITY_TYPE_LIST: +# bill_list.append(copy.deepcopy(self.utility_bill_dict[utility_type])) +# return bill_list + + + +# **** ugly test **** +# e_bill = Bill('electricity', db.analysis_date) +# e_bill.put_month_bill(db.raw_bill_table['electricity']) +# print('\n annual_bill_rough', e_bill.annual_bill) +# o_bill = Bill(db.raw_bill_dict['oil'], db.analysis_date) +# o_bill.put_annual_bill(db.raw_annual_bill_dict['oil']) +# print('\n annual_bill_oil', o_bill.get_annual_bill()) + + +# bill_table = Bill_Table(db.raw_bill_table, db.analysis_date) +# bill_table.put_annual_bill_table(db.raw_annual_bill_table) +# print(bill_table.utility_bill_dict['oil'].get_annual_bill()) +# print(bill_table.get_bill_overview()) +# print(bill_table.get_annual_bill_table()) diff --git a/bpfin/utilbills/bill_backend_call.py b/bpfin/utilbills/bill_backend_call.py index 5661bda5777c5c350d9ed54a09b243dbddc5a41d..970ed9d6c733ede7aa736e6a2829e53a5d430baf 100644 --- a/bpfin/utilbills/bill_backend_call.py +++ b/bpfin/utilbills/bill_backend_call.py @@ -4,8 +4,8 @@ from bpfin.utilbills import bill_lib as bl from bpfin.tests.testdata import sample_data as db -def bill_prior_proj_rough_annual(raw_bill, analysis_date): - norm_bill = bill_month_normalize_rough(raw_bill) +def bill_prior_proj_rough_annual(utility_type, raw_bill, analysis_date): + norm_bill = bill_month_normalize_rough(utility_type, raw_bill) prior_bill_rough = bill_prior_proj_rough(norm_bill, raw_bill, analysis_date, db.inflation_coeff_dict) annual_bill_rough = bl.annualizing_projection( prior_bill_rough['date_to'], @@ -13,8 +13,8 @@ def bill_prior_proj_rough_annual(raw_bill, analysis_date): return annual_bill_rough -def prior_proj_rough_month(raw_bill, analysis_date): - norm_bill = bill_month_normalize_rough(raw_bill) +def prior_proj_rough_month(utility_type, raw_bill, analysis_date): + norm_bill = bill_month_normalize_rough(utility_type, raw_bill) prior_bill_utility = bill_prior_proj_rough(norm_bill, raw_bill, analysis_date, db.inflation_coeff_dict) # annual_bill_utility = bl.annualizing_projection(prior_bill_utility['date_to'], prior_bill_utility['charge']) return prior_bill_utility diff --git a/bpfin/utilbills/bill_lib.py b/bpfin/utilbills/bill_lib.py index 51b1d5284eb9eabb8309ee000e6360ac8faee0a1..69d21ca09842c039d915905513df9114fd6f4b9a 100644 --- a/bpfin/utilbills/bill_lib.py +++ b/bpfin/utilbills/bill_lib.py @@ -4,6 +4,7 @@ import pandas as pd import copy from bpfin.lib.other import month_shift + def add_list(obj_list, number): """Add a number to each value in a list. @@ -118,7 +119,6 @@ def form_year_calendar(date_start, date_end): return year_list - def form_year_month(target_terms): """Return list of month,year of datetimes in a list. diff --git a/bpfin/utilbills/bill_month_normalize_rough.py b/bpfin/utilbills/bill_month_normalize_rough.py index 23909a9eeac2beafaa1447085ee05be8e26797ee..9c8565b76257b255db88f610310f049f423fc8e5 100644 --- a/bpfin/utilbills/bill_month_normalize_rough.py +++ b/bpfin/utilbills/bill_month_normalize_rough.py @@ -3,20 +3,23 @@ import bpfin.lib.other as lib import bpfin.utilbills.bill_lib as bl -def bill_month_normalize_rough(raw_bill): +def bill_month_normalize_rough(utility_type, raw_bill): """ take raw bill as input, normalize the date from random dates to beg/end of a month + + !! This function should be deleted when Bill Class is in use + math calculation is rough, by determining daily usage, daily price to calculate monthly usage and average price NOT using reggression neither BlocPower's disaggeration method inputs are at least 12 months of bills outcome are 12 months of values. If the raw bill covers more than 12 months, then an average will be taken Args: + utility_type (string): utility type raw_bill (dictionary): dictionary of lists. Elements are 'date_from': list of datetime, 'date_to', list of datetime, 'usage', list of float values, 'charge', list of float, - 'utility_type', a char indicating utility type Returns: dictionary: dictionary of lists. Elements are 'monthly_usage': a list of 12 float values, month normalized average energy usage @@ -28,9 +31,7 @@ def bill_month_normalize_rough(raw_bill): bill_end = lib.convert_timestamp_date(raw_bill['date_to']) usage = raw_bill['usage'] charge = raw_bill['charge'] - utility_type = raw_bill['utility_type'] - # !!!! we will need to update oil calculation test file if bill_start != []: month_matrix = lib.form_month_matrix(bill_start, bill_end) daily_usage = lib.division(usage, lib.date_diff(bill_start, bill_end)) @@ -51,3 +52,54 @@ def bill_month_normalize_rough(raw_bill): return {'monthly_usage': month_norm_usage, 'monthly_charge': month_norm_charge, 'monthly_price': month_norm_price} + + +# **** old copy **** +# def bill_month_normalize_rough(raw_bill): +# """ take raw bill as input, normalize the date from random dates to beg/end of a month +# math calculation is rough, by determining daily usage, daily price to calculate monthly usage and average price +# NOT using reggression neither BlocPower's disaggeration method +# inputs are at least 12 months of bills +# outcome are 12 months of values. If the raw bill covers more than 12 months, then an average will be taken + +# Args: +# raw_bill (dictionary): dictionary of lists. Elements are +# 'date_from': list of datetime, +# 'date_to', list of datetime, +# 'usage', list of float values, +# 'charge', list of float, +# 'utility_type', a char indicating utility type +# Returns: +# dictionary: dictionary of lists. Elements are +# 'monthly_usage': a list of 12 float values, month normalized average energy usage +# 'monthly_charge': a list of 12 float values, month normalized average energy charge +# 'monthly_price': a list of 12 float values, month normalized average energy price +# """ + +# bill_start = lib.convert_timestamp_date(raw_bill['date_from']) +# bill_end = lib.convert_timestamp_date(raw_bill['date_to']) +# usage = raw_bill['usage'] +# charge = raw_bill['charge'] +# utility_type = raw_bill['utility_type'] + +# # !!!! we will need to update oil calculation test file +# if bill_start != []: +# month_matrix = lib.form_month_matrix(bill_start, bill_end) +# daily_usage = lib.division(usage, lib.date_diff(bill_start, bill_end)) +# daily_charge = lib.division(charge, lib.date_diff(bill_start, bill_end)) + +# month_norm_usage = lib.monthly_average(daily_usage, month_matrix) +# if utility_type == 'oil': +# month_norm_price = bl.cal_oil_price(charge, usage) +# month_norm_charge = lib.product2list(month_norm_usage, month_norm_price) +# else: +# month_norm_charge = lib.monthly_average(daily_charge, month_matrix) +# month_norm_price = lib.division(month_norm_charge, month_norm_usage) +# else: +# month_norm_usage = [0] * 12 +# month_norm_charge = [0] * 12 +# month_norm_price = [0] * 12 + +# return {'monthly_usage': month_norm_usage, +# 'monthly_charge': month_norm_charge, +# 'monthly_price': month_norm_price}