From 70ac499d81c6793e88bd5ca4fafd2bd51ffd3f2d Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Tue, 20 Jun 2017 16:24:11 -0400 Subject: [PATCH 1/7] Implement the GET route for preliminary finance to fetch scenario data from the databse and use data from database to calculate the values for generating the savings schedule graph as well. Send all this data to the frontend. --- blocnote/apps/preliminaryFinance/views.py | 244 +++++++++++++++------- 1 file changed, 168 insertions(+), 76 deletions(-) diff --git a/blocnote/apps/preliminaryFinance/views.py b/blocnote/apps/preliminaryFinance/views.py index dbeefa1..fab21a4 100644 --- a/blocnote/apps/preliminaryFinance/views.py +++ b/blocnote/apps/preliminaryFinance/views.py @@ -82,6 +82,7 @@ class Scenarios(View): record['scenario_id'] = scenario_obj[0].id SavingsEstimation.objects.create(**record) + def put(self, request, building_id): """Handle HTTP PUT request. @@ -92,83 +93,12 @@ class Scenarios(View): Returns: Dictionary with data to display the graph. """ put = json.loads(request.body.decode()) - - # Fetch all relevant data from database. - 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. - costs = [] - cost_list = put['cost'] - for record in cost_list: - costs.append(float(record['cost'])) - total_cost = sum(costs) - 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 = { - 'electricity': None, - 'gas': None, - 'oil': None, - 'water': None, - } - req_dscr = { - 'req_noi_dscr': 1.15, - 'req_cash_dscr': 1.15, - 'req_saving_dscr': 1.10 - } - - # prelim_scenario returns the project economics which are a set of values determining the financial health - # of the building and graph dictionary which contains total expense, loan repayment and total net savings. - # The graph_dict will be plotted as a stacked bar graph in the frontend to visually see if savings can cover - # loan repayment and if there will be any savings left are loan repayment. - graph_dict, project_economics_overview = prelim_scenario( - prior_month_bill, - annual_bills, - raw_income_statement, - growth_rate, - liability_dictionary, - cash_balance_input_dict, - loan_options, - analysis_date, - commission_date, - total_cost, - savings_percent, - full_saving_dict, - req_dscr, - customer_preference, + project_economics_overview, energy_expense_list, total_loan_list, net_savings_list, year_list = prelim_analysis( + put['cost'], + put['savings'], + building_id ) - energy_expense = graph_dict['energy_expenses'] - total_loan = graph_dict['total_loan'] - net_savings = graph_dict['net_savings'] - energy_expense_list = [] - total_loan_list = [] - net_savings_list = [] - year_list = [] - for year in sorted(energy_expense): - year_list.append(year) - energy_expense_list.append(energy_expense[year]) - total_loan_list.append(total_loan[year]) - net_savings_list.append(net_savings[year]) economics_overview = { 'Estimated Cost': float("{0:.2f}".format(project_economics_overview['estimated_cost'])), 'Overall Saving': float("{0:.2f}".format(project_economics_overview['overall_saving'])), @@ -191,7 +121,7 @@ class Scenarios(View): 'min_cash_dscr': project_economics_overview['min_cash_dscr'], 'self_finance_amount': 0, # Hard-coded for now. Will be implemented soon. } - self.save_scenario(scenario_details, cost_list, put['savings']) + self.save_scenario(scenario_details, put['cost'], put['savings']) return JsonResponse({ 'year_list': year_list, 'energy_expense_list': energy_expense_list, @@ -199,3 +129,165 @@ class Scenarios(View): 'net_savings_list': net_savings_list, 'economics_overview': economics_overview, }) + + def get(self, request, building_id): + """HTTP GET request. + + Fetch the scenario, cost and savings information from the database and send to the frontend for viewing. This + will be updated in the future to fetch all scenarios from the database and send to the frontend. + + Args: + request: HTTP GET request. + building_id (integer): id of the building. + + Returns: + scenario_details (dictionary): Dictionary containing all the scenario details. + cost_estimation (dictionary): Dictionary with key as item name and value as the cost estimation. + savings (dictionary): Dictionary with utility type and savings in decimal. + """ + project_economics_fields = { + 'estimated_cost': 'Estimated Cost', + 'overall_savings': 'Overall Savings', + 'first_year_savings': 'First Year Savings', + 'simple_payback': 'Simple Payback', + 'self_finance_amount': 'Self Finance Amount', + 'min_savings_dscr': 'Minimum Savings DSCR', + 'min_net_operating_income_dscr': 'Minimum Net Operating Income DSCR', + 'min_cash_dscr': 'Minimum Cash DSCR', + } + savings_fields = [ + 'estimated_savings', + 'used_before_retrofit', + 'used_after_retrofit', + ] + scenario_obj = Scenario.objects.filter(building_id=building_id) + project_economics = {} + costs = [] + savings = {} + savings_list = [] + scenario_name = '' + energy_expense_list = [] + total_loan_list = [] + net_savings_list = [] + year_list = [] + if scenario_obj: + scenario_name = scenario_obj[0].name + print(scenario_obj[0].id) + scenario_id = scenario_obj[0].id + for field in project_economics_fields: + name = project_economics_fields[field] + project_economics[name] = float(scenario_obj[0].__dict__[field]) + cost_obj = CostEstimation.objects.filter(scenario_id=scenario_id) + for cost in cost_obj: + cost_estimate = { + 'cost_item': cost.__dict__['item'], + 'cost': float(cost.__dict__['cost']), + } + costs.append(cost_estimate) + + savings_obj = SavingsEstimation.objects.filter(scenario_id=scenario_id) + for obj in savings_obj: + saving = {} + for field in savings_fields: + saving[field] = obj.__dict__[field] + saving['estimated_savings'] = float(saving['estimated_savings']) * 100 + savings[obj.__dict__['utility_type']] = saving + saving['utility_type'] = obj.__dict__['utility_type'] + savings_list.append(saving) + project_economics_overview, energy_expense_list, total_loan_list, net_savings_list, year_list = prelim_analysis( + costs, + savings_list, + building_id + ) + print(project_economics) + print(costs) + print(savings) + return JsonResponse({ + 'name': scenario_name, + 'project_economics': project_economics, + 'costs': costs, + 'savings': savings, + 'energy_expense_list': energy_expense_list, + 'total_loan_list': total_loan_list, + 'net_savings_list': net_savings_list, + 'year_list': year_list, + }) + + +def prelim_analysis(cost, saving, building_id): + # Fetch all relevant data from database. + 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. + costs = [] + cost_list = cost + for record in cost_list: + costs.append(float(record['cost'])) + total_cost = sum(costs) + savings_percent = get_savings_percent(saving) + + # The following 2 variables are hard coded for now. They will be updated at a later point. + full_saving_dict = { + 'electricity': None, + 'gas': None, + 'oil': None, + 'water': None, + } + req_dscr = { + 'req_noi_dscr': 1.15, + 'req_cash_dscr': 1.15, + 'req_saving_dscr': 1.10 + } + + # prelim_scenario returns the project economics which are a set of values determining the financial health + # of the building and graph dictionary which contains total expense, loan repayment and total net savings. + # The graph_dict will be plotted as a stacked bar graph in the frontend to visually see if savings can cover + # loan repayment and if there will be any savings left are loan repayment. + graph_dict, project_economics_overview = prelim_scenario( + prior_month_bill, + annual_bills, + raw_income_statement, + growth_rate, + liability_dictionary, + cash_balance_input_dict, + loan_options, + analysis_date, + commission_date, + total_cost, + savings_percent, + full_saving_dict, + req_dscr, + customer_preference, + ) + energy_expense = graph_dict['energy_expenses'] + total_loan = graph_dict['total_loan'] + net_savings = graph_dict['net_savings'] + energy_expense_list = [] + total_loan_list = [] + net_savings_list = [] + year_list = [] + for year in sorted(energy_expense): + year_list.append(year) + energy_expense_list.append(energy_expense[year]) + total_loan_list.append(total_loan[year]) + net_savings_list.append(net_savings[year]) + return project_economics_overview, energy_expense_list, total_loan_list, net_savings_list, year_list -- GitLab From 091cc802a1ee5b8500065e46f5c74a50c7802479 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Tue, 20 Jun 2017 16:26:18 -0400 Subject: [PATCH 2/7] Update frontend to make a GET request to the backend. Upon getting response, if scenario has been previously saved then load all the details including the project economics page and the graph. If not, load a new page to fill in data. --- .../static/preliminaryFinance/scripts/app.js | 120 +++++++++++++----- 1 file changed, 91 insertions(+), 29 deletions(-) diff --git a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js index 4c92b93..2b2fa98 100644 --- a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js +++ b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js @@ -1,16 +1,16 @@ -costEstimationTableHead(); -costEstimationTableBody(); -savingEstimationTableHead(); -savingEstimationTableBody(); - /** * Return the Scenario name. */ function getScenarioName() { - var docs = document.querySelector('a.nav-link.active input'); + let docs = document.querySelector('a.nav-link.active input'); return docs.value; } +function setScenarioName(scenarioName) { + let docs = document.querySelector('a.nav-link.active input'); + docs.value = scenarioName; +} + /** * Add column headings to the Cost Estimation table. */ @@ -28,14 +28,14 @@ function costEstimationTableHead() { /** * Load the Cost Estimation table body. */ -function costEstimationTableBody() { +function costEstimationTableBody(item, cost) { const body = document.querySelector('#cost-estimation-table tbody'); - var rowCount = body.rows.length; - var row = body.insertRow(rowCount); - var cell = row.insertCell(0); - cell.innerHTML = ''; + let rowCount = body.rows.length; + let row = body.insertRow(rowCount); + let cell = row.insertCell(0); + cell.innerHTML = ``; cell = row.insertCell(1); - cell.innerHTML = '$'; + cell.innerHTML = ``; cell = row.insertCell(2); cell.innerHTML = ''; return false; @@ -76,7 +76,8 @@ function savingEstimationTableHead() { /** * Load Savings Estimation Table body. */ -function savingEstimationTableBody() { +function savingEstimationTableBody(utilities) { + console.log(utilities); const body = document.querySelector('#saving-estimation-table tbody'); var rowCount = body.rows.length; var row = body.insertRow(rowCount); @@ -84,18 +85,18 @@ function savingEstimationTableBody() { cell.innerHTML = 'Electricity'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; @@ -105,18 +106,18 @@ function savingEstimationTableBody() { cell.innerHTML = 'Gas'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; @@ -126,18 +127,18 @@ function savingEstimationTableBody() { cell.innerHTML = 'Oil'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; @@ -147,18 +148,18 @@ function savingEstimationTableBody() { cell.innerHTML = 'Water'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; return false; @@ -231,8 +232,10 @@ function submitScenario() { 'X-CSRFToken': Cookies.get('csrftoken') }) }).then(res => { - loadProjectEconomicsTable(res); - loadGraph(res); + if (!res.err) { + loadProjectEconomicsTable(res.payload.economics_overview); + loadGraph(res); + } }); return false; } @@ -241,10 +244,9 @@ function submitScenario() { * Take the response as argument and display the project economics table. This is the summary of the priliminary * analysis that gives vital information about the financial health of the building. */ -function loadProjectEconomicsTable(res) { +function loadProjectEconomicsTable(economicsOverview) { var title = document.querySelector('#project-economics-title'); title.innerHTML = 'Project Economics'; - var economicsOverview = res.payload.economics_overview; var body = document.querySelector('#project-economics-table tbody'); addPERow(body, economicsOverview); } @@ -312,3 +314,63 @@ function loadGraph(res) { new Chart(ctx, config); } +function getScenario() { + + request('scenario/', { + method: 'GET', + credentials: 'same-origin', + headers: new Headers({ + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken') + }) + }).then((res) => { + if (!res.err) { + console.log(res); + if (Object.keys(res.payload.project_economics).length > 0 ) { + loadProjectEconomicsTable(res.payload.project_economics); + loadGraph(res); + } + costEstimationTableHead(); + if (Object.keys(res.payload.costs).length > 0) { + res.payload.costs.forEach((costItem) => { + costEstimationTableBody(costItem.cost_item, costItem.cost); + }); + } else { + costEstimationTableBody('', ''); + } + savingEstimationTableHead(); + let savings = {}; + if (Object.keys(res.payload.savings).length > 0) { + savings = res.payload.savings; + console.log('fnkdfhdk'); + } else { + savings = { + 'Electricity': { + 'estimated_savings': '', + 'used_before_retrofit': '', + 'used_after_retrofit': '', + }, + 'Gas': { + 'estimated_savings': '', + 'used_before_retrofit': '', + 'used_after_retrofit': '', + }, + 'Oil': { + 'estimated_savings': '', + 'used_before_retrofit': '', + 'used_after_retrofit': '', + }, + 'Water': { + 'estimated_savings': '', + 'used_before_retrofit': '', + 'used_after_retrofit': '', + }, + }; + } + savingEstimationTableBody(savings); + setScenarioName(res.payload.name); + } + }); +} + +getScenario(); -- GitLab From 9e40e9656d5b139f08cb667af6b736297370f2ab Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Tue, 20 Jun 2017 16:27:28 -0400 Subject: [PATCH 3/7] Remove print statements. --- blocnote/apps/preliminaryFinance/views.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/blocnote/apps/preliminaryFinance/views.py b/blocnote/apps/preliminaryFinance/views.py index fab21a4..de1b3df 100644 --- a/blocnote/apps/preliminaryFinance/views.py +++ b/blocnote/apps/preliminaryFinance/views.py @@ -172,7 +172,6 @@ class Scenarios(View): year_list = [] if scenario_obj: scenario_name = scenario_obj[0].name - print(scenario_obj[0].id) scenario_id = scenario_obj[0].id for field in project_economics_fields: name = project_economics_fields[field] @@ -199,9 +198,6 @@ class Scenarios(View): savings_list, building_id ) - print(project_economics) - print(costs) - print(savings) return JsonResponse({ 'name': scenario_name, 'project_economics': project_economics, -- GitLab From 2291bbc399c568d5a7ea36d88618097861e7ec33 Mon Sep 17 00:00:00 2001 From: Adarsh Murthy Date: Tue, 20 Jun 2017 16:28:26 -0400 Subject: [PATCH 4/7] Remove console log statements. --- .../static/preliminaryFinance/scripts/app.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js index 2b2fa98..bfbddaa 100644 --- a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js +++ b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js @@ -77,7 +77,6 @@ function savingEstimationTableHead() { * Load Savings Estimation Table body. */ function savingEstimationTableBody(utilities) { - console.log(utilities); const body = document.querySelector('#saving-estimation-table tbody'); var rowCount = body.rows.length; var row = body.insertRow(rowCount); @@ -325,7 +324,6 @@ function getScenario() { }) }).then((res) => { if (!res.err) { - console.log(res); if (Object.keys(res.payload.project_economics).length > 0 ) { loadProjectEconomicsTable(res.payload.project_economics); loadGraph(res); @@ -342,7 +340,6 @@ function getScenario() { let savings = {}; if (Object.keys(res.payload.savings).length > 0) { savings = res.payload.savings; - console.log('fnkdfhdk'); } else { savings = { 'Electricity': { -- GitLab From 1101a8b69a39ad522e533456979f366bdedf45a4 Mon Sep 17 00:00:00 2001 From: Adarsh Date: Tue, 20 Jun 2017 17:16:08 -0400 Subject: [PATCH 5/7] Add comments to the new functions. --- .../static/preliminaryFinance/scripts/app.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js index bfbddaa..2ce9b83 100644 --- a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js +++ b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js @@ -6,6 +6,12 @@ function getScenarioName() { return docs.value; } +/** + * setScenarioName() Set the name of the scenario on the tab. + * + * @param {...} scenarioName - The name of the scenario. + * + */ function setScenarioName(scenarioName) { let docs = document.querySelector('a.nav-link.active input'); docs.value = scenarioName; @@ -313,8 +319,13 @@ function loadGraph(res) { new Chart(ctx, config); } +/** + * getScenario() Do a GET request to the backend. If scenario previously stored, populate the cost and savings table + * as well as fill project economics and generate the savings schedule graph. If not, display new cost and savings form + * to be filled. + * + */ function getScenario() { - request('scenario/', { method: 'GET', credentials: 'same-origin', -- GitLab From b55be36a78af556a4c38751244d50d0728b16ca3 Mon Sep 17 00:00:00 2001 From: Adarsh Date: Wed, 28 Jun 2017 11:29:46 -0400 Subject: [PATCH 6/7] Small variable fix. --- .../static/preliminaryFinance/scripts/app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js index a83ceb6..d73287d 100644 --- a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js +++ b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js @@ -249,10 +249,9 @@ function submitScenario() { * Take the response as argument and display the project economics table. This is the summary of the priliminary * analysis that gives vital information about the financial health of the building. */ -function loadProjectEconomicsTable(res) { +function loadProjectEconomicsTable(economicsOverview) { const title = document.querySelector('#project-economics-title'); title.innerHTML = 'Project Economics'; - const economicsOverview = res; const body = document.querySelector('#project-economics-table tbody'); addPERow(body, economicsOverview); } -- GitLab From de91da2b61f48988e20bfc4ec954622ab784aaaf Mon Sep 17 00:00:00 2001 From: Adarsh Date: Thu, 6 Jul 2017 17:09:44 -0400 Subject: [PATCH 7/7] Change preliminary finance app js to have all lower case attributes for savings. --- .../static/preliminaryFinance/scripts/app.js | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js index d73287d..0f957c1 100644 --- a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js +++ b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js @@ -90,18 +90,18 @@ function savingEstimationTableBody(utilities) { cell.innerHTML = 'Electricity'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; @@ -111,18 +111,18 @@ function savingEstimationTableBody(utilities) { cell.innerHTML = 'Gas'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; @@ -132,18 +132,18 @@ function savingEstimationTableBody(utilities) { cell.innerHTML = 'Oil'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; @@ -153,18 +153,18 @@ function savingEstimationTableBody(utilities) { cell.innerHTML = 'Water'; cell = row.insertCell(1); cell.innerHTML = ` - % + % `; cell = row.insertCell(2); cell.innerHTML = ` `; cell = row.insertCell(3); cell.innerHTML = ` `; return false; @@ -201,7 +201,7 @@ function submitScenario() { } record = { - 'utility_type': savingEstimationBody.rows[rowIndex].cells[0].innerHTML, + 'utility_type': savingEstimationBody.rows[rowIndex].cells[0].innerHTML.toLowerCase(), 'estimated_savings': savingEstimationBody.rows[rowIndex].cells[1].children[0].value, 'used_before_retrofit': beforeRetrofit, 'used_after_retrofit': afterRetrofit, @@ -353,22 +353,22 @@ function getScenario() { savings = res.payload.savings; } else { savings = { - 'Electricity': { + 'electricity': { 'estimated_savings': '', 'used_before_retrofit': '', 'used_after_retrofit': '', }, - 'Gas': { + 'gas': { 'estimated_savings': '', 'used_before_retrofit': '', 'used_after_retrofit': '', }, - 'Oil': { + 'oil': { 'estimated_savings': '', 'used_before_retrofit': '', 'used_after_retrofit': '', }, - 'Water': { + 'water': { 'estimated_savings': '', 'used_before_retrofit': '', 'used_after_retrofit': '', -- GitLab