Passed
Push — master ( 7b66e6...501a6a )
by Guangyu
07:37 queued 13s
created

reports.metercomparison   F

Complexity

Total Complexity 101

Size/Duplication

Total Lines 507
Duplicated Lines 24.85 %

Importance

Changes 0
Metric Value
wmc 101
eloc 368
dl 126
loc 507
rs 2
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A Reporting.__init__() 0 4 1
F Reporting.on_get() 126 474 99
A Reporting.on_options() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like reports.metercomparison often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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