Issues (1588)

myems-api/reports/spaceoutput.py (5 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.spaceoutput
9
from core import utilities
10
from core.useractivity import access_control, api_key_control
11
12
13
class Reporting:
14
    def __init__(self):
15
        """"Initializes Reporting"""
16
        pass
17
18
    @staticmethod
19
    def on_options(req, resp):
20
        _ = req
21
        resp.status = falcon.HTTP_200
22
23
    ####################################################################################################################
24
    # PROCEDURES
25
    # Step 1: valid parameters
26
    # Step 2: query the 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 output
32
    # Step 8: query reporting period energy output
33
    # Step 9: query tariff data
34
    # Step 10: query associated sensors and points data
35
    # Step 11: query child spaces energy output
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_energy = mysql.connector.connect(**config.myems_energy_db)
179
        cursor_energy = cnx_energy.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 View Code Duplication
        if row_space is None:
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
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_energy:
202
                cursor_energy.close()
203
            if cnx_energy:
204
                cnx_energy.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_energy.execute(" SELECT DISTINCT(energy_category_id) "
225
                              " FROM tbl_space_output_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_energy.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_energy.execute(" SELECT DISTINCT(energy_category_id) "
237
                              " FROM tbl_space_output_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_energy.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_energy:
259
                cursor_energy.close()
260
            if cnx_energy:
261
                cnx_energy.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 output
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_energy.execute(" SELECT start_datetime_utc, actual_value "
330
                                      " FROM tbl_space_output_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_energy.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 output
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
376
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
377
                                      " FROM tbl_space_output_category_hourly "
378
                                      " WHERE space_id = %s "
379
                                      "     AND energy_category_id = %s "
380
                                      "     AND start_datetime_utc >= %s "
381
                                      "     AND start_datetime_utc < %s "
382
                                      " ORDER BY start_datetime_utc ",
383
                                      (space['id'],
384
                                       energy_category_id,
385
                                       reporting_start_datetime_utc,
386
                                       reporting_end_datetime_utc))
387
                rows_space_hourly = cursor_energy.fetchall()
388
389
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
390
                                                                                    reporting_start_datetime_utc,
391
                                                                                    reporting_end_datetime_utc,
392
                                                                                    period_type)
393
                for row_space_periodically in rows_space_periodically:
394
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
395
                                             timedelta(minutes=timezone_offset)
396
                    if period_type == 'hourly':
397
                        current_datetime = current_datetime_local.isoformat()[0:19]
398
                    elif period_type == 'daily':
399
                        current_datetime = current_datetime_local.isoformat()[0:10]
400
                    elif period_type == 'weekly':
401
                        current_datetime = current_datetime_local.isoformat()[0:10]
402
                    elif period_type == 'monthly':
403
                        current_datetime = current_datetime_local.isoformat()[0:7]
404
                    elif period_type == 'yearly':
405
                        current_datetime = current_datetime_local.isoformat()[0:4]
406
407
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
408
                    reporting[energy_category_id]['timestamps'].append(current_datetime)
409
                    reporting[energy_category_id]['values'].append(actual_value)
410
                    reporting[energy_category_id]['subtotal'] += actual_value
411
412
        ################################################################################################################
413
        # Step 9: query tariff data
414
        ################################################################################################################
415
        parameters_data = dict()
416
        parameters_data['names'] = list()
417
        parameters_data['timestamps'] = list()
418
        parameters_data['values'] = list()
419
        if config.is_tariff_appended and energy_category_set is not None and len(energy_category_set) > 0 \
420
                and not is_quick_mode:
421
            for energy_category_id in energy_category_set:
422
                energy_category_tariff_dict = utilities.get_energy_category_tariffs(space['cost_center_id'],
423
                                                                                    energy_category_id,
424
                                                                                    reporting_start_datetime_utc,
425
                                                                                    reporting_end_datetime_utc)
426
                tariff_timestamp_list = list()
427
                tariff_value_list = list()
428
                for k, v in energy_category_tariff_dict.items():
429
                    # convert k from utc to local
430
                    k = k + timedelta(minutes=timezone_offset)
431
                    tariff_timestamp_list.append(k.isoformat()[0:19])
432
                    tariff_value_list.append(v)
433
434
                parameters_data['names'].append(_('Tariff') + '-' + energy_category_dict[energy_category_id]['name'])
435
                parameters_data['timestamps'].append(tariff_timestamp_list)
436
                parameters_data['values'].append(tariff_value_list)
437
438
        ################################################################################################################
439
        # Step 10: query associated sensors and points data
440
        ################################################################################################################
441
        if not is_quick_mode:
442
            for point in point_list:
443
                point_values = []
444
                point_timestamps = []
445
                if point['object_type'] == 'ENERGY_VALUE':
446
                    query = (" SELECT utc_date_time, actual_value "
447
                             " FROM tbl_energy_value "
448
                             " WHERE point_id = %s "
449
                             "       AND utc_date_time BETWEEN %s AND %s "
450
                             " ORDER BY utc_date_time ")
451
                    cursor_historical.execute(query, (point['id'],
452
                                                      reporting_start_datetime_utc,
453
                                                      reporting_end_datetime_utc))
454
                    rows = cursor_historical.fetchall()
455
456
                    if rows is not None and len(rows) > 0:
457
                        for row in rows:
458
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
459
                                                     timedelta(minutes=timezone_offset)
460
                            current_datetime = current_datetime_local.isoformat()[0:19]
461
                            point_timestamps.append(current_datetime)
462
                            point_values.append(row[1])
463
                elif point['object_type'] == 'ANALOG_VALUE':
464
                    query = (" SELECT utc_date_time, actual_value "
465
                             " FROM tbl_analog_value "
466
                             " WHERE point_id = %s "
467
                             "       AND utc_date_time BETWEEN %s AND %s "
468
                             " ORDER BY utc_date_time ")
469
                    cursor_historical.execute(query, (point['id'],
470
                                                      reporting_start_datetime_utc,
471
                                                      reporting_end_datetime_utc))
472
                    rows = cursor_historical.fetchall()
473
474
                    if rows is not None and len(rows) > 0:
475
                        for row in rows:
476
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
477
                                                     timedelta(minutes=timezone_offset)
478
                            current_datetime = current_datetime_local.isoformat()[0:19]
479
                            point_timestamps.append(current_datetime)
480
                            point_values.append(row[1])
481
                elif point['object_type'] == 'DIGITAL_VALUE':
482
                    query = (" SELECT utc_date_time, actual_value "
483
                             " FROM tbl_digital_value "
484
                             " WHERE point_id = %s "
485
                             "       AND utc_date_time BETWEEN %s AND %s "
486
                             " ORDER BY utc_date_time ")
487
                    cursor_historical.execute(query, (point['id'],
488
                                                      reporting_start_datetime_utc,
489
                                                      reporting_end_datetime_utc))
490
                    rows = cursor_historical.fetchall()
491
492
                    if rows is not None and len(rows) > 0:
493
                        for row in rows:
494
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
495
                                                     timedelta(minutes=timezone_offset)
496
                            current_datetime = current_datetime_local.isoformat()[0:19]
497
                            point_timestamps.append(current_datetime)
498
                            point_values.append(row[1])
499
500
                parameters_data['names'].append(point['name'] + ' (' + point['units'] + ')')
501
                parameters_data['timestamps'].append(point_timestamps)
502
                parameters_data['values'].append(point_values)
503
504
        ################################################################################################################
505
        # Step 11: query child spaces energy output
506
        ################################################################################################################
507
        child_space_data = dict()
508
509 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...
510
            for energy_category_id in energy_category_set:
511
                child_space_data[energy_category_id] = dict()
512
                child_space_data[energy_category_id]['child_space_names'] = list()
513
                child_space_data[energy_category_id]['subtotals'] = list()
514
                for child_space in child_space_list:
515
                    child_space_data[energy_category_id]['child_space_names'].append(child_space['name'])
516
517
                    cursor_energy.execute(" SELECT SUM(actual_value) "
518
                                          " FROM tbl_space_output_category_hourly "
519
                                          " WHERE space_id = %s "
520
                                          "     AND energy_category_id = %s "
521
                                          "     AND start_datetime_utc >= %s "
522
                                          "     AND start_datetime_utc < %s ",
523
                                          (child_space['id'],
524
                                           energy_category_id,
525
                                           reporting_start_datetime_utc,
526
                                           reporting_end_datetime_utc))
527
                    row_subtotal = cursor_energy.fetchone()
528
529
                    subtotal = Decimal(0.0) if (row_subtotal is None or row_subtotal[0] is None) else row_subtotal[0]
530
                    child_space_data[energy_category_id]['subtotals'].append(subtotal)
531
532
        ################################################################################################################
533
        # Step 12: construct the report
534
        ################################################################################################################
535
        if cursor_system:
536
            cursor_system.close()
537
        if cnx_system:
538
            cnx_system.close()
539
540
        if cursor_energy:
541
            cursor_energy.close()
542
        if cnx_energy:
543
            cnx_energy.close()
544
545
        if cursor_historical:
546
            cursor_historical.close()
547
        if cnx_historical:
548
            cnx_historical.close()
549
550
        result = dict()
551
552
        result['space'] = dict()
553
        result['space']['name'] = space['name']
554
        result['space']['area'] = space['area']
555
        result['space']['number_of_occupants'] = space['number_of_occupants']
556
557
        result['base_period'] = dict()
558
        result['base_period']['names'] = list()
559
        result['base_period']['units'] = list()
560
        result['base_period']['timestamps'] = list()
561
        result['base_period']['values'] = list()
562
        result['base_period']['subtotals'] = list()
563
        result['base_period']['subtotals_per_unit_area'] = list()
564
        result['base_period']['subtotals_per_capita'] = list()
565
        if energy_category_set is not None and len(energy_category_set) > 0:
566
            for energy_category_id in energy_category_set:
567
                result['base_period']['names'].append(energy_category_dict[energy_category_id]['name'])
568
                result['base_period']['units'].append(energy_category_dict[energy_category_id]['unit_of_measure'])
569
                result['base_period']['timestamps'].append(base[energy_category_id]['timestamps'])
570
                result['base_period']['values'].append(base[energy_category_id]['values'])
571
                result['base_period']['subtotals'].append(base[energy_category_id]['subtotal'])
572
573
        result['reporting_period'] = dict()
574
        result['reporting_period']['names'] = list()
575
        result['reporting_period']['energy_category_ids'] = list()
576
        result['reporting_period']['units'] = list()
577
        result['reporting_period']['timestamps'] = list()
578
        result['reporting_period']['values'] = list()
579
        result['reporting_period']['rates'] = list()
580
        result['reporting_period']['subtotals'] = list()
581
        result['reporting_period']['subtotals_per_unit_area'] = list()
582
        result['reporting_period']['subtotals_per_capita'] = list()
583
        result['reporting_period']['increment_rates'] = list()
584
585 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...
586
            for energy_category_id in energy_category_set:
587
                result['reporting_period']['names'].append(energy_category_dict[energy_category_id]['name'])
588
                result['reporting_period']['energy_category_ids'].append(energy_category_id)
589
                result['reporting_period']['units'].append(energy_category_dict[energy_category_id]['unit_of_measure'])
590
                result['reporting_period']['timestamps'].append(reporting[energy_category_id]['timestamps'])
591
                result['reporting_period']['values'].append(reporting[energy_category_id]['values'])
592
                result['reporting_period']['subtotals'].append(reporting[energy_category_id]['subtotal'])
593
                result['reporting_period']['subtotals_per_unit_area'].append(
594
                    reporting[energy_category_id]['subtotal'] / space['area'] if space['area'] > 0.0 else None)
595
                result['reporting_period']['subtotals_per_capita'].append(
596
                    reporting[energy_category_id]['subtotal'] / space['number_of_occupants']
597
                    if space['number_of_occupants'] > 0.0 else None)
598
                result['reporting_period']['increment_rates'].append(
599
                    (reporting[energy_category_id]['subtotal'] - base[energy_category_id]['subtotal']) /
600
                    base[energy_category_id]['subtotal']
601
                    if base[energy_category_id]['subtotal'] > 0.0 else None)
602
603
                rate = list()
604
                for index, value in enumerate(reporting[energy_category_id]['values']):
605
                    if index < len(base[energy_category_id]['values']) \
606
                            and base[energy_category_id]['values'][index] != 0 and value != 0:
607
                        rate.append((value - base[energy_category_id]['values'][index])
608
                                    / base[energy_category_id]['values'][index])
609
                    else:
610
                        rate.append(None)
611
                result['reporting_period']['rates'].append(rate)
612
613
        result['parameters'] = {
614
            "names": parameters_data['names'],
615
            "timestamps": parameters_data['timestamps'],
616
            "values": parameters_data['values']
617
        }
618
619
        result['child_space'] = dict()
620
        result['child_space']['energy_category_names'] = list()  # 1D array [energy category]
621
        result['child_space']['units'] = list()  # 1D array [energy category]
622
        result['child_space']['child_space_names_array'] = list()  # 2D array [energy category][child space]
623
        result['child_space']['subtotals_array'] = list()  # 2D array [energy category][child space]
624
        if energy_category_set is not None and len(energy_category_set) > 0:
625
            for energy_category_id in energy_category_set:
626
                result['child_space']['energy_category_names'].append(energy_category_dict[energy_category_id]['name'])
627
                result['child_space']['units'].append(energy_category_dict[energy_category_id]['unit_of_measure'])
628
                result['child_space']['child_space_names_array'].append(
629
                    child_space_data[energy_category_id]['child_space_names'])
630
                result['child_space']['subtotals_array'].append(
631
                    child_space_data[energy_category_id]['subtotals'])
632
633
        # export result to Excel file and then encode the file to base64 string
634
        if not is_quick_mode:
635
            result['excel_bytes_base64'] = excelexporters.spaceoutput.export(result,
636
                                                                             space['name'],
637
                                                                             base_period_start_datetime_local,
638
                                                                             base_period_end_datetime_local,
639
                                                                             reporting_period_start_datetime_local,
640
                                                                             reporting_period_end_datetime_local,
641
                                                                             period_type,
642
                                                                             language)
643
644
        resp.text = json.dumps(result)
645