Merge branch 'develop'

pull/121/MERGE
13621160019@163.com 2022-01-24 22:15:21 +08:00
commit da93a43819
9 changed files with 1135 additions and 7 deletions

View File

@ -11205,6 +11205,56 @@
},
"response": []
},
{
"name": "GET Meter Carbon Dioxide Emission Report",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/reports/metercarbon?meterid=1&periodtype=daily&baseperiodstartdatetime=2020-08-01T00:00:00&baseperiodenddatetime=2020-09-01T00:00:00&reportingperiodstartdatetime=2020-09-01T00:00:00&reportingperiodenddatetime=2020-10-01T00:00:00",
"host": [
"{{base_url}}"
],
"path": [
"reports",
"metercarbon"
],
"query": [
{
"key": "meterid",
"value": "1"
},
{
"key": "meteruuid",
"value": "eb78f7f9-f26f-463b-92fa-d9daf5b3651c",
"description": "use meterid or meteruuid",
"disabled": true
},
{
"key": "periodtype",
"value": "daily"
},
{
"key": "baseperiodstartdatetime",
"value": "2020-08-01T00:00:00"
},
{
"key": "baseperiodenddatetime",
"value": "2020-09-01T00:00:00"
},
{
"key": "reportingperiodstartdatetime",
"value": "2020-09-01T00:00:00"
},
{
"key": "reportingperiodenddatetime",
"value": "2020-10-01T00:00:00"
}
]
}
},
"response": []
},
{
"name": "GET Meter Cost Report",
"request": {

View File

@ -2360,12 +2360,19 @@ curl -i -X GET {{base_url}}/reports/equipmentstatistics?equipmentid=1&periodtype
```bash
curl -i -X GET {{base_url}}/reports/meterbatch?spaceid=1&reportingperiodstartdatetime=2021-05-01T00:00:00&reportingperiodenddatetime=2021-05-20T11:41:09
```
* GET Meter Carbon Dioxide Emission Report
(parameter meterid can be replaced with meteruuid)
```bash
curl -i -X GET {{base_url}}/reports/metercarbon?meterid=1&periodtype=daily&baseperiodstartdatetime=2020-08-01T00:00:00&baseperiodenddatetime=2020-09-01T00:00:00&reportingperiodstartdatetime=2020-09-01T00:00:00&reportingperiodenddatetime=2020-10-01T00:00:00
```
* GET Meter Cost Report
(parameter meterid can be replaced with meteruuid)
```bash
curl -i -X GET {{base_url}}/reports/metercost?meterid=6&periodtype=daily&baseperiodstartdatetime=2020-08-01T00:00:00&baseperiodenddatetime=2020-09-01T00:00:00&reportingperiodstartdatetime=2020-09-01T00:00:00&reportingperiodenddatetime=2020-10-01T00:00:00
curl -i -X GET {{base_url}}/reports/metercost?meterid=1&periodtype=daily&baseperiodstartdatetime=2020-08-01T00:00:00&baseperiodenddatetime=2020-09-01T00:00:00&reportingperiodstartdatetime=2020-09-01T00:00:00&reportingperiodenddatetime=2020-10-01T00:00:00
```
* GET Meter Energy Report

View File

@ -38,6 +38,7 @@ from reports import fddspacefault
from reports import fddstorefault
from reports import fddtenantfault
from reports import meterenergy
from reports import metercarbon
from reports import metercost
from reports import meterrealtime
from reports import metersubmetersbalance
@ -557,6 +558,8 @@ api.add_route('/reports/fddtenantfault',
fddtenantfault.Reporting())
api.add_route('/reports/meterbatch',
meterbatch.Reporting())
api.add_route('/reports/metercarbon',
metercarbon.Reporting())
api.add_route('/reports/metercost',
metercost.Reporting())
api.add_route('/reports/meterenergy',

View File

@ -73,6 +73,14 @@ myems_reporting_db = {
'password': config('MYEMS_REPORTING_DB_PASSWORD', default='!MyEMS1'),
}
myems_carbon_db = {
'host': config('MYEMS_CARBON_DB_HOST', default='127.0.0.1'),
'port': config('MYEMS_CARBON_DB_PORT', default=3306, cast=int),
'database': config('MYEMS_CARBON_DB_DATABASE', default='myems_carbon_db'),
'user': config('MYEMS_CARBON_DB_USER', default='root'),
'password': config('MYEMS_CARBON_DB_PASSWORD', default='!MyEMS1'),
}
# indicated in how many minutes to calculate meter energy consumption
# 30 for half hourly period
# 60 for hourly period

View File

@ -61,6 +61,13 @@ MYEMS_REPORTING_DB_DATABASE=myems_reporting_db
MYEMS_REPORTING_DB_USER=root
MYEMS_REPORTING_DB_PASSWORD=!MyEMS1
# config for myems_carbon_db
MYEMS_CARBON_DB_HOST=127.0.0.1
MYEMS_CARBON_DB_PORT=3306
MYEMS_CARBON_DB_DATABASE=myems_carbon_db
MYEMS_CARBON_DB_USER=root
MYEMS_CARBON_DB_PASSWORD=!MyEMS1
# indicated in how many minutes to calculate meter energy consumption
# 30 for half hourly period
# 60 for hourly period

View File

@ -0,0 +1,557 @@
import base64
import uuid
import os
import re
from openpyxl.chart import LineChart, Reference, Series
from openpyxl.styles import PatternFill, Border, Side, Alignment, Font
from openpyxl.drawing.image import Image
from openpyxl import Workbook
from openpyxl.chart.label import DataLabelList
import openpyxl.utils.cell as format_cell
########################################################################################################################
# PROCEDURES
# Step 1: Validate the report data
# Step 2: Generate excelexporters file
# Step 3: Encode the excelexporters file to Base64
########################################################################################################################
def export(report, name, reporting_start_datetime_local, reporting_end_datetime_local, period_type):
####################################################################################################################
# Step 1: Validate the report data
####################################################################################################################
if report is None:
return None
if "reporting_period" not in report.keys() or \
"values" not in report['reporting_period'].keys() or len(report['reporting_period']['values']) == 0:
return None
####################################################################################################################
# Step 2: Generate excel file from the report data
####################################################################################################################
filename = generate_excel(report,
name,
reporting_start_datetime_local,
reporting_end_datetime_local,
period_type)
####################################################################################################################
# Step 3: Encode the excel file to Base64
####################################################################################################################
binary_file_data = b''
try:
with open(filename, 'rb') as binary_file:
binary_file_data = binary_file.read()
except IOError as ex:
pass
# Base64 encode the bytes
base64_encoded_data = base64.b64encode(binary_file_data)
# get the Base64 encoded data using human-readable characters.
base64_message = base64_encoded_data.decode('utf-8')
# delete the file from server
try:
os.remove(filename)
except NotImplementedError as ex:
pass
return base64_message
def generate_excel(report, name, reporting_start_datetime_local, reporting_end_datetime_local, period_type):
wb = Workbook()
# todo
ws = wb.active
ws.title = "MeterCarbon"
# Row height
ws.row_dimensions[1].height = 102
for i in range(2, 2000 + 1):
ws.row_dimensions[i].height = 42
# Col width
ws.column_dimensions['A'].width = 1.5
ws.column_dimensions['B'].width = 25.0
for i in range(ord('C'), ord('L')):
ws.column_dimensions[chr(i)].width = 15.0
# Font
name_font = Font(name='Arial', size=15, bold=True)
title_font = Font(name='Arial', size=15, bold=True)
table_fill = PatternFill(fill_type='solid', fgColor='1F497D')
f_border = Border(left=Side(border_style='medium', color='00000000'),
right=Side(border_style='medium', color='00000000'),
bottom=Side(border_style='medium', color='00000000'),
top=Side(border_style='medium', color='00000000')
)
b_border = Border(
bottom=Side(border_style='medium', color='00000000'),
)
b_c_alignment = Alignment(vertical='bottom',
horizontal='center',
text_rotation=0,
wrap_text=True,
shrink_to_fit=False,
indent=0)
c_c_alignment = Alignment(vertical='center',
horizontal='center',
text_rotation=0,
wrap_text=True,
shrink_to_fit=False,
indent=0)
b_r_alignment = Alignment(vertical='bottom',
horizontal='right',
text_rotation=0,
wrap_text=True,
shrink_to_fit=False,
indent=0)
# Img
img = Image("excelexporters/myems.png")
ws.add_image(img, 'A1')
# Title
ws['B3'].alignment = b_r_alignment
ws['B3'] = 'Name:'
ws['C3'].border = b_border
ws['C3'].alignment = b_c_alignment
ws['C3'] = name
ws['D3'].alignment = b_r_alignment
ws['D3'] = 'Period:'
ws['E3'].border = b_border
ws['E3'].alignment = b_c_alignment
ws['E3'] = period_type
ws['B4'].alignment = b_r_alignment
ws['B4'] = 'Reporting Start Datetime:'
ws['C4'].border = b_border
ws['C4'].alignment = b_c_alignment
ws['C4'] = reporting_start_datetime_local
ws['D4'].alignment = b_r_alignment
ws['D4'] = 'Reporting End Datetime:'
ws['E4'].border = b_border
ws['E4'].alignment = b_c_alignment
ws['E4'] = reporting_end_datetime_local
if "reporting_period" not in report.keys() or \
"values" not in report['reporting_period'].keys() or len(report['reporting_period']['values']) == 0:
filename = str(uuid.uuid4()) + '.xlsx'
wb.save(filename)
return filename
####################################################################################################################
has_carbon_data_flag = True
if "values" not in report['reporting_period'].keys() or len(report['reporting_period']['values']) == 0:
has_carbon_data_flag = False
if has_carbon_data_flag:
ws['B6'].font = title_font
ws['B6'] = name + 'Reporting Period Carbon Dioxide Emissions'
reporting_period_data = report['reporting_period']
category = report['meter']['energy_category_name']
ca_len = len(category)
ws.row_dimensions[7].height = 60
ws['B7'].fill = table_fill
ws['B7'].border = f_border
ws['B8'].font = title_font
ws['B8'].alignment = c_c_alignment
ws['B8'] = 'Carbon Dioxide Emission'
ws['B8'].border = f_border
ws['B9'].font = title_font
ws['B9'].alignment = c_c_alignment
ws['B9'] = 'Increment Rate'
ws['B9'].border = f_border
col = 'B'
for i in range(0, ca_len):
col = chr(ord('C') + i)
ws[col + '7'].fill = table_fill
ws[col + '7'].font = name_font
ws[col + '7'].alignment = c_c_alignment
ws[col + '7'] = report['meter']['energy_category_name'] + " (" + report['meter']['unit_of_measure'] + ")"
ws[col + '7'].border = f_border
ws[col + '8'].font = name_font
ws[col + '8'].alignment = c_c_alignment
ws[col + '8'] = round(reporting_period_data['total_in_category'], 2)
ws[col + '8'].border = f_border
ws[col + '9'].font = name_font
ws[col + '9'].alignment = c_c_alignment
ws[col + '9'] = str(round(reporting_period_data['increment_rate'] * 100, 2)) + "%" \
if reporting_period_data['increment_rate'] is not None else "-"
ws[col + '9'].border = f_border
# TCE TCO2E
end_col = col
# TCE
tce_col = chr(ord(end_col) + 1)
ws[tce_col + '7'].fill = table_fill
ws[tce_col + '7'].font = name_font
ws[tce_col + '7'].alignment = c_c_alignment
ws[tce_col + '7'] = 'Ton of Standard Coal (TCE)'
ws[tce_col + '7'].border = f_border
ws[tce_col + '8'].font = name_font
ws[tce_col + '8'].alignment = c_c_alignment
ws[tce_col + '8'] = round(reporting_period_data['total_in_kgce'] / 1000, 2)
ws[tce_col + '8'].border = f_border
ws[tce_col + '9'].font = name_font
ws[tce_col + '9'].alignment = c_c_alignment
ws[tce_col + '9'] = str(round(reporting_period_data['increment_rate'] * 100, 2)) + "%" \
if reporting_period_data['increment_rate'] is not None else "-"
ws[tce_col + '9'].border = f_border
# TCO2E
tco2e_col = chr(ord(end_col) + 2)
ws[tco2e_col + '7'].fill = table_fill
ws[tco2e_col + '7'].font = name_font
ws[tco2e_col + '7'].alignment = c_c_alignment
ws[tco2e_col + '7'] = 'Ton of Carbon Dioxide Emissions (TCO2E)'
ws[tco2e_col + '7'].border = f_border
ws[tco2e_col + '8'].font = name_font
ws[tco2e_col + '8'].alignment = c_c_alignment
ws[tco2e_col + '8'] = round(reporting_period_data['total_in_kgco2e'] / 1000, 2)
ws[tco2e_col + '8'].border = f_border
ws[tco2e_col + '9'].font = name_font
ws[tco2e_col + '9'].alignment = c_c_alignment
ws[tco2e_col + '9'] = str(round(reporting_period_data['increment_rate'] * 100, 2)) + "%" \
if reporting_period_data['increment_rate'] is not None else "-"
ws[tco2e_col + '9'].border = f_border
else:
for i in range(6, 9 + 1):
ws.rows_dimensions[i].height = 0.1
####################################################################################################################
has_carbon_detail_flag = True
reporting_period_data = report['reporting_period']
category = report['meter']['energy_category_name']
ca_len = len(category)
times = reporting_period_data['timestamps']
parameters_names_len = len(report['parameters']['names'])
parameters_data = report['parameters']
parameters_parameters_datas_len = 0
for i in range(0, parameters_names_len):
if len(parameters_data['timestamps'][i]) == 0:
continue
parameters_parameters_datas_len += 1
if "values" not in reporting_period_data.keys() or len(reporting_period_data['values']) == 0:
has_carbon_detail_flag = False
if has_carbon_detail_flag:
start_detail_data_row_number = 13 + (parameters_parameters_datas_len + ca_len) * 6
ws['B11'].font = title_font
ws['B11'] = name + 'Detailed Data'
ws.row_dimensions[start_detail_data_row_number].height = 60
ws['B' + str(start_detail_data_row_number)].fill = table_fill
ws['B' + str(start_detail_data_row_number)].font = title_font
ws['B' + str(start_detail_data_row_number)].border = f_border
ws['B' + str(start_detail_data_row_number)].alignment = c_c_alignment
ws['B' + str(start_detail_data_row_number)] = 'Datetime'
time = times
has_data = False
max_row = 0
if len(time) > 0:
has_data = True
max_row = start_detail_data_row_number + len(time)
if has_data:
end_data_row_number = start_detail_data_row_number
for i in range(0, len(time)):
col = 'B'
end_data_row_number += 1
row = str(end_data_row_number)
ws[col + row].font = title_font
ws[col + row].alignment = c_c_alignment
ws[col + row] = time[i]
ws[col + row].border = f_border
ws['B' + str(end_data_row_number + 1)].font = title_font
ws['B' + str(end_data_row_number + 1)].alignment = c_c_alignment
ws['B' + str(end_data_row_number + 1)] = 'Total'
ws['B' + str(end_data_row_number + 1)].border = f_border
for i in range(0, ca_len):
col = chr(ord('C') + i)
ws[col + str(start_detail_data_row_number)].fill = table_fill
ws[col + str(start_detail_data_row_number)].font = title_font
ws[col + str(start_detail_data_row_number)].alignment = c_c_alignment
ws[col + str(start_detail_data_row_number)] = \
report['meter']['energy_category_name']+" (" + report['meter']['unit_of_measure'] + ")"
ws[col + str(start_detail_data_row_number)].border = f_border
time = times
time_len = len(time)
for j in range(0, time_len):
row = str(start_detail_data_row_number + 1 + j)
ws[col + row].font = title_font
ws[col + row].alignment = c_c_alignment
ws[col + row] = round(reporting_period_data['values'][j], 2)
ws[col + row].border = f_border
ws[col + str(end_data_row_number + 1)].font = title_font
ws[col + str(end_data_row_number + 1)].alignment = c_c_alignment
ws[col + str(end_data_row_number + 1)] = round(reporting_period_data['total_in_category'], 2)
ws[col + str(end_data_row_number + 1)].border = f_border
line = LineChart()
line.title = 'Reporting Period Carbon Dioxide Emissions - ' + report['meter']['energy_category_name'] + \
" (" + report['meter']['unit_of_measure'] + ")"
line_data = Reference(ws, min_col=3, min_row=start_detail_data_row_number, max_row=max_row)
line.series.append(Series(line_data, title_from_data=True))
labels = Reference(ws, min_col=2, min_row=start_detail_data_row_number + 1, max_row=max_row)
line.set_categories(labels)
line_data = line.series[0]
line_data.marker.symbol = "circle"
line_data.smooth = True
line.x_axis.crosses = 'min'
line.dLbls = DataLabelList()
line.dLbls.dLblPos = 't'
line.dLbls.showVal = True
line.height = 8.25
line.width = 24
ws.add_chart(line, "B12")
else:
for i in range(11, 43 + 1):
ws.row_dimensions[i].height = 0.0
####################################################################################################################
has_parameters_names_and_timestamps_and_values_data = True
# 12 is the starting line number of the last line chart in the report period
time_len = len(reporting_period_data['timestamps'])
current_sheet_parameters_row_number = 12 + ca_len * 6
if 'parameters' not in report.keys() or \
report['parameters'] is None or \
'names' not in report['parameters'].keys() or \
report['parameters']['names'] is None or \
len(report['parameters']['names']) == 0 or \
'timestamps' not in report['parameters'].keys() or \
report['parameters']['timestamps'] is None or \
len(report['parameters']['timestamps']) == 0 or \
'values' not in report['parameters'].keys() or \
report['parameters']['values'] is None or \
len(report['parameters']['values']) == 0 or \
timestamps_data_all_equal_0(report['parameters']['timestamps']):
has_parameters_names_and_timestamps_and_values_data = False
if has_parameters_names_and_timestamps_and_values_data:
################################################################################################################
# new worksheet
################################################################################################################
parameters_data = report['parameters']
parameters_names_len = len(parameters_data['names'])
file_name = (re.sub(r'[^A-Z]', '', ws.title))+'_'
parameters_ws = wb.create_sheet(file_name + 'Parameters')
parameters_timestamps_data_max_len = \
get_parameters_timestamps_lists_max_len(list(parameters_data['timestamps']))
# Row height
parameters_ws.row_dimensions[1].height = 102
for i in range(2, 7 + 1):
parameters_ws.row_dimensions[i].height = 42
for i in range(8, parameters_timestamps_data_max_len + 10):
parameters_ws.row_dimensions[i].height = 60
# Col width
parameters_ws.column_dimensions['A'].width = 1.5
parameters_ws.column_dimensions['B'].width = 25.0
for i in range(3, 12 + parameters_names_len * 3):
parameters_ws.column_dimensions[format_cell.get_column_letter(i)].width = 15.0
# Img
img = Image("excelexporters/myems.png")
parameters_ws.add_image(img, 'A1')
# Title
parameters_ws['B3'].alignment = b_r_alignment
parameters_ws['B3'] = 'Name:'
parameters_ws['C3'].border = b_border
parameters_ws['C3'].alignment = b_c_alignment
parameters_ws['C3'] = name
parameters_ws['D3'].alignment = b_r_alignment
parameters_ws['D3'] = 'Period:'
parameters_ws['E3'].border = b_border
parameters_ws['E3'].alignment = b_c_alignment
parameters_ws['E3'] = period_type
parameters_ws['B4'].alignment = b_r_alignment
parameters_ws['B4'] = 'Reporting Start Datetime:'
parameters_ws['C4'].border = b_border
parameters_ws['C4'].alignment = b_c_alignment
parameters_ws['C4'] = reporting_start_datetime_local
parameters_ws['D4'].alignment = b_r_alignment
parameters_ws['D4'] = 'Reporting End Datetime:'
parameters_ws['E4'].border = b_border
parameters_ws['E4'].alignment = b_c_alignment
parameters_ws['E4'] = reporting_end_datetime_local
parameters_ws_current_row_number = 6
parameters_ws['B' + str(parameters_ws_current_row_number)].font = title_font
parameters_ws['B' + str(parameters_ws_current_row_number)] = name + ' ' + 'Parameters'
parameters_ws_current_row_number += 1
parameters_table_start_row_number = parameters_ws_current_row_number
parameters_ws.row_dimensions[parameters_ws_current_row_number].height = 80
parameters_ws_current_row_number += 1
table_current_col_number = 2
for i in range(0, parameters_names_len):
if len(parameters_data['timestamps'][i]) == 0:
continue
col = format_cell.get_column_letter(table_current_col_number)
parameters_ws[col + str(parameters_ws_current_row_number - 1)].fill = table_fill
parameters_ws[col + str(parameters_ws_current_row_number - 1)].border = f_border
col = format_cell.get_column_letter(table_current_col_number + 1)
parameters_ws[col + str(parameters_ws_current_row_number - 1)].fill = table_fill
parameters_ws[col + str(parameters_ws_current_row_number - 1)].border = f_border
parameters_ws[col + str(parameters_ws_current_row_number - 1)].font = name_font
parameters_ws[col + str(parameters_ws_current_row_number - 1)].alignment = c_c_alignment
parameters_ws[col + str(parameters_ws_current_row_number - 1)] = parameters_data['names'][i]
table_current_row_number = parameters_ws_current_row_number
for j, value in enumerate(list(parameters_data['timestamps'][i])):
col = format_cell.get_column_letter(table_current_col_number)
parameters_ws[col + str(table_current_row_number)].border = f_border
parameters_ws[col + str(table_current_row_number)].font = title_font
parameters_ws[col + str(table_current_row_number)].alignment = c_c_alignment
parameters_ws[col + str(table_current_row_number)] = value
col = format_cell.get_column_letter(table_current_col_number + 1)
parameters_ws[col + str(table_current_row_number)].border = f_border
parameters_ws[col + str(table_current_row_number)].font = title_font
parameters_ws[col + str(table_current_row_number)].alignment = c_c_alignment
parameters_ws[col + str(table_current_row_number)] = round(parameters_data['values'][i][j], 2)
table_current_row_number += 1
table_current_col_number = table_current_col_number + 3
################################################################################################################
# parameters chart and parameters table
################################################################################################################
ws['B' + str(current_sheet_parameters_row_number)].font = title_font
ws['B' + str(current_sheet_parameters_row_number)] = name + ' ' + 'Parameters'
current_sheet_parameters_row_number += 1
chart_start_row_number = current_sheet_parameters_row_number
col_index = 0
for i in range(0, parameters_names_len):
if len(parameters_data['timestamps'][i]) == 0:
continue
line = LineChart()
data_col = 3 + col_index * 3
labels_col = 2 + col_index * 3
col_index += 1
line.title = 'Parameters - ' + \
parameters_ws.cell(row=parameters_table_start_row_number, column=data_col).value
labels = Reference(parameters_ws, min_col=labels_col, min_row=parameters_table_start_row_number + 1,
max_row=(len(parameters_data['timestamps'][i]) + parameters_table_start_row_number))
line_data = Reference(parameters_ws, min_col=data_col, min_row=parameters_table_start_row_number,
max_row=(len(parameters_data['timestamps'][i]) + parameters_table_start_row_number))
line.add_data(line_data, titles_from_data=True)
line.set_categories(labels)
line_data = line.series[0]
line_data.marker.symbol = "circle"
line_data.smooth = True
line.x_axis.crosses = 'min'
line.height = 8.25
line.width = 24
line.dLbls = DataLabelList()
line.dLbls.dLblPos = 't'
line.dLbls.showVal = False
line.dLbls.showPercent = False
chart_col = 'B'
chart_cell = chart_col + str(chart_start_row_number)
chart_start_row_number += 6
ws.add_chart(line, chart_cell)
current_sheet_parameters_row_number = chart_start_row_number
current_sheet_parameters_row_number += 1
filename = str(uuid.uuid4()) + '.xlsx'
wb.save(filename)
return filename
def timestamps_data_all_equal_0(lists):
for i, value in enumerate(list(lists)):
if len(value) > 0:
return False
return True
def get_parameters_timestamps_lists_max_len(parameters_timestamps_lists):
max_len = 0
for i, value in enumerate(list(parameters_timestamps_lists)):
if len(value) > max_len:
max_len = len(value)
return max_len
def timestamps_data_not_equal_0(lists):
number = 0
for i, value in enumerate(list(lists)):
if len(value) > 0:
number += 1
return number

View File

@ -242,7 +242,7 @@ def generate_excel(report, name, reporting_start_datetime_local, reporting_end_d
####################################################################################################################
has_cost_datail_flag = True
has_cost_detail_flag = True
reporting_period_data = report['reporting_period']
category = report['meter']['energy_category_name']
ca_len = len(category)
@ -256,9 +256,9 @@ def generate_excel(report, name, reporting_start_datetime_local, reporting_end_d
parameters_parameters_datas_len += 1
if "values" not in reporting_period_data.keys() or len(reporting_period_data['values']) == 0:
has_cost_datail_flag = False
has_cost_detail_flag = False
if has_cost_datail_flag:
if has_cost_detail_flag:
start_detail_data_row_number = 13 + (parameters_parameters_datas_len + ca_len) * 6
ws['B11'].font = title_font

View File

@ -241,16 +241,16 @@ def generate_excel(report, name, reporting_start_datetime_local, reporting_end_d
####################################################################################################################
has_cost_datail_flag = True
has_cost_detail_flag = True
reporting_period_data = report['reporting_period']
category = report['offline_meter']['energy_category_name']
ca_len = len(category)
times = reporting_period_data['timestamps']
if "values" not in reporting_period_data.keys() or len(reporting_period_data['values']) == 0:
has_cost_datail_flag = False
has_cost_detail_flag = False
if has_cost_datail_flag:
if has_cost_detail_flag:
ws['B11'].font = title_font
ws['B11'] = name + 'Detailed Data'

View File

@ -0,0 +1,496 @@
import re
import falcon
import simplejson as json
import mysql.connector
import config
from datetime import datetime, timedelta, timezone
from core import utilities
from decimal import Decimal
import excelexporters.metercarbon
class Reporting:
@staticmethod
def __init__():
""""Initializes Reporting"""
pass
@staticmethod
def on_options(req, resp):
resp.status = falcon.HTTP_200
####################################################################################################################
# PROCEDURES
# Step 1: valid parameters
# Step 2: query the meter and energy category
# Step 3: query associated points
# Step 4: query base period energy consumption
# Step 5: query base period carbon dioxide emission
# Step 6: query reporting period energy consumption
# Step 7: query reporting period carbon dioxide emission
# Step 8: query tariff data
# Step 9: query associated points data
# Step 10: construct the report
####################################################################################################################
@staticmethod
def on_get(req, resp):
print(req.params)
meter_id = req.params.get('meterid')
meter_uuid = req.params.get('meteruuid')
period_type = req.params.get('periodtype')
base_period_start_datetime_local = req.params.get('baseperiodstartdatetime')
base_period_end_datetime_local = req.params.get('baseperiodenddatetime')
reporting_period_start_datetime_local = req.params.get('reportingperiodstartdatetime')
reporting_period_end_datetime_local = req.params.get('reportingperiodenddatetime')
################################################################################################################
# Step 1: valid parameters
################################################################################################################
if meter_id is None and meter_uuid is None:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_METER_ID')
if meter_id is not None:
meter_id = str.strip(meter_id)
if not meter_id.isdigit() or int(meter_id) <= 0:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_METER_ID')
if meter_uuid is not None:
meter_uuid = str.strip(meter_uuid)
regex = re.compile('^[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}\Z', re.I)
match = regex.match(meter_uuid)
if not bool(match):
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_METER_UUID')
if period_type is None:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_PERIOD_TYPE')
else:
period_type = str.strip(period_type)
if period_type not in ['hourly', 'daily', 'weekly', 'monthly', 'yearly']:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_PERIOD_TYPE')
timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
if config.utc_offset[0] == '-':
timezone_offset = -timezone_offset
base_start_datetime_utc = None
if base_period_start_datetime_local is not None and len(str.strip(base_period_start_datetime_local)) > 0:
base_period_start_datetime_local = str.strip(base_period_start_datetime_local)
try:
base_start_datetime_utc = datetime.strptime(base_period_start_datetime_local, '%Y-%m-%dT%H:%M:%S')
except ValueError:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description="API.INVALID_BASE_PERIOD_START_DATETIME")
base_start_datetime_utc = base_start_datetime_utc.replace(tzinfo=timezone.utc) - \
timedelta(minutes=timezone_offset)
base_end_datetime_utc = None
if base_period_end_datetime_local is not None and len(str.strip(base_period_end_datetime_local)) > 0:
base_period_end_datetime_local = str.strip(base_period_end_datetime_local)
try:
base_end_datetime_utc = datetime.strptime(base_period_end_datetime_local, '%Y-%m-%dT%H:%M:%S')
except ValueError:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description="API.INVALID_BASE_PERIOD_END_DATETIME")
base_end_datetime_utc = base_end_datetime_utc.replace(tzinfo=timezone.utc) - \
timedelta(minutes=timezone_offset)
if base_start_datetime_utc is not None and base_end_datetime_utc is not None and \
base_start_datetime_utc >= base_end_datetime_utc:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description='API.INVALID_BASE_PERIOD_END_DATETIME')
if reporting_period_start_datetime_local is None:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
else:
reporting_period_start_datetime_local = str.strip(reporting_period_start_datetime_local)
try:
reporting_start_datetime_utc = datetime.strptime(reporting_period_start_datetime_local,
'%Y-%m-%dT%H:%M:%S')
except ValueError:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
reporting_start_datetime_utc = reporting_start_datetime_utc.replace(tzinfo=timezone.utc) - \
timedelta(minutes=timezone_offset)
if reporting_period_end_datetime_local is None:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
else:
reporting_period_end_datetime_local = str.strip(reporting_period_end_datetime_local)
try:
reporting_end_datetime_utc = datetime.strptime(reporting_period_end_datetime_local,
'%Y-%m-%dT%H:%M:%S')
except ValueError:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
reporting_end_datetime_utc = reporting_end_datetime_utc.replace(tzinfo=timezone.utc) - \
timedelta(minutes=timezone_offset)
if reporting_start_datetime_utc >= reporting_end_datetime_utc:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
################################################################################################################
# Step 2: query the meter and energy category
################################################################################################################
cnx_system = mysql.connector.connect(**config.myems_system_db)
cursor_system = cnx_system.cursor()
cnx_energy = mysql.connector.connect(**config.myems_energy_db)
cursor_energy = cnx_energy.cursor()
cnx_carbon = mysql.connector.connect(**config.myems_carbon_db)
cursor_carbon = cnx_carbon.cursor()
cnx_historical = mysql.connector.connect(**config.myems_historical_db)
cursor_historical = cnx_historical.cursor()
if meter_id is not None:
cursor_system.execute(" SELECT m.id, m.name, m.cost_center_id, m.energy_category_id, "
" ec.name, ec.unit_of_measure, ec.kgce, ec.kgco2e "
" FROM tbl_meters m, tbl_energy_categories ec "
" WHERE m.id = %s AND m.energy_category_id = ec.id ", (meter_id,))
row_meter = cursor_system.fetchone()
elif meter_uuid is not None:
cursor_system.execute(" SELECT m.id, m.name, m.cost_center_id, m.energy_category_id, "
" ec.name, ec.unit_of_measure, ec.kgce, ec.kgco2e "
" FROM tbl_meters m, tbl_energy_categories ec "
" WHERE m.uuid = %s AND m.energy_category_id = ec.id ", (meter_uuid,))
row_meter = cursor_system.fetchone()
if row_meter is None:
if cursor_system:
cursor_system.close()
if cnx_system:
cnx_system.disconnect()
if cursor_energy:
cursor_energy.close()
if cnx_energy:
cnx_energy.disconnect()
if cursor_carbon:
cursor_carbon.close()
if cnx_carbon:
cnx_carbon.disconnect()
if cursor_historical:
cursor_historical.close()
if cnx_historical:
cnx_historical.disconnect()
raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND', description='API.METER_NOT_FOUND')
if meter_id is not None and int(meter_id) != int(row_meter[0]):
raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND', description='API.METER_NOT_FOUND')
meter = dict()
meter['id'] = row_meter[0]
meter['name'] = row_meter[1]
meter['cost_center_id'] = row_meter[2]
meter['energy_category_id'] = row_meter[3]
meter['energy_category_name'] = row_meter[4]
meter['unit_of_measure'] = config.currency_unit
meter['kgce'] = row_meter[6]
meter['kgco2e'] = row_meter[7]
################################################################################################################
# Step 3: query associated points
################################################################################################################
point_list = list()
cursor_system.execute(" SELECT p.id, p.name, p.units, p.object_type "
" FROM tbl_meters m, tbl_meters_points mp, tbl_points p "
" WHERE m.id = %s AND m.id = mp.meter_id AND mp.point_id = p.id "
" ORDER BY p.id ", (meter['id'],))
rows_points = cursor_system.fetchall()
if rows_points is not None and len(rows_points) > 0:
for row in rows_points:
point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
################################################################################################################
# Step 4: query base period energy consumption
################################################################################################################
query = (" SELECT start_datetime_utc, actual_value "
" FROM tbl_meter_hourly "
" WHERE meter_id = %s "
" AND start_datetime_utc >= %s "
" AND start_datetime_utc < %s "
" ORDER BY start_datetime_utc ")
cursor_energy.execute(query, (meter['id'], base_start_datetime_utc, base_end_datetime_utc))
rows_meter_hourly = cursor_energy.fetchall()
rows_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_meter_hourly,
base_start_datetime_utc,
base_end_datetime_utc,
period_type)
base = dict()
base['timestamps'] = list()
base['values'] = list()
base['total_in_category'] = Decimal(0.0)
base['total_in_kgce'] = Decimal(0.0)
base['total_in_kgco2e'] = Decimal(0.0)
for row_meter_periodically in rows_meter_periodically:
current_datetime_local = row_meter_periodically[0].replace(tzinfo=timezone.utc) + \
timedelta(minutes=timezone_offset)
if period_type == 'hourly':
current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
elif period_type == 'daily':
current_datetime = current_datetime_local.strftime('%Y-%m-%d')
elif period_type == 'weekly':
current_datetime = current_datetime_local.strftime('%Y-%m-%d')
elif period_type == 'monthly':
current_datetime = current_datetime_local.strftime('%Y-%m')
elif period_type == 'yearly':
current_datetime = current_datetime_local.strftime('%Y')
actual_value = Decimal(0.0) if row_meter_periodically[1] is None \
else row_meter_periodically[1]
base['timestamps'].append(current_datetime)
base['total_in_kgce'] += actual_value * meter['kgce']
base['total_in_kgco2e'] += actual_value * meter['kgco2e']
################################################################################################################
# Step 5: query base period carbon dioxide emission
################################################################################################################
query = (" SELECT start_datetime_utc, actual_value "
" FROM tbl_meter_hourly "
" WHERE meter_id = %s "
" AND start_datetime_utc >= %s "
" AND start_datetime_utc < %s "
" ORDER BY start_datetime_utc ")
cursor_carbon.execute(query, (meter['id'], base_start_datetime_utc, base_end_datetime_utc))
rows_meter_hourly = cursor_carbon.fetchall()
rows_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_meter_hourly,
base_start_datetime_utc,
base_end_datetime_utc,
period_type)
base['values'] = list()
base['total_in_category'] = Decimal(0.0)
for row_meter_periodically in rows_meter_periodically:
actual_value = Decimal(0.0) if row_meter_periodically[1] is None \
else row_meter_periodically[1]
base['values'].append(actual_value)
base['total_in_category'] += actual_value
################################################################################################################
# Step 6: query reporting period energy consumption
################################################################################################################
query = (" SELECT start_datetime_utc, actual_value "
" FROM tbl_meter_hourly "
" WHERE meter_id = %s "
" AND start_datetime_utc >= %s "
" AND start_datetime_utc < %s "
" ORDER BY start_datetime_utc ")
cursor_energy.execute(query, (meter['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
rows_meter_hourly = cursor_energy.fetchall()
rows_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_meter_hourly,
reporting_start_datetime_utc,
reporting_end_datetime_utc,
period_type)
reporting = dict()
reporting['timestamps'] = list()
reporting['values'] = list()
reporting['total_in_category'] = Decimal(0.0)
reporting['total_in_kgce'] = Decimal(0.0)
reporting['total_in_kgco2e'] = Decimal(0.0)
for row_meter_periodically in rows_meter_periodically:
current_datetime_local = row_meter_periodically[0].replace(tzinfo=timezone.utc) + \
timedelta(minutes=timezone_offset)
if period_type == 'hourly':
current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
elif period_type == 'daily':
current_datetime = current_datetime_local.strftime('%Y-%m-%d')
elif period_type == 'weekly':
current_datetime = current_datetime_local.strftime('%Y-%m-%d')
elif period_type == 'monthly':
current_datetime = current_datetime_local.strftime('%Y-%m')
elif period_type == 'yearly':
current_datetime = current_datetime_local.strftime('%Y')
actual_value = Decimal(0.0) if row_meter_periodically[1] is None \
else row_meter_periodically[1]
reporting['timestamps'].append(current_datetime)
reporting['total_in_kgce'] += actual_value * meter['kgce']
reporting['total_in_kgco2e'] += actual_value * meter['kgco2e']
################################################################################################################
# Step 7: query reporting period carbon dioxide emission
################################################################################################################
query = (" SELECT start_datetime_utc, actual_value "
" FROM tbl_meter_hourly "
" WHERE meter_id = %s "
" AND start_datetime_utc >= %s "
" AND start_datetime_utc < %s "
" ORDER BY start_datetime_utc ")
cursor_carbon.execute(query, (meter['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
rows_meter_hourly = cursor_carbon.fetchall()
rows_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_meter_hourly,
reporting_start_datetime_utc,
reporting_end_datetime_utc,
period_type)
for row_meter_periodically in rows_meter_periodically:
actual_value = Decimal(0.0) if row_meter_periodically[1] is None \
else row_meter_periodically[1]
reporting['values'].append(actual_value)
reporting['total_in_category'] += actual_value
################################################################################################################
# Step 8: query tariff data
################################################################################################################
parameters_data = dict()
parameters_data['names'] = list()
parameters_data['timestamps'] = list()
parameters_data['values'] = list()
tariff_dict = utilities.get_energy_category_tariffs(meter['cost_center_id'],
meter['energy_category_id'],
reporting_start_datetime_utc,
reporting_end_datetime_utc)
tariff_timestamp_list = list()
tariff_value_list = list()
for k, v in tariff_dict.items():
# convert k from utc to local
k = k + timedelta(minutes=timezone_offset)
tariff_timestamp_list.append(k.isoformat()[0:19])
tariff_value_list.append(v)
parameters_data['names'].append('TARIFF-' + meter['energy_category_name'])
parameters_data['timestamps'].append(tariff_timestamp_list)
parameters_data['values'].append(tariff_value_list)
################################################################################################################
# Step 9: query associated points data
################################################################################################################
for point in point_list:
point_values = []
point_timestamps = []
if point['object_type'] == 'ANALOG_VALUE':
query = (" SELECT utc_date_time, actual_value "
" FROM tbl_analog_value "
" WHERE point_id = %s "
" AND utc_date_time BETWEEN %s AND %s "
" ORDER BY utc_date_time ")
cursor_historical.execute(query, (point['id'],
reporting_start_datetime_utc,
reporting_end_datetime_utc))
rows = cursor_historical.fetchall()
if rows is not None and len(rows) > 0:
for row in rows:
current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
timedelta(minutes=timezone_offset)
current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
point_timestamps.append(current_datetime)
point_values.append(row[1])
elif point['object_type'] == 'ENERGY_VALUE':
query = (" SELECT utc_date_time, actual_value "
" FROM tbl_energy_value "
" WHERE point_id = %s "
" AND utc_date_time BETWEEN %s AND %s "
" ORDER BY utc_date_time ")
cursor_historical.execute(query, (point['id'],
reporting_start_datetime_utc,
reporting_end_datetime_utc))
rows = cursor_historical.fetchall()
if rows is not None and len(rows) > 0:
for row in rows:
current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
timedelta(minutes=timezone_offset)
current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
point_timestamps.append(current_datetime)
point_values.append(row[1])
elif point['object_type'] == 'DIGITAL_VALUE':
query = (" SELECT utc_date_time, actual_value "
" FROM tbl_digital_value "
" WHERE point_id = %s "
" AND utc_date_time BETWEEN %s AND %s "
" ORDER BY utc_date_time ")
cursor_historical.execute(query, (point['id'],
reporting_start_datetime_utc,
reporting_end_datetime_utc))
rows = cursor_historical.fetchall()
if rows is not None and len(rows) > 0:
for row in rows:
current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
timedelta(minutes=timezone_offset)
current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
point_timestamps.append(current_datetime)
point_values.append(row[1])
parameters_data['names'].append(point['name'] + ' (' + point['units'] + ')')
parameters_data['timestamps'].append(point_timestamps)
parameters_data['values'].append(point_values)
################################################################################################################
# Step 10: construct the report
################################################################################################################
if cursor_system:
cursor_system.close()
if cnx_system:
cnx_system.disconnect()
if cursor_energy:
cursor_energy.close()
if cnx_energy:
cnx_energy.disconnect()
if cursor_carbon:
cursor_carbon.close()
if cnx_carbon:
cnx_carbon.disconnect()
if cursor_historical:
cursor_historical.close()
if cnx_historical:
cnx_historical.disconnect()
result = {
"meter": {
"cost_center_id": meter['cost_center_id'],
"energy_category_id": meter['energy_category_id'],
"energy_category_name": meter['energy_category_name'],
"unit_of_measure": 'KG',
"kgce": meter['kgce'],
"kgco2e": meter['kgco2e'],
},
"base_period": {
"total_in_category": base['total_in_category'],
"total_in_kgce": base['total_in_kgce'],
"total_in_kgco2e": base['total_in_kgco2e'],
"timestamps": base['timestamps'],
"values": base['values'],
},
"reporting_period": {
"increment_rate":
(reporting['total_in_category']-base['total_in_category'])/base['total_in_category']
if base['total_in_category'] > 0 else None,
"total_in_category": reporting['total_in_category'],
"total_in_kgce": reporting['total_in_kgce'],
"total_in_kgco2e": reporting['total_in_kgco2e'],
"timestamps": reporting['timestamps'],
"values": reporting['values'],
},
"parameters": {
"names": parameters_data['names'],
"timestamps": parameters_data['timestamps'],
"values": parameters_data['values']
},
}
# export result to Excel file and then encode the file to base64 string
result['excel_bytes_base64'] = \
excelexporters.metercarbon.export(result,
meter['name'],
reporting_period_start_datetime_local,
reporting_period_end_datetime_local,
period_type)
resp.text = json.dumps(result)