Issues (1656)

myems-api/reports/spaceefficiency.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.spaceefficiency
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 base period energy input
31
    # Step 7: query base period energy output
32
    # Step 8: query reporting period energy input
33
    # Step 9: query reporting period energy output
34
    # Step 10: query tariff data
35
    # Step 11: query associated sensors and points data
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
        if space_id is not None:
184
            cursor_system.execute(" SELECT id, name, area, number_of_occupants, cost_center_id "
185
                                  " FROM tbl_spaces "
186
                                  " WHERE id = %s ", (space_id,))
187
            row_space = cursor_system.fetchone()
188
        elif space_uuid is not None:
189
            cursor_system.execute(" SELECT id, name, area, number_of_occupants, cost_center_id "
190
                                  " FROM tbl_spaces "
191
                                  " WHERE uuid = %s ", (space_uuid,))
192
            row_space = cursor_system.fetchone()
193
194 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...
195
            if cursor_system:
196
                cursor_system.close()
197
            if cnx_system:
198
                cnx_system.close()
199
200
            if cursor_energy:
201
                cursor_energy.close()
202
            if cnx_energy:
203
                cnx_energy.close()
204
205
            if cursor_historical:
206
                cursor_historical.close()
207
            if cnx_historical:
208
                cnx_historical.close()
209
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND', description='API.SPACE_NOT_FOUND')
210
211
        space = dict()
212
        space['id'] = row_space[0]
213
        space['name'] = row_space[1]
214
        space['area'] = row_space[2]
215
        space['number_of_occupants'] = row_space[3]
216
        space['cost_center_id'] = row_space[4]
217
218
        ################################################################################################################
219
        # Step 3: query input energy categories and output energy categories
220
        ################################################################################################################
221
        energy_category_set_input = set()
222
        energy_category_set_output = set()
223
        # query input energy categories in base period
224
        cursor_energy.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_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_input.add(row_energy_category[0])
234
235
        # query input energy categories in reporting period
236
        cursor_energy.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_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_input.add(row_energy_category[0])
246
247
        # query output energy categories in base period
248
        cursor_energy.execute(" SELECT DISTINCT(energy_category_id) "
249
                              " FROM tbl_space_output_category_hourly "
250
                              " WHERE space_id = %s "
251
                              "     AND start_datetime_utc >= %s "
252
                              "     AND start_datetime_utc < %s ",
253
                              (space['id'], base_start_datetime_utc, base_end_datetime_utc))
254
        rows_energy_categories = cursor_energy.fetchall()
255
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
256
            for row_energy_category in rows_energy_categories:
257
                energy_category_set_output.add(row_energy_category[0])
258
259
        # query output energy categories in reporting period
260
        cursor_energy.execute(" SELECT DISTINCT(energy_category_id) "
261
                              " FROM tbl_space_output_category_hourly "
262
                              " WHERE space_id = %s "
263
                              "     AND start_datetime_utc >= %s "
264
                              "     AND start_datetime_utc < %s ",
265
                              (space['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
266
        rows_energy_categories = cursor_energy.fetchall()
267
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
268
            for row_energy_category in rows_energy_categories:
269
                energy_category_set_output.add(row_energy_category[0])
270
271
        # query properties of all energy categories above
272
        cursor_system.execute(" SELECT id, name, unit_of_measure, kgce, kgco2e "
273
                              " FROM tbl_energy_categories "
274
                              " ORDER BY id ", )
275
        rows_energy_categories = cursor_system.fetchall()
276
        if rows_energy_categories is None or len(rows_energy_categories) == 0:
277
            if cursor_system:
278
                cursor_system.close()
279
            if cnx_system:
280
                cnx_system.close()
281
282
            if cursor_energy:
283
                cursor_energy.close()
284
            if cnx_energy:
285
                cnx_energy.close()
286
287
            if cursor_historical:
288
                cursor_historical.close()
289
            if cnx_historical:
290
                cnx_historical.close()
291
            raise falcon.HTTPError(status=falcon.HTTP_404,
292
                                   title='API.NOT_FOUND',
293
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
294
        energy_category_dict = dict()
295
        for row_energy_category in rows_energy_categories:
296
            if row_energy_category[0] in energy_category_set_input or \
297
                    row_energy_category[0] in energy_category_set_output:
298
                energy_category_dict[row_energy_category[0]] = {"name": row_energy_category[1],
299
                                                                "unit_of_measure": row_energy_category[2],
300
                                                                "kgce": row_energy_category[3],
301
                                                                "kgco2e": row_energy_category[4]}
302
303
        ################################################################################################################
304
        # Step 4: query associated sensors
305
        ################################################################################################################
306
        point_list = list()
307
        cursor_system.execute(" SELECT po.id, po.name, po.units, po.object_type  "
308
                              " FROM tbl_spaces sp, tbl_sensors se, tbl_spaces_sensors spse, "
309
                              "      tbl_points po, tbl_sensors_points sepo "
310
                              " WHERE sp.id = %s AND sp.id = spse.space_id AND spse.sensor_id = se.id "
311
                              "       AND se.id = sepo.sensor_id AND sepo.point_id = po.id "
312
                              " ORDER BY po.id ", (space['id'],))
313
        rows_points = cursor_system.fetchall()
314
        if rows_points is not None and len(rows_points) > 0:
315
            for row in rows_points:
316
                point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
317
318
        ################################################################################################################
319
        # Step 5: query associated points
320
        ################################################################################################################
321
        cursor_system.execute(" SELECT po.id, po.name, po.units, po.object_type  "
322
                              " FROM tbl_spaces sp, tbl_spaces_points sppo, tbl_points po "
323
                              " WHERE sp.id = %s AND sp.id = sppo.space_id AND sppo.point_id = po.id "
324
                              " ORDER BY po.id ", (space['id'],))
325
        rows_points = cursor_system.fetchall()
326
        if rows_points is not None and len(rows_points) > 0:
327
            for row in rows_points:
328
                point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
329
330
        ################################################################################################################
331
        # Step 6: query base period energy input
332
        ################################################################################################################
333
        base_input = dict()
334
        if energy_category_set_input is not None and len(energy_category_set_input) > 0:
335
            for energy_category_id in energy_category_set_input:
336
                base_input[energy_category_id] = dict()
337
                base_input[energy_category_id]['timestamps'] = list()
338
                base_input[energy_category_id]['values'] = list()
339
                base_input[energy_category_id]['subtotal'] = Decimal(0.0)
340
341
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
342
                                      " FROM tbl_space_input_category_hourly "
343
                                      " WHERE space_id = %s "
344
                                      "     AND energy_category_id = %s "
345
                                      "     AND start_datetime_utc >= %s "
346
                                      "     AND start_datetime_utc < %s "
347
                                      " ORDER BY start_datetime_utc ",
348
                                      (space['id'],
349
                                       energy_category_id,
350
                                       base_start_datetime_utc,
351
                                       base_end_datetime_utc))
352
                rows_space_hourly = cursor_energy.fetchall()
353
354
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
355
                                                                                    base_start_datetime_utc,
356
                                                                                    base_end_datetime_utc,
357
                                                                                    period_type)
358
                for row_space_periodically in rows_space_periodically:
359
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
360
                                             timedelta(minutes=timezone_offset)
361
                    if period_type == 'hourly':
362
                        current_datetime = current_datetime_local.isoformat()[0:19]
363
                    elif period_type == 'daily':
364
                        current_datetime = current_datetime_local.isoformat()[0:10]
365
                    elif period_type == 'weekly':
366
                        current_datetime = current_datetime_local.isoformat()[0:10]
367
                    elif period_type == 'monthly':
368
                        current_datetime = current_datetime_local.isoformat()[0:7]
369
                    elif period_type == 'yearly':
370
                        current_datetime = current_datetime_local.isoformat()[0:4]
371
372
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
373
                    base_input[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...
374
                    base_input[energy_category_id]['values'].append(actual_value)
375
                    base_input[energy_category_id]['subtotal'] += actual_value
376
377
        ################################################################################################################
378
        # Step 7: query base period energy output
379
        ################################################################################################################
380
        base_output = dict()
381
        if energy_category_set_output is not None and len(energy_category_set_output) > 0:
382
            for energy_category_id in energy_category_set_output:
383
                base_output[energy_category_id] = dict()
384
                base_output[energy_category_id]['timestamps'] = list()
385
                base_output[energy_category_id]['values'] = list()
386
                base_output[energy_category_id]['subtotal'] = Decimal(0.0)
387
388
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
389
                                      " FROM tbl_space_output_category_hourly "
390
                                      " WHERE space_id = %s "
391
                                      "     AND energy_category_id = %s "
392
                                      "     AND start_datetime_utc >= %s "
393
                                      "     AND start_datetime_utc < %s "
394
                                      " ORDER BY start_datetime_utc ",
395
                                      (space['id'],
396
                                       energy_category_id,
397
                                       base_start_datetime_utc,
398
                                       base_end_datetime_utc))
399
                rows_space_hourly = cursor_energy.fetchall()
400
401
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
402
                                                                                    base_start_datetime_utc,
403
                                                                                    base_end_datetime_utc,
404
                                                                                    period_type)
405
                for row_space_periodically in rows_space_periodically:
406
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
407
                                             timedelta(minutes=timezone_offset)
408
                    if period_type == 'hourly':
409
                        current_datetime = current_datetime_local.isoformat()[0:19]
410
                    elif period_type == 'daily':
411
                        current_datetime = current_datetime_local.isoformat()[0:10]
412
                    elif period_type == 'weekly':
413
                        current_datetime = current_datetime_local.isoformat()[0:10]
414
                    elif period_type == 'monthly':
415
                        current_datetime = current_datetime_local.isoformat()[0:7]
416
                    elif period_type == 'yearly':
417
                        current_datetime = current_datetime_local.isoformat()[0:4]
418
419
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
420
                    base_output[energy_category_id]['timestamps'].append(current_datetime)
421
                    base_output[energy_category_id]['values'].append(actual_value)
422
                    base_output[energy_category_id]['subtotal'] += actual_value
423
        ################################################################################################################
424
        # Step 8: query reporting period energy input
425
        ################################################################################################################
426
        reporting_input = dict()
427
        if energy_category_set_input is not None and len(energy_category_set_input) > 0:
428
            for energy_category_id in energy_category_set_input:
429
430
                reporting_input[energy_category_id] = dict()
431
                reporting_input[energy_category_id]['timestamps'] = list()
432
                reporting_input[energy_category_id]['values'] = list()
433
                reporting_input[energy_category_id]['subtotal'] = Decimal(0.0)
434
435
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
436
                                      " FROM tbl_space_input_category_hourly "
437
                                      " WHERE space_id = %s "
438
                                      "     AND energy_category_id = %s "
439
                                      "     AND start_datetime_utc >= %s "
440
                                      "     AND start_datetime_utc < %s "
441
                                      " ORDER BY start_datetime_utc ",
442
                                      (space['id'],
443
                                       energy_category_id,
444
                                       reporting_start_datetime_utc,
445
                                       reporting_end_datetime_utc))
446
                rows_space_hourly = cursor_energy.fetchall()
447
448
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
449
                                                                                    reporting_start_datetime_utc,
450
                                                                                    reporting_end_datetime_utc,
451
                                                                                    period_type)
452
                for row_space_periodically in rows_space_periodically:
453
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
454
                                             timedelta(minutes=timezone_offset)
455
                    if period_type == 'hourly':
456
                        current_datetime = current_datetime_local.isoformat()[0:19]
457
                    elif period_type == 'daily':
458
                        current_datetime = current_datetime_local.isoformat()[0:10]
459
                    elif period_type == 'weekly':
460
                        current_datetime = current_datetime_local.isoformat()[0:10]
461
                    elif period_type == 'monthly':
462
                        current_datetime = current_datetime_local.isoformat()[0:7]
463
                    elif period_type == 'yearly':
464
                        current_datetime = current_datetime_local.isoformat()[0:4]
465
466
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
467
                    reporting_input[energy_category_id]['timestamps'].append(current_datetime)
468
                    reporting_input[energy_category_id]['values'].append(actual_value)
469
                    reporting_input[energy_category_id]['subtotal'] += actual_value
470
471
        ################################################################################################################
472
        # Step 9: query reporting period energy output
473
        ################################################################################################################
474
        reporting_output = dict()
475
        if energy_category_set_output is not None and len(energy_category_set_output) > 0:
476
            for energy_category_id in energy_category_set_output:
477
478
                reporting_output[energy_category_id] = dict()
479
                reporting_output[energy_category_id]['timestamps'] = list()
480
                reporting_output[energy_category_id]['values'] = list()
481
                reporting_output[energy_category_id]['subtotal'] = Decimal(0.0)
482
483
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
484
                                      " FROM tbl_space_output_category_hourly "
485
                                      " WHERE space_id = %s "
486
                                      "     AND energy_category_id = %s "
487
                                      "     AND start_datetime_utc >= %s "
488
                                      "     AND start_datetime_utc < %s "
489
                                      " ORDER BY start_datetime_utc ",
490
                                      (space['id'],
491
                                       energy_category_id,
492
                                       reporting_start_datetime_utc,
493
                                       reporting_end_datetime_utc))
494
                rows_space_hourly = cursor_energy.fetchall()
495
496
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
497
                                                                                    reporting_start_datetime_utc,
498
                                                                                    reporting_end_datetime_utc,
499
                                                                                    period_type)
500
                for row_space_periodically in rows_space_periodically:
501
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
502
                                             timedelta(minutes=timezone_offset)
503
                    if period_type == 'hourly':
504
                        current_datetime = current_datetime_local.isoformat()[0:19]
505
                    elif period_type == 'daily':
506
                        current_datetime = current_datetime_local.isoformat()[0:10]
507
                    elif period_type == 'weekly':
508
                        current_datetime = current_datetime_local.isoformat()[0:10]
509
                    elif period_type == 'monthly':
510
                        current_datetime = current_datetime_local.isoformat()[0:7]
511
                    elif period_type == 'yearly':
512
                        current_datetime = current_datetime_local.isoformat()[0:4]
513
514
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
515
                    reporting_output[energy_category_id]['timestamps'].append(current_datetime)
516
                    reporting_output[energy_category_id]['values'].append(actual_value)
517
                    reporting_output[energy_category_id]['subtotal'] += actual_value
518
519
        ################################################################################################################
520
        # Step 10: query tariff data
521
        ################################################################################################################
522
        parameters_data = dict()
523
        parameters_data['names'] = list()
524
        parameters_data['timestamps'] = list()
525
        parameters_data['values'] = list()
526
        if config.is_tariff_appended and energy_category_set_input is not None and len(energy_category_set_input) > 0 \
527
                and not is_quick_mode:
528
            for energy_category_id in energy_category_set_input:
529
                energy_category_tariff_dict = utilities.get_energy_category_tariffs(space['cost_center_id'],
530
                                                                                    energy_category_id,
531
                                                                                    reporting_start_datetime_utc,
532
                                                                                    reporting_end_datetime_utc)
533
                tariff_timestamp_list = list()
534
                tariff_value_list = list()
535
                for k, v in energy_category_tariff_dict.items():
536
                    # convert k from utc to local
537
                    k = k + timedelta(minutes=timezone_offset)
538
                    tariff_timestamp_list.append(k.isoformat()[0:19])
539
                    tariff_value_list.append(v)
540
541
                parameters_data['names'].append(_('Tariff') + '-' + energy_category_dict[energy_category_id]['name'])
542
                parameters_data['timestamps'].append(tariff_timestamp_list)
543
                parameters_data['values'].append(tariff_value_list)
544
545
        ################################################################################################################
546
        # Step 11: query associated sensors and points data
547
        ################################################################################################################
548
        if not is_quick_mode:
549
            for point in point_list:
550
                point_values = []
551
                point_timestamps = []
552
                if point['object_type'] == 'ENERGY_VALUE':
553
                    query = (" SELECT utc_date_time, actual_value "
554
                             " FROM tbl_energy_value "
555
                             " WHERE point_id = %s "
556
                             "       AND utc_date_time BETWEEN %s AND %s "
557
                             " ORDER BY utc_date_time ")
558
                    cursor_historical.execute(query, (point['id'],
559
                                                      reporting_start_datetime_utc,
560
                                                      reporting_end_datetime_utc))
561
                    rows = cursor_historical.fetchall()
562
563
                    if rows is not None and len(rows) > 0:
564
                        for row in rows:
565
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
566
                                                     timedelta(minutes=timezone_offset)
567
                            current_datetime = current_datetime_local.isoformat()[0:19]
568
                            point_timestamps.append(current_datetime)
569
                            point_values.append(row[1])
570
                elif point['object_type'] == 'ANALOG_VALUE':
571
                    query = (" SELECT utc_date_time, actual_value "
572
                             " FROM tbl_analog_value "
573
                             " WHERE point_id = %s "
574
                             "       AND utc_date_time BETWEEN %s AND %s "
575
                             " ORDER BY utc_date_time ")
576
                    cursor_historical.execute(query, (point['id'],
577
                                                      reporting_start_datetime_utc,
578
                                                      reporting_end_datetime_utc))
579
                    rows = cursor_historical.fetchall()
580
581
                    if rows is not None and len(rows) > 0:
582
                        for row in rows:
583
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
584
                                                     timedelta(minutes=timezone_offset)
585
                            current_datetime = current_datetime_local.isoformat()[0:19]
586
                            point_timestamps.append(current_datetime)
587
                            point_values.append(row[1])
588
                elif point['object_type'] == 'DIGITAL_VALUE':
589
                    query = (" SELECT utc_date_time, actual_value "
590
                             " FROM tbl_digital_value "
591
                             " WHERE point_id = %s "
592
                             "       AND utc_date_time BETWEEN %s AND %s "
593
                             " ORDER BY utc_date_time ")
594
                    cursor_historical.execute(query, (point['id'],
595
                                                      reporting_start_datetime_utc,
596
                                                      reporting_end_datetime_utc))
597
                    rows = cursor_historical.fetchall()
598
599
                    if rows is not None and len(rows) > 0:
600
                        for row in rows:
601
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
602
                                                     timedelta(minutes=timezone_offset)
603
                            current_datetime = current_datetime_local.isoformat()[0:19]
604
                            point_timestamps.append(current_datetime)
605
                            point_values.append(row[1])
606
607
                parameters_data['names'].append(point['name'] + ' (' + point['units'] + ')')
608
                parameters_data['timestamps'].append(point_timestamps)
609
                parameters_data['values'].append(point_values)
610
611
        ################################################################################################################
612
        # Step 12: construct the report
613
        ################################################################################################################
614
        if cursor_system:
615
            cursor_system.close()
616
        if cnx_system:
617
            cnx_system.close()
618
619
        if cursor_energy:
620
            cursor_energy.close()
621
        if cnx_energy:
622
            cnx_energy.close()
623
624
        if cursor_historical:
625
            cursor_historical.close()
626
        if cnx_historical:
627
            cnx_historical.close()
628
629
        result = dict()
630
631
        result['space'] = dict()
632
        result['space']['name'] = space['name']
633
        result['space']['area'] = space['area']
634
        result['space']['number_of_occupants'] = space['number_of_occupants']
635
636
        result['base_period_input'] = dict()
637
        result['base_period_input']['names'] = list()
638
        result['base_period_input']['units'] = list()
639
        result['base_period_input']['timestamps'] = list()
640
        result['base_period_input']['values'] = list()
641
        result['base_period_input']['subtotals'] = list()
642
        if energy_category_set_input is not None and len(energy_category_set_input) > 0:
643
            for energy_category_id in energy_category_set_input:
644
                result['base_period_input']['names'].append(energy_category_dict[energy_category_id]['name'])
645
                result['base_period_input']['units'].append(energy_category_dict[energy_category_id]['unit_of_measure'])
646
                result['base_period_input']['timestamps'].append(base_input[energy_category_id]['timestamps'])
647
                result['base_period_input']['values'].append(base_input[energy_category_id]['values'])
648
                result['base_period_input']['subtotals'].append(base_input[energy_category_id]['subtotal'])
649
650
        result['base_period_output'] = dict()
651
        result['base_period_output']['names'] = list()
652
        result['base_period_output']['units'] = list()
653
        result['base_period_output']['timestamps'] = list()
654
        result['base_period_output']['values'] = list()
655
        result['base_period_output']['subtotals'] = list()
656
657
        if energy_category_set_output is not None and len(energy_category_set_output) > 0:
658
            for energy_category_id in energy_category_set_output:
659
                result['base_period_output']['names'].append(energy_category_dict[energy_category_id]['name'])
660
                result['base_period_output']['units'].append(
661
                    energy_category_dict[energy_category_id]['unit_of_measure'])
662
                result['base_period_output']['timestamps'].append(base_output[energy_category_id]['timestamps'])
663
                result['base_period_output']['values'].append(base_output[energy_category_id]['values'])
664
                result['base_period_output']['subtotals'].append(base_output[energy_category_id]['subtotal'])
665
666
        result['base_period_efficiency'] = dict()
667
        result['base_period_efficiency']['names'] = list()
668
        result['base_period_efficiency']['units'] = list()
669
        result['base_period_efficiency']['timestamps'] = list()
670
        result['base_period_efficiency']['values'] = list()
671
        result['base_period_efficiency']['cumulations'] = list()
672
673
        if energy_category_set_output is not None and len(energy_category_set_output) > 0:
674
            for energy_category_id_output in energy_category_set_output:
675
                for energy_category_id_input in energy_category_set_input:
676
                    result['base_period_efficiency']['names'].append(
677
                        energy_category_dict[energy_category_id_output]['name'] + '/' +
678
                        energy_category_dict[energy_category_id_input]['name'])
679
                    result['base_period_efficiency']['units'].append(
680
                        energy_category_dict[energy_category_id_output]['unit_of_measure'] + '/' +
681
                        energy_category_dict[energy_category_id_input]['unit_of_measure'])
682
                    result['base_period_efficiency']['timestamps'].append(
683
                        base_output[energy_category_id_output]['timestamps'])
684
                    efficiency_values = list()
685
                    for i in range(len(base_output[energy_category_id_output]['timestamps'])):
686
                        efficiency_values.append((base_output[energy_category_id_output]['values'][i] /
687
                                                  base_input[energy_category_id_input]['values'][i])
688
                                                 if base_input[energy_category_id_input]['values'][i] > Decimal(0.0)
689
                                                 else None)
690
                    result['base_period_efficiency']['values'].append(efficiency_values)
691
692
                    base_cumulation = (base_output[energy_category_id_output]['subtotal'] /
693
                                       base_input[energy_category_id_input]['subtotal']) if \
694
                        base_input[energy_category_id_input]['subtotal'] > Decimal(0.0) else None
695
                    result['base_period_efficiency']['cumulations'].append(base_cumulation)
696
697
        result['reporting_period_input'] = dict()
698
        result['reporting_period_input']['names'] = list()
699
        result['reporting_period_input']['energy_category_ids'] = list()
700
        result['reporting_period_input']['units'] = list()
701
        result['reporting_period_input']['timestamps'] = list()
702
        result['reporting_period_input']['values'] = list()
703
        result['reporting_period_input']['subtotals'] = list()
704
        result['reporting_period_input']['subtotals_per_unit_area'] = list()
705
        result['reporting_period_input']['subtotals_per_capita'] = list()
706
        result['reporting_period_input']['increment_rates'] = list()
707
708
        if energy_category_set_input is not None and len(energy_category_set_input) > 0:
709
            for energy_category_id in energy_category_set_input:
710
                result['reporting_period_input']['names'].append(energy_category_dict[energy_category_id]['name'])
711
                result['reporting_period_input']['energy_category_ids'].append(energy_category_id)
712
                result['reporting_period_input']['units'].append(
713
                    energy_category_dict[energy_category_id]['unit_of_measure'])
714
                result['reporting_period_input']['timestamps'].append(
715
                    reporting_input[energy_category_id]['timestamps'])
716
                result['reporting_period_input']['values'].append(
717
                    reporting_input[energy_category_id]['values'])
718
                result['reporting_period_input']['subtotals'].append(
719
                    reporting_input[energy_category_id]['subtotal'])
720
                result['reporting_period_input']['increment_rates'].append(
721
                    (reporting_input[energy_category_id]['subtotal'] -
722
                     base_input[energy_category_id]['subtotal']) /
723
                    base_input[energy_category_id]['subtotal']
724
                    if base_input[energy_category_id]['subtotal'] > Decimal(0.0) else None)
725
726
        result['reporting_period_output'] = dict()
727
        result['reporting_period_output']['names'] = list()
728
        result['reporting_period_output']['energy_category_ids'] = list()
729
        result['reporting_period_output']['units'] = list()
730
        result['reporting_period_output']['timestamps'] = list()
731
        result['reporting_period_output']['values'] = list()
732
        result['reporting_period_output']['subtotals'] = list()
733
        result['reporting_period_output']['subtotals_per_unit_area'] = list()
734
        result['reporting_period_output']['subtotals_per_capita'] = list()
735
        result['reporting_period_output']['increment_rates'] = list()
736
737
        if energy_category_set_output is not None and len(energy_category_set_output) > 0:
738
            for energy_category_id in energy_category_set_output:
739
                result['reporting_period_output']['names'].append(energy_category_dict[energy_category_id]['name'])
740
                result['reporting_period_output']['energy_category_ids'].append(energy_category_id)
741
                result['reporting_period_output']['units'].append(
742
                    energy_category_dict[energy_category_id]['unit_of_measure'])
743
                result['reporting_period_output']['timestamps'].append(
744
                    reporting_output[energy_category_id]['timestamps'])
745
                result['reporting_period_output']['values'].append(reporting_output[energy_category_id]['values'])
746
                result['reporting_period_output']['subtotals'].append(reporting_output[energy_category_id]['subtotal'])
747
                result['reporting_period_output']['increment_rates'].append(
748
                    (reporting_output[energy_category_id]['subtotal'] -
749
                     base_output[energy_category_id]['subtotal']) /
750
                    base_output[energy_category_id]['subtotal']
751
                    if base_output[energy_category_id]['subtotal'] > 0.0 else None)
752
753
        result['reporting_period_efficiency'] = dict()
754
        result['reporting_period_efficiency']['names'] = list()
755
        result['reporting_period_efficiency']['units'] = list()
756
        result['reporting_period_efficiency']['timestamps'] = list()
757
        result['reporting_period_efficiency']['rates'] = list()
758
        result['reporting_period_efficiency']['values'] = list()
759
        result['reporting_period_efficiency']['cumulations'] = list()
760
        result['reporting_period_efficiency']['increment_rates'] = list()
761
762
        if energy_category_set_output is not None and len(energy_category_set_output) > 0:
763
            for energy_category_id_output in energy_category_set_output:
764
                for energy_category_id_input in energy_category_set_input:
765
                    result['reporting_period_efficiency']['names'].append(
766
                        energy_category_dict[energy_category_id_output]['name'] + '/' +
767
                        energy_category_dict[energy_category_id_input]['name'])
768
                    result['reporting_period_efficiency']['units'].append(
769
                        energy_category_dict[energy_category_id_output]['unit_of_measure'] + '/' +
770
                        energy_category_dict[energy_category_id_input]['unit_of_measure'])
771
                    result['reporting_period_efficiency']['timestamps'].append(
772
                        reporting_output[energy_category_id_output]['timestamps'])
773
                    efficiency_values = list()
774
                    for i in range(len(reporting_output[energy_category_id_output]['timestamps'])):
775
                        efficiency_values.append((reporting_output[energy_category_id_output]['values'][i] /
776
                                                  reporting_input[energy_category_id_input]['values'][i])
777
                                                 if reporting_input[energy_category_id_input]['values'][i] >
778
                                                 Decimal(0.0) else None)
779
                    result['reporting_period_efficiency']['values'].append(efficiency_values)
780
781
                    base_cumulation = (base_output[energy_category_id_output]['subtotal'] /
782
                                       base_input[energy_category_id_input]['subtotal']) if \
783
                        base_input[energy_category_id_input]['subtotal'] > Decimal(0.0) else None
784
785
                    reporting_cumulation = (reporting_output[energy_category_id_output]['subtotal'] /
786
                                            reporting_input[energy_category_id_input]['subtotal']) if \
787
                        reporting_input[energy_category_id_input]['subtotal'] > Decimal(0.0) else None
788
789
                    result['reporting_period_efficiency']['cumulations'].append(reporting_cumulation)
790
                    result['reporting_period_efficiency']['increment_rates'].append(
791
                        ((reporting_cumulation - base_cumulation) / base_cumulation
792
                         if (reporting_cumulation is not None and
793
                             base_cumulation is not None and
794
                             base_cumulation > Decimal(0.0))
795
                         else None)
796
                    )
797
798
        reporting_period_efficiency_values = result['reporting_period_efficiency']['values']
799
        base_period_efficiency_values = result['base_period_efficiency']['values']
800
801
        if len(reporting_period_efficiency_values) > 0:
802
            for i in range(len(reporting_period_efficiency_values)):
803
                rate = list()
804
                if len(reporting_period_efficiency_values[i]) > 0 \
805
                        and len(base_period_efficiency_values[i]) > 0:
806
                    for j in range(len(reporting_period_efficiency_values[i])):
807
                        if j < len(base_period_efficiency_values[i]) \
808
                                and reporting_period_efficiency_values[i][j] != 0 \
809
                                and base_period_efficiency_values[i][j] != 0 \
810
                                and reporting_period_efficiency_values[i][j] is not None \
811
                                and base_period_efficiency_values[i][j] is not None:
812
                            rate.append((reporting_period_efficiency_values[i][j] -
813
                                         base_period_efficiency_values[i][j]) / base_period_efficiency_values[i][j])
814
                        else:
815
                            rate.append(None)
816
                result['reporting_period_efficiency']['rates'].append(rate)
817
818
        result['parameters'] = {
819
            "names": parameters_data['names'],
820
            "timestamps": parameters_data['timestamps'],
821
            "values": parameters_data['values']
822
        }
823
        # export result to Excel file and then encode the file to base64 string
824
        result['excel_bytes_base64'] = None
825
        if not is_quick_mode:
826
            result['excel_bytes_base64'] = excelexporters.spaceefficiency.export(result,
827
                                                                                 space['name'],
828
                                                                                 base_period_start_datetime_local,
829
                                                                                 base_period_end_datetime_local,
830
                                                                                 reporting_period_start_datetime_local,
831
                                                                                 reporting_period_end_datetime_local,
832
                                                                                 period_type,
833
                                                                                 language)
834
835
        resp.text = json.dumps(result)
836