Issues (1656)

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

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