Issues (1656)

myems-api/reports/metercomparison.py (3 issues)

Severity
1
import re
2
from datetime import datetime, timedelta, timezone
3
from decimal import Decimal
4
import falcon
5
import mysql.connector
6
import simplejson as json
7
import config
8
import excelexporters.metercomparison
9
from core import utilities
10
from core.useractivity import access_control, api_key_control
11
12
13
class Reporting:
14
    def __init__(self):
15
        """"Initializes Reporting"""
16
        pass
17
18
    @staticmethod
19
    def on_options(req, resp):
20
        _ = req
21
        resp.status = falcon.HTTP_200
22
23
    ####################################################################################################################
24
    # PROCEDURES
25
    # Step 1: valid parameters
26
    # Step 2: query the meter and energy category
27
    # Step 3: query associated points
28
    # Step 4: query reporting period energy consumption
29
    # Step 5: query associated points data
30
    # Step 6: construct the report
31
    ####################################################################################################################
32
    @staticmethod
33
    def on_get(req, resp):
34
        if 'API-KEY' not in req.headers or \
35
                not isinstance(req.headers['API-KEY'], str) or \
36
                len(str.strip(req.headers['API-KEY'])) == 0:
37
            access_control(req)
38
        else:
39
            api_key_control(req)
40
        print(req.params)
41
        # this procedure accepts meter id or meter uuid to identify a meter
42
        meter_id1 = req.params.get('meterid1')
43
        meter_uuid1 = req.params.get('meteruuid1')
44
        meter_id2 = req.params.get('meterid2')
45
        meter_uuid2 = req.params.get('meteruuid2')
46
        period_type = req.params.get('periodtype')
47
        reporting_period_start_datetime_local = req.params.get('reportingperiodstartdatetime')
48
        reporting_period_end_datetime_local = req.params.get('reportingperiodenddatetime')
49
        language = req.params.get('language')
50
        quick_mode = req.params.get('quickmode')
51
52
        ################################################################################################################
53
        # Step 1: valid parameters
54
        ################################################################################################################
55
        if meter_id1 is None and meter_uuid1 is None:
56
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_METER_ID')
57
58
        if meter_id1 is not None:
59
            meter_id1 = str.strip(meter_id1)
60
            if not meter_id1.isdigit() or int(meter_id1) <= 0:
61
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
62
                                       description='API.INVALID_METER_ID')
63
64
        if meter_uuid1 is not None:
65
            regex = re.compile(r'^[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)
66
            match = regex.match(str.strip(meter_uuid1))
67
            if not bool(match):
68
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
69
                                       description='API.INVALID_METER_UUID')
70
71
        if meter_id2 is None and meter_uuid2 is None:
72
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
73
                                   description='API.INVALID_METER_ID')
74
75
        if meter_id2 is not None:
76
            meter_id2 = str.strip(meter_id2)
77
            if not meter_id2.isdigit() or int(meter_id2) <= 0:
78
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
79
                                       description='API.INVALID_METER_ID')
80
81
        if meter_uuid2 is not None:
82
            regex = re.compile(r'^[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)
83
            match = regex.match(str.strip(meter_uuid2))
84
            if not bool(match):
85
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
86
                                       description='API.INVALID_METER_UUID')
87
88
        if period_type is None:
89
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
90
                                   description='API.INVALID_PERIOD_TYPE')
91
        else:
92
            period_type = str.strip(period_type)
93
            if period_type not in ['hourly', 'daily', 'weekly', 'monthly', 'yearly']:
94
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
95
                                       description='API.INVALID_PERIOD_TYPE')
96
97
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
98
        if config.utc_offset[0] == '-':
99
            timezone_offset = -timezone_offset
100
101
        if reporting_period_start_datetime_local is None:
102
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
103
                                   description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
104
        else:
105
            reporting_period_start_datetime_local = str.strip(reporting_period_start_datetime_local)
106
            try:
107
                reporting_start_datetime_utc = datetime.strptime(reporting_period_start_datetime_local,
108
                                                                 '%Y-%m-%dT%H:%M:%S')
109
            except ValueError:
110
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
111
                                       description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
112
            reporting_start_datetime_utc = \
113
                reporting_start_datetime_utc.replace(tzinfo=timezone.utc) - timedelta(minutes=timezone_offset)
114
            # nomalize the start datetime
115
            if config.minutes_to_count == 30 and reporting_start_datetime_utc.minute >= 30:
116
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=30, second=0, microsecond=0)
117
            else:
118
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=0, second=0, microsecond=0)
119
120
        if reporting_period_end_datetime_local is None:
121
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
122
                                   description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
123
        else:
124
            reporting_period_end_datetime_local = str.strip(reporting_period_end_datetime_local)
125
            try:
126
                reporting_end_datetime_utc = datetime.strptime(reporting_period_end_datetime_local,
127
                                                               '%Y-%m-%dT%H:%M:%S')
128
            except ValueError:
129
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
130
                                       description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
131
            reporting_end_datetime_utc = reporting_end_datetime_utc.replace(tzinfo=timezone.utc) - \
132
                timedelta(minutes=timezone_offset)
133
134
        if reporting_start_datetime_utc >= reporting_end_datetime_utc:
135
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
136
                                   description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
137
138
        # if turn quick mode on, do not return parameters data and excel file
139
        is_quick_mode = False
140
        if quick_mode is not None and \
141
                len(str.strip(quick_mode)) > 0 and \
142
                str.lower(str.strip(quick_mode)) in ('true', 't', 'on', 'yes', 'y'):
143
            is_quick_mode = True
144
145
        ################################################################################################################
146
        # Step 2: query the meter and energy category
147
        ################################################################################################################
148
        cnx_system = mysql.connector.connect(**config.myems_system_db)
149
        cursor_system = cnx_system.cursor()
150
151
        cnx_energy = mysql.connector.connect(**config.myems_energy_db)
152
        cursor_energy = cnx_energy.cursor()
153
154
        cnx_historical = mysql.connector.connect(**config.myems_historical_db)
155
        cursor_historical = cnx_historical.cursor()
156
157
        if meter_id1 is not None:
158
            cursor_system.execute(" SELECT m.id, m.name, m.energy_category_id, ec.name, ec.unit_of_measure "
159
                                  " FROM tbl_meters m, tbl_energy_categories ec "
160
                                  " WHERE m.id = %s AND m.energy_category_id = ec.id ", (meter_id1,))
161
            row_meter1 = cursor_system.fetchone()
162
        elif meter_uuid1 is not None:
163
            cursor_system.execute(" SELECT m.id, m.name, m.energy_category_id, ec.name, ec.unit_of_measure "
164
                                  " FROM tbl_meters m, tbl_energy_categories ec "
165
                                  " WHERE m.uuid = %s AND m.energy_category_id = ec.id ", (meter_uuid1,))
166
            row_meter1 = cursor_system.fetchone()
167
168
        if row_meter1 is None:
0 ignored issues
show
The variable row_meter1 does not seem to be defined for all execution paths.
Loading history...
169
            if cursor_system:
170
                cursor_system.close()
171
            if cnx_system:
172
                cnx_system.close()
173
174
            if cursor_energy:
175
                cursor_energy.close()
176
            if cnx_energy:
177
                cnx_energy.close()
178
179
            if cursor_historical:
180
                cursor_historical.close()
181
            if cnx_historical:
182
                cnx_historical.close()
183
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND', description='API.METER_NOT_FOUND')
184
185
        meter1 = dict()
186
        meter1['id'] = row_meter1[0]
187
        meter1['name'] = row_meter1[1]
188
        meter1['energy_category_id'] = row_meter1[2]
189
        meter1['energy_category_name'] = row_meter1[3]
190
        meter1['unit_of_measure'] = row_meter1[4]
191
192
        if meter_id2 is not None:
193
            cursor_system.execute(" SELECT m.id, m.name, m.energy_category_id, ec.name, ec.unit_of_measure "
194
                                  " FROM tbl_meters m, tbl_energy_categories ec "
195
                                  " WHERE m.id = %s AND m.energy_category_id = ec.id ", (meter_id2,))
196
            row_meter2 = cursor_system.fetchone()
197
        elif meter_uuid2 is not None:
198
            cursor_system.execute(" SELECT m.id, m.name, m.energy_category_id, ec.name, ec.unit_of_measure "
199
                                  " FROM tbl_meters m, tbl_energy_categories ec "
200
                                  " WHERE m.uuid = %s AND m.energy_category_id = ec.id ", (meter_uuid2,))
201
            row_meter2 = cursor_system.fetchone()
202
203
        if row_meter2 is None:
0 ignored issues
show
The variable row_meter2 does not seem to be defined for all execution paths.
Loading history...
204
            if cursor_system:
205
                cursor_system.close()
206
            if cnx_system:
207
                cnx_system.close()
208
209
            if cursor_energy:
210
                cursor_energy.close()
211
            if cnx_energy:
212
                cnx_energy.close()
213
214
            if cursor_historical:
215
                cursor_historical.close()
216
            if cnx_historical:
217
                cnx_historical.close()
218
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND', description='API.METER_NOT_FOUND')
219
220
        meter2 = dict()
221
        meter2['id'] = row_meter2[0]
222
        meter2['name'] = row_meter2[1]
223
        meter2['energy_category_id'] = row_meter2[2]
224
        meter2['energy_category_name'] = row_meter2[3]
225
        meter2['unit_of_measure'] = row_meter2[4]
226
        ################################################################################################################
227
        # Step 3: query associated points
228
        ################################################################################################################
229
        point_list1 = list()
230
        cursor_system.execute(" SELECT p.id, p.name, p.units, p.object_type  "
231
                              " FROM tbl_meters m, tbl_meters_points mp, tbl_points p "
232
                              " WHERE m.id = %s AND m.id = mp.meter_id AND mp.point_id = p.id "
233
                              " ORDER BY p.id ", (meter1['id'],))
234
        rows_points1 = cursor_system.fetchall()
235
        if rows_points1 is not None and len(rows_points1) > 0:
236
            for row in rows_points1:
237
                point_list1.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
238
239
        point_list2 = list()
240
        cursor_system.execute(" SELECT p.id, p.name, p.units, p.object_type  "
241
                              " FROM tbl_meters m, tbl_meters_points mp, tbl_points p "
242
                              " WHERE m.id = %s AND m.id = mp.meter_id AND mp.point_id = p.id "
243
                              " ORDER BY p.id ", (meter2['id'],))
244
        rows_points2 = cursor_system.fetchall()
245
        if rows_points2 is not None and len(rows_points2) > 0:
246
            for row in rows_points2:
247
                point_list2.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
248
        ################################################################################################################
249
        # Step 4: query reporting period energy consumption
250
        ################################################################################################################
251
        query1 = (" SELECT start_datetime_utc, actual_value "
252
                  " FROM tbl_meter_hourly "
253
                  " WHERE meter_id = %s "
254
                  " AND start_datetime_utc >= %s "
255
                  " AND start_datetime_utc < %s "
256
                  " ORDER BY start_datetime_utc ")
257
        cursor_energy.execute(query1, (meter1['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
258
        rows_meter1_hourly = cursor_energy.fetchall()
259
260
        rows_meter1_periodically = utilities.aggregate_hourly_data_by_period(rows_meter1_hourly,
261
                                                                             reporting_start_datetime_utc,
262
                                                                             reporting_end_datetime_utc,
263
                                                                             period_type)
264
        reporting1 = dict()
265
        reporting1['timestamps'] = list()
266
        reporting1['values'] = list()
267
        reporting1['total_in_category'] = Decimal(0.0)
268
269
        for row_meter1_periodically in rows_meter1_periodically:
270
            current_datetime_local = row_meter1_periodically[0].replace(tzinfo=timezone.utc) + \
271
                timedelta(minutes=timezone_offset)
272
            if period_type == 'hourly':
273
                current_datetime = current_datetime_local.isoformat()[0:19]
274
            elif period_type == 'daily':
275
                current_datetime = current_datetime_local.isoformat()[0:10]
276
            elif period_type == 'weekly':
277
                current_datetime = current_datetime_local.isoformat()[0:10]
278
            elif period_type == 'monthly':
279
                current_datetime = current_datetime_local.isoformat()[0:7]
280
            elif period_type == 'yearly':
281
                current_datetime = current_datetime_local.isoformat()[0:4]
282
283
            actual_value = Decimal(0.0) if row_meter1_periodically[1] is None else row_meter1_periodically[1]
284
285
            reporting1['timestamps'].append(current_datetime)
0 ignored issues
show
The variable current_datetime does not seem to be defined for all execution paths.
Loading history...
286
            reporting1['values'].append(actual_value)
287
            reporting1['total_in_category'] += actual_value
288
289
        query2 = (" SELECT start_datetime_utc, actual_value "
290
                  " FROM tbl_meter_hourly "
291
                  " WHERE meter_id = %s "
292
                  " AND start_datetime_utc >= %s "
293
                  " AND start_datetime_utc < %s "
294
                  " ORDER BY start_datetime_utc ")
295
        cursor_energy.execute(query2, (meter2['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
296
        rows_meter2_hourly = cursor_energy.fetchall()
297
298
        rows_meter2_periodically = utilities.aggregate_hourly_data_by_period(rows_meter2_hourly,
299
                                                                             reporting_start_datetime_utc,
300
                                                                             reporting_end_datetime_utc,
301
                                                                             period_type)
302
        reporting2 = dict()
303
        diff = dict()
304
        reporting2['timestamps'] = list()
305
        reporting2['values'] = list()
306
        reporting2['total_in_category'] = Decimal(0.0)
307
        diff['values'] = list()
308
        diff['total_in_category'] = Decimal(0.0)
309
310
        for row_meter2_periodically in rows_meter2_periodically:
311
            current_datetime_local = row_meter2_periodically[0].replace(tzinfo=timezone.utc) + \
312
                                     timedelta(minutes=timezone_offset)
313
            if period_type == 'hourly':
314
                current_datetime = current_datetime_local.isoformat()[0:19]
315
            elif period_type == 'daily':
316
                current_datetime = current_datetime_local.isoformat()[0:10]
317
            elif period_type == 'weekly':
318
                current_datetime = current_datetime_local.isoformat()[0:10]
319
            elif period_type == 'monthly':
320
                current_datetime = current_datetime_local.isoformat()[0:7]
321
            elif period_type == 'yearly':
322
                current_datetime = current_datetime_local.isoformat()[0:4]
323
324
            actual_value = Decimal(0.0) if row_meter2_periodically[1] is None else row_meter2_periodically[1]
325
326
            reporting2['timestamps'].append(current_datetime)
327
            reporting2['values'].append(actual_value)
328
            reporting2['total_in_category'] += actual_value
329
        
330
        for meter1_value, meter2_value in zip(reporting1['values'], reporting2['values']):
331
            diff['values'].append(meter1_value - meter2_value)
332
            diff['total_in_category'] += meter1_value - meter2_value
333
        ################################################################################################################
334
        # Step 5: query associated points data
335
        ################################################################################################################
336
        parameters_data1 = dict()
337
        parameters_data1['names'] = list()
338
        parameters_data1['timestamps'] = list()
339
        parameters_data1['values'] = list()
340
341
        if not is_quick_mode:
342
            for point in point_list1:
343
                point_values = []
344
                point_timestamps = []
345
                if point['object_type'] == 'ENERGY_VALUE':
346
                    query = (" SELECT utc_date_time, actual_value "
347
                             " FROM tbl_energy_value "
348
                             " WHERE point_id = %s "
349
                             "       AND utc_date_time BETWEEN %s AND %s "
350
                             " ORDER BY utc_date_time ")
351
                    cursor_historical.execute(query, (point['id'],
352
                                                      reporting_start_datetime_utc,
353
                                                      reporting_end_datetime_utc))
354
                    rows = cursor_historical.fetchall()
355
356
                    if rows is not None and len(rows) > 0:
357
                        for row in rows:
358
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
359
                                                     timedelta(minutes=timezone_offset)
360
                            current_datetime = current_datetime_local.isoformat()[0:19]
361
                            point_timestamps.append(current_datetime)
362
                            point_values.append(row[1])
363
                elif point['object_type'] == 'ANALOG_VALUE':
364
                    query = (" SELECT utc_date_time, actual_value "
365
                             " FROM tbl_analog_value "
366
                             " WHERE point_id = %s "
367
                             "       AND utc_date_time BETWEEN %s AND %s "
368
                             " ORDER BY utc_date_time ")
369
                    cursor_historical.execute(query, (point['id'],
370
                                                      reporting_start_datetime_utc,
371
                                                      reporting_end_datetime_utc))
372
                    rows = cursor_historical.fetchall()
373
374
                    if rows is not None and len(rows) > 0:
375
                        for row in rows:
376
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
377
                                                     timedelta(minutes=timezone_offset)
378
                            current_datetime = current_datetime_local.isoformat()[0:19]
379
                            point_timestamps.append(current_datetime)
380
                            point_values.append(row[1])
381
                elif point['object_type'] == 'DIGITAL_VALUE':
382
                    query = (" SELECT utc_date_time, actual_value "
383
                             " FROM tbl_digital_value "
384
                             " WHERE point_id = %s "
385
                             "       AND utc_date_time BETWEEN %s AND %s "
386
                             " ORDER BY utc_date_time ")
387
                    cursor_historical.execute(query, (point['id'],
388
                                                      reporting_start_datetime_utc,
389
                                                      reporting_end_datetime_utc))
390
                    rows = cursor_historical.fetchall()
391
392
                    if rows is not None and len(rows) > 0:
393
                        for row in rows:
394
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
395
                                                     timedelta(minutes=timezone_offset)
396
                            current_datetime = current_datetime_local.isoformat()[0:19]
397
                            point_timestamps.append(current_datetime)
398
                            point_values.append(row[1])
399
400
                parameters_data1['names'].append(point['name'] + ' (' + point['units'] + ')')
401
                parameters_data1['timestamps'].append(point_timestamps)
402
                parameters_data1['values'].append(point_values)
403
404
        parameters_data2 = dict()
405
        parameters_data2['names'] = list()
406
        parameters_data2['timestamps'] = list()
407
        parameters_data2['values'] = list()
408
        if not is_quick_mode:
409
            for point in point_list2:
410
                point_values = []
411
                point_timestamps = []
412
                if point['object_type'] == 'ENERGY_VALUE':
413
                    query = (" SELECT utc_date_time, actual_value "
414
                             " FROM tbl_energy_value "
415
                             " WHERE point_id = %s "
416
                             "       AND utc_date_time BETWEEN %s AND %s "
417
                             " ORDER BY utc_date_time ")
418
                    cursor_historical.execute(query, (point['id'],
419
                                                      reporting_start_datetime_utc,
420
                                                      reporting_end_datetime_utc))
421
                    rows = cursor_historical.fetchall()
422
423
                    if rows is not None and len(rows) > 0:
424
                        for row in rows:
425
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
426
                                                     timedelta(minutes=timezone_offset)
427
                            current_datetime = current_datetime_local.isoformat()[0:19]
428
                            point_timestamps.append(current_datetime)
429
                            point_values.append(row[1])
430
                elif point['object_type'] == 'ANALOG_VALUE':
431
                    query = (" SELECT utc_date_time, actual_value "
432
                             " FROM tbl_analog_value "
433
                             " WHERE point_id = %s "
434
                             "       AND utc_date_time BETWEEN %s AND %s "
435
                             " ORDER BY utc_date_time ")
436
                    cursor_historical.execute(query, (point['id'],
437
                                                      reporting_start_datetime_utc,
438
                                                      reporting_end_datetime_utc))
439
                    rows = cursor_historical.fetchall()
440
441
                    if rows is not None and len(rows) > 0:
442
                        for row in rows:
443
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
444
                                                     timedelta(minutes=timezone_offset)
445
                            current_datetime = current_datetime_local.isoformat()[0:19]
446
                            point_timestamps.append(current_datetime)
447
                            point_values.append(row[1])
448
                elif point['object_type'] == 'DIGITAL_VALUE':
449
                    query = (" SELECT utc_date_time, actual_value "
450
                             " FROM tbl_digital_value "
451
                             " WHERE point_id = %s "
452
                             "       AND utc_date_time BETWEEN %s AND %s "
453
                             " ORDER BY utc_date_time ")
454
                    cursor_historical.execute(query, (point['id'],
455
                                                      reporting_start_datetime_utc,
456
                                                      reporting_end_datetime_utc))
457
                    rows = cursor_historical.fetchall()
458
459
                    if rows is not None and len(rows) > 0:
460
                        for row in rows:
461
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
462
                                                     timedelta(minutes=timezone_offset)
463
                            current_datetime = current_datetime_local.isoformat()[0:19]
464
                            point_timestamps.append(current_datetime)
465
                            point_values.append(row[1])
466
467
                parameters_data2['names'].append(point['name'] + ' (' + point['units'] + ')')
468
                parameters_data2['timestamps'].append(point_timestamps)
469
                parameters_data2['values'].append(point_values)
470
        ################################################################################################################
471
        # Step 6: construct the report
472
        ################################################################################################################
473
        if cursor_system:
474
            cursor_system.close()
475
        if cnx_system:
476
            cnx_system.close()
477
478
        if cursor_energy:
479
            cursor_energy.close()
480
        if cnx_energy:
481
            cnx_energy.close()
482
483
        if cursor_historical:
484
            cursor_historical.close()
485
        if cnx_historical:
486
            cnx_historical.close()
487
        result = {
488
            "meter1": {
489
                "name": meter1['name'],
490
                "energy_category_id": meter1['energy_category_id'],
491
                "energy_category_name": meter1['energy_category_name'],
492
                "unit_of_measure": meter1['unit_of_measure'],
493
            },
494
            "reporting_period1": {
495
                "total_in_category": reporting1['total_in_category'],
496
                "timestamps": reporting1['timestamps'],
497
                "values": reporting1['values'],
498
            },
499
            "parameters1": {
500
                "names": parameters_data1['names'],
501
                "timestamps": parameters_data1['timestamps'],
502
                "values": parameters_data1['values']
503
            },
504
            "meter2": {
505
                "name": meter2['name'],
506
                "energy_category_id": meter2['energy_category_id'],
507
                "energy_category_name": meter2['energy_category_name'],
508
                "unit_of_measure": meter2['unit_of_measure'],
509
            },
510
            "reporting_period2": {
511
                "total_in_category": reporting2['total_in_category'],
512
                "timestamps": reporting2['timestamps'],
513
                "values": reporting2['values'],
514
            },
515
            "parameters2": {
516
                "names": parameters_data2['names'],
517
                "timestamps": parameters_data2['timestamps'],
518
                "values": parameters_data2['values']
519
            },
520
            "diff": {
521
                "values": diff['values'],
522
                "total_in_category": diff['total_in_category'],
523
            }
524
        }
525
        # export result to Excel file and then encode the file to base64 string
526
        if not is_quick_mode:
527
            result['excel_bytes_base64'] = \
528
                excelexporters.metercomparison.export(result,
529
                                                      meter1['name'],
530
                                                      meter2['name'],
531
                                                      reporting_period_start_datetime_local,
532
                                                      reporting_period_end_datetime_local,
533
                                                      period_type,
534
                                                      language)
535
536
        resp.text = json.dumps(result)
537