diff --git a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js index f1de81d1b9b54a09011c66b646544de293f8a077..74ea41d226185d3ec1a58fbcbff140663999aa80 100644 --- a/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js +++ b/blocnote/apps/preliminaryFinance/static/preliminaryFinance/scripts/app.js @@ -1,4 +1,3 @@ -var tabCounter = 1; costEstimationTableHead(); costEstimationTableBody(); savingEstimationTableHead(); @@ -8,161 +7,161 @@ savingEstimationTableBody(); * Return the Scenario name. */ function getScenarioName() { - var docs = document.querySelector("a.nav-link.active input"); - return docs.value; + const docs = document.querySelector('a.nav-link.active input'); + return docs.value; } /** * Add column headings to the Cost Estimation table. */ function costEstimationTableHead() { - head = document.querySelector('#cost-estimation-table thead'); - head.innerHTML = ` - - Item - Estimated Cost (in $) - Option - - `; + const head = document.querySelector('#cost-estimation-table thead'); + head.innerHTML = ` + + Item + Estimated Cost (in $) + Option + + `; } /** * Load the Cost Estimation table body. */ function costEstimationTableBody() { - body = document.querySelector('#cost-estimation-table tbody'); - var rowCount = body.rows.length; - var row = body.insertRow(rowCount); - var cell = row.insertCell(0); - cell.innerHTML = ``; - cell = row.insertCell(1); - cell.innerHTML = `$`; - cell = row.insertCell(2); - cell.innerHTML = ``; - return false; + const body = document.querySelector('#cost-estimation-table tbody'); + const rowCount = body.rows.length; + const row = body.insertRow(rowCount); + let cell = row.insertCell(0); + cell.innerHTML = ''; + cell = row.insertCell(1); + cell.innerHTML = '$'; + cell = row.insertCell(2); + cell.innerHTML = ''; + return false; } /** * Delete a row in Cost Estimation table. */ function deleteCostEstimationRow(rowIndex) { - table = document.querySelector('#cost-estimation-table tbody'); - var result = confirm('ARE YOU SURE YOU WANT TO DELETE THIS ROW?'); - if (result) { - var rowCount = table.rows.length; - if (rowCount === 1) { - alert('YOU NEED ATLEAST ONE ITEM WITH ITS COST ESTIMATION!'); - return false; - } - body.deleteRow(rowIndex-1); + const table = document.querySelector('#cost-estimation-table tbody'); + const result = confirm('ARE YOU SURE YOU WANT TO DELETE THIS ROW?'); + if (result) { + const rowCount = table.rows.length; + if (rowCount === 1) { + alert('YOU NEED ATLEAST ONE ITEM WITH ITS COST ESTIMATION!'); + return false; } - return false; + table.deleteRow(rowIndex-1); + } + return false; } /** * Display column headings in Savings Estimation Table. */ function savingEstimationTableHead() { - head = document.querySelector('#saving-estimation-table thead'); - head.innerHTML = ` - - Utility - Estimated Savings (in %) - Used Before Retrofit - Used After Retrofit - - `; + const head = document.querySelector('#saving-estimation-table thead'); + head.innerHTML = ` + + Utility + Estimated Savings (in %) + Used Before Retrofit + Used After Retrofit + + `; } /** * Load Savings Estimation Table body. */ function savingEstimationTableBody() { - body = document.querySelector('#saving-estimation-table tbody'); - var rowCount = body.rows.length; - var row = body.insertRow(rowCount); - var cell = row.insertCell(0); - cell.innerHTML = `Electricity`; - cell = row.insertCell(1); - cell.innerHTML = ` - % - `; - cell = row.insertCell(2); - cell.innerHTML = ` - - `; - cell = row.insertCell(3); - cell.innerHTML = ` - - `; + const body = document.querySelector('#saving-estimation-table tbody'); + let rowCount = body.rows.length; + let row = body.insertRow(rowCount); + let cell = row.insertCell(0); + cell.innerHTML = 'Electricity'; + cell = row.insertCell(1); + cell.innerHTML = ` + % + `; + cell = row.insertCell(2); + cell.innerHTML = ` + + `; + cell = row.insertCell(3); + cell.innerHTML = ` + + `; - rowCount += 1; - row = body.insertRow(rowCount); - cell = row.insertCell(0); - cell.innerHTML = `Gas`; - cell = row.insertCell(1); - cell.innerHTML = ` - % - `; - cell = row.insertCell(2); - cell.innerHTML = ` - - `; - cell = row.insertCell(3); - cell.innerHTML = ` - - `; + rowCount += 1; + row = body.insertRow(rowCount); + cell = row.insertCell(0); + cell.innerHTML = 'Gas'; + cell = row.insertCell(1); + cell.innerHTML = ` + % + `; + cell = row.insertCell(2); + cell.innerHTML = ` + + `; + cell = row.insertCell(3); + cell.innerHTML = ` + + `; - rowCount += 1; - row = body.insertRow(rowCount); - cell = row.insertCell(0); - cell.innerHTML = `Oil`; - cell = row.insertCell(1); - cell.innerHTML = ` - % - `; - cell = row.insertCell(2); - cell.innerHTML = ` - - `; - cell = row.insertCell(3); - cell.innerHTML = ` - - `; + rowCount += 1; + row = body.insertRow(rowCount); + cell = row.insertCell(0); + cell.innerHTML = 'Oil'; + cell = row.insertCell(1); + cell.innerHTML = ` + % + `; + cell = row.insertCell(2); + cell.innerHTML = ` + + `; + cell = row.insertCell(3); + cell.innerHTML = ` + + `; - rowCount += 1; - row = body.insertRow(rowCount); - cell = row.insertCell(0); - cell.innerHTML = `Water`; - cell = row.insertCell(1); - cell.innerHTML = ` - % - `; - cell = row.insertCell(2); - cell.innerHTML = ` - - `; - cell = row.insertCell(3); - cell.innerHTML = ` - - `; - return false; + rowCount += 1; + row = body.insertRow(rowCount); + cell = row.insertCell(0); + cell.innerHTML = 'Water'; + cell = row.insertCell(1); + cell.innerHTML = ` + % + `; + cell = row.insertCell(2); + cell.innerHTML = ` + + `; + cell = row.insertCell(3); + cell.innerHTML = ` + + `; + return false; } /** @@ -170,72 +169,72 @@ function savingEstimationTableBody() { * Upon getting response, display Project Economics table and Energy Expense Savings Projection graph. */ function submitScenario() { - var scenarioName = getScenarioName(); - var savingEstimationBody = document.querySelector('#saving-estimation-table tbody'); - var rowCount = savingEstimationBody.rows.length; - - // Package savings estimate into a list of dictionaries. - savings = []; - for (var rowIndex=0; rowIndex < rowCount; rowIndex++){ - beforeRetrofit = false; - afterRetrofit = false; - record = {}; - checkBoxes = savingEstimationBody.rows[rowIndex].querySelectorAll('input[type="checkbox"]:checked'); - for (index = 0; index < checkBoxes.length; index++) { - checkbox = checkBoxes[index]; - if (checkbox.name === 'Used before retrofit') { - beforeRetrofit = true; - } - if (checkbox.name === 'Used after retrofit') { - afterRetrofit = true; - } - } - if (Number(savingEstimationBody.rows[rowIndex].cells[1].children[0].value) > 100) { - alert(`Please enter a valid savings percentage for ${savingEstimationBody.rows[rowIndex].cells[0].innerHTML}`); - return false; - } + const scenarioName = getScenarioName(); + const savingEstimationBody = document.querySelector('#saving-estimation-table tbody'); + let rowCount = savingEstimationBody.rows.length; - record = { - 'utility_type': savingEstimationBody.rows[rowIndex].cells[0].innerHTML, - 'savings_estimate': savingEstimationBody.rows[rowIndex].cells[1].children[0].value, - 'used_before_retrofit': beforeRetrofit, - 'used_after_retrofit': afterRetrofit, - } - savings.push(record); + // Package savings estimate into a list of dictionaries. + let savings = []; + for (let rowIndex=0; rowIndex < rowCount; rowIndex++){ + let beforeRetrofit = false; + let afterRetrofit = false; + let record = {}; + const checkBoxes = savingEstimationBody.rows[rowIndex].querySelectorAll('input[type="checkbox"]:checked'); + for (let index = 0; index < checkBoxes.length; index++) { + let checkbox = checkBoxes[index]; + if (checkbox.name === 'Used before retrofit') { + beforeRetrofit = true; + } + if (checkbox.name === 'Used after retrofit') { + afterRetrofit = true; + } + } + if (Number(savingEstimationBody.rows[rowIndex].cells[1].children[0].value) > 100) { + alert(`Please enter a valid savings percentage for ${savingEstimationBody.rows[rowIndex].cells[0].innerHTML}`); + return false; } - var costEstimationBody = document.querySelector('#cost-estimation-table tbody'); - rowCount = costEstimationBody.rows.length; - const ITEM_INDEX = 0; - const COST_INDEX = 1; + record = { + 'utility_type': savingEstimationBody.rows[rowIndex].cells[0].innerHTML, + 'estimated_savings': savingEstimationBody.rows[rowIndex].cells[1].children[0].value, + 'used_before_retrofit': beforeRetrofit, + 'used_after_retrofit': afterRetrofit, + }; + savings.push(record); + } - // Package cost estimation into a list of dictionaries. - var cost = []; - for (rowIndex = 0; rowIndex < rowCount; rowIndex++) { - record = { - 'item': costEstimationBody.rows[rowIndex].cells[ITEM_INDEX].children[0].value, - 'cost': costEstimationBody.rows[rowIndex].cells[COST_INDEX].children[0].value, - }; - cost.push(record); - } - result = { - 'scenario': scenarioName, - 'savings': savings, - 'cost': cost, - } - request(`scenario/`, { - method: 'PUT', - credentials: 'same-origin', - body: JSON.stringify(result), - headers: new Headers({ - 'Content-Type': 'application/json', - 'X-CSRFToken': Cookies.get('csrftoken') - }) - }).then(res => { - loadProjectEconomicsTable(res) - loadGraph(res); - }); - return false; + const costEstimationBody = document.querySelector('#cost-estimation-table tbody'); + rowCount = costEstimationBody.rows.length; + const ITEM_INDEX = 0; + const COST_INDEX = 1; + + // Package cost estimation into a list of dictionaries. + const cost = []; + for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) { + let record = { + 'item': costEstimationBody.rows[rowIndex].cells[ITEM_INDEX].children[0].value, + 'cost': costEstimationBody.rows[rowIndex].cells[COST_INDEX].children[0].value, + }; + cost.push(record); + } + const result = { + 'scenario': scenarioName, + 'savings': savings, + 'cost': cost, + }; + request('scenario/', { + method: 'PUT', + credentials: 'same-origin', + body: JSON.stringify(result), + headers: new Headers({ + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken') + }) + }).then(res => { + loadProjectEconomicsTable(res); + loadGraph(res); + }); + return false; } /** @@ -243,75 +242,73 @@ function submitScenario() { * analysis that gives vital information about the financial health of the building. */ function loadProjectEconomicsTable(res) { - var doc = document.querySelector('#project-economics'); - 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); + const title = document.querySelector('#project-economics-title'); + title.innerHTML = 'Project Economics'; + const economicsOverview = res.payload.economics_overview; + const body = document.querySelector('#project-economics-table tbody'); + addPERow(body, economicsOverview); } /** * Add rows to the project economics table. */ function addPERow(body, economicsOverview) { - body.innerHTML = ``; - for (var key in economicsOverview){ - rowCount = body.rows.length; - row = body.insertRow(rowCount); - cell = row.insertCell(0); - cell.innerHTML = `${key}`; - cell = row.insertCell(1); - cell.innerHTML = `${economicsOverview[key]}`; - } + body.innerHTML = ''; + for (let key in economicsOverview){ + let rowCount = body.rows.length; + let row = body.insertRow(rowCount); + let cell = row.insertCell(0); + cell.innerHTML = `${key}`; + cell = row.insertCell(1); + cell.innerHTML = `${economicsOverview[key]}`; + } } /** * Load the Energy Expense Savings Projection stacked bar graph. */ function loadGraph(res) { - var graph = document.querySelector('#savings-schedule'); - graph.innerHTML = ` -

-

- - `; - var config = { + const graph = document.querySelector('#savings-schedule'); + graph.innerHTML = ` +

+

+ + `; + const config = { + type: 'bar', + data: { + labels: res.payload.year_list, + datasets: [{ type: 'bar', - data: { - labels: res.payload.year_list, - datasets: [{ - type: 'bar', - label: 'Energy Expense', - backgroundColor: "#66B2FF", - data: res.payload.energy_expense_list, - }, { - type: 'bar', - label: 'Total Loan', - backgroundColor: "#E0E0E0", - data: res.payload.total_loan_list, - }, { - type: 'bar', - label: 'Net Savings', - backgroundColor: "#99FF99", - data: res.payload.net_savings_list, - }] - }, - options: { - scales: { - xAxes: [{ - stacked: true - }], - yAxes: [{ - stacked: true - }] - } - } - }; - var doc = document.querySelector('#savings-schedule'); - var title = document.querySelector('#savings-schedule-title'); - title.innerHTML = 'Energy Expense Savings Projection'; - var ctx = document.getElementById("myChart").getContext("2d"); - new Chart(ctx, config); + label: 'Energy Expense', + backgroundColor: '#66B2FF', + data: res.payload.energy_expense_list, + }, { + type: 'bar', + label: 'Total Loan', + backgroundColor: '#E0E0E0', + data: res.payload.total_loan_list, + }, { + type: 'bar', + label: 'Net Savings', + backgroundColor: '#99FF99', + data: res.payload.net_savings_list, + }] + }, + options: { + scales: { + xAxes: [{ + stacked: true + }], + yAxes: [{ + stacked: true + }] + } + } + }; + const title = document.querySelector('#savings-schedule-title'); + title.innerHTML = 'Energy Expense Savings Projection'; + const ctx = document.getElementById('myChart').getContext('2d'); + new Chart(ctx, config); } diff --git a/blocnote/apps/preliminaryFinance/views.py b/blocnote/apps/preliminaryFinance/views.py index ea789de4f61f401d11cf9accea3e68be5f8066f7..d5c7800f0b372977294a46674e11bbaec778e836 100644 --- a/blocnote/apps/preliminaryFinance/views.py +++ b/blocnote/apps/preliminaryFinance/views.py @@ -9,6 +9,7 @@ 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 blocnote.apps.preliminaryFinance.models import Scenario, CostEstimation, SavingsEstimation from bpfin.back_end_call.back_end_prelim import prelim_scenario def get_savings_percent(savings): @@ -22,7 +23,7 @@ def get_savings_percent(savings): """ savings_percent = {} for record in savings: - savings_percent[str.lower(record['utility_type'])] = float(record['savings_estimate'])/100 + savings_percent[str.lower(record['utility_type'])] = float(record['estimated_savings'])/100 return savings_percent class Index(View): @@ -52,6 +53,34 @@ class Scenarios(View): to bpfin to calculate all the outputs to be displayed and pass the information to the frontend. """ + def save_scenario(self, scenario_details, cost_list, savings): + """Save given scenario for the building id. + + Take the scenario details and save it in the database. This will create or update a scenario as we are working + with one scenario right now. This will change in the future to keep storing new scenarios. + + Args: + scenario_details (Dictionary): This contains the scenario details in the form of a dictionary. + """ + scenario_obj = Scenario.objects.update_or_create( + building_id=scenario_details['building_id'], + defaults=scenario_details, + ) + CostEstimation.objects.filter(scenario_id=scenario_obj[0].id).delete() + for cost in cost_list: + cost_estimation = { + 'scenario_id': scenario_obj[0].id, + 'item': cost['item'], + 'cost': float(cost['cost']), + } + # CostEstimation.objects.create(cost_estimation) + cost_obj = CostEstimation(**cost_estimation) + cost_obj.save() + SavingsEstimation.objects.filter(scenario_id=scenario_obj[0].id).delete() + for record in savings: + record['estimated_savings'] = float(record['estimated_savings'])/100 + record['scenario_id'] = scenario_obj[0].id + SavingsEstimation.objects.create(**record) def put(self, request, building_id): """Handle HTTP PUT request. @@ -149,6 +178,20 @@ class Scenarios(View): 'Minimum NOI DSCR': float("{0:.2f}".format(project_economics_overview['min_noi_dscr'])), 'Minimum Cash DSCR': float("{0:.2f}".format(project_economics_overview['min_cash_dscr'])), } + scenario_details = { + 'building_id': building_id, + 'name': put['scenario'], + 'analysis_level': 'prelim', + 'estimated_cost': project_economics_overview['estimated_cost'], + 'overall_savings': project_economics_overview['overall_saving'], + 'first_year_savings': project_economics_overview['first_year_saving'], + 'simple_payback': project_economics_overview['simple_payback'], + 'min_savings_dscr': project_economics_overview['min_saving_dscr'], + 'min_net_operating_income_dscr': 1.5, # TODO: Hard-coded for now. Will be implemented soon. + 'min_cash_dscr': project_economics_overview['min_cash_dscr'], + 'self_finance_amount': 0, # TODO: Hard-coded for now. Will be implemented soon. + } + self.save_scenario(scenario_details, cost_list, put['savings']) return JsonResponse({ 'year_list': year_list, 'energy_expense_list': energy_expense_list,