From e4c9fd1a8960e62fc0a95d13e9ca4dd4f19d2661 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Tue, 13 Jun 2017 17:04:31 -0400 Subject: [PATCH 01/16] Create lib directory before the apps levels. Add an init file. Create file fetch_data that contains methods to fetch data from the database for each model. Add test_fetch_data which is a test file to test all the functions in fetch_data. The test file has tests up till customer preference. --- blocnote/lib/__init__.py | 0 blocnote/lib/fetch_data.py | 210 ++++++++++++++++++++++++ blocnote/lib/test_fetch_data.py | 279 ++++++++++++++++++++++++++++++++ 3 files changed, 489 insertions(+) create mode 100644 blocnote/lib/__init__.py create mode 100644 blocnote/lib/fetch_data.py create mode 100644 blocnote/lib/test_fetch_data.py diff --git a/blocnote/lib/__init__.py b/blocnote/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/blocnote/lib/fetch_data.py b/blocnote/lib/fetch_data.py new file mode 100644 index 0000000..9d232a0 --- /dev/null +++ b/blocnote/lib/fetch_data.py @@ -0,0 +1,210 @@ +"""This file contains functions that fetch all data from a model in financialInputs.""" +from blocnote.apps.financialInputs.models import Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference +from blocnote.apps.financialInputs.models import Liabilities, CashBalance, IncomeStatement, LoanOptions, GrowthRate + +def get_fund(): + """Fetch all the funds in the database. If none exist, return empty list. + + Returns: + funds_list (tuple): Tuple with fund id and name in the form of (id, name) + """ + funds_objects = Fund.objects.all() + funds_list = [] + for funds_object in funds_objects: + funds_list.append((funds_object.id, funds_object.Name)) + return funds_list + + +def get_financing_overview(building_id): + """Fetch the financing overview data for a given building_id as a dictionary. If not present, return empty + dictionary. + + Returns: + financing_overview (dictionary): Dictionary with all the relevant fields. + """ + fields = [ + 'fund_id', + 'pro_forma_start_date', + 'pro_forma_duration', + 'analysis_date', + 'anticipated_construction_start_date', + 'anticipated_commissioning_date', + 'anticipated_construction_period', + ] + financing_overview = {} + financing_overview_object = FinancingOverview.objects.filter(building_id=building_id) + if financing_overview_object: + for field in fields: + financing_overview[field] = financing_overview_object[0].__dict__[field] + return financing_overview + + +def get_utility_bill(building_id, utility_type): + """Fetch the bills for a building and a utility type. If bills are not present, return empty list. + + Returns: + utility_bills (List of dictionaries): List of dictionaries where each dictionary contains all the relevant data + of a utility bill for a building. + """ + fields = [ + 'date_from', + 'date_to', + 'usage', + 'charge', + ] + utility_bills = [] + utility_bills_objects = Bills.objects.filter(building_id=building_id, utility_type=utility_type) + for utility_bills_object in utility_bills_objects: + bill = {} + for field in fields: + bill[field] = utility_bills_object.__dict__[field] + utility_bills.append(bill) + return utility_bills + + +def get_bills_overview(building_id): + """Fetch the bills overview for a building. If not available, return empty list. + + Returns: + bills_overview (list of dictionaries): Each dictionary contains the bills overview information for a year. + """ + fields = [ + 'year', + 'electricity', + 'electricity_is_user_input', + 'gas', + 'gas_is_user_input', + 'oil', + 'oil_is_user_input', + 'water', + 'water_is_user_input', + ] + bills_overview = [] + bills_overview_objects = BillsOverview.objects.filter(building_id=building_id) + for bills_overview_object in bills_overview_objects: + bill_overview = {} + for field in fields: + bill_overview[field] = bills_overview_object.__dict__[field] + bills_overview.append(bill_overview) + return bills_overview + + +def get_customer_preference(building_id): + """Fetch the customer preference data for a building. If not present, return empty. + + Returns: + customer_preference (dictionary): Dictionary with the relevant data. + """ + fields = [ + 'downpayment', + 'expected_payback', + 'expected_net_noi_dscr', + ] + customer_preference = {} + customer_preference_object = CustomerPreference.objects.filter(building_id=building_id) + if customer_preference_object: + for field in fields: + customer_preference[field] = customer_preference_object[0].__dict__[field] + return customer_preference + +def get_liabilities(building_id): + """Fetch the liabilities information of a given building. If not available, return empty. + + Returns: + liabilities (list of dictionaries): Each dictionary will contain the relevant fields of liabilities for a + building. + """ + fields = [ + 'input_date', + 'lender', + 'monthly_service', + 'remaining_term', + ] + liabilities = [] + liabilities_objects = Liabilities.objects.filter(building_id=building_id) + for liabilities_object in liabilities_objects: + liability = {} + for field in fields: + liability[field] = liabilities_object.__dict__[field] + liabilities.append(liability) + return liabilities + + +def get_cash_balance(building_id): + """Fetch the cash balance for a given building. If not available, return empty. + + Returns: + cash_balances (List of dictionaries): Each dictionary contains the cash balance information for a building. + """ + fields = [ + 'statement_date', + 'is_from_balance_sheet', + 'balance_amount', + ] + cash_balances = [] + cash_balance_objects = CashBalance.objects.filter(building_id=building_id) + for cash_balance_object in cash_balance_objects: + cash_balance = {} + for field in fields: + cash_balance[field] = cash_balance_object.__dict__[field] + cash_balances.append(cash_balance) + return cash_balances + + +def get_income_statement(building_id): + """Fetch the income statements for a given building. If not available return empty. + + Returns: + income_statements (dictionary of dictionaries): Dictionary with key as value and all relevant information from + that year as it's value. + """ + fields = [ + 'revenue', + 'utility_expense', + 'other_utility_expense', + 'non_utility_operating_expense', + ] + income_statements = {} + income_statement_objects = IncomeStatement.objects.filter(building_id=building_id) + for income_statement_object in income_statement_objects: + income_statement = {} + for field in fields: + income_statement[field] = income_statement_object.__dict__[field] + income_statements[income_statement_object.__dict__['year']] = income_statement + return income_statements + + +def get_loan_options(building_id): + """Fetch the loan options for a given building. If not available return empty. + + Returns: + loan_options (list of dictionaries): Each dictionary contains the loan options information. The list is for a + building. + """ + fields = [ + 'lender_id', + 'interest_rate', + 'duration', + 'max_loan_amount', + ] + loan_options = [] + loan_options_objects = LoanOptions.objects.filter(building_id=building_id) + for loan_options_object in loan_options_objects: + loan_option = {} + for field in fields: + loan_option[field] = loan_options_object.__dict__[field] + loan_options.append(loan_option) + return loan_options + + +def get_growth_rate(building_id): + """Fetch the growth rate for a building. If growth rate has not been stored, return None. + + Returns: + growth_rate (float): growth rate selected for the building id to project income statement. + """ + growth_rate_object = GrowthRate.objects.filter(building_id=building_id) + growth_rate = None + if growth_rate_object: + growth_rate = growth_rate_object[0].__dict__['growth_rate'] + return growth_rate diff --git a/blocnote/lib/test_fetch_data.py b/blocnote/lib/test_fetch_data.py new file mode 100644 index 0000000..b3f403d --- /dev/null +++ b/blocnote/lib/test_fetch_data.py @@ -0,0 +1,279 @@ +"""Tests for the fetch data file.""" +from datetime import date +from django.test import TestCase +from blocnote.apps.financialInputs.models import Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference +from blocnote.lib import fetch_data + +BUILDING_ID = 100 +class TestGetFunds(TestCase): + """Test the get_fund function.""" + + def test_fund_1(self): + """Test when there are no funds in the database.""" + funds = fetch_data.get_fund() + expected_result = [] + self.assertEqual(funds, expected_result) + + def test_fund_2(self): + """Test when Funds are created.""" + Fund.objects.create(id=1, Name='Fund1') + Fund.objects.create(id=2, Name='Fund2') + Fund.objects.create(id=3, Name='Fund3') + funds = fetch_data.get_fund() + expected_result = [ + (1, 'Fund1'), + (2, 'Fund2'), + (3, 'Fund3'), + ] + self.assertEqual(funds, expected_result) + +class TestGetFinancingOverview(TestCase): + """Test the get_financing_overview function.""" + + def setUp(self): + Fund.objects.create(id=1, Name='Fund1') + Fund.objects.create(id=2, Name='Fund2') + Fund.objects.create(id=3, Name='Fund3') + + def test_financing_overview_1(self): + """Test case when no entry has been made.""" + financing_overview = fetch_data.get_financing_overview(BUILDING_ID) + expected_result = {} + self.assertEqual(financing_overview, expected_result) + + def test_financing_overview_2(self): + """Create a valid financing overview entry and test the function.""" + FinancingOverview.objects.create( + building_id=BUILDING_ID, + fund_id=1, + pro_forma_start_date='2014-01-01', + pro_forma_duration=25, + analysis_date='2017-06-12', + anticipated_construction_start_date='2017-07-01', + anticipated_commissioning_date='2017-08-01', + anticipated_construction_period=4, + ) + financing_overview = fetch_data.get_financing_overview(BUILDING_ID) + expected_result = { + 'fund_id': 1, + 'pro_forma_start_date': date(2014, 1, 1), + 'pro_forma_duration': 25, + 'analysis_date': date(2017, 6, 12), + 'anticipated_construction_start_date': date(2017, 7, 1), + 'anticipated_commissioning_date': date(2017, 8, 1), + 'anticipated_construction_period': 4, + } + self.assertEqual(financing_overview, expected_result) + + +class TestGetUtilityBills(TestCase): + """Test the get_utility_bill function.""" + + def setUp(self): + self.utilities = [ + 'electricity', + 'gas', + 'oil', + 'water', + ] + + def use_function(self): + """Call the get_utility_bill function for each utility type.""" + utility_bills = {} + for utility in self.utilities: + utility_bills[utility] = fetch_data.get_utility_bill(BUILDING_ID, utility) + return utility_bills + + def test_get_util_bills_1(self): + """Fetch when no bills are present.""" + utility_bills = self.use_function() + expected_result = { + 'electricity': [], + 'gas': [], + 'oil': [], + 'water': [], + } + self.assertEqual(utility_bills, expected_result) + + def test_get_util_bills_2(self): + """Test when only electricity bills are present.""" + Bills.objects.create( + building_id=BUILDING_ID, + date_from='2014-01-01', + date_to='2015-01-01', + utility_type='electricity', + usage=100, + charge=1000, + ) + utility_bills = self.use_function() + expected_result = { + 'electricity': [ + { + 'date_from': date(2014, 1, 1), + 'date_to': date(2015, 1, 1), + 'usage': 100, + 'charge': 1000, + }, + ], + 'gas': [], + 'oil': [], + 'water': [], + } + self.assertEqual(utility_bills, expected_result) + + def test_get_util_bills_3(self): + """Test when all utility bills are present.""" + Bills.objects.create( + building_id=BUILDING_ID, + date_from='2014-01-01', + date_to='2015-01-01', + utility_type='electricity', + usage=100, + charge=1000, + ) + Bills.objects.create( + building_id=BUILDING_ID, + date_from='2014-01-01', + date_to='2015-01-01', + utility_type='gas', + usage=100, + charge=1000, + ) + Bills.objects.create( + building_id=BUILDING_ID, + date_from='2014-01-01', + date_to='2015-01-01', + utility_type='oil', + usage=100, + charge=1000, + ) + Bills.objects.create( + building_id=BUILDING_ID, + date_from='2014-01-01', + date_to='2015-01-01', + utility_type='water', + usage=100, + charge=1000, + ) + utility_bills = self.use_function() + expected_result = { + 'electricity': [ + { + 'date_from': date(2014, 1, 1), + 'date_to': date(2015, 1, 1), + 'usage': 100, + 'charge': 1000, + }, + ], + 'gas': [ + { + 'date_from': date(2014, 1, 1), + 'date_to': date(2015, 1, 1), + 'usage': 100, + 'charge': 1000, + }, + ], + 'oil': [ + { + 'date_from': date(2014, 1, 1), + 'date_to': date(2015, 1, 1), + 'usage': 100, + 'charge': 1000, + }, + ], + 'water': [ + { + 'date_from': date(2014, 1, 1), + 'date_to': date(2015, 1, 1), + 'usage': 100, + 'charge': 1000, + }, + ], + } + self.assertEqual(utility_bills, expected_result) + + +class TestGetBillsOverview(TestCase): + """Test the get_bills_overview function.""" + + def test_get_bills_overview_1(self): + """Test when no data for bills overview is stored.""" + bills_overview = fetch_data.get_bills_overview(BUILDING_ID) + expected_result = [] + self.assertEqual(bills_overview, expected_result) + + def test_get_bills_overview_2(self): + """Test when only electricity is store manually.""" + BillsOverview.objects.create( + building_id=BUILDING_ID, + year=2014, + electricity=100, + electricity_is_user_input=True, + ) + bills_overview = fetch_data.get_bills_overview(BUILDING_ID) + expected_result = [ + { + 'year': 2014, + 'electricity': 100, + 'electricity_is_user_input': True, + 'gas': None, + 'gas_is_user_input': False, + 'oil': None, + 'oil_is_user_input': False, + 'water': None, + 'water_is_user_input': False, + } + ] + self.assertEqual(bills_overview, expected_result) + + def test_get_bills_overview_3(self): + """Test when 2 utility bills are manually input.""" + BillsOverview.objects.create( + building_id=BUILDING_ID, + year=2014, + gas=100, + gas_is_user_input=True, + water=100, + water_is_user_input=True, + ) + bills_overview = fetch_data.get_bills_overview(BUILDING_ID) + expected_result = [ + { + 'year': 2014, + 'electricity': None, + 'electricity_is_user_input': False, + 'gas': 100, + 'gas_is_user_input': True, + 'oil': None, + 'oil_is_user_input': False, + 'water': 100, + 'water_is_user_input': True, + } + ] + self.assertEqual(bills_overview, expected_result) + + +class TestGetCustomerPreference(TestCase): + """Test the get_customer_preference function.""" + + def test_get_customer_preference_1(self): + """Test when no customer preference data is stored.""" + customer_preference = fetch_data.get_customer_preference(BUILDING_ID) + expected_result = {} + self.assertEqual(customer_preference, expected_result) + + def test_get_customer_preference_2(self): + """Test when there is customer preference data stored.""" + CustomerPreference.objects.create( + building_id=BUILDING_ID, + downpayment=100, + expected_payback=10, + expected_net_noi_dscr=1.15, + ) + customer_preference = fetch_data.get_customer_preference(BUILDING_ID) + expected_result = { + 'downpayment': 100, + 'expected_payback': 10, + 'expected_net_noi_dscr': 1.15, + } + self.assertEqual(customer_preference, expected_result) -- GitLab From 33d6610ed0777b896f6d99e9c45b8a0435bcaba0 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Tue, 13 Jun 2017 17:07:56 -0400 Subject: [PATCH 02/16] Remove the eslint.json file. --- .eslintrc.json | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index dbca14d..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "airbnb", - "plugins": [ - "jsx-a11y", - "import" - ], - "rules": { - }, - "env": { - "browser": true - } -} -- GitLab From 79c6a326f2423cb61c26ecdb6b159eb2210a0126 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Fri, 16 Jun 2017 10:43:42 -0400 Subject: [PATCH 03/16] Remove comments from the settings file. --- blocnote/settings.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/blocnote/settings.py b/blocnote/settings.py index 91909c0..effe354 100644 --- a/blocnote/settings.py +++ b/blocnote/settings.py @@ -79,10 +79,12 @@ TEMPLATES = [ # Use nose to run all tests TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' -# Tell nose to measure coverage on the 'foo' and 'bar' apps +# Tell nose to measure coverage on the different apps with tests and the lib directory. NOSE_ARGS = [ '--with-coverage', - '--cover-package=blocnote.apps.budgetSimulator,blocnote.apps.financialInputs', + '--cover-package=blocnote.apps.budgetSimulator', + '--cover-package=blocnote.apps.financialInputs', + '--cover-package=blocnote.lib', ] WSGI_APPLICATION = 'blocnote.wsgi.application' -- GitLab From 426d102fb2d89115a180bb7dcc4c872688b252a2 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Fri, 16 Jun 2017 10:44:45 -0400 Subject: [PATCH 04/16] Add get_building_data to fetch_data. Change some functions to return values in appropriate formats. --- blocnote/lib/fetch_data.py | 182 +++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 90 deletions(-) diff --git a/blocnote/lib/fetch_data.py b/blocnote/lib/fetch_data.py index 9d232a0..1af69a9 100644 --- a/blocnote/lib/fetch_data.py +++ b/blocnote/lib/fetch_data.py @@ -1,6 +1,30 @@ """This file contains functions that fetch all data from a model in financialInputs.""" -from blocnote.apps.financialInputs.models import Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference -from blocnote.apps.financialInputs.models import Liabilities, CashBalance, IncomeStatement, LoanOptions, GrowthRate +from blocnote.apps.financialInputs.models import ( + Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference, Liabilities, CashBalance, IncomeStatement, + LoanOptions, GrowthRate +) +from django.db import connections + + +def get_building_data(building_id): + """Return building data given it's id. + + Connect to the building database and retrieves the building data. + + Args: + building_id: id of the building_id. + + Returns: + building: A dictionary containing all the relevant building data. + """ + cursor = connections['building_db'].cursor() + command = 'SELECT * FROM public.get_building(in_building_id := {})' + cursor.execute(command.format(building_id)) + columns = [col[0] for col in cursor.description] + row = cursor.fetchone() + building = dict(zip(columns, row)) + return building + def get_fund(): """Fetch all the funds in the database. If none exist, return empty list. @@ -25,10 +49,10 @@ def get_financing_overview(building_id): fields = [ 'fund_id', 'pro_forma_start_date', - 'pro_forma_duration', 'analysis_date', 'anticipated_construction_start_date', 'anticipated_commissioning_date', + 'pro_forma_duration', 'anticipated_construction_period', ] financing_overview = {} @@ -36,6 +60,10 @@ def get_financing_overview(building_id): if financing_overview_object: for field in fields: financing_overview[field] = financing_overview_object[0].__dict__[field] + financing_overview['pro_forma_duration'] = int(financing_overview['pro_forma_duration']) + financing_overview['anticipated_construction_period'] = float( + financing_overview['anticipated_construction_period'] + ) return financing_overview @@ -46,48 +74,45 @@ def get_utility_bill(building_id, utility_type): utility_bills (List of dictionaries): List of dictionaries where each dictionary contains all the relevant data of a utility bill for a building. """ - fields = [ - 'date_from', - 'date_to', - 'usage', - 'charge', - ] - utility_bills = [] utility_bills_objects = Bills.objects.filter(building_id=building_id, utility_type=utility_type) - for utility_bills_object in utility_bills_objects: - bill = {} - for field in fields: - bill[field] = utility_bills_object.__dict__[field] - utility_bills.append(bill) - return utility_bills - - -def get_bills_overview(building_id): + if utility_bills_objects: + utility_bills = {} + utility_bills = { + 'utility_type': utility_type, + 'date_from': [], + 'date_to': [], + 'usage': [], + 'charge': [], + } + for utility_bills_object in utility_bills_objects: + utility_bills['date_from'].append(utility_bills_object.date_from) + utility_bills['date_to'].append(utility_bills_object.date_to) + utility_bills['usage'].append(float(utility_bills_object.usage)) + utility_bills['charge'].append(float(utility_bills_object.charge)) + return utility_bills + + +def get_annual_bills(building_id): """Fetch the bills overview for a building. If not available, return empty list. Returns: bills_overview (list of dictionaries): Each dictionary contains the bills overview information for a year. """ - fields = [ - 'year', - 'electricity', - 'electricity_is_user_input', - 'gas', - 'gas_is_user_input', - 'oil', - 'oil_is_user_input', - 'water', - 'water_is_user_input', - ] - bills_overview = [] + utilities = ['electricity', 'gas', 'oil', 'water'] + annual_bills = {} bills_overview_objects = BillsOverview.objects.filter(building_id=building_id) for bills_overview_object in bills_overview_objects: - bill_overview = {} - for field in fields: - bill_overview[field] = bills_overview_object.__dict__[field] - bills_overview.append(bill_overview) - return bills_overview - + for utility in utilities: + if bills_overview_object.__dict__[utility + '_is_user_input']: + if utility not in annual_bills: + annual_bills[utility] = {} + charge = bills_overview_object.__dict__[utility] + value = float(charge) if charge else None + annual_bills[utility][int(bills_overview_object.__dict__['year'])] = value + for utility in utilities: + if utility not in annual_bills: + annual_bills[utility] = None + return annual_bills def get_customer_preference(building_id): """Fetch the customer preference data for a building. If not present, return empty. @@ -95,16 +120,14 @@ def get_customer_preference(building_id): Returns: customer_preference (dictionary): Dictionary with the relevant data. """ - fields = [ - 'downpayment', - 'expected_payback', - 'expected_net_noi_dscr', - ] customer_preference = {} customer_preference_object = CustomerPreference.objects.filter(building_id=building_id) if customer_preference_object: - for field in fields: - customer_preference[field] = customer_preference_object[0].__dict__[field] + customer_preference = { + 'downpayment_max': float(customer_preference_object[0].downpayment), + 'expected_payback': int(customer_preference_object[0].expected_payback), + 'cust_saving_dscr': float(customer_preference_object[0].expected_net_noi_dscr), + } return customer_preference def get_liabilities(building_id): @@ -114,19 +137,15 @@ def get_liabilities(building_id): liabilities (list of dictionaries): Each dictionary will contain the relevant fields of liabilities for a building. """ - fields = [ - 'input_date', - 'lender', - 'monthly_service', - 'remaining_term', - ] - liabilities = [] + liabilities = {} liabilities_objects = Liabilities.objects.filter(building_id=building_id) - for liabilities_object in liabilities_objects: - liability = {} - for field in fields: - liability[field] = liabilities_object.__dict__[field] - liabilities.append(liability) + for index, liabilities_object in enumerate(liabilities_objects, 1): + key = 'debt' + str(index) + liability = float(liabilities_object.monthly_service) + lender = liabilities_object.lender + remaining_term = int(liabilities_object.remaining_term) + input_date = liabilities_object.input_date + liabilities[key] = (liability, lender, remaining_term, input_date) return liabilities @@ -136,18 +155,11 @@ def get_cash_balance(building_id): Returns: cash_balances (List of dictionaries): Each dictionary contains the cash balance information for a building. """ - fields = [ - 'statement_date', - 'is_from_balance_sheet', - 'balance_amount', - ] - cash_balances = [] + cash_balances = {} cash_balance_objects = CashBalance.objects.filter(building_id=building_id) for cash_balance_object in cash_balance_objects: - cash_balance = {} - for field in fields: - cash_balance[field] = cash_balance_object.__dict__[field] - cash_balances.append(cash_balance) + cash_balances[cash_balance_object.statement_date] = (float(cash_balance_object.balance_amount), + cash_balance_object.is_from_balance_sheet) return cash_balances @@ -158,19 +170,15 @@ def get_income_statement(building_id): income_statements (dictionary of dictionaries): Dictionary with key as value and all relevant information from that year as it's value. """ - fields = [ - 'revenue', - 'utility_expense', - 'other_utility_expense', - 'non_utility_operating_expense', - ] income_statements = {} income_statement_objects = IncomeStatement.objects.filter(building_id=building_id) for income_statement_object in income_statement_objects: - income_statement = {} - for field in fields: - income_statement[field] = income_statement_object.__dict__[field] - income_statements[income_statement_object.__dict__['year']] = income_statement + income_statement = { + 'revenue': float(income_statement_object.revenue), + 'utility_expense': float(income_statement_object.utility_expense), + 'non_utility_expense': float(income_statement_object.non_utility_operating_expense) + } + income_statements[int(income_statement_object.year)] = income_statement return income_statements @@ -181,19 +189,13 @@ def get_loan_options(building_id): loan_options (list of dictionaries): Each dictionary contains the loan options information. The list is for a building. """ - fields = [ - 'lender_id', - 'interest_rate', - 'duration', - 'max_loan_amount', - ] - loan_options = [] loan_options_objects = LoanOptions.objects.filter(building_id=building_id) - for loan_options_object in loan_options_objects: - loan_option = {} - for field in fields: - loan_option[field] = loan_options_object.__dict__[field] - loan_options.append(loan_option) + loan_options = [{ + 'institute': loan_options_object.lender.name, + 'max_amount': float(loan_options_object.max_loan_amount), + 'interest': float(loan_options_object.interest_rate)/100, + 'duration': int(loan_options_object.duration), + } for loan_options_object in loan_options_objects] return loan_options @@ -206,5 +208,5 @@ def get_growth_rate(building_id): growth_rate_object = GrowthRate.objects.filter(building_id=building_id) growth_rate = None if growth_rate_object: - growth_rate = growth_rate_object[0].__dict__['growth_rate'] - return growth_rate + growth_rate = float(growth_rate_object[0].growth_rate) + return growth_rate -- GitLab From 5591c8c1309d2d9310418825eb20cf5eacf6d4f4 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Fri, 16 Jun 2017 10:45:13 -0400 Subject: [PATCH 05/16] Modify test cases to proper output formats --- blocnote/lib/test_fetch_data.py | 332 +++++++++++++++++++++++--------- 1 file changed, 245 insertions(+), 87 deletions(-) diff --git a/blocnote/lib/test_fetch_data.py b/blocnote/lib/test_fetch_data.py index b3f403d..8b9bcdd 100644 --- a/blocnote/lib/test_fetch_data.py +++ b/blocnote/lib/test_fetch_data.py @@ -1,7 +1,9 @@ """Tests for the fetch data file.""" from datetime import date from django.test import TestCase -from blocnote.apps.financialInputs.models import Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference +from blocnote.apps.financialInputs.models import ( + Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference, Liabilities, CashBalance, IncomeStatement +) from blocnote.lib import fetch_data BUILDING_ID = 100 @@ -88,10 +90,10 @@ class TestGetUtilityBills(TestCase): """Fetch when no bills are present.""" utility_bills = self.use_function() expected_result = { - 'electricity': [], - 'gas': [], - 'oil': [], - 'water': [], + 'electricity': None, + 'gas': None, + 'oil': None, + 'water': None, } self.assertEqual(utility_bills, expected_result) @@ -107,17 +109,16 @@ class TestGetUtilityBills(TestCase): ) utility_bills = self.use_function() expected_result = { - 'electricity': [ - { - 'date_from': date(2014, 1, 1), - 'date_to': date(2015, 1, 1), - 'usage': 100, - 'charge': 1000, - }, - ], - 'gas': [], - 'oil': [], - 'water': [], + 'electricity': { + 'utility_type': 'electricity', + 'date_from': [date(2014, 1, 1)], + 'date_to': [date(2015, 1, 1)], + 'usage': [100], + 'charge': [1000], + }, + 'gas': None, + 'oil': None, + 'water': None, } self.assertEqual(utility_bills, expected_result) @@ -157,52 +158,53 @@ class TestGetUtilityBills(TestCase): ) utility_bills = self.use_function() expected_result = { - 'electricity': [ - { - 'date_from': date(2014, 1, 1), - 'date_to': date(2015, 1, 1), - 'usage': 100, - 'charge': 1000, - }, - ], - 'gas': [ - { - 'date_from': date(2014, 1, 1), - 'date_to': date(2015, 1, 1), - 'usage': 100, - 'charge': 1000, - }, - ], - 'oil': [ - { - 'date_from': date(2014, 1, 1), - 'date_to': date(2015, 1, 1), - 'usage': 100, - 'charge': 1000, - }, - ], - 'water': [ - { - 'date_from': date(2014, 1, 1), - 'date_to': date(2015, 1, 1), - 'usage': 100, - 'charge': 1000, - }, - ], + 'electricity': { + 'utility_type': 'electricity', + 'date_from': [date(2014, 1, 1)], + 'date_to': [date(2015, 1, 1)], + 'usage': [100], + 'charge': [1000], + }, + 'gas': { + 'utility_type': 'gas', + 'date_from': [date(2014, 1, 1)], + 'date_to': [date(2015, 1, 1)], + 'usage': [100], + 'charge': [1000], + }, + 'oil': { + 'utility_type': 'oil', + 'date_from': [date(2014, 1, 1)], + 'date_to': [date(2015, 1, 1)], + 'usage': [100], + 'charge': [1000], + }, + 'water': { + 'utility_type': 'water', + 'date_from': [date(2014, 1, 1)], + 'date_to': [date(2015, 1, 1)], + 'usage': [100], + 'charge': [1000], + }, } self.assertEqual(utility_bills, expected_result) -class TestGetBillsOverview(TestCase): - """Test the get_bills_overview function.""" +class TestGetAnnualBills(TestCase): + """Test the get_annual_bills function.""" - def test_get_bills_overview_1(self): + def test_get_annual_bills_1(self): """Test when no data for bills overview is stored.""" - bills_overview = fetch_data.get_bills_overview(BUILDING_ID) - expected_result = [] - self.assertEqual(bills_overview, expected_result) + annual_bills = fetch_data.get_annual_bills(BUILDING_ID) + expected_result = { + 'electricity': None, + 'gas': None, + 'oil': None, + 'water': None, + } + self.assertEqual(annual_bills, expected_result) - def test_get_bills_overview_2(self): + def test_get_annual_bills_2(self): """Test when only electricity is store manually.""" BillsOverview.objects.create( building_id=BUILDING_ID, @@ -210,21 +212,16 @@ class TestGetBillsOverview(TestCase): electricity=100, electricity_is_user_input=True, ) - bills_overview = fetch_data.get_bills_overview(BUILDING_ID) - expected_result = [ - { - 'year': 2014, - 'electricity': 100, - 'electricity_is_user_input': True, - 'gas': None, - 'gas_is_user_input': False, - 'oil': None, - 'oil_is_user_input': False, - 'water': None, - 'water_is_user_input': False, - } - ] - self.assertEqual(bills_overview, expected_result) + annual_bills = fetch_data.get_annual_bills(BUILDING_ID) + expected_result = { + 'electricity': { + 2014: 100, + }, + 'gas': None, + 'oil': None, + 'water': None, + } + self.assertEqual(annual_bills, expected_result) def test_get_bills_overview_3(self): """Test when 2 utility bills are manually input.""" @@ -236,21 +233,18 @@ class TestGetBillsOverview(TestCase): water=100, water_is_user_input=True, ) - bills_overview = fetch_data.get_bills_overview(BUILDING_ID) - expected_result = [ - { - 'year': 2014, - 'electricity': None, - 'electricity_is_user_input': False, - 'gas': 100, - 'gas_is_user_input': True, - 'oil': None, - 'oil_is_user_input': False, - 'water': 100, - 'water_is_user_input': True, - } - ] - self.assertEqual(bills_overview, expected_result) + annual_bills = fetch_data.get_annual_bills(BUILDING_ID) + expected_result = { + 'electricity': None, + 'gas': { + 2014: 100, + }, + 'oil': None, + 'water': { + 2014: 100, + }, + } + self.assertEqual(annual_bills, expected_result) class TestGetCustomerPreference(TestCase): @@ -272,8 +266,172 @@ class TestGetCustomerPreference(TestCase): ) customer_preference = fetch_data.get_customer_preference(BUILDING_ID) expected_result = { - 'downpayment': 100, + 'downpayment_max': 100, 'expected_payback': 10, - 'expected_net_noi_dscr': 1.15, + 'cust_saving_dscr': 1.15, } self.assertEqual(customer_preference, expected_result) + + +class TestGetLiabilities(TestCase): + """Test the get_liabilities function.""" + + def test_get_liabilities_1(self): + """Test the function when no data is stored.""" + liabilities = fetch_data.get_liabilities(BUILDING_ID) + expected_result = {} + self.assertEqual(liabilities, expected_result) + + def test_get_liabilities_2(self): + """Test when one record is stored.""" + Liabilities.objects.create( + building_id=BUILDING_ID, + input_date='2015-03-03', + lender='Adarsh', + monthly_service=200, + remaining_term=120, + ) + liabilities = fetch_data.get_liabilities(BUILDING_ID) + expected_result = { + 'debt1': (200, 'Adarsh', 120, date(2015, 3, 3)), + } + self.assertEqual(liabilities, expected_result) + + def test_get_liabilities_3(self): + """Test when 3 liabilities records are present.""" + Liabilities.objects.create( + building_id=BUILDING_ID, + input_date='2014-03-03', + lender='Adarsh', + monthly_service=200, + remaining_term=120, + ) + Liabilities.objects.create( + building_id=BUILDING_ID, + input_date='2015-03-03', + lender='Joe', + monthly_service=100, + remaining_term=100, + ) + Liabilities.objects.create( + building_id=BUILDING_ID, + input_date='2016-03-03', + lender='Conrad', + monthly_service=300, + remaining_term=90, + ) + liabilities = fetch_data.get_liabilities(BUILDING_ID) + expected_result = { + 'debt1': (200, 'Adarsh', 120, date(2014, 3, 3)), + 'debt2': (100, 'Joe', 100, date(2015, 3, 3)), + 'debt3': (300, 'Conrad', 90, date(2016, 3, 3)), + } + self.assertEqual(liabilities, expected_result) + + +class TestGetCashBalance(TestCase): + """Test the get_cash_balance function.""" + + def test_get_cash_balance_1(self): + """Test the function when no data is saved in the database.""" + cash_balance = fetch_data.get_cash_balance(BUILDING_ID) + expected_result = {} + self.assertEqual(cash_balance, expected_result) + + def test_get_cash_balance_2(self): + """Test when one record is present in the database.""" + CashBalance.objects.create( + building_id=BUILDING_ID, + statement_date='2016-12-01', + is_from_balance_sheet=True, + balance_amount=25000, + ) + cash_balance = fetch_data.get_cash_balance(BUILDING_ID) + expected_result = { + date(2016, 12, 1): (25000, True), + } + self.assertEqual(cash_balance, expected_result) + + def test_get_cash_balance_3(self): + """Test when there are 3 records saved in the database.""" + CashBalance.objects.create( + building_id=BUILDING_ID, + statement_date='2014-12-01', + is_from_balance_sheet=True, + balance_amount=20000, + ) + CashBalance.objects.create( + building_id=BUILDING_ID, + statement_date='2015-12-01', + is_from_balance_sheet=False, + balance_amount=23000, + ) + CashBalance.objects.create( + building_id=BUILDING_ID, + statement_date='2016-12-01', + is_from_balance_sheet=True, + balance_amount=25000, + ) + cash_balance = fetch_data.get_cash_balance(BUILDING_ID) + expected_result = { + date(2014, 12, 1): (20000, True), + date(2015, 12, 1): (23000, False), + date(2016, 12, 1): (25000, True), + } + self.assertEqual(cash_balance, expected_result) + + +class TestGetIncomeStatement(TestCase): + """Test the get_income_statement function.""" + maxDiff = None + def test_get_income_statement_1(self): + """Test when there are no records stored in the database.""" + income_statments = fetch_data.get_income_statement(BUILDING_ID) + expected_result = {} + self.assertEqual(income_statments, expected_result) + + def test_get_income_statement_2(self): + """Test when income statements present in the database.""" + IncomeStatement.objects.create( + building_id=BUILDING_ID, + year=2014, + revenue=200000, + utility_expense=80000, + other_utility_expense=50000, + non_utility_operating_expense=2000, + ) + IncomeStatement.objects.create( + building_id=BUILDING_ID, + year=2015, + revenue=205000, + utility_expense=83000, + other_utility_expense=54000, + non_utility_operating_expense=3000, + ) + IncomeStatement.objects.create( + building_id=BUILDING_ID, + year=2016, + revenue=210000, + utility_expense=90000, + other_utility_expense=60000, + non_utility_operating_expense=5000, + ) + income_statments = fetch_data.get_income_statement(BUILDING_ID) + expected_result = { + 2014: { + 'revenue': 200000.0, + 'utility_expense': 80000.0, + 'non_utility_expense': 2000.0, + }, + 2015: { + 'revenue': 205000.0, + 'utility_expense': 83000.0, + 'non_utility_expense': 3000.0, + }, + 2016: { + 'revenue': 210000.0, + 'utility_expense': 90000.0, + 'non_utility_expense': 5000.0, + } + } + self.assertEqual(income_statments, expected_result) -- GitLab From 9db2460a3c2815927a876c6592cc79a616310bc5 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Fri, 16 Jun 2017 10:45:53 -0400 Subject: [PATCH 06/16] Remove all the get functions and use the lib directory get functions to achieve the same. --- blocnote/apps/budgetSimulator/views.py | 256 ++++--------------------- 1 file changed, 38 insertions(+), 218 deletions(-) diff --git a/blocnote/apps/budgetSimulator/views.py b/blocnote/apps/budgetSimulator/views.py index 8d1c432..7a65dd2 100644 --- a/blocnote/apps/budgetSimulator/views.py +++ b/blocnote/apps/budgetSimulator/views.py @@ -5,210 +5,10 @@ from django.http import JsonResponse from bpfin.back_end_call.back_end_budget import budget_simulation -from blocnote.apps.financialInputs.models import FinancingOverview, Bills, BillsOverview, IncomeStatement, GrowthRate -from blocnote.apps.financialInputs.models import Liabilities, CashBalance, CustomerPreference, LoanOptions - - -def get_analysis_commissioning_dates(building_id): - """Fetch the proforma start date, proforma duration and commissioning date.""" - financing_overview_objects = FinancingOverview.objects.filter(building_id=building_id) - if financing_overview_objects: - analysis_date = { - 'proforma_start': financing_overview_objects[0].pro_forma_start_date, - 'proforma_duration': int(financing_overview_objects[0].pro_forma_duration), - } - return analysis_date, financing_overview_objects[0].anticipated_commissioning_date - else: - raise AttributeError("Could not find commissioning date for given building id.") - - -def get_customer_preference(building_id): - """Fetch the customer preference from the database for this building id.""" - customer_preference_objs = CustomerPreference.objects.filter(building_id=building_id) - if customer_preference_objs: - customer_preference_obj = customer_preference_objs[0] - customer_preference = { - 'downpayment_max': float(customer_preference_obj.downpayment), - 'expected_payback': int(customer_preference_obj.expected_payback), - 'cust_saving_dscr': float(customer_preference_obj.expected_net_noi_dscr) - } - return customer_preference - else: - raise AttributeError("Could not find customer preference data for this building id.") - - -def get_raw_bill(building_id): - """Fetch the utility bills for a given building id.""" - utilities = [ - 'electricity', - 'gas', - 'oil', - 'water', - ] - raw_bill_table = {} - for utility_type in utilities: - bills_object = Bills.objects.filter( - building_id=building_id, - utility_type=utility_type, - ) - - if bills_object: - raw_bill = {} - raw_bill['utility_type'] = utility_type - raw_bill['date_from'] = [] - raw_bill['date_to'] = [] - raw_bill['charge'] = [] - raw_bill['usage'] = [] - - for bill in bills_object: - raw_bill['date_from'].append(bill.date_from) - raw_bill['date_to'].append(bill.date_to) - raw_bill['usage'].append(float(bill.usage)) - raw_bill['charge'].append(float(bill.charge)) - raw_bill_table[utility_type] = raw_bill - return raw_bill_table - - -def get_annual_bills(building_id): - """Fetch manually input annual values from the database.""" - annual_bills = {} - utilities = ['electricity', 'gas', 'oil', 'water'] - bills_overview_objs = BillsOverview.objects.filter(building_id=building_id) - for obj in bills_overview_objs: - for utility in utilities: - if obj.__dict__[utility+'_is_user_input']: - if utility not in annual_bills: - annual_bills[utility] = {} - value = float(obj.__dict__[utility]) if obj.__dict__[utility] else None - annual_bills[utility][obj.__dict__['year']] = value - for utility in utilities: - if utility not in annual_bills: - annual_bills[utility] = None - return annual_bills - - -def get_raw_income_statement(building_id): - """Fetch income statement data from database. - - Args: - building_id: id of the building. - - Returns: - raw_income_statement: Dictionary with key as year and value a dictionary with revenue, utility expense and - non-utility expense data. - """ - income_statement_objs = IncomeStatement.objects.filter(building_id=building_id) - raw_income_statment = {} - if income_statement_objs: - for obj in income_statement_objs: - record = {} - record['revenue'] = float(obj.revenue) - record['utility_expense'] = float(obj.utility_expense) - record['non_utility_expense'] = float(obj.non_utility_operating_expense) - raw_income_statment[int(obj.year)] = record - return raw_income_statment - else: - raise AttributeError("Could not find income statement values for this building id.") - - -def get_growth_rate(building_id): - """Fetch growth rate from the database. - - Args: - building_id: id of the building. - - Returns: - growth_rate: Growth rate stored for this building. - """ - growth_rate_obj = GrowthRate.objects.filter(building_id=building_id) - if growth_rate_obj: - return float(growth_rate_obj[0].growth_rate) - else: - raise AttributeError("Could not find growth rate for this building id.") - - -def get_liability(building_id): - """Obtain liability information. - - Fetch the records containing the liability information from the database. - - Args: - building_id: id of the building. - analysis_date: Dictionary containing: - proforma_start with value Start of projection. - proforma_duration with value Total duration of projection. - - Returns: - liability_dictionary: Dictionary containing: - keys: - debt_id- id of the debt. - value- dictionary containing: - keys: - 1. lender with value as lender for the liability/mortgage. - 2. date with value when the mortgage/liabiity statment was - requested. - 3. remaining_term with value the number of months remaining - in the mortgage/liability. - 4. liability with value as monthly service for the mortgage. - """ - liability_objs = Liabilities.objects.filter(building_id=building_id) - liabilities_input = {} - if liability_objs: - for index, obj in enumerate(liability_objs, 1): - key = 'debt'+str(index) - liability = float(obj.monthly_service) - lender = obj.lender - remaining_term = int(obj.remaining_term) - input_date = obj.input_date - liabilities_input[key] = (liability, lender, remaining_term, input_date) - return liabilities_input - - -def get_cash_balance(building_id): - """Fetch Cash Balance data from Database. - - Args: - building_id: id of the building. - - Returns: - cash_balance: Dictionary that contains: - keys: - statement_date- date of the statement(Cash balance or bank statement). - value- Tuple containing balance amount and a boolean saying if it is - from balance sheet or not. - """ - cash_balance_objs = CashBalance.objects.filter(building_id=building_id) - if cash_balance_objs: - cash_balance_input = {} - for obj in cash_balance_objs: - cash_balance_input[obj.statement_date] = (float(obj.balance_amount), obj.is_from_balance_sheet) - return cash_balance_input - else: - raise AttributeError("Could not find cash balance for this building id.") - - -def get_loan_input(building_id): - """Fetch loan options from database. - - Args: - building_id: id of the building. - - Returns: - loan_options: List of records. Each record is a dicitonary containing instititue, max loan amount, rate of - interest and duration for loan. - """ - loan_options_objs = LoanOptions.objects.filter(building_id=building_id) - if loan_options_objs: - loan_options = [{ - 'institute': obj.lender.name, - 'max_amount': float(obj.max_loan_amount), - 'interest': float(obj.interest_rate)/100, - 'duration': int(obj.duration), - } for obj in loan_options_objs] - return loan_options - else: - raise AttributeError("Could not find Loan Options for this building id.") - +from blocnote.lib.fetch_data import ( + get_financing_overview, get_customer_preference, get_utility_bill, get_annual_bills, get_income_statement, + get_growth_rate, get_liabilities, get_cash_balance, get_loan_options +) class Index(View): """Fetch the inputs, calculate the budget and display the graph and tables.""" @@ -238,19 +38,40 @@ class Budget(View): } # Fetch values from the database needed to perform the budget simulation. - try: - analysis_date, commissioning_date = get_analysis_commissioning_dates(building_id) - customer_preference = get_customer_preference(building_id) - raw_bills = get_raw_bill(building_id) - annual_bills = get_annual_bills(building_id) - raw_income_statement = get_raw_income_statement(building_id) - growth_rate = get_growth_rate(building_id) - liabilities = get_liability(building_id) - cash_balance = get_cash_balance(building_id) - loan_options = get_loan_input(building_id) - except Exception as err: - error = err.args[0] - return JsonResponse({'error': error}, status=400) + financing_overview = get_financing_overview(building_id) + analysis_date = { + 'proforma_start': financing_overview['pro_forma_start_date'], + 'proforma_duration': int(financing_overview['pro_forma_duration']), + } + commissioning_date = financing_overview['anticipated_commissioning_date'] + customer_preference = get_customer_preference(building_id) + utilities = ['electricity', 'gas', 'oil', 'water'] + raw_bills = {} + for utility in utilities: + bill = get_utility_bill(building_id, utility) + if bill: + raw_bills[utility] = bill + annual_bills = get_annual_bills(building_id) + raw_income_statement = get_income_statement(building_id) + growth_rate = get_growth_rate(building_id) + liabilities = get_liabilities(building_id) + cash_balance = get_cash_balance(building_id) + loan_options = get_loan_options(building_id) + if not analysis_date: + err_var = 'Proforma date and duration ' + if not commissioning_date: + err_var = 'Anticipated commissioning date ' + if not customer_preference: + err_var = 'Customer preference' + if not raw_income_statement: + err_var = 'Income statements ' + if not growth_rate: + err_var = 'Growth rate ' + if not cash_balance: + err_var = 'Cash balance ' + if not loan_options: + err_var = 'Loan options ' + return JsonResponse({'error': err_var + 'not filled.'}, status=400) try: result = budget_simulation( analysis_date, @@ -265,7 +86,6 @@ class Budget(View): cash_balance, loan_options ) - # Go over all the tables in result. for table in result: # Go over each row in the table. -- GitLab From c91e375e30c59ee916e1e4398cc37338a02c450f Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Fri, 16 Jun 2017 10:46:33 -0400 Subject: [PATCH 07/16] Remove all the get functions from preliminary finance view and use the lib directory functions to do the same. --- blocnote/apps/preliminaryFinance/views.py | 359 +++------------------- 1 file changed, 38 insertions(+), 321 deletions(-) diff --git a/blocnote/apps/preliminaryFinance/views.py b/blocnote/apps/preliminaryFinance/views.py index 2c57397..ea789de 100644 --- a/blocnote/apps/preliminaryFinance/views.py +++ b/blocnote/apps/preliminaryFinance/views.py @@ -4,34 +4,29 @@ import json from django.shortcuts import render from django.views import View from django.http import JsonResponse -from django.db import connections -from blocnote.apps.financialInputs.models import GrowthRate, IncomeStatement, BillsOverview, FinancingOverview -from blocnote.apps.financialInputs.models import CashBalance, Liabilities, LoanOptions, CustomerPreference, Bills +from blocnote.lib.fetch_data import ( + get_liabilities, get_cash_balance, get_financing_overview, get_utility_bill, get_annual_bills, get_income_statement, + get_loan_options, get_growth_rate, get_customer_preference, get_building_data +) from bpfin.back_end_call.back_end_prelim import prelim_scenario +def get_savings_percent(savings): + """Format savings to be in decimal value for each utility. -class Index(View): - """Render the view when you hit preliminary-finance endpoint.""" - - def get_building_data(self, building_id): - """Return building data given it's id. - - Connect to the building database and retrieves the building data. + Args: + savings: List of records. Each record has a utility type and savings as keys. - Args: - building_id: id of the building_id. + Returns: + savings_percent: Dictionary with key as utility_type and value as savings as a decimal. + """ + savings_percent = {} + for record in savings: + savings_percent[str.lower(record['utility_type'])] = float(record['savings_estimate'])/100 + return savings_percent - Returns: - building: A dictionary containing all the relevant building data. - """ - cursor = connections['building_db'].cursor() - command = 'SELECT * FROM public.get_building(in_building_id := {})' - cursor.execute(command.format(building_id)) - columns = [col[0] for col in cursor.description] - row = cursor.fetchone() - building = dict(zip(columns, row)) - return building +class Index(View): + """Render the view when you hit preliminary-finance endpoint.""" def get(self, request, building_id): """Implement HTTP GET request. @@ -45,7 +40,7 @@ class Index(View): Returns: render: Renders index.html page with building data. """ - building = self.get_building_data(building_id) + building = get_building_data(building_id) context = {'building': building} return render(request, 'preliminaryFinance/index.html', context=context) @@ -57,293 +52,6 @@ class Scenarios(View): to bpfin to calculate all the outputs to be displayed and pass the information to the frontend. """ - def get_liability(self, building_id): - """Obtain liability information. - - Fetch the records containing the liability information from the database. - - Args: - building_id: id of the building. - analysis_date: Dictionary containing: - proforma_start with value Start of projection. - proforma_duration with value Total duration of projection. - - Returns: - liability_dictionary: Dictionary containing: - keys: - debt_id- id of the debt. - value- dictionary containing: - keys: - 1. lender with value as lender for the liability/mortgage. - 2. date with value when the mortgage/liabiity statment was - requested. - 3. remaining_term with value the number of months remaining - in the mortgage/liability. - 4. liability with value as monthly service for the mortgage. - """ - liability_objs = Liabilities.objects.filter(building_id=building_id) - liabilities_input = {} - index = 1 - for obj in liability_objs: - key = 'debt'+str(index) - index += 1 - liability = float(obj.monthly_service) - lender = obj.lender - remaining_term = int(obj.remaining_term) - input_date = obj.input_date - liabilities_input[key] = (liability, lender, remaining_term, input_date) - return liabilities_input - - def get_cash_balance(self, building_id): - """Fetch Cash Balance data from Database. - - Args: - building_id: id of the building. - - Returns: - cash_balance: Dictionary that contains: - keys: - statement_date- date of the statement(Cash balance or bank statement). - value- Tuple containing balance amount and a boolean saying if it is - from balance sheet or not. - """ - cash_balance_objs = CashBalance.objects.filter(building_id=building_id) - cash_balance_input = {} - for obj in cash_balance_objs: - cash_balance_input[obj.statement_date] = (float(obj.balance_amount), obj.is_from_balance_sheet) - return cash_balance_input - - def get_analysis_date(self, building_id): - """Fetch analysis date for the building. - - Args: - building_id: id of the building. - - Returns: - analysis_date: Dictionary: - keys: - 1. proforma_start- Start date for projection. - 2. proforma_duration- Duration of the projection. - """ - financing_overview_obj = FinancingOverview.objects.get(building_id=building_id) - analysis_date = { - 'proforma_start': financing_overview_obj.pro_forma_start_date, - 'proforma_duration': int(financing_overview_obj.pro_forma_duration), - } - return analysis_date - - def get_bill_overview(self, building_id): - """Fetch bill overview data from database. - - Args: - building_id: id of the building_id. - - Returns: - bill_overview: Dictionary containing utility as key and value as a dictionary with key as year and value - as annual charge for that utility. - """ - bills_overview_objs = BillsOverview.objects.filter(building_id=building_id) - bill_overview = {} - electricity = {} - gas = {} - oil = {} - water = {} - if bills_overview_objs: - for obj in bills_overview_objs: - electricity[obj.year] = float(obj.electricity) if obj.electricity else None - gas[obj.year] = float(obj.gas) if obj.gas else None - oil[obj.year] = float(obj.oil) if obj.oil else None - water[obj.year] = float(obj.water) if obj.water else None - obj = bills_overview_objs[0] - bill_overview = { - 'electricity': [electricity, not obj.electricity_is_user_input], - 'gas': [gas, not obj.gas_is_user_input], - 'oil': [oil, not obj.oil_is_user_input], - 'water': [water, not obj.water_is_user_input], - } - return bill_overview - - def get_raw_income_statement(self, building_id): - """Fetch income statement data from database. - - Args: - building_id: id of the building. - - Returns: - raw_income_statement: Dictionary with key as year and value a dictionary with revenue, utility expense and - non-utility expense data. - """ - income_statement_objs = IncomeStatement.objects.filter(building_id=building_id) - raw_income_statment = {} - for obj in income_statement_objs: - record = {} - record['revenue'] = float(obj.revenue) - record['utility_expense'] = float(obj.utility_expense) - record['non_utility_expense'] = float(obj.non_utility_operating_expense) - raw_income_statment[int(obj.year)] = record - return raw_income_statment - - def get_growth_rate(self, building_id): - """Fetch growth rate from the database. - - Args: - building_id: id of the building. - - Returns: - growth_rate: Growth rate stored for this building. - """ - growth_rate_obj = GrowthRate.objects.filter(building_id=building_id) - return float(growth_rate_obj[0].growth_rate) - - def get_loan_input(self, building_id): - """Fetch loan options from database. - - Args: - building_id: id of the building. - - Returns: - loan_options: List of records. Each record is a dicitonary containing instititue, max loan amount, rate of - interest and duration for loan. - """ - loan_options_objs = LoanOptions.objects.filter(building_id=building_id) - loan_options = [] - for obj in loan_options_objs: - record = { - 'institute': obj.lender.name, - 'max_amount': float(obj.max_loan_amount), - 'interest': float(obj.interest_rate)/100, - 'duration': int(obj.duration), - } - loan_options.append(record) - return loan_options - - def get_commission_date(self, building_id): - """Fetch the commissioning date. - - Args: - building_id: id of the building. - - Returns: - Anticipated commissioning date. - """ - return FinancingOverview.objects.filter( - building_id=building_id - )[0].anticipated_commissioning_date - - def get_savings_percent(self, savings): - """Format savings to be in decimal value for each utility. - - Args: - savings: List of records. Each record has a utility type and savings as keys. - - Returns: - savings_percent: Dictionary with key as utility_type and value as savings as a decimal. - """ - savings_percent = {} - for record in savings: - savings_percent[str.lower(record['utility_type'])] = float(record['savings_estimate'])/100 - return savings_percent - - def get_customer_preference(self, building_id): - """Fetch customer preference data from database. - - Args: - building_id: id of the building. - - Returns: - customer_preference: Dictionary with downpayment, expected payback and customer savings DCSR. - """ - customer_preference_obj = CustomerPreference.objects.filter(building_id=building_id)[0] - customer_preference = { - 'downpayment_max': float(customer_preference_obj.downpayment), - 'expected_payback': int(customer_preference_obj.expected_payback), - 'cust_saving_dscr': float(customer_preference_obj.expected_net_noi_dscr) - } - return customer_preference - - def get_prior_month_bill(self, building_id): - """Get the prior bills on a monthly basis than annual. - - Args: - building_id: id of the building. - analysis_date: Dictionary with proforma_start and proforma_duration. - - Returns: - prior_month_bill: Dictionary with utility type as key value as monthly charge. - """ - utilities = [ - 'electricity', - 'gas', - 'oil', - 'water', - ] - raw_bill_table = {} - for utility_type in utilities: - bills_object = Bills.objects.filter( - building_id=building_id, - utility_type=utility_type, - ) - - if bills_object: - raw_bill = {} - raw_bill['utility_type'] = utility_type - raw_bill['date_from'] = [] - raw_bill['date_to'] = [] - raw_bill['charge'] = [] - raw_bill['usage'] = [] - - for bill in bills_object: - raw_bill['date_from'].append(bill.date_from) - raw_bill['date_to'].append(bill.date_to) - raw_bill['usage'].append(float(bill.usage)) - raw_bill['charge'].append(float(bill.charge)) - raw_bill_table[utility_type] = raw_bill - return raw_bill_table - - def get_annual_bills(self, building_id): - """Fetch manually input annual values from the database.""" - annual_bills = {} - - electricity_objs = BillsOverview.objects.filter( - building_id=building_id, - electricity_is_user_input=True, - ) - - gas_objs = BillsOverview.objects.filter( - building_id=building_id, - gas_is_user_input=True, - ) - - oil_objs = BillsOverview.objects.filter( - building_id=building_id, - oil_is_user_input=True, - ) - - water_objs = BillsOverview.objects.filter( - building_id=building_id, - water_is_user_input=True, - ) - - if electricity_objs: - annual_bills['electricity'] = {} - for obj in electricity_objs: - annual_bills['electricity'][obj.year] = float(obj.electricity) if obj.electricity else None - - if gas_objs: - annual_bills['gas'] = {} - for obj in gas_objs: - annual_bills['gas'][obj.year] = float(obj.gas) if obj.gas else None - - if oil_objs: - annual_bills['oil'] = {} - for obj in oil_objs: - annual_bills['oil'][obj.year] = float(obj.oil) if obj.oil else None - - if water_objs: - annual_bills['water'] = {} - for obj in water_objs: - annual_bills['water'][obj.year] = float(obj.water) if obj.water else None - return annual_bills def put(self, request, building_id): """Handle HTTP PUT request. @@ -357,16 +65,25 @@ class Scenarios(View): put = json.loads(request.body.decode()) # Fetch all relevant data from database. - growth_rate = self.get_growth_rate(building_id) - raw_income_statement = self.get_raw_income_statement(building_id) - analysis_date = self.get_analysis_date(building_id) - cash_balance_input_dict = self.get_cash_balance(building_id) - liability_dictionary = self.get_liability(building_id) - loan_options = self.get_loan_input(building_id) - commission_date = self.get_commission_date(building_id) - customer_preference = self.get_customer_preference(building_id) - prior_month_bill = self.get_prior_month_bill(building_id) - annual_bills = self.get_annual_bills(building_id) + financing_overview = get_financing_overview(building_id) + analysis_date = { + 'proforma_start': financing_overview['pro_forma_start_date'], + 'proforma_duration': financing_overview['pro_forma_duration'] + } + commission_date = financing_overview['anticipated_commissioning_date'] + utilities = ['electricity', 'gas', 'oil', 'water'] + prior_month_bill = {} + for utility in utilities: + bill = get_utility_bill(building_id, utility) + if bill: + prior_month_bill[utility] = bill + growth_rate = get_growth_rate(building_id) + raw_income_statement = get_income_statement(building_id) + cash_balance_input_dict = get_cash_balance(building_id) + liability_dictionary = get_liabilities(building_id) + loan_options = get_loan_options(building_id) + customer_preference = get_customer_preference(building_id) + annual_bills = get_annual_bills(building_id) # Extract data from the PUT request body. # Calculate the total cost of items. @@ -375,7 +92,7 @@ class Scenarios(View): for record in cost_list: costs.append(float(record['cost'])) total_cost = sum(costs) - savings_percent = self.get_savings_percent(put['savings']) + savings_percent = get_savings_percent(put['savings']) # The following 2 variables are hard coded for now. They will be updated at a later point. full_saving_dict = { -- GitLab From ea3f17762e1053ec99cc58b920db15d84111fe01 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Fri, 16 Jun 2017 11:23:54 -0400 Subject: [PATCH 08/16] Remove get_building_data function from old_views and use one in lib. --- blocnote/apps/financialInputs/old_views.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/blocnote/apps/financialInputs/old_views.py b/blocnote/apps/financialInputs/old_views.py index c2a86ae..a3ebd4d 100644 --- a/blocnote/apps/financialInputs/old_views.py +++ b/blocnote/apps/financialInputs/old_views.py @@ -10,6 +10,7 @@ from django.views import View from bpfin.back_end_call.back_end_inputs import monthly_bill from bpfin.back_end_call.back_end_inputs import form_prior_income_table as prior_income_statement_table +from blocnote.lib.fetch_data import get_building_data from .models import Fund, FinancingOverview, Bills, BillsOverview, CustomerPreference, EstimationAlgorithm from .models import Liabilities, CashBalance, IncomeStatement, LoanOptions, DefaultLoan, Lender, GrowthRate @@ -107,25 +108,6 @@ class Index(View): model = FinancingOverview - def get_building_data(self, building_id): - """Return building data given it's id. - - Connect to the building database and retrieves the building data. - - Args: - building_id: id of the building_id. - - Returns: - building: A dictionary containing all the relevant building data. - """ - cursor = connections['building_db'].cursor() - command = 'SELECT * FROM public.get_building(in_building_id := {})' - cursor.execute(command.format(building_id)) - columns = [col[0] for col in cursor.description] - row = cursor.fetchone() - building = dict(zip(columns, row)) - return building - def get(self, request, building_id): """GET route for Index page. @@ -138,7 +120,7 @@ class Index(View): Returns: render: The index.html page with the context. """ - building = self.get_building_data(building_id) + building = get_building_data(building_id) context = { 'building': building, } -- GitLab From 90e77ad79a61821119186970a8bbe77a6d1538d9 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Mon, 19 Jun 2017 09:11:34 -0400 Subject: [PATCH 09/16] Remove old function tests from tests in budget. --- blocnote/apps/budgetSimulator/tests.py | 460 ------------------------- 1 file changed, 460 deletions(-) diff --git a/blocnote/apps/budgetSimulator/tests.py b/blocnote/apps/budgetSimulator/tests.py index 73493e4..f60a012 100644 --- a/blocnote/apps/budgetSimulator/tests.py +++ b/blocnote/apps/budgetSimulator/tests.py @@ -1,426 +1,6 @@ """Test all the function and views in budget endpoint.""" -from datetime import date from django.test import TestCase -from bpfin.back_end_call.back_end_budget import budget_simulation - -from .views import get_analysis_commissioning_dates, get_customer_preference, get_growth_rate, get_liability -from .views import get_cash_balance, get_loan_input, get_raw_income_statement, get_annual_bills, get_raw_bill - - -class GetAnalysisCommissioningDatesTest(TestCase): - """Test get_analysis_commissioning_dates function.""" - - fixtures = ['funds.json', 'financing_overview_testdata.json'] - - def test_analysis_commissioning_date_1(self): - """Fetch analysis date and commissioning date for a building with no data in database. - - Building with id 233 does not have financing overview records stored. This should raise an attribute error. - """ - self.assertRaises(AttributeError, get_analysis_commissioning_dates, 233) - - def test_analysis_commissioning_date_2(self): - """Fetch analysis date and commissioning date for a building with data.""" - analysis_date, commissioning_date = get_analysis_commissioning_dates(100) - expected_analysis_date = { - 'proforma_start': date(2014, 1, 1), - 'proforma_duration': 25, - } - expected_commissioning_date = date(2017, 8, 1) - self.assertEqual(analysis_date, expected_analysis_date) - self.assertEqual(commissioning_date, expected_commissioning_date) - - -class GetCustomerPreferenceTest(TestCase): - """Test get_customer_preference function.""" - - fixtures = ['customer_preference_testdata.json'] - - def test_customer_preference_1(self): - """Fetch customer preference for building with no data. - - No customer preference record exists for building id 233. AttributeError should be raised. - """ - self.assertRaises(AttributeError, get_customer_preference, 233) - - def test_customer_preference_2(self): - """Create customer preference record and test get_customer_preference function.""" - customer_preference = get_customer_preference(100) - expected_customer_preference = { - 'downpayment_max': 100.0, - 'expected_payback': 10, - 'cust_saving_dscr': 1.15, - } - self.assertEqual(customer_preference, expected_customer_preference) - - -class GetGrowthRateTest(TestCase): - """Test get_growth_rate function.""" - - fixtures = ['growth_rate_testdata.json'] - - def test_growth_rate_1(self): - """Fetch growth rate for building with no data in database. - - No growth is stored for building with id 233. This should raise an AttributeError. - """ - self.assertRaises(AttributeError, get_growth_rate, 233) - - def test_growth_rate_2(self): - """Create growth rate and test get_growth_rate function.""" - growth_rate = get_growth_rate(100) - expected_growth_rate = 1 - self.assertEqual(growth_rate, expected_growth_rate) - - -class GetLiabilitiesTest(TestCase): - """Test get_liability function.""" - - fixtures = ['liabilities_testdata.json'] - - def test_liabilities_1(self): - """Test get_liability for building with with no data. - - No liabilities records exists for this building hence empty dictionary should be returned. - """ - liabilities = get_liability(233) - self.assertEqual(liabilities, {}) - - def test_liabilities_2(self): - """Test get_liability when 1 record exists. - - Create a liability record for this building id test the reult. - """ - liability = get_liability(100) - expected_liability = { - 'debt1': (300, "Adarsh", 120, date(2016, 1, 1)) - } - self.assertEqual(liability, expected_liability) - - def test_liability_3(self): - """Test get_liability when 2 records exist. - - Create 2 liability records for building 233 and validate the output. - """ - liability = get_liability(101) - expected_liability = { - 'debt1': (300, "Adarsh", 120, date(2016, 1, 1)), - 'debt2': (250, "Tom", 100, date(2015, 1, 1)) - } - self.assertEqual(liability, expected_liability) - - -class GetCashBalanceTest(TestCase): - """Test get_cash_balance function.""" - - fixtures = ['cash_balance_testdata.json'] - - def test_cash_balance_1(self): - """Test get_cash_balance for building with no data. - - No cash balance record exist for this building so it should raise an AttributeError. - """ - self.assertRaises(AttributeError, get_cash_balance, 233) - - def test_cash_balance_2(self): - """Test get_cash_balance with 1 record. - - Create a cash balance record for building 233 and validate the result. - """ - cash_balance = get_cash_balance(100) - expected_cash_balance = { - date(2016, 12, 1): (20000, True), - } - self.assertEqual(cash_balance, expected_cash_balance) - - def test_cash_balance_3(self): - """Test get_cash_balance with 2 records. - - Create 2 cash balance records for building 233 and validate the result. - """ - cash_balance = get_cash_balance(101) - expected_cash_balance = { - date(2016, 12, 1): (20000, True), - date(2015, 12, 1): (30000, False), - } - self.assertEqual(cash_balance, expected_cash_balance) - - -class GetLoanInputTest(TestCase): - """Test get_loan_input function.""" - - fixtures = ['lender.json', 'loan_options_testdata.json'] - - def test_loan_input_1(self): - """Test get_cash_balance for building with no data in database. - - No loan options record exist for this building hence it should raise AttributeError. - """ - self.assertRaises(AttributeError, get_loan_input, 233) - - def test_loan_input_2(self): - """Test get_cash_balance with one record. - - Create a lender record. Create a loan options record and validate get_loan_input. - """ - loan_options = get_loan_input(100) - expected_loan_options = [ - { - 'institute': "Green Bank", - 'max_amount': 1000000, - 'interest': 0.08, - 'duration': 100, - } - ] - self.assertEqual(loan_options, expected_loan_options) - - -class GetRawIncomeStatementTest(TestCase): - """Test get_raw_income_statement function.""" - - fixtures = ['income_statement_testdata.json'] - - def test_income_statement_1(self): - """Test get_raw_income_statement for building with no data in database. - - No income statement exists for building 233. This should raise AttributeError. - """ - self.assertRaises(AttributeError, get_raw_income_statement, 233) - - def test_income_statement_2(self): - """Test get_raw_income_statement for a set of good values. - - Create income statement objects for the 3 years and test the function. - """ - raw_income_statement_input = get_raw_income_statement(100) - expected_raw_income_statement = { - 2014: { - 'revenue': 200000, - 'utility_expense': 90000, - 'non_utility_expense': 50000, - }, - 2015: { - 'revenue': 205000, - 'utility_expense': 92000, - 'non_utility_expense': 55000, - }, - 2016: { - 'revenue': 210000, - 'utility_expense': 94000, - 'non_utility_expense': 57000, - }, - } - self.assertEqual(raw_income_statement_input, expected_raw_income_statement) - - -class GetAnnualBillsTest(TestCase): - """Test get_annual_bills function.""" - - fixtures = ['bills_overview_testdata'] - - def test_annual_bills_1(self): - """Test get_annual_bills for building with no records in database. - - No records exist for this building hence this should return an empty dictionary. - """ - annual_bills = get_annual_bills(233) - expected_annual_bills = { - 'electricity': None, - 'gas': None, - 'oil': None, - 'water': None, - } - self.assertEqual(annual_bills, expected_annual_bills) - - def test_annual_bills_2(self): - """Test get_annual_bills for electricity records for a year. - - Create a bills overview record with only electricity data and test the function. - """ - annual_bills = get_annual_bills(100) - expected_annual_bills = { - 'electricity': { - 2014: 10000, - }, - 'gas': None, - 'oil': None, - 'water': None, - } - self.assertEqual(annual_bills, expected_annual_bills) - - def test_annual_bills_3(self): - """Test get_annual_bills with electricity values for 3 years. - - Create a bills overview records with only electricity data for 3 different years and test the function. - """ - annual_bills = get_annual_bills(101) - expected_annual_bills = { - 'electricity': { - 2014: 10000, - 2015: 12000, - 2016: 15000, - }, - 'gas': None, - 'oil': None, - 'water': None, - } - self.assertEqual(annual_bills, expected_annual_bills) - - def test_annual_bills_4(self): - """Test get_annual_bills for electricity and gas values for 1 year each. - - Create a bills overview record with only electricity and gas data and test the function. - """ - annual_bills = get_annual_bills(102) - expected_annual_bills = { - 'electricity': { - 2014: 10000, - }, - 'gas': { - 2014: 5000, - }, - 'oil': None, - 'water': None, - } - self.assertEqual(annual_bills, expected_annual_bills) - - def test_annual_bills_5(self): - """Test get_annual_bills for electricity, gas and water records for 1 year. - - Create a bills overview record with only electricity, gas and water data and test the function. - """ - annual_bills = get_annual_bills(103) - expected_annual_bills = { - 'electricity': { - 2014: 10000, - }, - 'gas': { - 2014: 5000, - }, - 'water': { - 2014: 17000, - }, - 'oil': None, - } - self.assertEqual(annual_bills, expected_annual_bills) - - def test_annual_bills_6(self): - """Test get_annual_bills with electricity 3 years, gas 2 and water 1 year. - - Create a bills overview record and test the function. - """ - annual_bills = get_annual_bills(104) - expected_annual_bills = { - 'electricity': { - 2014: 10000, - 2015: 12000, - 2016: 14000, - }, - 'gas': { - 2015: 5000, - 2016: 7000, - }, - 'water': { - 2016: 17000, - }, - 'oil': None, - } - self.assertEqual(annual_bills, expected_annual_bills) - - -class GetRawBillsTest(TestCase): - """Test get_raw_bill function.""" - - fixtures = ['bills_testdata.json'] - - def test_raw_bills_1(self): - """Test the function for building with no records. - - No bills exist for this building so it should return empty dictionary. - """ - raw_bills = get_raw_bill(233) - self.assertEqual(raw_bills, {}) - - def test_raw_bills_2(self): - """Test function with electricity bill. - - Create Bills record with electricity bill and validate the function. - """ - raw_bills = get_raw_bill(100) - expected_raw_bills = { - 'electricity': { - 'utility_type': 'electricity', - 'date_from': [date(2015, 1, 1)], - 'date_to': [date(2015, 12, 31)], - 'charge': [5000], - 'usage': [100], - }, - } - self.assertEqual(raw_bills, expected_raw_bills) - - def test_raw_bills_3(self): - """Test function with electricity and gas bill. - - Create Bills record with electricity and gas bills and validate the function. - """ - raw_bills = get_raw_bill(102) - expected_raw_bills = { - 'electricity': { - 'utility_type': 'electricity', - 'date_from': [date(2015, 1, 1)], - 'date_to': [date(2015, 12, 31)], - 'charge': [5000], - 'usage': [100], - }, - 'gas': { - 'utility_type': 'gas', - 'date_from': [date(2014, 1, 1)], - 'date_to': [date(2014, 12, 31)], - 'charge': [7000], - 'usage': [200], - } - } - self.assertEqual(raw_bills, expected_raw_bills) - - def test_raw_bills_4(self): - """Test function for all 4 utilities. - - Create Bills record with all bills and validate the function. - """ - raw_bills = get_raw_bill(103) - expected_raw_bills = { - 'electricity': { - 'utility_type': 'electricity', - 'date_from': [date(2014, 1, 1), date(2015, 1, 1), date(2016, 1, 1)], - 'date_to': [date(2014, 12, 31), date(2015, 12, 31), date(2016, 12, 31)], - 'charge': [5000, 10000, 13000], - 'usage': [100, 200, 300], - }, - 'gas': { - 'utility_type': 'gas', - 'date_from': [date(2015, 1, 1), date(2016, 1, 1)], - 'date_to': [date(2015, 12, 31), date(2016, 12, 31)], - 'charge': [7000, 9500], - 'usage': [200, 400], - }, - 'oil': { - 'utility_type': 'oil', - 'date_from': [date(2015, 1, 1), date(2016, 1, 1)], - 'date_to': [date(2015, 12, 31), date(2016, 12, 31)], - 'charge': [5000, 6000], - 'usage': [100, 250], - }, - 'water': { - 'utility_type': 'water', - 'date_from': [date(2016, 1, 1)], - 'date_to': [date(2016, 12, 31)], - 'charge': [2000], - 'usage': [200], - }, - } - self.assertEqual(raw_bills, expected_raw_bills) - class BudgetSimulatorIndexTest(TestCase): """Test the index view of budget endpoint.""" @@ -471,30 +51,10 @@ class BudgetSimulatorBudgetView(TestCase): """ response = self.client.get('/buildings/289/budget/budget-data/') response_content = response.json() - req_dscr = { - 'req_noi_dscr': 1.15, - 'req_cash_dscr': 1.15, - 'req_saving_dscr': 1.10, - } - analysis_date, commissioning_date = get_analysis_commissioning_dates(289) - result = budget_simulation( - analysis_date, - commissioning_date, - get_customer_preference(289), - req_dscr, - get_raw_bill(289), - get_annual_bills(289), - get_raw_income_statement(289), - get_growth_rate(289), - get_liability(289), - get_cash_balance(289), - get_loan_input(289) - ) self.assertEqual(response.status_code, 200) self.assertTrue('instance' in response_content) self.assertTrue('tables' in response_content['instance']) self.assertTrue('saving_potential_list' in response_content['instance']) - self.assertEqual(len(result), 4) def test_budget_view_3(self): """Test budget view for building with id 2001 with only the proforma inputs stored. @@ -525,30 +85,10 @@ class BudgetSimulatorBudgetView(TestCase): """ response = self.client.get('/buildings/2003/budget/budget-data/') response_content = response.json() - req_dscr = { - 'req_noi_dscr': 1.15, - 'req_cash_dscr': 1.15, - 'req_saving_dscr': 1.10, - } - analysis_date, commissioning_date = get_analysis_commissioning_dates(2003) - result = budget_simulation( - analysis_date, - commissioning_date, - get_customer_preference(2003), - req_dscr, - get_raw_bill(2003), - get_annual_bills(2003), - get_raw_income_statement(2003), - get_growth_rate(2003), - get_liability(2003), - get_cash_balance(2003), - get_loan_input(2003) - ) self.assertEqual(response.status_code, 200) self.assertTrue('instance' in response_content) self.assertTrue('tables' in response_content['instance']) self.assertTrue('saving_potential_list' in response_content['instance']) - self.assertEqual(len(result), 4) def test_budget_view_6(self): """"Test budget view when electricity bill does not have 12 months data.""" -- GitLab From cee10eab05142a2916ab4cba9163efe47dd4b111 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Mon, 19 Jun 2017 09:12:14 -0400 Subject: [PATCH 10/16] Make small changes to catch error when financing overview is not stored. --- blocnote/apps/budgetSimulator/views.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/blocnote/apps/budgetSimulator/views.py b/blocnote/apps/budgetSimulator/views.py index 7a65dd2..6ec3a85 100644 --- a/blocnote/apps/budgetSimulator/views.py +++ b/blocnote/apps/budgetSimulator/views.py @@ -39,11 +39,14 @@ class Budget(View): # Fetch values from the database needed to perform the budget simulation. financing_overview = get_financing_overview(building_id) - analysis_date = { - 'proforma_start': financing_overview['pro_forma_start_date'], - 'proforma_duration': int(financing_overview['pro_forma_duration']), - } - commissioning_date = financing_overview['anticipated_commissioning_date'] + analysis_date = {} + commissioning_date = {} + if financing_overview: + analysis_date = { + 'proforma_start': financing_overview['pro_forma_start_date'], + 'proforma_duration': int(financing_overview['pro_forma_duration']), + } + commissioning_date = financing_overview['anticipated_commissioning_date'] customer_preference = get_customer_preference(building_id) utilities = ['electricity', 'gas', 'oil', 'water'] raw_bills = {} @@ -57,6 +60,7 @@ class Budget(View): liabilities = get_liabilities(building_id) cash_balance = get_cash_balance(building_id) loan_options = get_loan_options(building_id) + err_var = None if not analysis_date: err_var = 'Proforma date and duration ' if not commissioning_date: @@ -71,7 +75,8 @@ class Budget(View): err_var = 'Cash balance ' if not loan_options: err_var = 'Loan options ' - return JsonResponse({'error': err_var + 'not filled.'}, status=400) + if err_var: + return JsonResponse({'error': err_var + 'not filled.'}, status=400) try: result = budget_simulation( analysis_date, -- GitLab From 720c8c2ebbea40f7f96d76a67a5b5f09f71e282b Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Mon, 19 Jun 2017 09:33:56 -0400 Subject: [PATCH 11/16] Add django nose and coverage to requirements. --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index c7936fa..679957d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ Django==1.10.6 psycopg2==2.7 python-decouple==3.0 +django-nose +coverage git+ssh://git@github.com/Blocp/bpfin.git@v0.1.3 -- GitLab From 7e56f89b06d3266783110d37fa00f0f1c0df3936 Mon Sep 17 00:00:00 2001 From: Adarsh Date: Tue, 20 Jun 2017 17:36:46 -0400 Subject: [PATCH 12/16] Style changes according to Conrad's comments. --- blocnote/lib/fetch_data.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/blocnote/lib/fetch_data.py b/blocnote/lib/fetch_data.py index 1af69a9..b04ad2a 100644 --- a/blocnote/lib/fetch_data.py +++ b/blocnote/lib/fetch_data.py @@ -158,8 +158,10 @@ def get_cash_balance(building_id): cash_balances = {} cash_balance_objects = CashBalance.objects.filter(building_id=building_id) for cash_balance_object in cash_balance_objects: - cash_balances[cash_balance_object.statement_date] = (float(cash_balance_object.balance_amount), - cash_balance_object.is_from_balance_sheet) + cash_balances[cash_balance_object.statement_date] = ( + float(cash_balance_object.balance_amount), + cash_balance_object.is_from_balance_sheet + ) return cash_balances @@ -176,7 +178,7 @@ def get_income_statement(building_id): income_statement = { 'revenue': float(income_statement_object.revenue), 'utility_expense': float(income_statement_object.utility_expense), - 'non_utility_expense': float(income_statement_object.non_utility_operating_expense) + 'non_utility_expense': float(income_statement_object.non_utility_operating_expense), } income_statements[int(income_statement_object.year)] = income_statement return income_statements -- GitLab From 43469c969e603d6c9b60f4e6dc7dbc252da58aa2 Mon Sep 17 00:00:00 2001 From: Adarsh Date: Wed, 21 Jun 2017 10:13:41 -0400 Subject: [PATCH 13/16] Update views in budget simulator to display all errors instead of only the last error. --- blocnote/apps/budgetSimulator/views.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/blocnote/apps/budgetSimulator/views.py b/blocnote/apps/budgetSimulator/views.py index 6ec3a85..2dddb39 100644 --- a/blocnote/apps/budgetSimulator/views.py +++ b/blocnote/apps/budgetSimulator/views.py @@ -60,22 +60,23 @@ class Budget(View): liabilities = get_liabilities(building_id) cash_balance = get_cash_balance(building_id) loan_options = get_loan_options(building_id) - err_var = None + err_var_list =[] if not analysis_date: - err_var = 'Proforma date and duration ' + err_var_list.append('Proforma date and duration') if not commissioning_date: - err_var = 'Anticipated commissioning date ' + err_var_list.append('Anticipated commissioning date') if not customer_preference: - err_var = 'Customer preference' + err_var_list.append('Customer preference') if not raw_income_statement: - err_var = 'Income statements ' + err_var_list.append('Income statements') if not growth_rate: - err_var = 'Growth rate ' + err_var_list.append('Growth rate') if not cash_balance: - err_var = 'Cash balance ' + err_var_list.append('Cash balance') if not loan_options: - err_var = 'Loan options ' - if err_var: + err_var_list.append('Loan options') + if err_var_list: + err_var = ",".join(err_var_list) return JsonResponse({'error': err_var + 'not filled.'}, status=400) try: result = budget_simulation( -- GitLab From ea73dbf68bb893b610112cccdf3ddd0e72c5e21c Mon Sep 17 00:00:00 2001 From: Adarsh Date: Wed, 21 Jun 2017 11:24:37 -0400 Subject: [PATCH 14/16] Remove django-nose from settings. --- blocnote/settings.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/blocnote/settings.py b/blocnote/settings.py index effe354..6b0fe09 100644 --- a/blocnote/settings.py +++ b/blocnote/settings.py @@ -45,7 +45,6 @@ INSTALLED_APPS = [ 'blocnote.apps.preliminaryFinance', 'blocnote.apps.budgetSimulator', 'blocnote.apps.landingPage', - 'django_nose', ] MIDDLEWARE = [ @@ -76,17 +75,6 @@ TEMPLATES = [ }, ] -# Use nose to run all tests -TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' - -# Tell nose to measure coverage on the different apps with tests and the lib directory. -NOSE_ARGS = [ - '--with-coverage', - '--cover-package=blocnote.apps.budgetSimulator', - '--cover-package=blocnote.apps.financialInputs', - '--cover-package=blocnote.lib', -] - WSGI_APPLICATION = 'blocnote.wsgi.application' -- GitLab From 0a532e47e13e7fb6ddd06d704b874da1e1044b65 Mon Sep 17 00:00:00 2001 From: Adarsh Date: Wed, 21 Jun 2017 11:25:14 -0400 Subject: [PATCH 15/16] Remove django-nose and coverage from requirements. --- requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 679957d..c7936fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,4 @@ Django==1.10.6 psycopg2==2.7 python-decouple==3.0 -django-nose -coverage git+ssh://git@github.com/Blocp/bpfin.git@v0.1.3 -- GitLab From b3150a244d74c696465f56e506289546aad8479c Mon Sep 17 00:00:00 2001 From: Adarsh Date: Wed, 21 Jun 2017 11:25:38 -0400 Subject: [PATCH 16/16] Remove django-nose from requirements-dev. --- requirements-dev.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 9fa50a1..7afc772 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,5 +4,4 @@ pydocstyle==1.0.0 pylint>=1.5.6 pylint-django>=0.7.2 yapf>=0.16.1 -django-nose coverage -- GitLab