Passed
Push — master ( ef3fde...570acf )
by Guangyu
06:33 queued 12s
created

reports.spacecarbon.Reporting.on_get()   F

Complexity

Conditions 125

Size

Total Lines 593
Code Lines 438

Duplication

Lines 593
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 438
dl 593
loc 593
rs 0
c 0
b 0
f 0
cc 125
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

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