Issues (1662)

myems-api/reports/spaceenergyitem.py (4 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.spaceenergyitem
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 items
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 input
32
    # Step 8: query reporting period energy input
33
    # Step 9: query tariff data
34
    # Step 10: query associated sensors and points data
35
    # Step 11: query child spaces energy input
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
The variable row_space does not seem to be defined for all execution paths.
Loading history...
This code seems to be duplicated in your project.
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 items
221
        ################################################################################################################
222
        energy_item_set = set()
223
        # query energy items in base period
224
        cursor_energy.execute(" SELECT DISTINCT(energy_item_id) "
225
                              " FROM tbl_space_input_item_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_items = cursor_energy.fetchall()
231
        if rows_energy_items is not None and len(rows_energy_items) > 0:
232
            for row_item in rows_energy_items:
233
                energy_item_set.add(row_item[0])
234
235
        # query energy items in reporting period
236
        cursor_energy.execute(" SELECT DISTINCT(energy_item_id) "
237
                              " FROM tbl_space_input_item_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_items = cursor_energy.fetchall()
243
        if rows_energy_items is not None and len(rows_energy_items) > 0:
244
            for row_item in rows_energy_items:
245
                energy_item_set.add(row_item[0])
246
247
        # query all energy items in base period and reporting period
248
        cursor_system.execute(" SELECT ei.id, ei.name, ei.energy_category_id, "
249
                              "        ec.name AS energy_category_name, ec.unit_of_measure, ec.kgce, ec.kgco2e "
250
                              " FROM tbl_energy_items ei, tbl_energy_categories ec "
251
                              " WHERE ei.energy_category_id = ec.id "
252
                              " ORDER BY ei.id ", )
253
        rows_energy_items = cursor_system.fetchall()
254
        if rows_energy_items is None or len(rows_energy_items) == 0:
255
            if cursor_system:
256
                cursor_system.close()
257
            if cnx_system:
258
                cnx_system.close()
259
260
            if cursor_energy:
261
                cursor_energy.close()
262
            if cnx_energy:
263
                cnx_energy.close()
264
265
            if cursor_historical:
266
                cursor_historical.close()
267
            if cnx_historical:
268
                cnx_historical.close()
269
            raise falcon.HTTPError(status=falcon.HTTP_404,
270
                                   title='API.NOT_FOUND',
271
                                   description='API.ENERGY_ITEM_NOT_FOUND')
272
        energy_item_dict = dict()
273
        for row_energy_item in rows_energy_items:
274
            if row_energy_item[0] in energy_item_set:
275
                energy_item_dict[row_energy_item[0]] = {"name": row_energy_item[1],
276
                                                        "energy_category_id": row_energy_item[2],
277
                                                        "energy_category_name": row_energy_item[3],
278
                                                        "unit_of_measure": row_energy_item[4],
279
                                                        "kgce": row_energy_item[5],
280
                                                        "kgco2e": row_energy_item[6]}
281
282
        ################################################################################################################
283
        # Step 4: query associated sensors
284
        ################################################################################################################
285
        point_list = list()
286
        cursor_system.execute(" SELECT po.id, po.name, po.units, po.object_type  "
287
                              " FROM tbl_spaces sp, tbl_sensors se, tbl_spaces_sensors spse, "
288
                              "      tbl_points po, tbl_sensors_points sepo "
289
                              " WHERE sp.id = %s AND sp.id = spse.space_id AND spse.sensor_id = se.id "
290
                              "       AND se.id = sepo.sensor_id AND sepo.point_id = po.id "
291
                              " ORDER BY po.id ", (space['id'],))
292
        rows_points = cursor_system.fetchall()
293
        if rows_points is not None and len(rows_points) > 0:
294
            for row in rows_points:
295
                point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
296
297
        ################################################################################################################
298
        # Step 5: query associated points
299
        ################################################################################################################
300
        cursor_system.execute(" SELECT po.id, po.name, po.units, po.object_type  "
301
                              " FROM tbl_spaces sp, tbl_spaces_points sppo, tbl_points po "
302
                              " WHERE sp.id = %s AND sp.id = sppo.space_id AND sppo.point_id = po.id "
303
                              " ORDER BY po.id ", (space['id'],))
304
        rows_points = cursor_system.fetchall()
305
        if rows_points is not None and len(rows_points) > 0:
306
            for row in rows_points:
307
                point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
308
309
        ################################################################################################################
310
        # Step 6: query child spaces
311
        ################################################################################################################
312
        child_space_list = list()
313
        cursor_system.execute(" SELECT id, name  "
314
                              " FROM tbl_spaces "
315
                              " WHERE parent_space_id = %s "
316
                              " ORDER BY id ", (space['id'],))
317
        rows_child_spaces = cursor_system.fetchall()
318
        if rows_child_spaces is not None and len(rows_child_spaces) > 0:
319
            for row in rows_child_spaces:
320
                child_space_list.append({"id": row[0], "name": row[1]})
321
322
        ################################################################################################################
323
        # Step 7: query base period energy input
324
        ################################################################################################################
325
        base = dict()
326
        if energy_item_set is not None and len(energy_item_set) > 0:
327
            for energy_item_id in energy_item_set:
328
                base[energy_item_id] = dict()
329
                base[energy_item_id]['timestamps'] = list()
330
                base[energy_item_id]['values'] = list()
331
                base[energy_item_id]['subtotal'] = Decimal(0.0)
332
333
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
334
                                      " FROM tbl_space_input_item_hourly "
335
                                      " WHERE space_id = %s "
336
                                      "     AND energy_item_id = %s "
337
                                      "     AND start_datetime_utc >= %s "
338
                                      "     AND start_datetime_utc < %s "
339
                                      " ORDER BY start_datetime_utc ",
340
                                      (space['id'],
341
                                       energy_item_id,
342
                                       base_start_datetime_utc,
343
                                       base_end_datetime_utc))
344
                rows_space_hourly = cursor_energy.fetchall()
345
346
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
347
                                                                                    base_start_datetime_utc,
348
                                                                                    base_end_datetime_utc,
349
                                                                                    period_type)
350
                for row_space_periodically in rows_space_periodically:
351
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
352
                                             timedelta(minutes=timezone_offset)
353
                    if period_type == 'hourly':
354
                        current_datetime = current_datetime_local.isoformat()[0:19]
355
                    elif period_type == 'daily':
356
                        current_datetime = current_datetime_local.isoformat()[0:10]
357
                    elif period_type == 'weekly':
358
                        current_datetime = current_datetime_local.isoformat()[0:10]
359
                    elif period_type == 'monthly':
360
                        current_datetime = current_datetime_local.isoformat()[0:7]
361
                    elif period_type == 'yearly':
362
                        current_datetime = current_datetime_local.isoformat()[0:4]
363
364
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
365
                    base[energy_item_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...
366
                    base[energy_item_id]['values'].append(actual_value)
367
                    base[energy_item_id]['subtotal'] += actual_value
368
369
        ################################################################################################################
370
        # Step 8: query reporting period energy input
371
        ################################################################################################################
372
        reporting = dict()
373
        if energy_item_set is not None and len(energy_item_set) > 0:
374
            for energy_item_id in energy_item_set:
375
                reporting[energy_item_id] = dict()
376
                reporting[energy_item_id]['timestamps'] = list()
377
                reporting[energy_item_id]['values'] = list()
378
                reporting[energy_item_id]['subtotal'] = Decimal(0.0)
379
                reporting[energy_item_id]['toppeak'] = Decimal(0.0)
380
                reporting[energy_item_id]['onpeak'] = Decimal(0.0)
381
                reporting[energy_item_id]['midpeak'] = Decimal(0.0)
382
                reporting[energy_item_id]['offpeak'] = Decimal(0.0)
383
                reporting[energy_item_id]['deep'] = Decimal(0.0)
384
385
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
386
                                      " FROM tbl_space_input_item_hourly "
387
                                      " WHERE space_id = %s "
388
                                      "     AND energy_item_id = %s "
389
                                      "     AND start_datetime_utc >= %s "
390
                                      "     AND start_datetime_utc < %s "
391
                                      " ORDER BY start_datetime_utc ",
392
                                      (space['id'],
393
                                       energy_item_id,
394
                                       reporting_start_datetime_utc,
395
                                       reporting_end_datetime_utc))
396
                rows_space_hourly = cursor_energy.fetchall()
397
398
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
399
                                                                                    reporting_start_datetime_utc,
400
                                                                                    reporting_end_datetime_utc,
401
                                                                                    period_type)
402
                for row_space_periodically in rows_space_periodically:
403
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
404
                                             timedelta(minutes=timezone_offset)
405
                    if period_type == 'hourly':
406
                        current_datetime = current_datetime_local.isoformat()[0:19]
407
                    elif period_type == 'daily':
408
                        current_datetime = current_datetime_local.isoformat()[0:10]
409
                    elif period_type == 'weekly':
410
                        current_datetime = current_datetime_local.isoformat()[0:10]
411
                    elif period_type == 'monthly':
412
                        current_datetime = current_datetime_local.isoformat()[0:7]
413
                    elif period_type == 'yearly':
414
                        current_datetime = current_datetime_local.isoformat()[0:4]
415
416
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
417
                    reporting[energy_item_id]['timestamps'].append(current_datetime)
418
                    reporting[energy_item_id]['values'].append(actual_value)
419
                    reporting[energy_item_id]['subtotal'] += actual_value
420
421
                energy_category_tariff_dict = \
422
                    utilities.get_energy_category_peak_types(space['cost_center_id'],
423
                                                             energy_item_dict[energy_item_id]['energy_category_id'],
424
                                                             reporting_start_datetime_utc,
425
                                                             reporting_end_datetime_utc)
426
                for row in rows_space_hourly:
427
                    peak_type = energy_category_tariff_dict.get(row[0], None)
428
                    if peak_type == 'toppeak':
429
                        reporting[energy_item_id]['toppeak'] += row[1]
430
                    elif peak_type == 'onpeak':
431
                        reporting[energy_item_id]['onpeak'] += row[1]
432
                    elif peak_type == 'midpeak':
433
                        reporting[energy_item_id]['midpeak'] += row[1]
434
                    elif peak_type == 'offpeak':
435
                        reporting[energy_item_id]['offpeak'] += row[1]
436
                    elif peak_type == 'deep':
437
                        reporting[energy_item_id]['deep'] += row[1]
438
439
        ################################################################################################################
440
        # Step 9: query tariff data
441
        ################################################################################################################
442
        parameters_data = dict()
443
        parameters_data['names'] = list()
444
        parameters_data['timestamps'] = list()
445
        parameters_data['values'] = list()
446
        if config.is_tariff_appended and energy_item_set is not None and len(energy_item_set) > 0 and not is_quick_mode:
447
            for energy_item_id in energy_item_set:
448
                energy_category_tariff_dict = \
449
                    utilities.get_energy_category_tariffs(space['cost_center_id'],
450
                                                          energy_item_dict[energy_item_id]['energy_category_id'],
451
                                                          reporting_start_datetime_utc,
452
                                                          reporting_end_datetime_utc)
453
                tariff_timestamp_list = list()
454
                tariff_value_list = list()
455
                for k, v in energy_category_tariff_dict.items():
456
                    # convert k from utc to local
457
                    k = k + timedelta(minutes=timezone_offset)
458
                    tariff_timestamp_list.append(k.isoformat()[0:19])
459
                    tariff_value_list.append(v)
460
461
                parameters_data['names'].append(_('Tariff') + '-' + energy_item_dict[energy_item_id]['name'])
462
                parameters_data['timestamps'].append(tariff_timestamp_list)
463
                parameters_data['values'].append(tariff_value_list)
464
465
        ################################################################################################################
466
        # Step 10: query associated sensors and points data
467
        ################################################################################################################
468
        if not is_quick_mode:
469
            for point in point_list:
470
                point_values = []
471
                point_timestamps = []
472
                if point['object_type'] == 'ENERGY_VALUE':
473
                    query = (" SELECT utc_date_time, actual_value "
474
                             " FROM tbl_energy_value "
475
                             " WHERE point_id = %s "
476
                             "       AND utc_date_time BETWEEN %s AND %s "
477
                             " ORDER BY utc_date_time ")
478
                    cursor_historical.execute(query, (point['id'],
479
                                                      reporting_start_datetime_utc,
480
                                                      reporting_end_datetime_utc))
481
                    rows = cursor_historical.fetchall()
482
483
                    if rows is not None and len(rows) > 0:
484
                        for row in rows:
485
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
486
                                                     timedelta(minutes=timezone_offset)
487
                            current_datetime = current_datetime_local.isoformat()[0:19]
488
                            point_timestamps.append(current_datetime)
489
                            point_values.append(row[1])
490
                elif point['object_type'] == 'ANALOG_VALUE':
491
                    query = (" SELECT utc_date_time, actual_value "
492
                             " FROM tbl_analog_value "
493
                             " WHERE point_id = %s "
494
                             "       AND utc_date_time BETWEEN %s AND %s "
495
                             " ORDER BY utc_date_time ")
496
                    cursor_historical.execute(query, (point['id'],
497
                                                      reporting_start_datetime_utc,
498
                                                      reporting_end_datetime_utc))
499
                    rows = cursor_historical.fetchall()
500
501
                    if rows is not None and len(rows) > 0:
502
                        for row in rows:
503
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
504
                                                     timedelta(minutes=timezone_offset)
505
                            current_datetime = current_datetime_local.isoformat()[0:19]
506
                            point_timestamps.append(current_datetime)
507
                            point_values.append(row[1])
508
                elif point['object_type'] == 'DIGITAL_VALUE':
509
                    query = (" SELECT utc_date_time, actual_value "
510
                             " FROM tbl_digital_value "
511
                             " WHERE point_id = %s "
512
                             "       AND utc_date_time BETWEEN %s AND %s "
513
                             " ORDER BY utc_date_time ")
514
                    cursor_historical.execute(query, (point['id'],
515
                                                      reporting_start_datetime_utc,
516
                                                      reporting_end_datetime_utc))
517
                    rows = cursor_historical.fetchall()
518
519
                    if rows is not None and len(rows) > 0:
520
                        for row in rows:
521
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
522
                                                     timedelta(minutes=timezone_offset)
523
                            current_datetime = current_datetime_local.isoformat()[0:19]
524
                            point_timestamps.append(current_datetime)
525
                            point_values.append(row[1])
526
527
                parameters_data['names'].append(point['name'] + ' (' + point['units'] + ')')
528
                parameters_data['timestamps'].append(point_timestamps)
529
                parameters_data['values'].append(point_values)
530
531
        ################################################################################################################
532
        # Step 11: query child spaces energy input
533
        ################################################################################################################
534
        child_space_data = dict()
535
536 View Code Duplication
        if energy_item_set is not None and len(energy_item_set) > 0:
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
537
            for energy_item_id in energy_item_set:
538
                child_space_data[energy_item_id] = dict()
539
                child_space_data[energy_item_id]['child_space_names'] = list()
540
                child_space_data[energy_item_id]['subtotals'] = list()
541
542
                for child_space in child_space_list:
543
                    child_space_data[energy_item_id]['child_space_names'].append(child_space['name'])
544
545
                    cursor_energy.execute(" SELECT SUM(actual_value) "
546
                                          " FROM tbl_space_input_item_hourly "
547
                                          " WHERE space_id = %s "
548
                                          "     AND energy_item_id = %s "
549
                                          "     AND start_datetime_utc >= %s "
550
                                          "     AND start_datetime_utc < %s ",
551
                                          (child_space['id'],
552
                                           energy_item_id,
553
                                           reporting_start_datetime_utc,
554
                                           reporting_end_datetime_utc))
555
                    row_subtotal = cursor_energy.fetchone()
556
557
                    subtotal = Decimal(0.0) if (row_subtotal is None or row_subtotal[0] is None) else row_subtotal[0]
558
                    child_space_data[energy_item_id]['subtotals'].append(subtotal)
559
560
        ################################################################################################################
561
        # Step 12: construct the report
562
        ################################################################################################################
563
        if cursor_system:
564
            cursor_system.close()
565
        if cnx_system:
566
            cnx_system.close()
567
568
        if cursor_energy:
569
            cursor_energy.close()
570
        if cnx_energy:
571
            cnx_energy.close()
572
573
        if cursor_historical:
574
            cursor_historical.close()
575
        if cnx_historical:
576
            cnx_historical.close()
577
578
        result = dict()
579
580
        result['space'] = dict()
581
        result['space']['name'] = space['name']
582
        result['space']['area'] = space['area']
583
        result['space']['number_of_occupants'] = space['number_of_occupants']
584
585
        result['base_period'] = dict()
586
        result['base_period']['names'] = list()
587
        result['base_period']['units'] = list()
588
        result['base_period']['timestamps'] = list()
589
        result['base_period']['values'] = list()
590
        result['base_period']['subtotals'] = list()
591
        if energy_item_set is not None and len(energy_item_set) > 0:
592
            for energy_item_id in energy_item_set:
593
                result['base_period']['names'].append(energy_item_dict[energy_item_id]['name'])
594
                result['base_period']['units'].append(energy_item_dict[energy_item_id]['unit_of_measure'])
595
                result['base_period']['timestamps'].append(base[energy_item_id]['timestamps'])
596
                result['base_period']['values'].append(base[energy_item_id]['values'])
597
                result['base_period']['subtotals'].append(base[energy_item_id]['subtotal'])
598
599
        result['reporting_period'] = dict()
600
        result['reporting_period']['names'] = list()
601
        result['reporting_period']['energy_item_ids'] = list()
602
        result['reporting_period']['energy_category_names'] = list()
603
        result['reporting_period']['energy_category_ids'] = list()
604
        result['reporting_period']['units'] = list()
605
        result['reporting_period']['timestamps'] = list()
606
        result['reporting_period']['values'] = list()
607
        result['reporting_period']['rates'] = list()
608
        result['reporting_period']['subtotals'] = list()
609
        result['reporting_period']['subtotals_per_unit_area'] = list()
610
        result['reporting_period']['subtotals_per_capita'] = list()
611
        result['reporting_period']['toppeaks'] = list()
612
        result['reporting_period']['onpeaks'] = list()
613
        result['reporting_period']['midpeaks'] = list()
614
        result['reporting_period']['offpeaks'] = list()
615
        result['reporting_period']['deeps'] = list()
616
        result['reporting_period']['increment_rates'] = list()
617
618
        if energy_item_set is not None and len(energy_item_set) > 0:
619
            for energy_item_id in energy_item_set:
620
                result['reporting_period']['names'].append(energy_item_dict[energy_item_id]['name'])
621
                result['reporting_period']['energy_item_ids'].append(energy_item_id)
622
                result['reporting_period']['energy_category_names'].append(
623
                    energy_item_dict[energy_item_id]['energy_category_name'])
624
                result['reporting_period']['energy_category_ids'].append(
625
                    energy_item_dict[energy_item_id]['energy_category_id'])
626
                result['reporting_period']['units'].append(energy_item_dict[energy_item_id]['unit_of_measure'])
627
                result['reporting_period']['timestamps'].append(reporting[energy_item_id]['timestamps'])
628
                result['reporting_period']['values'].append(reporting[energy_item_id]['values'])
629
                result['reporting_period']['subtotals'].append(reporting[energy_item_id]['subtotal'])
630
                result['reporting_period']['subtotals_per_unit_area'].append(
631
                    reporting[energy_item_id]['subtotal'] / space['area'] if space['area'] > 0.0 else None)
632
                result['reporting_period']['subtotals_per_capita'].append(
633
                    reporting[energy_item_id]['subtotal'] / space['number_of_occupants']
634
                    if space['number_of_occupants'] > 0.0 else None)
635
                result['reporting_period']['toppeaks'].append(reporting[energy_item_id]['toppeak'])
636
                result['reporting_period']['onpeaks'].append(reporting[energy_item_id]['onpeak'])
637
                result['reporting_period']['midpeaks'].append(reporting[energy_item_id]['midpeak'])
638
                result['reporting_period']['offpeaks'].append(reporting[energy_item_id]['offpeak'])
639
                result['reporting_period']['deeps'].append(reporting[energy_item_id]['deep'])
640
                result['reporting_period']['increment_rates'].append(
641
                    (reporting[energy_item_id]['subtotal'] - base[energy_item_id]['subtotal']) /
642
                    base[energy_item_id]['subtotal']
643
                    if base[energy_item_id]['subtotal'] > 0.0 else None)
644
645
                rate = list()
646
                for index, value in enumerate(reporting[energy_item_id]['values']):
647
                    if index < len(base[energy_item_id]['values']) \
648
                            and base[energy_item_id]['values'][index] != 0 and value != 0:
649
                        rate.append((value - base[energy_item_id]['values'][index])
650
                                    / base[energy_item_id]['values'][index])
651
                    else:
652
                        rate.append(None)
653
                result['reporting_period']['rates'].append(rate)
654
655
        result['parameters'] = {
656
            "names": parameters_data['names'],
657
            "timestamps": parameters_data['timestamps'],
658
            "values": parameters_data['values']
659
        }
660
661
        result['child_space'] = dict()
662
        result['child_space']['energy_item_names'] = list()  # 1D array [energy item]
663
        result['child_space']['units'] = list()  # 1D array [energy item]
664
        result['child_space']['child_space_names_array'] = list()  # 2D array [energy item][child space]
665
        result['child_space']['subtotals_array'] = list()  # 2D array [energy item][child space]
666
        if energy_item_set is not None and len(energy_item_set) > 0:
667
            for energy_item_id in energy_item_set:
668
                result['child_space']['energy_item_names'].append(energy_item_dict[energy_item_id]['name'])
669
                result['child_space']['units'].append(energy_item_dict[energy_item_id]['unit_of_measure'])
670
                result['child_space']['child_space_names_array'].append(
671
                    child_space_data[energy_item_id]['child_space_names'])
672
                result['child_space']['subtotals_array'].append(
673
                    child_space_data[energy_item_id]['subtotals'])
674
675
        # export result to Excel file and then encode the file to base64 string
676
        result['excel_bytes_base64'] = None
677
        if not is_quick_mode:
678
            result['excel_bytes_base64'] = excelexporters.spaceenergyitem.export(result,
679
                                                                                 space['name'],
680
                                                                                 base_period_start_datetime_local,
681
                                                                                 base_period_end_datetime_local,
682
                                                                                 reporting_period_start_datetime_local,
683
                                                                                 reporting_period_end_datetime_local,
684
                                                                                 period_type,
685
                                                                                 language)
686
687
        resp.text = json.dumps(result)
688