added Virtual Meter Carbon Dioxide Emissions Report

pull/127/head
13621160019@163.com 2022-02-20 18:22:20 +08:00
parent 7e5c39bae7
commit 37d27a1492
11 changed files with 1364 additions and 10 deletions

View File

@ -432,6 +432,7 @@ VALUES
(310,'Batch Analysis','/meter/batch',300,0),
(311,'Meter Tracking','/meter/tracking',300,0),
(312,'Meter Carbon','/meter/metercarbon',300,0),
(313,'Virtual Meter Carbon','/meter/virtualmetercarbon',300,0),
(400,'Tenant Data','/tenant',NULL,0),
(401,'Energy Category Data','/tenant/energycategory',400,0),
(402,'Energy Item Data','/tenant/energyitem',400,0),

View File

@ -8,7 +8,8 @@ START TRANSACTION;
INSERT INTO myems_system_db.tbl_menus (id,name,route, parent_menu_id,is_hidden)
VALUES
(312,'Meter Carbon','/meter/metercarbon',300,0);
(312,'Meter Carbon','/meter/metercarbon',300,0),
(313,'Virtual Meter Carbon','/meter/virtualmetercarbon',300,0);
-- UPDATE VERSION NUMBER
UPDATE `myems_system_db`.`tbl_versions` SET version='1.8.0', release_date='2022-02-28' WHERE id=1;

View File

@ -79,6 +79,7 @@ from reports import tenantbatch
from reports import tenantsaving
from reports import tenantstatistics
from reports import virtualmeterenergy
from reports import virtualmetercarbon
from reports import virtualmetercost
########################################################################################################################
@ -640,6 +641,8 @@ api.add_route('/reports/tenantstatistics',
tenantstatistics.Reporting())
api.add_route('/reports/virtualmeterenergy',
virtualmeterenergy.Reporting())
api.add_route('/reports/virtualmetercarbon',
virtualmetercarbon.Reporting())
api.add_route('/reports/virtualmetercost',
virtualmetercost.Reporting())

View File

@ -0,0 +1,339 @@
import base64
import uuid
import os
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
########################################################################################################################
# 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()
ws = wb.active
ws.title = "VirtualMeterCarbonDioxideEmissions"
# 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_values_flag = True
if "values" not in report['reporting_period'].keys() or len(report['reporting_period']['values']) == 0:
has_values_flag = False
if has_values_flag:
ws['B6'].font = title_font
ws['B6'] = name + 'Reporting Period Carbon Dioxide Emissions'
reporting_period_data = report['reporting_period']
category = report['virtual_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 Emissions'
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['virtual_meter']['energy_category_name'] + \
" (" + report['virtual_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_detailed_data_flag = True
reporting_period_data = report['reporting_period']
category = report['virtual_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_detailed_data_flag = False
if has_detailed_data_flag:
ws['B11'].font = title_font
ws['B11'] = name + 'Detailed Data'
ws.row_dimensions[18].height = 60
ws['B18'].fill = table_fill
ws['B18'].font = title_font
ws['B18'].border = f_border
ws['B18'].alignment = c_c_alignment
ws['B18'] = 'Datetime'
time = times
has_data = False
max_row = 0
if len(time) > 0:
has_data = True
max_row = 18 + len(time)
if has_data:
end_data_row_number = 19
for i in range(0, len(time)):
col = 'B'
end_data_row_number = 19 + i
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 + '18'].fill = table_fill
ws[col + '18'].font = title_font
ws[col + '18'].alignment = c_c_alignment
ws[col + '18'] = report['virtual_meter']['energy_category_name'] + \
" (" + report['virtual_meter']['unit_of_measure'] + ")"
ws[col + '18'].border = f_border
time = times
time_len = len(time)
for j in range(0, time_len):
row = str(19 + 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()
labels = Reference(ws, min_col=2, min_row=19, max_row=max_row)
line_data = Reference(ws, min_col=3, min_row=18, max_row=max_row)
line.series.append(Series(line_data, title_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.title = 'Reporting Period Carbon Dioxide Emissions - ' + \
report['virtual_meter']['energy_category_name'] + \
" (" + report['virtual_meter']['unit_of_measure'] + ")"
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
filename = str(uuid.uuid4()) + '.xlsx'
wb.save(filename)
return filename

View File

@ -58,7 +58,6 @@ def export(report, name, reporting_start_datetime_local, reporting_end_datetime_
def generate_excel(report, name, reporting_start_datetime_local, reporting_end_datetime_local, period_type):
wb = Workbook()
# todo
ws = wb.active
ws.title = "VirtualMeterCost"
# Row height

View File

@ -25,9 +25,9 @@ class Reporting:
# 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 5: query base period carbon dioxide emissions
# Step 6: query reporting period energy consumption
# Step 7: query reporting period carbon dioxide emission
# Step 7: query reporting period carbon dioxide emissions
# Step 8: query tariff data
# Step 9: query associated points data
# Step 10: construct the report
@ -55,9 +55,8 @@ class Reporting:
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)
match = regex.match(str.strip(meter_uuid))
if not bool(match):
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_METER_UUID')
@ -179,8 +178,7 @@ class Reporting:
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]
@ -248,7 +246,7 @@ class Reporting:
base['total_in_kgco2e'] += actual_value * meter['kgco2e']
################################################################################################################
# Step 5: query base period carbon dioxide emission
# Step 5: query base period carbon dioxide emissions
################################################################################################################
query = (" SELECT start_datetime_utc, actual_value "
" FROM tbl_meter_hourly "
@ -318,7 +316,7 @@ class Reporting:
reporting['total_in_kgco2e'] += actual_value * meter['kgco2e']
################################################################################################################
# Step 7: query reporting period carbon dioxide emission
# Step 7: query reporting period carbon dioxide emissions
################################################################################################################
query = (" SELECT start_datetime_utc, actual_value "
" FROM tbl_meter_hourly "

View File

@ -0,0 +1,390 @@
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.virtualmetercarbon
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 virtual meter and energy category
# Step 3: query base period energy consumption
# Step 4: query base period energy carbon dioxide emissions
# Step 5: query reporting period energy consumption
# Step 6: query reporting period energy carbon dioxide emissions
# Step 7: query tariff data
# Step 8: construct the report
####################################################################################################################
@staticmethod
def on_get(req, resp):
print(req.params)
virtual_meter_id = req.params.get('virtualmeterid')
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 virtual_meter_id is None:
raise falcon.HTTPError(falcon.HTTP_400,
title='API.BAD_REQUEST',
description='API.INVALID_VIRTUAL_METER_ID')
else:
virtual_meter_id = str.strip(virtual_meter_id)
if not virtual_meter_id.isdigit() or int(virtual_meter_id) <= 0:
raise falcon.HTTPError(falcon.HTTP_400,
title='API.BAD_REQUEST',
description='API.INVALID_VIRTUAL_METER_ID')
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 virtual 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()
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_virtual_meters m, tbl_energy_categories ec "
" WHERE m.id = %s AND m.energy_category_id = ec.id ", (virtual_meter_id,))
row_virtual_meter = cursor_system.fetchone()
if row_virtual_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()
raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND', description='API.VIRTUAL_METER_NOT_FOUND')
virtual_meter = dict()
virtual_meter['id'] = row_virtual_meter[0]
virtual_meter['name'] = row_virtual_meter[1]
virtual_meter['cost_center_id'] = row_virtual_meter[2]
virtual_meter['energy_category_id'] = row_virtual_meter[3]
virtual_meter['energy_category_name'] = row_virtual_meter[4]
virtual_meter['unit_of_measure'] = config.currency_unit
virtual_meter['kgce'] = row_virtual_meter[6]
virtual_meter['kgco2e'] = row_virtual_meter[7]
################################################################################################################
# Step 3: query base period energy consumption
################################################################################################################
query = (" SELECT start_datetime_utc, actual_value "
" FROM tbl_virtual_meter_hourly "
" WHERE virtual_meter_id = %s "
" AND start_datetime_utc >= %s "
" AND start_datetime_utc < %s "
" ORDER BY start_datetime_utc ")
cursor_energy.execute(query, (virtual_meter['id'], base_start_datetime_utc, base_end_datetime_utc))
rows_virtual_meter_hourly = cursor_energy.fetchall()
rows_virtual_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_virtual_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_virtual_meter_periodically in rows_virtual_meter_periodically:
current_datetime_local = row_virtual_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_virtual_meter_periodically[1] is None \
else row_virtual_meter_periodically[1]
base['timestamps'].append(current_datetime)
base['total_in_kgce'] += actual_value * virtual_meter['kgce']
base['total_in_kgco2e'] += actual_value * virtual_meter['kgco2e']
################################################################################################################
# Step 4: query base period energy carbon dioxide emissions
################################################################################################################
query = (" SELECT start_datetime_utc, actual_value "
" FROM tbl_virtual_meter_hourly "
" WHERE virtual_meter_id = %s "
" AND start_datetime_utc >= %s "
" AND start_datetime_utc < %s "
" ORDER BY start_datetime_utc ")
cursor_carbon.execute(query, (virtual_meter['id'], base_start_datetime_utc, base_end_datetime_utc))
rows_virtual_meter_hourly = cursor_carbon.fetchall()
rows_virtual_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_virtual_meter_hourly,
base_start_datetime_utc,
base_end_datetime_utc,
period_type)
base['values'] = list()
base['total_in_category'] = Decimal(0.0)
for row_virtual_meter_periodically in rows_virtual_meter_periodically:
actual_value = Decimal(0.0) if row_virtual_meter_periodically[1] is None \
else row_virtual_meter_periodically[1]
base['values'].append(actual_value)
base['total_in_category'] += actual_value
################################################################################################################
# Step 5: query reporting period energy consumption
################################################################################################################
query = (" SELECT start_datetime_utc, actual_value "
" FROM tbl_virtual_meter_hourly "
" WHERE virtual_meter_id = %s "
" AND start_datetime_utc >= %s "
" AND start_datetime_utc < %s "
" ORDER BY start_datetime_utc ")
cursor_carbon.execute(query, (virtual_meter['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
rows_virtual_meter_hourly = cursor_carbon.fetchall()
rows_virtual_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_virtual_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_virtual_meter_periodically in rows_virtual_meter_periodically:
current_datetime_local = row_virtual_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_virtual_meter_periodically[1] is None \
else row_virtual_meter_periodically[1]
reporting['timestamps'].append(current_datetime)
reporting['total_in_kgce'] += actual_value * virtual_meter['kgce']
reporting['total_in_kgco2e'] += actual_value * virtual_meter['kgco2e']
################################################################################################################
# Step 6: query reporting period energy carbon dioxide emissions
################################################################################################################
query = (" SELECT start_datetime_utc, actual_value "
" FROM tbl_virtual_meter_hourly "
" WHERE virtual_meter_id = %s "
" AND start_datetime_utc >= %s "
" AND start_datetime_utc < %s "
" ORDER BY start_datetime_utc ")
cursor_carbon.execute(query, (virtual_meter['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
rows_virtual_meter_hourly = cursor_carbon.fetchall()
rows_virtual_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_virtual_meter_hourly,
reporting_start_datetime_utc,
reporting_end_datetime_utc,
period_type)
for row_virtual_meter_periodically in rows_virtual_meter_periodically:
actual_value = Decimal(0.0) if row_virtual_meter_periodically[1] is None \
else row_virtual_meter_periodically[1]
reporting['values'].append(actual_value)
reporting['total_in_category'] += actual_value
################################################################################################################
# Step 7: 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(virtual_meter['cost_center_id'],
virtual_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-' + virtual_meter['energy_category_name'])
parameters_data['timestamps'].append(tariff_timestamp_list)
parameters_data['values'].append(tariff_value_list)
################################################################################################################
# Step 8: 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()
result = {
"virtual_meter": {
"cost_center_id": virtual_meter['cost_center_id'],
"energy_category_id": virtual_meter['energy_category_id'],
"energy_category_name": virtual_meter['energy_category_name'],
"unit_of_measure": 'KG',
"kgce": virtual_meter['kgce'],
"kgco2e": virtual_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.virtualmetercarbon.export(result,
virtual_meter['name'],
reporting_period_start_datetime_local,
reporting_period_end_datetime_local,
period_type)
resp.text = json.dumps(result)

View File

@ -0,0 +1,617 @@
import React, { Fragment, useEffect, useState } from 'react';
import {
Breadcrumb,
BreadcrumbItem,
Row,
Col,
Card,
CardBody,
Button,
ButtonGroup,
Form,
FormGroup,
Input,
Label,
CustomInput,
Spinner,
} from 'reactstrap';
import CountUp from 'react-countup';
import Datetime from 'react-datetime';
import moment from 'moment';
import loadable from '@loadable/component';
import Cascader from 'rc-cascader';
import CardSummary from '../common/CardSummary';
import LineChart from '../common/LineChart';
import { getCookieValue, createCookie } from '../../../helpers/utils';
import withRedirect from '../../../hoc/withRedirect';
import { withTranslation } from 'react-i18next';
import { toast } from 'react-toastify';
import ButtonIcon from '../../common/ButtonIcon';
import { APIBaseURL } from '../../../config';
import { periodTypeOptions } from '../common/PeriodTypeOptions';
import { comparisonTypeOptions } from '../common/ComparisonTypeOptions';
const DetailedDataTable = loadable(() => import('../common/DetailedDataTable'));
const VirtualMeterCarbon = ({ setRedirect, setRedirectUrl, t }) => {
let current_moment = moment();
useEffect(() => {
let is_logged_in = getCookieValue('is_logged_in');
let user_name = getCookieValue('user_name');
let user_display_name = getCookieValue('user_display_name');
let user_uuid = getCookieValue('user_uuid');
let token = getCookieValue('token');
if (is_logged_in === null || !is_logged_in) {
setRedirectUrl(`/authentication/basic/login`);
setRedirect(true);
} else {
//update expires time of cookies
createCookie('is_logged_in', true, 1000 * 60 * 60 * 8);
createCookie('user_name', user_name, 1000 * 60 * 60 * 8);
createCookie('user_display_name', user_display_name, 1000 * 60 * 60 * 8);
createCookie('user_uuid', user_uuid, 1000 * 60 * 60 * 8);
createCookie('token', token, 1000 * 60 * 60 * 8);
}
});
// State
//Query From
const [selectedSpaceName, setSelectedSpaceName] = useState(undefined);
const [selectedSpaceID, setSelectedSpaceID] = useState(undefined);
const [virtualMeterList, setVirtualMeterList] = useState([]);
const [selectedVirtualMeter, setSelectedVirtualMeter] = useState(undefined);
const [comparisonType, setComparisonType] = useState('month-on-month');
const [periodType, setPeriodType] = useState('daily');
const [basePeriodBeginsDatetime, setBasePeriodBeginsDatetime] = useState(current_moment.clone().subtract(1, 'months').startOf('month'));
const [basePeriodEndsDatetime, setBasePeriodEndsDatetime] = useState(current_moment.clone().subtract(1, 'months'));
const [basePeriodBeginsDatetimeDisabled, setBasePeriodBeginsDatetimeDisabled] = useState(true);
const [basePeriodEndsDatetimeDisabled, setBasePeriodEndsDatetimeDisabled] = useState(true);
const [reportingPeriodBeginsDatetime, setReportingPeriodBeginsDatetime] = useState(current_moment.clone().startOf('month'));
const [reportingPeriodEndsDatetime, setReportingPeriodEndsDatetime] = useState(current_moment);
const [cascaderOptions, setCascaderOptions] = useState(undefined);
// buttons
const [submitButtonDisabled, setSubmitButtonDisabled] = useState(true);
const [spinnerHidden, setSpinnerHidden] = useState(true);
const [exportButtonHidden, setExportButtonHidden] = useState(true);
//Results
const [virtualMeterEnergyCategory, setVirtualMeterEnergyCategory] = useState({ 'name': '', 'unit': '' });
const [reportingPeriodEnergyCarbonInCategory, setReportingPeriodEnergyCarbonInCategory] = useState(0);
const [reportingPeriodEnergyCarbonRate, setReportingPeriodEnergyCarbonRate] = useState('');
const [reportingPeriodEnergyConsumptionInTCE, setReportingPeriodEnergyConsumptionInTCE] = useState(0);
const [reportingPeriodEnergyConsumptionInCO2, setReportingPeriodEnergyConsumptionInCO2] = useState(0);
const [basePeriodEnergyCarbonInCategory, setBasePeriodEnergyCarbonInCategory] = useState(0);
const [virtualMeterLineChartOptions, setVirtualMeterLineChartOptions] = useState([]);
const [virtualMeterLineChartData, setVirtualMeterLineChartData] = useState({});
const [virtualMeterLineChartLabels, setVirtualMeterLineChartLabels] = useState([]);
const [parameterLineChartOptions, setParameterLineChartOptions] = useState([]);
const [parameterLineChartData, setParameterLineChartData] = useState({});
const [parameterLineChartLabels, setParameterLineChartLabels] = useState([]);
const [detailedDataTableColumns, setDetailedDataTableColumns] = useState([{dataField: 'startdatetime', text: t('Datetime'), sort: true}]);
const [detailedDataTableData, setDetailedDataTableData] = useState([]);
const [excelBytesBase64, setExcelBytesBase64] = useState(undefined);
useEffect(() => {
let isResponseOK = false;
fetch(APIBaseURL + '/spaces/tree', {
method: 'GET',
headers: {
"Content-type": "application/json",
"User-UUID": getCookieValue('user_uuid'),
"Token": getCookieValue('token')
},
body: null,
}).then(response => {
console.log(response);
if (response.ok) {
isResponseOK = true;
}
return response.json();
}).then(json => {
console.log(json);
if (isResponseOK) {
// rename keys
json = JSON.parse(JSON.stringify([json]).split('"id":').join('"value":').split('"name":').join('"label":'));
setCascaderOptions(json);
setSelectedSpaceName([json[0]].map(o => o.label));
setSelectedSpaceID([json[0]].map(o => o.value));
// get Virtual Meters by root Space ID
let isResponseOK = false;
fetch(APIBaseURL + '/spaces/' + [json[0]].map(o => o.value) + '/virtualmeters', {
method: 'GET',
headers: {
"Content-type": "application/json",
"User-UUID": getCookieValue('user_uuid'),
"Token": getCookieValue('token')
},
body: null,
}).then(response => {
if (response.ok) {
isResponseOK = true;
}
return response.json();
}).then(json => {
if (isResponseOK) {
json = JSON.parse(JSON.stringify([json]).split('"id":').join('"value":').split('"name":').join('"label":'));
console.log(json);
setVirtualMeterList(json[0]);
if (json[0].length > 0) {
setSelectedVirtualMeter(json[0][0].value);
// enable submit button
setSubmitButtonDisabled(false);
} else {
setSelectedVirtualMeter(undefined);
// disable submit button
setSubmitButtonDisabled(true);
}
} else {
toast.error(json.description)
}
}).catch(err => {
console.log(err);
});
// end of get Virtual Meters by root Space ID
} else {
toast.error(json.description);
}
}).catch(err => {
console.log(err);
});
}, [t, ]);
const labelClasses = 'ls text-uppercase text-600 font-weight-semi-bold mb-0';
let onSpaceCascaderChange = (value, selectedOptions) => {
setSelectedSpaceName(selectedOptions.map(o => o.label).join('/'));
setSelectedSpaceID(value[value.length - 1]);
let isResponseOK = false;
fetch(APIBaseURL + '/spaces/' + value[value.length - 1] + '/virtualmeters', {
method: 'GET',
headers: {
"Content-type": "application/json",
"User-UUID": getCookieValue('user_uuid'),
"Token": getCookieValue('token')
},
body: null,
}).then(response => {
if (response.ok) {
isResponseOK = true;
}
return response.json();
}).then(json => {
if (isResponseOK) {
json = JSON.parse(JSON.stringify([json]).split('"id":').join('"value":').split('"name":').join('"label":'));
console.log(json)
setVirtualMeterList(json[0]);
if (json[0].length > 0) {
setSelectedVirtualMeter(json[0][0].value);
// enable submit button
setSubmitButtonDisabled(false);
} else {
setSelectedVirtualMeter(undefined);
// disable submit button
setSubmitButtonDisabled(true);
}
} else {
toast.error(json.description)
}
}).catch(err => {
console.log(err);
});
}
let onComparisonTypeChange = ({ target }) => {
console.log(target.value);
setComparisonType(target.value);
if (target.value === 'year-over-year') {
setBasePeriodBeginsDatetimeDisabled(true);
setBasePeriodEndsDatetimeDisabled(true);
setBasePeriodBeginsDatetime(moment(reportingPeriodBeginsDatetime).subtract(1, 'years'));
setBasePeriodEndsDatetime(moment(reportingPeriodEndsDatetime).subtract(1, 'years'));
} else if (target.value === 'month-on-month') {
setBasePeriodBeginsDatetimeDisabled(true);
setBasePeriodEndsDatetimeDisabled(true);
setBasePeriodBeginsDatetime(moment(reportingPeriodBeginsDatetime).subtract(1, 'months'));
setBasePeriodEndsDatetime(moment(reportingPeriodEndsDatetime).subtract(1, 'months'));
} else if (target.value === 'free-comparison') {
setBasePeriodBeginsDatetimeDisabled(false);
setBasePeriodEndsDatetimeDisabled(false);
} else if (target.value === 'none-comparison') {
setBasePeriodBeginsDatetime(undefined);
setBasePeriodEndsDatetime(undefined);
setBasePeriodBeginsDatetimeDisabled(true);
setBasePeriodEndsDatetimeDisabled(true);
}
}
let onBasePeriodBeginsDatetimeChange = (newDateTime) => {
setBasePeriodBeginsDatetime(newDateTime);
}
let onBasePeriodEndsDatetimeChange = (newDateTime) => {
setBasePeriodEndsDatetime(newDateTime);
}
let onReportingPeriodBeginsDatetimeChange = (newDateTime) => {
setReportingPeriodBeginsDatetime(newDateTime);
if (comparisonType === 'year-over-year') {
setBasePeriodBeginsDatetime(newDateTime.clone().subtract(1, 'years'));
} else if (comparisonType === 'month-on-month') {
setBasePeriodBeginsDatetime(newDateTime.clone().subtract(1, 'months'));
}
}
let onReportingPeriodEndsDatetimeChange = (newDateTime) => {
setReportingPeriodEndsDatetime(newDateTime);
if (comparisonType === 'year-over-year') {
setBasePeriodEndsDatetime(newDateTime.clone().subtract(1, 'years'));
} else if (comparisonType === 'month-on-month') {
setBasePeriodEndsDatetime(newDateTime.clone().subtract(1, 'months'));
}
}
var getValidBasePeriodBeginsDatetimes = function (currentDate) {
return currentDate.isBefore(moment(basePeriodEndsDatetime, 'MM/DD/YYYY, hh:mm:ss a'));
}
var getValidBasePeriodEndsDatetimes = function (currentDate) {
return currentDate.isAfter(moment(basePeriodBeginsDatetime, 'MM/DD/YYYY, hh:mm:ss a'));
}
var getValidReportingPeriodBeginsDatetimes = function (currentDate) {
return currentDate.isBefore(moment(reportingPeriodEndsDatetime, 'MM/DD/YYYY, hh:mm:ss a'));
}
var getValidReportingPeriodEndsDatetimes = function (currentDate) {
return currentDate.isAfter(moment(reportingPeriodBeginsDatetime, 'MM/DD/YYYY, hh:mm:ss a'));
}
// Handler
const handleSubmit = e => {
e.preventDefault();
console.log('handleSubmit');
console.log(selectedSpaceID);
console.log(selectedVirtualMeter);
console.log(comparisonType);
console.log(periodType);
console.log(basePeriodBeginsDatetime != null ? basePeriodBeginsDatetime.format('YYYY-MM-DDTHH:mm:ss') : undefined);
console.log(basePeriodEndsDatetime != null ? basePeriodEndsDatetime.format('YYYY-MM-DDTHH:mm:ss') : undefined);
console.log(reportingPeriodBeginsDatetime.format('YYYY-MM-DDTHH:mm:ss'));
console.log(reportingPeriodEndsDatetime.format('YYYY-MM-DDTHH:mm:ss'));
// disable submit button
setSubmitButtonDisabled(true);
// show spinner
setSpinnerHidden(false);
// hide export button
setExportButtonHidden(true)
// Reinitialize tables
setDetailedDataTableData([]);
let isResponseOK = false;
fetch(APIBaseURL + '/reports/virtualmetercarbon?' +
'virtualmeterid=' + selectedVirtualMeter +
'&periodtype=' + periodType +
'&baseperiodstartdatetime=' + (basePeriodBeginsDatetime != null ? basePeriodBeginsDatetime.format('YYYY-MM-DDTHH:mm:ss') : '') +
'&baseperiodenddatetime=' + (basePeriodEndsDatetime != null ? basePeriodEndsDatetime.format('YYYY-MM-DDTHH:mm:ss') : '') +
'&reportingperiodstartdatetime=' + reportingPeriodBeginsDatetime.format('YYYY-MM-DDTHH:mm:ss') +
'&reportingperiodenddatetime=' + reportingPeriodEndsDatetime.format('YYYY-MM-DDTHH:mm:ss'), {
method: 'GET',
headers: {
"Content-type": "application/json",
"User-UUID": getCookieValue('user_uuid'),
"Token": getCookieValue('token')
},
body: null,
}).then(response => {
if (response.ok) {
isResponseOK = true;
};
return response.json();
}).then(json => {
if (isResponseOK) {
console.log(json)
setVirtualMeterEnergyCategory({
'name': json['virtual_meter']['energy_category_name'],
'unit': json['virtual_meter']['unit_of_measure']
});
setReportingPeriodEnergyCarbonRate(parseFloat(json['reporting_period']['increment_rate']*100).toFixed(2) + "%");
setReportingPeriodEnergyCarbonInCategory(json['reporting_period']['total_in_category']);
setReportingPeriodEnergyConsumptionInTCE(json['reporting_period']['total_in_kgce'] / 1000);
setReportingPeriodEnergyConsumptionInCO2(json['reporting_period']['total_in_kgco2e'] / 1000);
setBasePeriodEnergyCarbonInCategory(json['base_period']['total_in_category']);
let names = Array();
names.push({ 'value': 'a0', 'label': json['virtual_meter']['energy_category_name'] });
setVirtualMeterLineChartOptions(names);
let timestamps = {}
timestamps['a0'] = json['reporting_period']['timestamps'];
setVirtualMeterLineChartLabels(timestamps);
let values = {'a0':[]}
json['reporting_period']['values'].forEach((currentValue, index) => {
values['a0'][index] = currentValue.toFixed(2);
});
setVirtualMeterLineChartData(values)
names = Array();
json['parameters']['names'].forEach((currentValue, index) => {
names.push({ 'value': 'a' + index, 'label': currentValue });
});
setParameterLineChartOptions(names);
timestamps = {}
json['parameters']['timestamps'].forEach((currentValue, index) => {
timestamps['a' + index] = currentValue;
});
setParameterLineChartLabels(timestamps);
values = {}
json['parameters']['values'].forEach((currentValue, index) => {
values['a' + index] = currentValue;
});
setParameterLineChartData(values);
setDetailedDataTableColumns([{
dataField: 'startdatetime',
text: t('Datetime'),
sort: true
}, {
dataField: 'a0',
text: json['virtual_meter']['energy_category_name'] + ' (' + json['virtual_meter']['unit_of_measure'] + ')',
sort: true,
formatter: function (decimalValue) {
if (decimalValue !== null) {
return decimalValue.toFixed(2);
} else {
return null;
}
}
}]);
let detailed_value_list = [];
json['reporting_period']['timestamps'].forEach((currentTimestamp, timestampIndex) => {
let detailed_value = {};
detailed_value['id'] = timestampIndex;
detailed_value['startdatetime'] = currentTimestamp;
detailed_value['a0'] = json['reporting_period']['values'][timestampIndex];
detailed_value_list.push(detailed_value);
});
let detailed_value = {};
detailed_value['id'] = detailed_value_list.length;
detailed_value['startdatetime'] = t('Total');
detailed_value['a0'] = json['reporting_period']['total_in_category'];
detailed_value_list.push(detailed_value);
setDetailedDataTableData(detailed_value_list);
setExcelBytesBase64(json['excel_bytes_base64']);
// enable submit button
setSubmitButtonDisabled(false);
// hide spinner
setSpinnerHidden(true);
// show export button
setExportButtonHidden(false);
} else {
toast.error(json.description)
}
}).catch(err => {
console.log(err);
});
};
const handleExport = e => {
e.preventDefault();
const mimeType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
const fileName = 'virtualmetercarbon.xlsx'
var fileUrl = "data:" + mimeType + ";base64," + excelBytesBase64;
fetch(fileUrl)
.then(response => response.blob())
.then(blob => {
var link = window.document.createElement("a");
link.href = window.URL.createObjectURL(blob, { type: mimeType });
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
};
return (
<Fragment>
<div>
<Breadcrumb>
<BreadcrumbItem>{t('Meter Data')}</BreadcrumbItem><BreadcrumbItem active>{t('Virtual Meter Carbon')}</BreadcrumbItem>
</Breadcrumb>
</div>
<Card className="bg-light mb-3">
<CardBody className="p-3">
<Form onSubmit={handleSubmit}>
<Row form >
<Col xs={6} sm={3}>
<FormGroup className="form-group">
<Label className={labelClasses} for="space">
{t('Space')}
</Label>
<br />
<Cascader options={cascaderOptions}
onChange={onSpaceCascaderChange}
changeOnSelect
expandTrigger="hover">
<Input value={selectedSpaceName || ''} readOnly />
</Cascader>
</FormGroup>
</Col>
<Col xs="auto">
<FormGroup>
<Label className={labelClasses} for="virtualMeterSelect">
{t('Virtual Meter')}
</Label>
<CustomInput type="select" id="virtualMeterSelect" name="virtualMeterSelect" onChange={({ target }) => setSelectedVirtualMeter(target.value)}
>
{virtualMeterList.map((virtualMeter, index) => (
<option value={virtualMeter.value} key={virtualMeter.value}>
{virtualMeter.label}
</option>
))}
</CustomInput>
</FormGroup>
</Col>
<Col xs="auto">
<FormGroup>
<Label className={labelClasses} for="comparisonType">
{t('Comparison Types')}
</Label>
<CustomInput type="select" id="comparisonType" name="comparisonType"
defaultValue="month-on-month"
onChange={onComparisonTypeChange}
>
{comparisonTypeOptions.map((comparisonType, index) => (
<option value={comparisonType.value} key={comparisonType.value} >
{t(comparisonType.label)}
</option>
))}
</CustomInput>
</FormGroup>
</Col>
<Col xs="auto">
<FormGroup>
<Label className={labelClasses} for="periodType">
{t('Period Types')}
</Label>
<CustomInput type="select" id="periodType" name="periodType" defaultValue="daily" onChange={({ target }) => setPeriodType(target.value)}
>
{periodTypeOptions.map((periodType, index) => (
<option value={periodType.value} key={periodType.value} >
{t(periodType.label)}
</option>
))}
</CustomInput>
</FormGroup>
</Col>
<Col xs={6} sm={3}>
<FormGroup className="form-group">
<Label className={labelClasses} for="basePeriodBeginsDatetime">
{t('Base Period Begins')}{t('(Optional)')}
</Label>
<Datetime id='basePeriodBeginsDatetime'
value={basePeriodBeginsDatetime}
inputProps={{ disabled: basePeriodBeginsDatetimeDisabled }}
onChange={onBasePeriodBeginsDatetimeChange}
isValidDate={getValidBasePeriodBeginsDatetimes}
closeOnSelect={true} />
</FormGroup>
</Col>
<Col xs={6} sm={3}>
<FormGroup className="form-group">
<Label className={labelClasses} for="basePeriodEndsDatetime">
{t('Base Period Ends')}{t('(Optional)')}
</Label>
<Datetime id='basePeriodEndsDatetime'
value={basePeriodEndsDatetime}
inputProps={{ disabled: basePeriodEndsDatetimeDisabled }}
onChange={onBasePeriodEndsDatetimeChange}
isValidDate={getValidBasePeriodEndsDatetimes}
closeOnSelect={true} />
</FormGroup>
</Col>
<Col xs={6} sm={3}>
<FormGroup className="form-group">
<Label className={labelClasses} for="reportingPeriodBeginsDatetime">
{t('Reporting Period Begins')}
</Label>
<Datetime id='reportingPeriodBeginsDatetime'
value={reportingPeriodBeginsDatetime}
onChange={onReportingPeriodBeginsDatetimeChange}
isValidDate={getValidReportingPeriodBeginsDatetimes}
closeOnSelect={true} />
</FormGroup>
</Col>
<Col xs={6} sm={3}>
<FormGroup className="form-group">
<Label className={labelClasses} for="reportingPeriodEndsDatetime">
{t('Reporting Period Ends')}
</Label>
<Datetime id='reportingPeriodEndsDatetime'
value={reportingPeriodEndsDatetime}
onChange={onReportingPeriodEndsDatetimeChange}
isValidDate={getValidReportingPeriodEndsDatetimes}
closeOnSelect={true} />
</FormGroup>
</Col>
<Col xs="auto">
<FormGroup>
<br></br>
<ButtonGroup id="submit">
<Button color="success" disabled={submitButtonDisabled} >{t('Submit')}</Button>
</ButtonGroup>
</FormGroup>
</Col>
<Col xs="auto">
<FormGroup>
<br></br>
<Spinner color="primary" hidden={spinnerHidden} />
</FormGroup>
</Col>
<Col xs="auto">
<br></br>
<ButtonIcon icon="external-link-alt" transform="shrink-3 down-2" color="falcon-default"
hidden={exportButtonHidden}
onClick={handleExport} >
{t('Export')}
</ButtonIcon>
</Col>
</Row>
</Form>
</CardBody>
</Card>
<div className="card-deck">
<CardSummary rate={reportingPeriodEnergyCarbonRate} title={t('Reporting Period Carbon Dioxide Emissions CATEGORY UNIT', { 'CATEGORY': virtualMeterEnergyCategory['name'], 'UNIT': '(' + virtualMeterEnergyCategory['unit'] + ')' })}
color="success" >
<CountUp end={reportingPeriodEnergyCarbonInCategory} duration={2} prefix="" separator="," decimals={2} decimal="." />
</CardSummary>
<CardSummary rate={reportingPeriodEnergyCarbonRate} title={t('Reporting Period Consumption CATEGORY UNIT', { 'CATEGORY': t('Ton of Standard Coal'), 'UNIT': '(TCE)' })}
color="warning" >
<CountUp end={reportingPeriodEnergyConsumptionInTCE} duration={2} prefix="" separator="," decimal="." decimals={2} />
</CardSummary>
<CardSummary rate={reportingPeriodEnergyCarbonRate} title={t('Reporting Period Consumption CATEGORY UNIT', { 'CATEGORY': t('Ton of Carbon Dioxide Emissions'), 'UNIT': '(T)' })}
color="warning" >
<CountUp end={reportingPeriodEnergyConsumptionInCO2} duration={2} prefix="" separator="," decimal="." decimals={2} />
</CardSummary>
</div>
<LineChart reportingTitle={t('Reporting Period Carbon Dioxide Emissions CATEGORY VALUE UNIT', { 'CATEGORY': virtualMeterEnergyCategory['name'], 'VALUE': reportingPeriodEnergyCarbonInCategory.toFixed(2), 'UNIT': '(' + virtualMeterEnergyCategory['unit'] + ')' })}
baseTitle={t('Base Period Carbon Dioxide Emissions CATEGORY VALUE UNIT', { 'CATEGORY': virtualMeterEnergyCategory['name'], 'VALUE': basePeriodEnergyCarbonInCategory.toFixed(2), 'UNIT': '(' + virtualMeterEnergyCategory['unit'] + ')' })}
labels={virtualMeterLineChartLabels}
data={virtualMeterLineChartData}
options={virtualMeterLineChartOptions}>
</LineChart>
<LineChart reportingTitle={t('Related Parameters')}
baseTitle=''
labels={parameterLineChartLabels}
data={parameterLineChartData}
options={parameterLineChartOptions}>
</LineChart>
<br />
<DetailedDataTable data={detailedDataTableData} title={t('Detailed Data')} columns={detailedDataTableColumns} pagesize={50} >
</DetailedDataTable>
</Fragment>
);
};
export default withTranslation()(withRedirect(VirtualMeterCarbon));

View File

@ -38,6 +38,7 @@ const resources = {
'Meter Realtime': 'Meter Realtime',
'Master Meter Submeters Balance': 'Master Meter Submeters Balance',
'Virtual Meter Energy': 'Virtual Meter Energy',
'Virtual Meter Carbon': 'Virtual Meter Carbon Dioxide Emissions',
'Virtual Meter Cost': 'Virtual Meter Cost',
'Offline Meter Energy': 'Offline Meter Energy',
'Offline Meter Cost': 'Offline Meter Cost',
@ -822,6 +823,7 @@ const resources = {
'Meter Realtime': 'Meter Echtzeit',
'Master Meter Submeters Balance': 'Master Meter Submeters Balance',
'Virtual Meter Energy': 'Virtuelles Meter Energie',
'Virtual Meter Carbon': 'Virtual Meter Kohlendioxidemissionen',
'Virtual Meter Cost': 'Virtuelles Meter Kosten',
'Offline Meter Energy': 'Offline Meter Energie',
'Offline Meter Cost': 'Offline Meter Kosten',
@ -1616,6 +1618,7 @@ const resources = {
'Meter Realtime': '计量表实时分析',
'Master Meter Submeters Balance': '总分表平衡分析',
'Virtual Meter Energy': '虚拟表能耗分析',
'Virtual Meter Carbon': '虚拟表碳排放分析',
'Virtual Meter Cost': '虚拟表成本分析',
'Offline Meter Energy': '离线表能耗分析',
'Offline Meter Cost': '离线表成本分析',

View File

@ -135,6 +135,7 @@ import MeterTracking from '../components/MyEMS/Meter/MeterTracking';
import MeterTrend from '../components/MyEMS/Meter/MeterTrend';
import OfflineMeterCost from '../components/MyEMS/Meter/OfflineMeterCost';
import OfflineMeterEnergy from '../components/MyEMS/Meter/OfflineMeterEnergy';
import VirtualMeterCarbon from '../components/MyEMS/Meter/VirtualMeterCarbon';
import VirtualMeterCost from '../components/MyEMS/Meter/VirtualMeterCost';
import VirtualMeterEnergy from '../components/MyEMS/Meter/VirtualMeterEnergy';
// Tenant
@ -374,6 +375,7 @@ const MyEMSRoutes = () => (
<Route path="/meter/offlinemeterenergy" exact component={OfflineMeterEnergy} />
<Route path="/meter/offlinemetercost" exact component={OfflineMeterCost} />
<Route path="/meter/virtualmeterenergy" exact component={VirtualMeterEnergy} />
<Route path="/meter/virtualmetercarbon" exact component={VirtualMeterCarbon} />
<Route path="/meter/virtualmetercost" exact component={VirtualMeterCost} />
<Route path="/meter/tracking" exact component={MeterTracking} />

View File

@ -364,6 +364,7 @@ export const meterRoutes = {
{ to: '/meter/meterrealtime', name: 'Meter Realtime' },
{ to: '/meter/metersubmetersbalance', name: 'Master Meter Submeters Balance' },
{ to: '/meter/virtualmeterenergy', name: 'Virtual Meter Energy' },
{ to: '/meter/virtualmetercarbon', name: 'Virtual Meter Carbon' },
{ to: '/meter/virtualmetercost', name: 'Virtual Meter Cost' },
{ to: '/meter/offlinemeterenergy', name: 'Offline Meter Energy' },
{ to: '/meter/offlinemetercost', name: 'Offline Meter Cost' },