Passed
Push — master ( d35f3b...b6c9eb )
by Guangyu
07:20 queued 12s
created

reports.storecarbon   F

Complexity

Total Complexity 116

Size/Duplication

Total Lines 571
Duplicated Lines 97.9 %

Importance

Changes 0
Metric Value
wmc 116
eloc 419
dl 559
loc 571
rs 2
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A Reporting.__init__() 4 4 1
F Reporting.on_get() 536 536 114
A Reporting.on_options() 3 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.storecarbon 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 re
2
import falcon
3
import simplejson as json
4
import mysql.connector
5
import config
6
from datetime import datetime, timedelta, timezone
7
from core import utilities
8
from decimal import Decimal
9
import excelexporters.storecarbon
10
11
12 View Code Duplication
class Reporting:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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 store
26
    # Step 3: query energy categories
27
    # Step 4: query associated sensors
28
    # Step 5: query associated points
29
    # Step 6: query base period energy carbon dioxide emissions
30
    # Step 7: query reporting period energy carbon dioxide emissions
31
    # Step 8: query tariff data
32
    # Step 9: query associated sensors and points data
33
    # Step 10: construct the report
34
    ####################################################################################################################
35
    @staticmethod
36
    def on_get(req, resp):
37
        print(req.params)
38
        store_id = req.params.get('storeid')
39
        store_uuid = req.params.get('storeuuid')
40
        period_type = req.params.get('periodtype')
41
        base_start_datetime_local = req.params.get('baseperiodstartdatetime')
42
        base_end_datetime_local = req.params.get('baseperiodenddatetime')
43
        reporting_start_datetime_local = req.params.get('reportingperiodstartdatetime')
44
        reporting_end_datetime_local = req.params.get('reportingperiodenddatetime')
45
46
        ################################################################################################################
47
        # Step 1: valid parameters
48
        ################################################################################################################
49
        if store_id is None and store_uuid is None:
50
            raise falcon.HTTPError(falcon.HTTP_400,
51
                                   title='API.BAD_REQUEST',
52
                                   description='API.INVALID_STORE_ID')
53
54
        if store_id is not None:
55
            store_id = str.strip(store_id)
56
            if not store_id.isdigit() or int(store_id) <= 0:
57
                raise falcon.HTTPError(falcon.HTTP_400,
58
                                       title='API.BAD_REQUEST',
59
                                       description='API.INVALID_STORE_ID')
60
61
        if store_uuid is not None:
62
            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)
63
            match = regex.match(str.strip(store_uuid))
64
            if not bool(match):
65
                raise falcon.HTTPError(falcon.HTTP_400,
66
                                       title='API.BAD_REQUEST',
67
                                       description='API.INVALID_STORE_UUID')
68
69
        if period_type is None:
70
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_PERIOD_TYPE')
71
        else:
72
            period_type = str.strip(period_type)
73
            if period_type not in ['hourly', 'daily', 'weekly', 'monthly', 'yearly']:
74
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_PERIOD_TYPE')
75
76
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
77
        if config.utc_offset[0] == '-':
78
            timezone_offset = -timezone_offset
79
80
        base_start_datetime_utc = None
81
        if base_start_datetime_local is not None and len(str.strip(base_start_datetime_local)) > 0:
82
            base_start_datetime_local = str.strip(base_start_datetime_local)
83
            try:
84
                base_start_datetime_utc = datetime.strptime(base_start_datetime_local,
85
                                                            '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
86
                    timedelta(minutes=timezone_offset)
87
            except ValueError:
88
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
89
                                       description="API.INVALID_BASE_PERIOD_START_DATETIME")
90
91
        base_end_datetime_utc = None
92
        if base_end_datetime_local is not None and len(str.strip(base_end_datetime_local)) > 0:
93
            base_end_datetime_local = str.strip(base_end_datetime_local)
94
            try:
95
                base_end_datetime_utc = datetime.strptime(base_end_datetime_local,
96
                                                          '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
97
                    timedelta(minutes=timezone_offset)
98
            except ValueError:
99
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
100
                                       description="API.INVALID_BASE_PERIOD_END_DATETIME")
101
102
        if base_start_datetime_utc is not None and base_end_datetime_utc is not None and \
103
                base_start_datetime_utc >= base_end_datetime_utc:
104
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
105
                                   description='API.INVALID_BASE_PERIOD_END_DATETIME')
106
107
        if reporting_start_datetime_local is None:
108
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
109
                                   description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
110
        else:
111
            reporting_start_datetime_local = str.strip(reporting_start_datetime_local)
112
            try:
113
                reporting_start_datetime_utc = datetime.strptime(reporting_start_datetime_local,
114
                                                                 '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
115
                    timedelta(minutes=timezone_offset)
116
            except ValueError:
117
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
118
                                       description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
119
120
        if reporting_end_datetime_local is None:
121
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
122
                                   description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
123
        else:
124
            reporting_end_datetime_local = str.strip(reporting_end_datetime_local)
125
            try:
126
                reporting_end_datetime_utc = datetime.strptime(reporting_end_datetime_local,
127
                                                               '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
128
                    timedelta(minutes=timezone_offset)
129
            except ValueError:
130
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
131
                                       description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
132
133
        if reporting_start_datetime_utc >= reporting_end_datetime_utc:
134
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
135
                                   description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
136
137
        ################################################################################################################
138
        # Step 2: query the store
139
        ################################################################################################################
140
        cnx_system = mysql.connector.connect(**config.myems_system_db)
141
        cursor_system = cnx_system.cursor()
142
143
        cnx_carbon = mysql.connector.connect(**config.myems_carbon_db)
144
        cursor_carbon = cnx_carbon.cursor()
145
146
        cnx_historical = mysql.connector.connect(**config.myems_historical_db)
147
        cursor_historical = cnx_historical.cursor()
148
149
        if store_id is not None:
150
            cursor_system.execute(" SELECT id, name, area, cost_center_id "
151
                                  " FROM tbl_stores "
152
                                  " WHERE id = %s ", (store_id,))
153
            row_store = cursor_system.fetchone()
154
        elif store_uuid is not None:
155
            cursor_system.execute(" SELECT id, name, area, cost_center_id "
156
                                  " FROM tbl_stores "
157
                                  " WHERE uuid = %s ", (store_uuid,))
158
            row_store = cursor_system.fetchone()
159
160
        if row_store is None:
0 ignored issues
show
introduced by
The variable row_store does not seem to be defined for all execution paths.
Loading history...
161
            if cursor_system:
162
                cursor_system.close()
163
            if cnx_system:
164
                cnx_system.close()
165
166
            if cursor_carbon:
167
                cursor_carbon.close()
168
            if cnx_carbon:
169
                cnx_carbon.close()
170
171
            if cursor_historical:
172
                cursor_historical.close()
173
            if cnx_historical:
174
                cnx_historical.close()
175
            raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND', description='API.STORE_NOT_FOUND')
176
177
        store = dict()
178
        store['id'] = row_store[0]
179
        store['name'] = row_store[1]
180
        store['area'] = row_store[2]
181
        store['cost_center_id'] = row_store[3]
182
183
        ################################################################################################################
184
        # Step 3: query energy categories
185
        ################################################################################################################
186
        energy_category_set = set()
187
        # query energy categories in base period
188
        cursor_carbon.execute(" SELECT DISTINCT(energy_category_id) "
189
                               " FROM tbl_store_input_category_hourly "
190
                               " WHERE store_id = %s "
191
                               "     AND start_datetime_utc >= %s "
192
                               "     AND start_datetime_utc < %s ",
193
                               (store['id'], base_start_datetime_utc, base_end_datetime_utc))
194
        rows_energy_categories = cursor_carbon.fetchall()
195
        if rows_energy_categories is not None or len(rows_energy_categories) > 0:
196
            for row_energy_category in rows_energy_categories:
197
                energy_category_set.add(row_energy_category[0])
198
199
        # query energy categories in reporting period
200
        cursor_carbon.execute(" SELECT DISTINCT(energy_category_id) "
201
                               " FROM tbl_store_input_category_hourly "
202
                               " WHERE store_id = %s "
203
                               "     AND start_datetime_utc >= %s "
204
                               "     AND start_datetime_utc < %s ",
205
                               (store['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
206
        rows_energy_categories = cursor_carbon.fetchall()
207
        if rows_energy_categories is not None or len(rows_energy_categories) > 0:
208
            for row_energy_category in rows_energy_categories:
209
                energy_category_set.add(row_energy_category[0])
210
211
        # query all energy categories in base period and reporting period
212
        cursor_system.execute(" SELECT id, name, unit_of_measure, kgce, kgco2e "
213
                              " FROM tbl_energy_categories "
214
                              " ORDER BY id ", )
215
        rows_energy_categories = cursor_system.fetchall()
216
        if rows_energy_categories is None or len(rows_energy_categories) == 0:
217
            if cursor_system:
218
                cursor_system.close()
219
            if cnx_system:
220
                cnx_system.close()
221
222
            if cursor_carbon:
223
                cursor_carbon.close()
224
            if cnx_carbon:
225
                cnx_carbon.close()
226
227
            if cursor_historical:
228
                cursor_historical.close()
229
            if cnx_historical:
230
                cnx_historical.close()
231
            raise falcon.HTTPError(falcon.HTTP_404,
232
                                   title='API.NOT_FOUND',
233
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
234
        energy_category_dict = dict()
235
        for row_energy_category in rows_energy_categories:
236
            if row_energy_category[0] in energy_category_set:
237
                energy_category_dict[row_energy_category[0]] = {"name": row_energy_category[1],
238
                                                                "unit_of_measure": row_energy_category[2],
239
                                                                "kgce": row_energy_category[3],
240
                                                                "kgco2e": row_energy_category[4]}
241
242
        ################################################################################################################
243
        # Step 4: query associated sensors
244
        ################################################################################################################
245
        point_list = list()
246
        cursor_system.execute(" SELECT p.id, p.name, p.units, p.object_type  "
247
                              " FROM tbl_stores st, tbl_sensors se, tbl_stores_sensors ss, "
248
                              "      tbl_points p, tbl_sensors_points sp "
249
                              " WHERE st.id = %s AND st.id = ss.store_id AND ss.sensor_id = se.id "
250
                              "       AND se.id = sp.sensor_id AND sp.point_id = p.id "
251
                              " ORDER BY p.id ", (store['id'], ))
252
        rows_points = cursor_system.fetchall()
253
        if rows_points is not None and len(rows_points) > 0:
254
            for row in rows_points:
255
                point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
256
257
        ################################################################################################################
258
        # Step 5: query associated points
259
        ################################################################################################################
260
        cursor_system.execute(" SELECT p.id, p.name, p.units, p.object_type  "
261
                              " FROM tbl_stores s, tbl_stores_points sp, tbl_points p "
262
                              " WHERE s.id = %s AND s.id = sp.store_id AND sp.point_id = p.id "
263
                              " ORDER BY p.id ", (store['id'], ))
264
        rows_points = cursor_system.fetchall()
265
        if rows_points is not None and len(rows_points) > 0:
266
            for row in rows_points:
267
                point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
268
269
        ################################################################################################################
270
        # Step 6: query base period energy carbon dioxide emissions
271
        ################################################################################################################
272
        base = dict()
273
        if energy_category_set is not None and len(energy_category_set) > 0:
274
            for energy_category_id in energy_category_set:
275
                base[energy_category_id] = dict()
276
                base[energy_category_id]['timestamps'] = list()
277
                base[energy_category_id]['values'] = list()
278
                base[energy_category_id]['subtotal'] = Decimal(0.0)
279
280
                cursor_carbon.execute(" SELECT start_datetime_utc, actual_value "
281
                                       " FROM tbl_store_input_category_hourly "
282
                                       " WHERE store_id = %s "
283
                                       "     AND energy_category_id = %s "
284
                                       "     AND start_datetime_utc >= %s "
285
                                       "     AND start_datetime_utc < %s "
286
                                       " ORDER BY start_datetime_utc ",
287
                                       (store['id'],
288
                                        energy_category_id,
289
                                        base_start_datetime_utc,
290
                                        base_end_datetime_utc))
291
                rows_store_hourly = cursor_carbon.fetchall()
292
293
                rows_store_periodically = utilities.aggregate_hourly_data_by_period(rows_store_hourly,
294
                                                                                    base_start_datetime_utc,
295
                                                                                    base_end_datetime_utc,
296
                                                                                    period_type)
297
                for row_store_periodically in rows_store_periodically:
298
                    current_datetime_local = row_store_periodically[0].replace(tzinfo=timezone.utc) + \
299
                                             timedelta(minutes=timezone_offset)
300
                    if period_type == 'hourly':
301
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
302
                    elif period_type == 'daily':
303
                        current_datetime = current_datetime_local.strftime('%Y-%m-%d')
304
                    elif period_type == 'weekly':
305
                        current_datetime = current_datetime_local.strftime('%Y-%m-%d')
306
                    elif period_type == 'monthly':
307
                        current_datetime = current_datetime_local.strftime('%Y-%m')
308
                    elif period_type == 'yearly':
309
                        current_datetime = current_datetime_local.strftime('%Y')
310
311
                    actual_value = Decimal(0.0) if row_store_periodically[1] is None else row_store_periodically[1]
312
                    base[energy_category_id]['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...
313
                    base[energy_category_id]['values'].append(actual_value)
314
                    base[energy_category_id]['subtotal'] += actual_value
315
316
        ################################################################################################################
317
        # Step 7: query reporting period energy carbon dioxide emissions
318
        ################################################################################################################
319
        reporting = dict()
320
        if energy_category_set is not None and len(energy_category_set) > 0:
321
            for energy_category_id in energy_category_set:
322
                reporting[energy_category_id] = dict()
323
                reporting[energy_category_id]['timestamps'] = list()
324
                reporting[energy_category_id]['values'] = list()
325
                reporting[energy_category_id]['subtotal'] = Decimal(0.0)
326
                reporting[energy_category_id]['toppeak'] = Decimal(0.0)
327
                reporting[energy_category_id]['onpeak'] = Decimal(0.0)
328
                reporting[energy_category_id]['midpeak'] = Decimal(0.0)
329
                reporting[energy_category_id]['offpeak'] = Decimal(0.0)
330
331
                cursor_carbon.execute(" SELECT start_datetime_utc, actual_value "
332
                                       " FROM tbl_store_input_category_hourly "
333
                                       " WHERE store_id = %s "
334
                                       "     AND energy_category_id = %s "
335
                                       "     AND start_datetime_utc >= %s "
336
                                       "     AND start_datetime_utc < %s "
337
                                       " ORDER BY start_datetime_utc ",
338
                                       (store['id'],
339
                                        energy_category_id,
340
                                        reporting_start_datetime_utc,
341
                                        reporting_end_datetime_utc))
342
                rows_store_hourly = cursor_carbon.fetchall()
343
344
                rows_store_periodically = utilities.aggregate_hourly_data_by_period(rows_store_hourly,
345
                                                                                    reporting_start_datetime_utc,
346
                                                                                    reporting_end_datetime_utc,
347
                                                                                    period_type)
348
                for row_store_periodically in rows_store_periodically:
349
                    current_datetime_local = row_store_periodically[0].replace(tzinfo=timezone.utc) + \
350
                                             timedelta(minutes=timezone_offset)
351
                    if period_type == 'hourly':
352
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
353
                    elif period_type == 'daily':
354
                        current_datetime = current_datetime_local.strftime('%Y-%m-%d')
355
                    elif period_type == 'weekly':
356
                        current_datetime = current_datetime_local.strftime('%Y-%m-%d')
357
                    elif period_type == 'monthly':
358
                        current_datetime = current_datetime_local.strftime('%Y-%m')
359
                    elif period_type == 'yearly':
360
                        current_datetime = current_datetime_local.strftime('%Y')
361
362
                    actual_value = Decimal(0.0) if row_store_periodically[1] is None else row_store_periodically[1]
363
                    reporting[energy_category_id]['timestamps'].append(current_datetime)
364
                    reporting[energy_category_id]['values'].append(actual_value)
365
                    reporting[energy_category_id]['subtotal'] += actual_value
366
367
                energy_category_tariff_dict = utilities.get_energy_category_peak_types(store['cost_center_id'],
368
                                                                                       energy_category_id,
369
                                                                                       reporting_start_datetime_utc,
370
                                                                                       reporting_end_datetime_utc)
371
                for row in rows_store_hourly:
372
                    peak_type = energy_category_tariff_dict.get(row[0], None)
373
                    if peak_type == 'toppeak':
374
                        reporting[energy_category_id]['toppeak'] += row[1]
375
                    elif peak_type == 'onpeak':
376
                        reporting[energy_category_id]['onpeak'] += row[1]
377
                    elif peak_type == 'midpeak':
378
                        reporting[energy_category_id]['midpeak'] += row[1]
379
                    elif peak_type == 'offpeak':
380
                        reporting[energy_category_id]['offpeak'] += row[1]
381
382
        ################################################################################################################
383
        # Step 8: query tariff data
384
        ################################################################################################################
385
        parameters_data = dict()
386
        parameters_data['names'] = list()
387
        parameters_data['timestamps'] = list()
388
        parameters_data['values'] = list()
389
        if energy_category_set is not None and len(energy_category_set) > 0:
390
            for energy_category_id in energy_category_set:
391
                energy_category_tariff_dict = utilities.get_energy_category_tariffs(store['cost_center_id'],
392
                                                                                    energy_category_id,
393
                                                                                    reporting_start_datetime_utc,
394
                                                                                    reporting_end_datetime_utc)
395
                tariff_timestamp_list = list()
396
                tariff_value_list = list()
397
                for k, v in energy_category_tariff_dict.items():
398
                    # convert k from utc to local
399
                    k = k + timedelta(minutes=timezone_offset)
400
                    tariff_timestamp_list.append(k.isoformat()[0:19][0:19])
401
                    tariff_value_list.append(v)
402
403
                parameters_data['names'].append('TARIFF-' + energy_category_dict[energy_category_id]['name'])
404
                parameters_data['timestamps'].append(tariff_timestamp_list)
405
                parameters_data['values'].append(tariff_value_list)
406
407
        ################################################################################################################
408
        # Step 9: query associated sensors and points data
409
        ################################################################################################################
410
        for point in point_list:
411
            point_values = []
412
            point_timestamps = []
413
            if point['object_type'] == 'ANALOG_VALUE':
414
                query = (" SELECT utc_date_time, actual_value "
415
                         " FROM tbl_analog_value "
416
                         " WHERE point_id = %s "
417
                         "       AND utc_date_time BETWEEN %s AND %s "
418
                         " ORDER BY utc_date_time ")
419
                cursor_historical.execute(query, (point['id'],
420
                                                  reporting_start_datetime_utc,
421
                                                  reporting_end_datetime_utc))
422
                rows = cursor_historical.fetchall()
423
424
                if rows is not None and len(rows) > 0:
425
                    for row in rows:
426
                        current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
427
                                                 timedelta(minutes=timezone_offset)
428
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
429
                        point_timestamps.append(current_datetime)
430
                        point_values.append(row[1])
431
432
            elif point['object_type'] == 'ENERGY_VALUE':
433
                query = (" SELECT utc_date_time, actual_value "
434
                         " FROM tbl_energy_value "
435
                         " WHERE point_id = %s "
436
                         "       AND utc_date_time BETWEEN %s AND %s "
437
                         " ORDER BY utc_date_time ")
438
                cursor_historical.execute(query, (point['id'],
439
                                                  reporting_start_datetime_utc,
440
                                                  reporting_end_datetime_utc))
441
                rows = cursor_historical.fetchall()
442
443
                if rows is not None and len(rows) > 0:
444
                    for row in rows:
445
                        current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
446
                                                 timedelta(minutes=timezone_offset)
447
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
448
                        point_timestamps.append(current_datetime)
449
                        point_values.append(row[1])
450
            elif point['object_type'] == 'DIGITAL_VALUE':
451
                query = (" SELECT utc_date_time, actual_value "
452
                         " FROM tbl_digital_value "
453
                         " WHERE point_id = %s "
454
                         "       AND utc_date_time BETWEEN %s AND %s "
455
                         " ORDER BY utc_date_time ")
456
                cursor_historical.execute(query, (point['id'],
457
                                                  reporting_start_datetime_utc,
458
                                                  reporting_end_datetime_utc))
459
                rows = cursor_historical.fetchall()
460
461
                if rows is not None and len(rows) > 0:
462
                    for row in rows:
463
                        current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
464
                                                 timedelta(minutes=timezone_offset)
465
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
466
                        point_timestamps.append(current_datetime)
467
                        point_values.append(row[1])
468
469
            parameters_data['names'].append(point['name'] + ' (' + point['units'] + ')')
470
            parameters_data['timestamps'].append(point_timestamps)
471
            parameters_data['values'].append(point_values)
472
473
        ################################################################################################################
474
        # Step 10: construct the report
475
        ################################################################################################################
476
        if cursor_system:
477
            cursor_system.close()
478
        if cnx_system:
479
            cnx_system.close()
480
481
        if cursor_carbon:
482
            cursor_carbon.close()
483
        if cnx_carbon:
484
            cnx_carbon.close()
485
486
        if cursor_historical:
487
            cursor_historical.close()
488
        if cnx_historical:
489
            cnx_historical.close()
490
491
        result = dict()
492
493
        result['store'] = dict()
494
        result['store']['name'] = store['name']
495
        result['store']['area'] = store['area']
496
497
        result['base_period'] = dict()
498
        result['base_period']['names'] = list()
499
        result['base_period']['units'] = list()
500
        result['base_period']['timestamps'] = list()
501
        result['base_period']['values'] = list()
502
        result['base_period']['subtotals'] = list()
503
        result['base_period']['total'] = Decimal(0.0)
504
        if energy_category_set is not None and len(energy_category_set) > 0:
505
            for energy_category_id in energy_category_set:
506
                result['base_period']['names'].append(energy_category_dict[energy_category_id]['name'])
507
                result['base_period']['units'].append('KG')
508
                result['base_period']['timestamps'].append(base[energy_category_id]['timestamps'])
509
                result['base_period']['values'].append(base[energy_category_id]['values'])
510
                result['base_period']['subtotals'].append(base[energy_category_id]['subtotal'])
511
                result['base_period']['total'] += base[energy_category_id]['subtotal']
512
513
        result['reporting_period'] = dict()
514
        result['reporting_period']['names'] = list()
515
        result['reporting_period']['energy_category_ids'] = list()
516
        result['reporting_period']['units'] = list()
517
        result['reporting_period']['timestamps'] = list()
518
        result['reporting_period']['values'] = list()
519
        result['reporting_period']['subtotals'] = list()
520
        result['reporting_period']['subtotals_per_unit_area'] = list()
521
        result['reporting_period']['toppeaks'] = list()
522
        result['reporting_period']['onpeaks'] = list()
523
        result['reporting_period']['midpeaks'] = list()
524
        result['reporting_period']['offpeaks'] = list()
525
        result['reporting_period']['increment_rates'] = list()
526
        result['reporting_period']['total'] = Decimal(0.0)
527
        result['reporting_period']['total_per_unit_area'] = Decimal(0.0)
528
        result['reporting_period']['total_increment_rate'] = Decimal(0.0)
529
        result['reporting_period']['total_unit'] = 'KG'
530
531
        if energy_category_set is not None and len(energy_category_set) > 0:
532
            for energy_category_id in energy_category_set:
533
                result['reporting_period']['names'].append(energy_category_dict[energy_category_id]['name'])
534
                result['reporting_period']['energy_category_ids'].append(energy_category_id)
535
                result['reporting_period']['units'].append('KG')
536
                result['reporting_period']['timestamps'].append(reporting[energy_category_id]['timestamps'])
537
                result['reporting_period']['values'].append(reporting[energy_category_id]['values'])
538
                result['reporting_period']['subtotals'].append(reporting[energy_category_id]['subtotal'])
539
                result['reporting_period']['subtotals_per_unit_area'].append(
540
                    reporting[energy_category_id]['subtotal'] / store['area'] if store['area'] > 0.0 else None)
541
                result['reporting_period']['toppeaks'].append(reporting[energy_category_id]['toppeak'])
542
                result['reporting_period']['onpeaks'].append(reporting[energy_category_id]['onpeak'])
543
                result['reporting_period']['midpeaks'].append(reporting[energy_category_id]['midpeak'])
544
                result['reporting_period']['offpeaks'].append(reporting[energy_category_id]['offpeak'])
545
                result['reporting_period']['increment_rates'].append(
546
                    (reporting[energy_category_id]['subtotal'] - base[energy_category_id]['subtotal']) /
547
                    base[energy_category_id]['subtotal']
548
                    if base[energy_category_id]['subtotal'] > 0.0 else None)
549
                result['reporting_period']['total'] += reporting[energy_category_id]['subtotal']
550
551
        result['reporting_period']['total_per_unit_area'] = \
552
            result['reporting_period']['total'] / store['area'] if store['area'] > 0.0 else None
553
554
        result['reporting_period']['total_increment_rate'] = \
555
            (result['reporting_period']['total'] - result['base_period']['total']) / \
556
            result['base_period']['total'] \
557
            if result['base_period']['total'] > Decimal(0.0) else None
558
559
        result['parameters'] = {
560
            "names": parameters_data['names'],
561
            "timestamps": parameters_data['timestamps'],
562
            "values": parameters_data['values']
563
        }
564
        # export result to Excel file and then encode the file to base64 string
565
        result['excel_bytes_base64'] = excelexporters.storecarbon.export(result,
566
                                                                         store['name'],
567
                                                                         reporting_start_datetime_local,
568
                                                                         reporting_end_datetime_local,
569
                                                                         period_type)
570
        resp.text = json.dumps(result)
571