Completed the start and end values increase

pull/40/head
YangZhang-GitHub 2021-04-29 20:23:18 +08:00
parent 16734a220b
commit c6f4ed9003
4 changed files with 354 additions and 127 deletions

View File

@ -18,7 +18,7 @@ from openpyxl.chart.label import DataLabelList
# Step 3: Encode the excelexporters file to Base64 # Step 3: Encode the excelexporters file to Base64
#################################################################################################################### ####################################################################################################################
def export(result, space_name): def export(result, space_name, reporting_start_datetime_local, reporting_end_datetime_local):
#################################################################################################################### ####################################################################################################################
# Step 1: Validate the report data # Step 1: Validate the report data
#################################################################################################################### ####################################################################################################################
@ -29,7 +29,9 @@ def export(result, space_name):
# Step 2: Generate excel file from the report data # Step 2: Generate excel file from the report data
#################################################################################################################### ####################################################################################################################
filename = generate_excel(result, filename = generate_excel(result,
space_name) space_name,
reporting_start_datetime_local,
reporting_end_datetime_local)
#################################################################################################################### ####################################################################################################################
# Step 3: Encode the excel file to Base64 # Step 3: Encode the excel file to Base64
#################################################################################################################### ####################################################################################################################
@ -51,17 +53,26 @@ def export(result, space_name):
return base64_message return base64_message
def generate_excel(report, space_name): def generate_excel(report, space_name, reporting_start_datetime_local, reporting_end_datetime_local):
wb = Workbook() wb = Workbook()
ws = wb.active ws = wb.active
# Row height # Row height
ws.row_dimensions[1].height = 118 ws.row_dimensions[1].height = 102
for i in range(2, 5000 + 1): for i in range(2, 5 + 1):
ws.row_dimensions[i].height = 30 ws.row_dimensions[i].height = 42
for i in range(6, len(report['meters']) + 15):
ws.row_dimensions[i].height = 60
# Col width # Col width
ws.column_dimensions['A'].width = 1 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 # Font
name_font = Font(name='Constantia', size=15, bold=True) name_font = Font(name='Constantia', size=15, bold=True)
@ -81,63 +92,98 @@ def generate_excel(report, space_name):
b_c_alignment = Alignment(vertical='bottom', b_c_alignment = Alignment(vertical='bottom',
horizontal='center', horizontal='center',
text_rotation=0, text_rotation=0,
wrap_text=False, wrap_text=True,
shrink_to_fit=False, shrink_to_fit=False,
indent=0) indent=0)
c_c_alignment = Alignment(vertical='center', c_c_alignment = Alignment(vertical='center',
horizontal='center', horizontal='center',
text_rotation=0, text_rotation=0,
wrap_text=False, wrap_text=True,
shrink_to_fit=False, shrink_to_fit=False,
indent=0) indent=0)
b_r_alignment = Alignment(vertical='bottom', b_r_alignment = Alignment(vertical='bottom',
horizontal='right', horizontal='right',
text_rotation=0, text_rotation=0,
wrap_text=False, wrap_text=True,
shrink_to_fit=False, shrink_to_fit=False,
indent=0) indent=0)
c_r_alignment = Alignment(vertical='bottom', c_r_alignment = Alignment(vertical='bottom',
horizontal='center', horizontal='center',
text_rotation=0, text_rotation=0,
wrap_text=False, wrap_text=True,
shrink_to_fit=False, shrink_to_fit=False,
indent=0) indent=0)
for i in range(ord('B'), ord('G')):
ws.column_dimensions[chr(i)].width = 25.0
# Img # Img
ws.merge_cells("B1:F1")
ws.merge_cells("B2:F2")
img = Image("excelexporters/myems.png") img = Image("excelexporters/myems.png")
img.width = img.width * 0.85
img.height = img.height * 0.85
ws.add_image(img, 'B1') ws.add_image(img, 'B1')
# Title # Title
ws['B3'].border = f_border ws.row_dimensions[3].height = 60
ws['B3'].font = name_font
ws['B3'].alignment = b_c_alignment
ws['B3'] = '名称'
ws['C3'].border = f_border ws['B3'].font = name_font
ws['B3'].alignment = b_r_alignment
ws['B3'] = 'Name:'
ws['C3'].border = b_border
ws['C3'].alignment = b_c_alignment ws['C3'].alignment = b_c_alignment
ws['C3'].font = name_font ws['C3'].font = name_font
ws['C3'] = '空间' ws['C3'] = space_name
ws['D3'].border = f_border
ws['D3'].font = name_font
ws['D3'].alignment = b_c_alignment
ws['D3'] = '成本中心:'
ws['E3'].border = f_border
ws['E3'].alignment = b_c_alignment
ws['E3'].font = name_font
ws['E3'] = '能耗分类'
ws['F3'].border = f_border
ws['F3'].font = name_font ws['F3'].font = name_font
ws['F3'].alignment = b_c_alignment ws['F3'].alignment = b_r_alignment
ws['F3'] = ' 描述' ws['F3'] = 'Date:'
ws['G3'].border = b_border
ws['G3'].alignment = b_c_alignment
ws['G3'].font = name_font
ws['G3'] = reporting_start_datetime_local + "__" + reporting_end_datetime_local
ws.merge_cells("G3:H3")
current_row_number = 4 # Title
ws['B6'].border = f_border
ws['B6'].font = name_font
ws['B6'].alignment = c_c_alignment
ws['B6'].fill = table_fill
ws['B6'] = '名称'
ws['C6'].border = f_border
ws['C6'].alignment = c_c_alignment
ws['C6'].font = name_font
ws['C6'].fill = table_fill
ws['C6'] = '空间'
ws['D6'].border = f_border
ws['D6'].font = name_font
ws['D6'].alignment = c_c_alignment
ws['D6'].fill = table_fill
ws['D6'] = '成本中心'
ws['E6'].border = f_border
ws['E6'].alignment = c_c_alignment
ws['E6'].font = name_font
ws['E6'].fill = table_fill
ws['E6'] = '能耗分类'
ws['F6'].border = f_border
ws['F6'].font = name_font
ws['F6'].alignment = c_c_alignment
ws['F6'].fill = table_fill
ws['F6'] = ' 描述'
ws['G6'].border = f_border
ws['G6'].font = name_font
ws['G6'].alignment = c_c_alignment
ws['G6'].fill = table_fill
ws['G6'] = '开始值'
ws['H6'].border = f_border
ws['H6'].font = name_font
ws['H6'].alignment = c_c_alignment
ws['H6'].fill = table_fill
ws['H6'] = ' 结束值'
current_row_number = 7
for i in range(0, len(report['meters'])): for i in range(0, len(report['meters'])):
ws['B' + str(current_row_number)].font = title_font ws['B' + str(current_row_number)].font = title_font
@ -164,6 +210,17 @@ def generate_excel(report, space_name):
ws['F' + str(current_row_number)].border = f_border ws['F' + str(current_row_number)].border = f_border
ws['F' + str(current_row_number)].alignment = c_c_alignment ws['F' + str(current_row_number)].alignment = c_c_alignment
ws['F' + str(current_row_number)] = report['meters'][i]['description'] ws['F' + str(current_row_number)] = report['meters'][i]['description']
ws['G' + str(current_row_number)].font = title_font
ws['G' + str(current_row_number)].border = f_border
ws['G' + str(current_row_number)].alignment = c_c_alignment
ws['G' + str(current_row_number)] = report['meters'][i]['start_value']
ws['H' + str(current_row_number)].font = title_font
ws['H' + str(current_row_number)].border = f_border
ws['H' + str(current_row_number)].alignment = c_c_alignment
ws['H' + str(current_row_number)] = report['meters'][i]['end_value']
current_row_number += 1 current_row_number += 1
filename = str(uuid.uuid4()) + '.xlsx' filename = str(uuid.uuid4()) + '.xlsx'

View File

@ -4,6 +4,7 @@ import mysql.connector
import config import config
from anytree import Node, AnyNode, LevelOrderIter from anytree import Node, AnyNode, LevelOrderIter
import excelexporters.metertracking import excelexporters.metertracking
from datetime import datetime, timedelta, timezone
class Reporting: class Reporting:
@ -26,6 +27,8 @@ class Reporting:
def on_get(req, resp): def on_get(req, resp):
print(req.params) print(req.params)
space_id = req.params.get('spaceid') space_id = req.params.get('spaceid')
reporting_period_start_datetime_local = req.params.get('reportingperiodstartdatetime')
reporting_period_end_datetime_local = req.params.get('reportingperiodenddatetime')
################################################################################################################ ################################################################################################################
# Step 1: valid parameters # Step 1: valid parameters
@ -39,9 +42,52 @@ class Reporting:
else: else:
space_id = int(space_id) space_id = int(space_id)
timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
if config.utc_offset[0] == '-':
timezone_offset = -timezone_offset
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')
if reporting_start_datetime_utc + timedelta(minutes=15) >= reporting_end_datetime_utc:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description='API.THE_REPORTING_PERIOD_MUST_BE_LONGER_THAN_15_MINUTES')
cnx = mysql.connector.connect(**config.myems_system_db) cnx = mysql.connector.connect(**config.myems_system_db)
cursor = cnx.cursor(dictionary=True) cursor = cnx.cursor(dictionary=True)
cnx_historical = mysql.connector.connect(**config.myems_historical_db)
cursor_historical = cnx_historical.cursor()
cursor.execute(" SELECT name " cursor.execute(" SELECT name "
" FROM tbl_spaces " " FROM tbl_spaces "
" WHERE id = %s ", (space_id,)) " WHERE id = %s ", (space_id,))
@ -83,7 +129,7 @@ class Reporting:
cursor.execute(" SELECT m.id, m.name AS meter_name, s.name AS space_name, " cursor.execute(" SELECT m.id, m.name AS meter_name, s.name AS space_name, "
" cc.name AS cost_center_name, ec.name AS energy_category_name, " " cc.name AS cost_center_name, ec.name AS energy_category_name, "
" m.description " " m.description, m.id AS meter_id"
" FROM tbl_spaces s, tbl_spaces_meters sm, tbl_meters m, tbl_cost_centers cc, " " FROM tbl_spaces s, tbl_spaces_meters sm, tbl_meters m, tbl_cost_centers cc, "
" tbl_energy_categories ec " " tbl_energy_categories ec "
" WHERE s.id IN ( " + ', '.join(map(str, space_dict.keys())) + ") " " WHERE s.id IN ( " + ', '.join(map(str, space_dict.keys())) + ") "
@ -93,17 +139,81 @@ class Reporting:
if rows_meters is not None and len(rows_meters) > 0: if rows_meters is not None and len(rows_meters) > 0:
for row in rows_meters: for row in rows_meters:
meter_list.append({"id": row['id'], meter_list.append({"id": row['id'],
"meter_id": row['meter_id'],
"meter_name": row['meter_name'], "meter_name": row['meter_name'],
"space_name": row['space_name'], "space_name": row['space_name'],
"cost_center_name": row['cost_center_name'], "cost_center_name": row['cost_center_name'],
"energy_category_name": row['energy_category_name'], "energy_category_name": row['energy_category_name'],
"description": row['description']}) "description": row['description']})
################################################################################################################
# Step 3.1: Add start and end values
################################################################################################################
save_meter_id_value = dict()
for meter in meter_list:
meter_id = meter['meter_id']
if meter_id not in save_meter_id_value.keys():
cursor.execute(" SELECT point_id "
" FROM tbl_meters_points "
" WHERE meter_id = %s ", (meter_id, ))
rows_points_id = cursor.fetchall()
start_value = None
end_value = None
if rows_points_id is not None and len(rows_points_id) > 0:
query_start_value = (" SELECT actual_value "
" FROM tbl_energy_value "
" where point_id in ("
+ ', '.join(map(lambda x: str(x['point_id']), rows_points_id)) + ") "
" AND utc_date_time BETWEEN %s AND %s "
" order by utc_date_time ASC LIMIT 0,1")
query_end_value = (" SELECT actual_value "
" FROM tbl_energy_value "
" where point_id in ("
+ ', '.join(map(lambda x: str(x['point_id']), rows_points_id)) + ") "
" AND utc_date_time BETWEEN %s AND %s "
" order by utc_date_time DESC LIMIT 0,1")
cursor_historical.execute(query_start_value,
(reporting_start_datetime_utc,
(reporting_start_datetime_utc + timedelta(minutes=15)), ))
row_start_value = cursor_historical.fetchone()
if row_start_value is not None:
start_value = row_start_value[0]
cursor_historical.execute(query_end_value,
((reporting_end_datetime_utc - timedelta(minutes=15)),
reporting_end_datetime_utc, ))
row_end_value = cursor_historical.fetchone()
if row_end_value is not None:
end_value = row_end_value[0]
save_meter_id_value[meter_id] = [start_value, end_value]
meter['start_value'] = start_value
meter['end_value'] = end_value
else:
meter['start_value'] = save_meter_id_value[meter_id][0]
meter['end_value'] = save_meter_id_value[meter_id][1]
if cursor: if cursor:
cursor.close() cursor.close()
if cnx: if cnx:
cnx.disconnect() cnx.disconnect()
if cursor_historical:
cursor_historical.close()
if cnx_historical:
cnx_historical.disconnect()
################################################################################################################ ################################################################################################################
# Step 4: construct the report # Step 4: construct the report
################################################################################################################ ################################################################################################################
@ -111,5 +221,7 @@ class Reporting:
# export result to Excel file and then encode the file to base64 string # export result to Excel file and then encode the file to base64 string
result['excel_bytes_base64'] = \ result['excel_bytes_base64'] = \
excelexporters.metertracking.export(result, excelexporters.metertracking.export(result,
space_name) space_name,
reporting_period_start_datetime_local,
reporting_period_end_datetime_local)
resp.body = json.dumps(result) resp.body = json.dumps(result)

View File

@ -31,9 +31,12 @@ import { withTranslation } from 'react-i18next';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import ButtonIcon from '../../common/ButtonIcon'; import ButtonIcon from '../../common/ButtonIcon';
import { APIBaseURL } from '../../../config'; import { APIBaseURL } from '../../../config';
import Datetime from "react-datetime";
import moment from "moment";
const MeterTracking = ({ setRedirect, setRedirectUrl, t }) => { const MeterTracking = ({ setRedirect, setRedirectUrl, t }) => {
let current_moment = moment();
useEffect(() => { useEffect(() => {
let is_logged_in = getCookieValue('is_logged_in'); let is_logged_in = getCookieValue('is_logged_in');
let user_name = getCookieValue('user_name'); let user_name = getCookieValue('user_name');
@ -60,6 +63,14 @@ const MeterTracking = ({ setRedirect, setRedirectUrl, t }) => {
const [spinnerHidden, setSpinnerHidden] = useState(false); const [spinnerHidden, setSpinnerHidden] = useState(false);
const [exportButtonHidden, setExportButtonHidden] = useState(true); const [exportButtonHidden, setExportButtonHidden] = useState(true);
const [excelBytesBase64, setExcelBytesBase64] = useState(undefined); const [excelBytesBase64, setExcelBytesBase64] = useState(undefined);
const [selectedSpaceID, setSelectedSpaceID] = useState(undefined)
//Query From
const [reportingPeriodBeginsDatetime, setReportingPeriodBeginsDatetime] = useState(current_moment.clone().startOf('month'));
const [reportingPeriodEndsDatetime, setReportingPeriodEndsDatetime] = useState(current_moment);
// buttons
const [submitButtonDisabled, setSubmitButtonDisabled] = useState(true);
useEffect(() => { useEffect(() => {
// begin of getting space tree // begin of getting space tree
@ -87,51 +98,10 @@ const MeterTracking = ({ setRedirect, setRedirectUrl, t }) => {
setCascaderOptions(json); setCascaderOptions(json);
// set the default selected space // set the default selected space
setSelectedSpaceName([json[0]].map(o => o.label)); setSelectedSpaceName([json[0]].map(o => o.label));
let selectedSpaceID = [json[0]].map(o => o.value); setSelectedSpaceID([json[0]].map(o => o.value));
// begin of gettting meter list
let isSecondResponseOK = false;
fetch(APIBaseURL + '/reports/metertracking?spaceid=' + selectedSpaceID, {
method: 'GET',
headers: {
"Content-type": "application/json",
"User-UUID": getCookieValue('user_uuid'),
"Token": getCookieValue('token')
},
body: null,
}).then(response => { setSubmitButtonDisabled(false);
if (response.ok) { setSpinnerHidden(true);
isSecondResponseOK = true;
}
return response.json();
}).then(json => {
if (isSecondResponseOK) {
let json_meters = JSON.parse(JSON.stringify([json['meters']]).split('"id":').join('"value":').split('"name":').join('"label":'));
let meters = [];
json_meters[0].forEach((currentValue, index) => {
meters.push({
'id': currentValue['id'],
'name': currentValue['meter_name'],
'space': currentValue['space_name'],
'costcenter': currentValue['cost_center_name'],
'energycategory': currentValue['energy_category_name'],
'description': currentValue['description']});
});
setMeterList(meters);
setExcelBytesBase64(json['excel_bytes_base64']);
// hide spinner
setSpinnerHidden(true);
// show export buttion
setExportButtonHidden(false);
} else {
toast.error(json.description)
}
}).catch(err => {
console.log(err);
});
// end of getting meter list
} else { } else {
toast.error(json.description); toast.error(json.description);
} }
@ -203,6 +173,20 @@ const MeterTracking = ({ setRedirect, setRedirectUrl, t }) => {
classes: 'border-0 py-2 align-middle', classes: 'border-0 py-2 align-middle',
sort: true sort: true
}, },
{
dataField: 'startvalue',
headerClasses: 'border-0',
text: t('Start Value'),
classes: 'border-0 py-2 align-middle',
sort: true
},
{
dataField: 'endvalue',
headerClasses: 'border-0',
text: t('End Value'),
classes: 'border-0 py-2 align-middle',
sort: true
},
{ {
dataField: '', dataField: '',
headerClasses: 'border-0', headerClasses: 'border-0',
@ -217,58 +201,98 @@ const MeterTracking = ({ setRedirect, setRedirectUrl, t }) => {
let onSpaceCascaderChange = (value, selectedOptions) => { let onSpaceCascaderChange = (value, selectedOptions) => {
setSelectedSpaceName(selectedOptions.map(o => o.label).join('/')); setSelectedSpaceName(selectedOptions.map(o => o.label).join('/'));
let selectedSpaceID = value[value.length - 1]; setSelectedSpaceID(value[value.length - 1]);
setMeterList([]);
setExportButtonHidden(true);
setSubmitButtonDisabled(false);
};
let onReportingPeriodBeginsDatetimeChange = (newDateTime) => {
setReportingPeriodBeginsDatetime(newDateTime);
}
let onReportingPeriodEndsDatetimeChange = (newDateTime) => {
setReportingPeriodEndsDatetime(newDateTime);
}
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(reportingPeriodBeginsDatetime.format('YYYY-MM-DDTHH:mm:ss'));
console.log(reportingPeriodEndsDatetime.format('YYYY-MM-DDTHH:mm:ss'));
// disable submit button
setSubmitButtonDisabled(true);
// show spinner // show spinner
setSpinnerHidden(false); setSpinnerHidden(false);
// hide export buttion // hide export buttion
setExportButtonHidden(true) setExportButtonHidden(true)
// begin of gettting meter list
let isSecondResponseOK = false;
fetch(APIBaseURL + '/reports/metertracking?spaceid=' + selectedSpaceID, {
method: 'GET',
headers: {
"Content-type": "application/json",
"User-UUID": getCookieValue('user_uuid'),
"Token": getCookieValue('token')
},
body: null,
}).then(response => { setMeterList([]);
if (response.ok) {
isSecondResponseOK = true;
}
return response.json();
}).then(json => {
if (isSecondResponseOK) {
let json_meters = JSON.parse(JSON.stringify([json['meters']]).split('"id":').join('"value":').split('"name":').join('"label":'));
let meters = [];
json_meters[0].forEach((currentValue, index) => {
meters.push({
'id': currentValue['id'],
'name': currentValue['meter_name'],
'space': currentValue['space_name'],
'costcenter': currentValue['cost_center_name'],
'energycategory': currentValue['energy_category_name'],
'description': currentValue['description']});
});
setMeterList(meters);
setExcelBytesBase64(json['excel_bytes_base64']); let isResponseOK = false;
fetch(APIBaseURL + '/reports/metertracking?' +
// hide spinner 'spaceid=' + selectedSpaceID +
setSpinnerHidden(true); '&reportingperiodstartdatetime=' + reportingPeriodBeginsDatetime.format('YYYY-MM-DDTHH:mm:ss') +
// show export buttion '&reportingperiodenddatetime=' + reportingPeriodEndsDatetime.format('YYYY-MM-DDTHH:mm:ss'), {
setExportButtonHidden(false); method: 'GET',
} else { headers: {
toast.error(json.description) "Content-type": "application/json",
} "User-UUID": getCookieValue('user_uuid'),
}).catch(err => { "Token": getCookieValue('token')
console.log(err); },
}); body: null,
// end of getting meter list
}).then(response => {
if (response.ok) {
isResponseOK = true;
}
return response.json();
}).then(json => {
if (isResponseOK) {
let json_meters = JSON.parse(JSON.stringify([json['meters']]).split('"id":').join('"value":').split('"name":').join('"label":'));
let meters = [];
json_meters[0].forEach((currentValue, index) => {
meters.push({
'id': currentValue['id'],
'name': currentValue['meter_name'],
'space': currentValue['space_name'],
'costcenter': currentValue['cost_center_name'],
'energycategory': currentValue['energy_category_name'],
'description': currentValue['description'],
'startvalue': currentValue['start_value'],
'endvalue': currentValue['end_value']});
});
setMeterList(meters);
setExcelBytesBase64(json['excel_bytes_base64']);
// hide spinner
setSpinnerHidden(true);
// show export buttion
setExportButtonHidden(false);
setSubmitButtonDisabled(false);
} else {
toast.error(json.description)
}
}).catch(err => {
console.log(err);
});
}; };
const handleExport = e => { const handleExport = e => {
e.preventDefault(); e.preventDefault();
const mimeType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' const mimeType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
@ -295,7 +319,7 @@ const MeterTracking = ({ setRedirect, setRedirectUrl, t }) => {
</div> </div>
<Card className="bg-light mb-3"> <Card className="bg-light mb-3">
<CardBody className="p-3"> <CardBody className="p-3">
<Form > <Form onSubmit={handleSubmit}>
<Row form> <Row form>
<Col xs={6} sm={3}> <Col xs={6} sm={3}>
<FormGroup className="form-group"> <FormGroup className="form-group">
@ -311,6 +335,38 @@ const MeterTracking = ({ setRedirect, setRedirectUrl, t }) => {
</Cascader> </Cascader>
</FormGroup> </FormGroup>
</Col> </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"> <Col xs="auto">
<FormGroup> <FormGroup>
<br></br> <br></br>

View File

@ -924,6 +924,8 @@ const resources = {
//Meter Tracking //Meter Tracking
"Meter List": "计量表列表", "Meter List": "计量表列表",
"Edit Meter": "编辑", "Edit Meter": "编辑",
"Start Value": "开始值",
"End Value": "结束值",
//Equipment Tracking //Equipment Tracking
"Equipment List": "设备列表", "Equipment List": "设备列表",
"Edit Equipment": "编辑", "Edit Equipment": "编辑",