diff --git a/blocnote/apps/financialInputs/old_views.py b/blocnote/apps/financialInputs/old_views.py index d8c232be8350e48c9cf7f34a6e11f67668f16a4b..66542078e409f981af7e45d8decce8ec3f314b65 100644 --- a/blocnote/apps/financialInputs/old_views.py +++ b/blocnote/apps/financialInputs/old_views.py @@ -44,23 +44,6 @@ def get_model_object(model, building_id): return 0 -def change_date_format(dates): - """Change date format. - - The CSV file contains date in MM/DD/YYYY format. The database needs it - to be in YYYY-MM-DD. This function performs this task. - - Args: - date: The date in MM/DD/YYYY format. - - Returns: - new_date: The date in YYYY-MM-DD format. - """ - date_split = dates.split('/') - new_date = date_split[2] + '-' + date_split[0] + '-' + date_split[1] - return new_date - - def get_raw_bill(building_id): """Fetch all the bills in the database. @@ -126,140 +109,6 @@ class Index(View): return render(request, 'financialInputs/index.html', context=context) -class BillsTable(View): - """Create the Energy Bills tables. - - Class to upload energy bills for each utility, store bills and send - the data to the frontend. - """ - - model = Bills - - def put(self, request, building_id): - """Handle HTTP PUT request. - - Obtain bill information from frontend as JSON. The data is in CSV. - The relevant data is stored in the database. Upon successfull upload, - the data is sent to the frontend to create the table. - - Args: - request: HTTP PUT request. - building_id: id of the building. - - Returns: - JsonResponse: JSON of the result which is a list containing - rows to be displayed. - """ - put = json.loads(request.body.decode()) - try: - result = self.handle_file(put, building_id) - except ValueError as err: - return JsonResponse({'error': err.args[0] + 'Please make sure you uploaded the right file.'}, status=400) - return JsonResponse({'result': result}) - - def handle_file(self, data, building_id): - """Handle file input. - - Take file data from frontend and obtain the indices of the relevant - columns. Go through the file and get the relevant data and store in the - database. - - Args: - data: The file data obtained from frontend. - building_id: id of the building. - - Returns: - result: List of rows to be sent to the frontend to display table. - """ - utility_type = data['utility_type'] - - self.model.objects.filter( - building_id=building_id, - utility_type=utility_type - ).delete() - - text = data['text'].split('\n') - date_from_index = "" - date_to_index = "" - usage_index = "" - charge_index = "" - column_headings = text[0].split(',') - for index, heading in enumerate(column_headings): - if heading == "Bill From Date": - date_from_index = index - elif heading == "Bill To Date": - date_to_index = index - elif heading == "Usage": - usage_index = index - elif "Total Charges" in heading: - charge_index = index - - if not date_from_index: - raise ValueError('Could not find "Bill From Date" column.') - if not date_to_index: - raise ValueError('Could not find "Bill To Date" column.') - if not usage_index: - raise ValueError('Could not find "Usage" column.') - if not charge_index: - raise ValueError('Could not find "Total Charge" column.') - - result = [] - for line in range(1, len(text)-1): - row_list = [] - line_array = text[line].split(',') - date_from = change_date_format(line_array[date_from_index]) - row_list.append(date_from) - date_to = change_date_format(line_array[date_to_index]) - row_list.append(date_to) - usage = line_array[usage_index] - row_list.append(usage) - charge = line_array[charge_index] - row_list.append(charge) - self.model.objects.create(building_id=building_id, - date_from=date_from, - date_to=date_to, - utility_type=data['utility_type'], - usage=usage, - charge=charge) - result.append(row_list) - - return result - - def get(self, request, building_id): - """Handle HTTP GET request. - - Fetch stored utility bills data and send to the frontend to display as - table. - - Args: - request: HTTP GET request. - building_id: id of the building. - - Return: - JsonResponse: JSON of the result which is a list of rows to populate - the table. - """ - result = [] - present = False - utility_type = request.GET.get('utility_type') - - model_obj = self.model.objects.filter( - building_id=building_id, - utility_type=utility_type - ) - if model_obj: - present = True - for row in model_obj: - row_list = [] - row_list.append(row.date_from) - row_list.append(row.date_to) - row_list.append(row.usage) - row_list.append(row.charge) - result.append(row_list) - - return JsonResponse({'result': result, 'present': present}) - - def get_if_user_input(building_id): """Check if utility charge input was user or from bills. diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index 51c74d7167d3ca8d3d8f0ec304ab21e9f0328f9a..c0653d4092a9879502c46b831438f7019896300f 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -1,13 +1,9 @@ -const utilities = ['electricity', 'gas', 'oil', 'water']; var today = new Date(); const todaysDate = { 'day': today.getDate(), 'month': today.getMonth()+1, 'year': today.getFullYear(), } -for (var utility_index in utilities) { - loadInitialBillsTable(utilities[utility_index]); -} loadBillsOverview(); getIncomeStatementTable(); @@ -37,111 +33,6 @@ function validateDate(startDate, endDate) { return true; } -/** - * Load initial bills table given a utility. Send GET request to obtain - * bill information and call updateTable function to update the bills table - * with the data obtained from backend. - */ -function loadInitialBillsTable(utility) { - const table = document.querySelector('#'+utility); - - request(`bills/?utility_type=${utility}`, { - method: 'GET', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json' - }, - }).then(res => { - if (res.payload.present) { - var text = getText(res.payload.result, utility); - updateTable(utility, text); - } - }) -} - -/** - * Update the Bills table with the data. Function queries to get the table - * component and then updates the component with the text. - */ -function updateTable(utility, text) { - table = document.querySelector('#'+utility); - table.innerHTML = text; -} - -/** Generate html text for a given utility and bills data from backend. */ -function getText(result, utility) { - units = {}; - units['electricity'] = `kWh`; - units['gas'] = `mmBTU`; - units['oil'] = `mmBTU`; - units['water'] = `Gallons`; - var text = ` - - - - - - - - - `; - for (var i =0; i`; - for (var j=0; j` + result[i][j] + ``; - } - text += ``; - } - text += ``; - return text; -} - -/** - * Upload an energy bill. Get the id of the component to identify the - * utility type. Use reader to read as text and call the sendFile function - * to send the contents to the backend. - */ -function uploadFile(data) { - const id = document.getElementsByClassName("show")[0].getAttribute("id"); - csvFile = data.files[0]; - var reader = new FileReader(); - reader.onload = function(e) { - var content = {} - content.text = reader.result; - content.utility_type = id; - sendFile(id, content); - } - reader.readAsText(csvFile); - return false; -} - -/** HTTP PUT request to upload the contents of the utility bills file */ -function sendFile(id, content) { - request(`bills/`, { - method: 'PUT', - credentials: 'same-origin', - body: JSON.stringify(content), - headers: new Headers({ - 'Content-Type': 'application/json', - 'X-CSRFToken': Cookies.get('csrftoken') - }) - }).then(res => { - if (!res.err) { - document.querySelector('#bills-response-message').innerHTML = ` - ${id} bill upload success - `; - var text = getText(res.payload.result, id); - updateTable(id, text); - } - else { - res.err.responseBody.then((error) => { - document.querySelector('#bills-warning-message').innerHTML = ` - ${error.error} - `; - }) - } - }); -} function createEstimateModelForm() { var estimateModelForm = ``; @@ -225,7 +116,7 @@ function createBillsOverviewTableCellInput(id, state, value, year) { if (value === null) { value = ''; } - text = ``; + text = ``; return text; } diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/bills.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/bills.js new file mode 100644 index 0000000000000000000000000000000000000000..b7c0346d47e061d1abc6f9757e181f236aac2766 --- /dev/null +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/bills.js @@ -0,0 +1,123 @@ +/** + * Load initial bills table given a utility. Send GET request to obtain + * bill information and call updateTable function to update the bills table + * with the data obtained from backend. + */ +function getInitialBillsTable(utility) { + request(`bills/?utility_type=${utility}`, { + method: 'GET', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json' + }, + }).then(res => { + if (res.payload) { + document.querySelector(`#${utility}-bill-status`).innerHTML = ''; + createBillTable(utility, res.payload.result); + } + }); +} + + +/** + * Take in the utility and the utility bill content and display it in the repective table. + * + * @param {...} utility - String denoting which utility it is. + * @param {...} content - List of lists which represents the table. + * + */ +function createBillTable(utility, content) { + const utilityTable = document.querySelector(`#${utility}-table`); + const head = utilityTable.querySelector('thead'); + const body = utilityTable.querySelector('tbody'); + head.innerHTML = ''; + body.innerHTML = ''; + const headRow = head.insertRow(0); + const headings = ['Date From', 'Date To', 'Usage', 'Bill Charge ($)']; + const units = { + 'electricity': 'kWh', + 'gas': 'mmBTU', + 'oil': 'mmBTU', + 'water': 'Gallons', + }; + + headings.forEach((heading, cellIndex) => { + const cell = headRow.insertCell(cellIndex); + let cellValue = heading; + if(cellIndex == 2) { + cellValue += ` (${units[utility]})`; + } + cell.innerHTML = cellValue; + }); + + content.forEach((rowContents, rowCount) => { + const bodyRow = body.insertRow(rowCount); + rowContents.forEach((cellValue, cellIndex) => { + const cell = bodyRow.insertCell(cellIndex); + cell.innerHTML = cellValue; + }); + }); + +} + + +/** + * Upload an utility bill. Get the id of the component to identify the + * utility type. Use reader to read as text and call the sendFile function + * to send the contents to the backend. + */ +function uploadFile(data) { + if (data.files.length > 0) { + const id = document.getElementsByClassName('show')[0].getAttribute('id'); + const csvFile = data.files[0]; + const type = /text.csv/; + if (csvFile.type.match(type)) { + const reader = new FileReader(); + reader.onload = function(e) { + let content = {}; + content.text = reader.result; + content.utility_type = id; + sendFile(id, content); + }; + reader.readAsText(csvFile); + } else { + document.querySelector('#bills-response-message').innerHTML = ` + Please upload a csv file. + `; + } + } + return false; +} + +/** HTTP PUT request to upload the contents of the utility bills file */ +function sendFile(id, content) { + request('bills/', { + method: 'PUT', + credentials: 'same-origin', + body: JSON.stringify(content), + headers: new Headers({ + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken') + }) + }).then(res => { + if (!res.err) { + document.querySelector('#bills-response-message').innerHTML = ` + ${id} bill upload success + `; + createBillTable(id, res.payload.result); + } + else { + res.err.responseBody.then((error) => { + document.querySelector('#bills-response-message').innerHTML = ` + ${error.error} + `; + }); + } + }); +} + +const utilities = ['electricity', 'gas', 'oil', 'water']; + +utilities.forEach((utility) => { + getInitialBillsTable(utility); +}); diff --git a/blocnote/apps/financialInputs/templates/financialInputs/bills.html b/blocnote/apps/financialInputs/templates/financialInputs/bills.html index 98cc36e310ad50e4a28edae030401f517fa8b9e8..726783ab008cbd9c108dc3649e6d2821fabcfb16 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/bills.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/bills.html @@ -24,16 +24,56 @@
-
No Bills available
-
No Bills available
-
No Bills available
-
No Bills available
+
+
+ No Bills available +
+
Date FromDate ToUsage (${units[utility]})Bill Charge ($)
+ + +
+ +
+
+ No Bills available +
+ + + +
+
+
+
+ No Bills available +
+ + + +
+
+
+
+ No Bills available +
+ + + +
+
diff --git a/blocnote/apps/financialInputs/templates/financialInputs/index.html b/blocnote/apps/financialInputs/templates/financialInputs/index.html index 3a2430c19e680847b924c97ff77ea3431a8ad850..fa4460f7f666a1d5318d80d62336f69373eb3dc7 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/index.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/index.html @@ -58,6 +58,7 @@ + {% endblock %} diff --git a/blocnote/apps/financialInputs/urls.py b/blocnote/apps/financialInputs/urls.py index e3563f900e57e9829665a8c9abd9e2546370950f..a134698af65e030732e17a04d026a5dcc2960e46 100644 --- a/blocnote/apps/financialInputs/urls.py +++ b/blocnote/apps/financialInputs/urls.py @@ -3,14 +3,14 @@ from django.conf.urls import url from . import old_views from .views import ( - proforma_input, customer_preference, cash_balance, liabilities, + proforma_input, customer_preference, cash_balance, liabilities, bills ) app_name = 'financial-inputs' urlpatterns = [ url(r'^$', old_views.Index.as_view(), name='index'), url(r'^finance-overview/$', proforma_input.ProformaInputs.as_view(), name='header_new'), - url(r'^bills/$', old_views.BillsTable.as_view(), name='bills'), + url(r'^bills/$', bills.BillsTable.as_view(), name='bills'), url(r'^bills-overview/$', old_views.BillsOverviewView.as_view(), name='bills_overview'), url(r'^customer-preference/$', customer_preference.CustomerPreferenceView.as_view(), name='customer_preference'), url(r'^liabilities/$', liabilities.LiabilitiesTable.as_view(), name='liabilities'), diff --git a/blocnote/apps/financialInputs/views/bills.py b/blocnote/apps/financialInputs/views/bills.py new file mode 100644 index 0000000000000000000000000000000000000000..227cb3ed9b4d801bae3d55f07e871ac55952e5f6 --- /dev/null +++ b/blocnote/apps/financialInputs/views/bills.py @@ -0,0 +1,148 @@ +"""Views for bills component in financialInputs.""" +import json + +from django.views import View +from django.http import JsonResponse + +from blocnote.lib.functions import change_date_format + +from blocnote.apps.financialInputs.models import Bills + + +class BillsTable(View): + """Create the Energy Bills tables. + + Class to upload energy bills for each utility, store bills and send + the data to the frontend. + """ + + model = Bills + + def put(self, request, building_id): + """Handle HTTP PUT request. + + Obtain bill information from frontend as JSON. The data is in CSV. + The relevant data is stored in the database. Upon successfull upload, + the data is sent to the frontend to create the table. + + Args: + request: HTTP PUT request. + building_id: id of the building. + + Returns: + JsonResponse: JSON of the result which is a list containing + rows to be displayed. + """ + put = json.loads(request.body.decode()) + try: + result = self.handle_file(put, building_id) + except ValueError as err: + return JsonResponse({'error': err.args[0] + 'Please make sure you uploaded the right file.'}, status=400) + return JsonResponse({'result': result}) + + def handle_file(self, data, building_id): + """Handle file input. + + Take file data from frontend and obtain the indices of the relevant + columns. Go through the file and get the relevant data and store in the + database. + + Args: + data: The file data obtained from frontend. + building_id: id of the building. + + Returns: + result: List of rows to be sent to the frontend to display table. + """ + utility_type = data['utility_type'] + + self.model.objects.filter( + building_id=building_id, + utility_type=utility_type + ).delete() + + text = data['text'].split('\n') + date_from_index = None + date_to_index = None + usage_index = None + charge_index = None + column_headings = text[0].split(',') + for index, heading in enumerate(column_headings): + if "bill from date" in heading.lower(): + date_from_index = index + elif "bill to date" in heading.lower(): + date_to_index = index + elif heading.lower() == "usage": + usage_index = index + elif "total charge" in heading.lower(): + charge_index = index + if date_from_index is None: + raise ValueError('Could not find "Bill From Date" column.') + if date_to_index is None: + raise ValueError('Could not find "Bill To Date" column.') + if usage_index is None: + raise ValueError('Could not find "Usage" column.') + if charge_index is None: + raise ValueError('Could not find "Total Charge" column.') + + result = [] + for line in range(1, len(text)-1): + row_list = [] + line_array = text[line].split(',') + try: + date_from = change_date_format(line_array[date_from_index]) + except ValueError as err: + raise ValueError(err.args[0]) + row_list.append(date_from) + try: + date_to = change_date_format(line_array[date_to_index]) + except ValueError as err: + raise ValueError(err.args[0]) + row_list.append(date_to) + usage = line_array[usage_index] + row_list.append(usage) + charge = line_array[charge_index] + row_list.append(charge) + self.model.objects.create( + building_id=building_id, + date_from=date_from, + date_to=date_to, + utility_type=data['utility_type'], + usage=usage, + charge=charge, + ) + result.append(row_list) + + return result + + def get(self, request, building_id): + """Handle HTTP GET request. + + Fetch stored utility bills data and send to the frontend to display as + table. + + Args: + request: HTTP GET request. + building_id: id of the building. + + Return: + JsonResponse: JSON of the result which is a list of rows to populate + the table. + """ + result = [] + utility_type = request.GET.get('utility_type') + + model_obj = self.model.objects.filter( + building_id=building_id, + utility_type=utility_type + ) + if model_obj: + for row in model_obj: + row_list = [] + row_list.append(row.date_from) + row_list.append(row.date_to) + row_list.append(row.usage) + row_list.append(row.charge) + result.append(row_list) + + return JsonResponse({'result': result}) diff --git a/blocnote/lib/functions.py b/blocnote/lib/functions.py new file mode 100644 index 0000000000000000000000000000000000000000..4b02ad7566c4b011bee32edfed6df47f71d8417f --- /dev/null +++ b/blocnote/lib/functions.py @@ -0,0 +1,21 @@ +"""Define functions used across apps or are generic and not a fetch data.""" +def change_date_format(dates): + """Change date format. + + The CSV file contains date in MM/DD/YYYY format. The database needs it + to be in YYYY-MM-DD. This function performs this task. + + Args: + date: The date in MM/DD/YYYY format. + + Returns: + new_date: The date in YYYY-MM-DD format. + """ + date_split = dates.split('/') + try: + new_date = date_split[2] + '-' + date_split[0] + '-' + date_split[1] + except Exception: + raise ValueError('Please make sure the date is in mm/dd/yyyy format.') + if len(date_split[2]) < 4: + raise ValueError('Please make sure the date is in mm/dd/yyyy format.') + return new_date