reports.spacesaving   F
last analyzed

Complexity

Total Complexity 135

Size/Duplication

Total Lines 783
Duplicated Lines 1.53 %

Importance

Changes 0
Metric Value
eloc 573
dl 12
loc 783
rs 2
c 0
b 0
f 0
wmc 135

3 Methods

Rating   Name   Duplication   Size   Complexity  
F Reporting.on_get() 12 748 133
A Reporting.on_options() 0 3 1
A Reporting.__init__() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like reports.spacesaving often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import falcon
2
import simplejson as json
3
import mysql.connector
4
import config
5
from datetime import datetime, timedelta, timezone
6
from core import utilities
7
from decimal import Decimal
8
import excelexporters.spacesaving
9
10
11
class Reporting:
12
    @staticmethod
13
    def __init__():
14
        pass
15
16
    @staticmethod
17
    def on_options(req, resp):
18
        resp.status = falcon.HTTP_200
19
20
    ####################################################################################################################
21
    # PROCEDURES
22
    # Step 1: valid parameters
23
    # Step 2: query the space
24
    # Step 3: query energy categories
25
    # Step 4: query associated sensors
26
    # Step 5: query associated points
27
    # Step 6: query child spaces
28
    # Step 7: query base period energy saving
29
    # Step 8: query reporting period energy saving
30
    # Step 9: query tariff data
31
    # Step 10: query associated sensors and points data
32
    # Step 11: query child spaces energy saving
33
    # Step 12: construct the report
34
    ####################################################################################################################
35
    @staticmethod
36
    def on_get(req, resp):
37
        print(req.params)
38
        space_id = req.params.get('spaceid')
39
        period_type = req.params.get('periodtype')
40
        base_start_datetime_local = req.params.get('baseperiodstartdatetime')
41
        base_end_datetime_local = req.params.get('baseperiodenddatetime')
42
        reporting_start_datetime_local = req.params.get('reportingperiodstartdatetime')
43
        reporting_end_datetime_local = req.params.get('reportingperiodenddatetime')
44
45
        ################################################################################################################
46
        # Step 1: valid parameters
47
        ################################################################################################################
48
        if space_id is None:
49
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_SPACE_ID')
50
        else:
51
            space_id = str.strip(space_id)
52
            if not space_id.isdigit() or int(space_id) <= 0:
53
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_SPACE_ID')
54
55
        if period_type is None:
56
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_PERIOD_TYPE')
57
        else:
58
            period_type = str.strip(period_type)
59
            if period_type not in ['hourly', 'daily', 'monthly', 'yearly']:
60
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_PERIOD_TYPE')
61
62
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
63
        if config.utc_offset[0] == '-':
64
            timezone_offset = -timezone_offset
65
66
        base_start_datetime_utc = None
67
        if base_start_datetime_local is not None and len(str.strip(base_start_datetime_local)) > 0:
68
            base_start_datetime_local = str.strip(base_start_datetime_local)
69
            try:
70
                base_start_datetime_utc = datetime.strptime(base_start_datetime_local,
71
                                                            '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
72
                                          timedelta(minutes=timezone_offset)
73
            except ValueError:
74
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
75
                                       description="API.INVALID_BASE_PERIOD_START_DATETIME")
76
77
        base_end_datetime_utc = None
78
        if base_end_datetime_local is not None and len(str.strip(base_end_datetime_local)) > 0:
79
            base_end_datetime_local = str.strip(base_end_datetime_local)
80
            try:
81
                base_end_datetime_utc = datetime.strptime(base_end_datetime_local,
82
                                                          '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
83
                                        timedelta(minutes=timezone_offset)
84
            except ValueError:
85
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
86
                                       description="API.INVALID_BASE_PERIOD_END_DATETIME")
87
88
        if base_start_datetime_utc is not None and base_end_datetime_utc is not None and \
89
                base_start_datetime_utc >= base_end_datetime_utc:
90
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
91
                                   description='API.INVALID_BASE_PERIOD_END_DATETIME')
92
93
        if reporting_start_datetime_local is None:
94
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
95
                                   description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
96
        else:
97
            reporting_start_datetime_local = str.strip(reporting_start_datetime_local)
98
            try:
99
                reporting_start_datetime_utc = datetime.strptime(reporting_start_datetime_local,
100
                                                                 '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
101
                                               timedelta(minutes=timezone_offset)
102
            except ValueError:
103
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
104
                                       description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
105
106
        if reporting_end_datetime_local is None:
107
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
108
                                   description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
109
        else:
110
            reporting_end_datetime_local = str.strip(reporting_end_datetime_local)
111
            try:
112
                reporting_end_datetime_utc = datetime.strptime(reporting_end_datetime_local,
113
                                                               '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
114
                                             timedelta(minutes=timezone_offset)
115
            except ValueError:
116
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
117
                                       description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
118
119
        if reporting_start_datetime_utc >= reporting_end_datetime_utc:
120
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
121
                                   description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
122
123
        ################################################################################################################
124
        # Step 2: query the space
125
        ################################################################################################################
126
        cnx_system = mysql.connector.connect(**config.myems_system_db)
127
        cursor_system = cnx_system.cursor()
128
129
        cnx_energy = mysql.connector.connect(**config.myems_energy_db)
130
        cursor_energy = cnx_energy.cursor()
131
132
        cnx_energy_baseline = mysql.connector.connect(**config.myems_energy_baseline_db)
133
        cursor_energy_baseline = cnx_energy_baseline.cursor()
134
135
        cnx_historical = mysql.connector.connect(**config.myems_historical_db)
136
        cursor_historical = cnx_historical.cursor()
137
138
        cursor_system.execute(" SELECT id, name, area, cost_center_id "
139
                              " FROM tbl_spaces "
140
                              " WHERE id = %s ", (space_id,))
141
        row_space = cursor_system.fetchone()
142
        if row_space is None:
143
            if cursor_system:
144
                cursor_system.close()
145
            if cnx_system:
146
                cnx_system.disconnect()
147
148
            if cursor_energy:
149
                cursor_energy.close()
150
            if cnx_energy:
151
                cnx_energy.disconnect()
152
153
            if cursor_energy_baseline:
154
                cursor_energy_baseline.close()
155
            if cnx_energy_baseline:
156
                cnx_energy_baseline.disconnect()
157
158
            if cnx_historical:
159
                cnx_historical.close()
160
            if cursor_historical:
161
                cursor_historical.disconnect()
162
            raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND', description='API.SPACE_NOT_FOUND')
163
164
        space = dict()
165
        space['id'] = row_space[0]
166
        space['name'] = row_space[1]
167
        space['area'] = row_space[2]
168
        space['cost_center_id'] = row_space[3]
169
170
        ################################################################################################################
171
        # Step 3: query energy categories
172
        ################################################################################################################
173
        energy_category_set = set()
174
        # query energy categories in base period
175
        cursor_energy.execute(" SELECT DISTINCT(energy_category_id) "
176
                              " FROM tbl_space_input_category_hourly "
177
                              " WHERE space_id = %s "
178
                              "     AND start_datetime_utc >= %s "
179
                              "     AND start_datetime_utc < %s ",
180
                              (space['id'], base_start_datetime_utc, base_end_datetime_utc))
181
        rows_energy_categories = cursor_energy.fetchall()
182
        if rows_energy_categories is not None or len(rows_energy_categories) > 0:
183
            for row_energy_category in rows_energy_categories:
184
                energy_category_set.add(row_energy_category[0])
185
186
        # query energy categories in reporting period
187
        cursor_energy.execute(" SELECT DISTINCT(energy_category_id) "
188
                              " FROM tbl_space_input_category_hourly "
189
                              " WHERE space_id = %s "
190
                              "     AND start_datetime_utc >= %s "
191
                              "     AND start_datetime_utc < %s ",
192
                              (space['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
193
        rows_energy_categories = cursor_energy.fetchall()
194
        if rows_energy_categories is not None or len(rows_energy_categories) > 0:
195
            for row_energy_category in rows_energy_categories:
196
                energy_category_set.add(row_energy_category[0])
197
198
        # query all energy categories in base period and reporting period
199
        cursor_system.execute(" SELECT id, name, unit_of_measure, kgce, kgco2e "
200
                              " FROM tbl_energy_categories "
201
                              " ORDER BY id ", )
202
        rows_energy_categories = cursor_system.fetchall()
203
        if rows_energy_categories is None or len(rows_energy_categories) == 0:
204
            if cursor_system:
205
                cursor_system.close()
206
            if cnx_system:
207
                cnx_system.disconnect()
208
209
            if cursor_energy:
210
                cursor_energy.close()
211
            if cnx_energy:
212
                cnx_energy.disconnect()
213
214
            if cursor_energy_baseline:
215
                cursor_energy_baseline.close()
216
            if cnx_energy_baseline:
217
                cnx_energy_baseline.disconnect()
218
219
            if cnx_historical:
220
                cnx_historical.close()
221
            if cursor_historical:
222
                cursor_historical.disconnect()
223
            raise falcon.HTTPError(falcon.HTTP_404,
224
                                   title='API.NOT_FOUND',
225
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
226
        energy_category_dict = dict()
227
        for row_energy_category in rows_energy_categories:
228
            if row_energy_category[0] in energy_category_set:
229
                energy_category_dict[row_energy_category[0]] = {"name": row_energy_category[1],
230
                                                                "unit_of_measure": row_energy_category[2],
231
                                                                "kgce": row_energy_category[3],
232
                                                                "kgco2e": row_energy_category[4]}
233
234
        ################################################################################################################
235
        # Step 4: query associated sensors
236
        ################################################################################################################
237
        point_list = list()
238
        cursor_system.execute(" SELECT po.id, po.name, po.units, po.object_type  "
239
                              " FROM tbl_spaces sp, tbl_sensors se, tbl_spaces_sensors spse, "
240
                              "      tbl_points po, tbl_sensors_points sepo "
241
                              " WHERE sp.id = %s AND sp.id = spse.space_id AND spse.sensor_id = se.id "
242
                              "       AND se.id = sepo.sensor_id AND sepo.point_id = po.id "
243
                              " ORDER BY po.id ", (space['id'],))
244
        rows_points = cursor_system.fetchall()
245
        if rows_points is not None and len(rows_points) > 0:
246
            for row in rows_points:
247
                point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
248
249
        ################################################################################################################
250
        # Step 5: query associated points
251
        ################################################################################################################
252
        cursor_system.execute(" SELECT po.id, po.name, po.units, po.object_type  "
253
                              " FROM tbl_spaces sp, tbl_spaces_points sppo, tbl_points po "
254
                              " WHERE sp.id = %s AND sp.id = sppo.space_id AND sppo.point_id = po.id "
255
                              " ORDER BY po.id ", (space['id'],))
256
        rows_points = cursor_system.fetchall()
257
        if rows_points is not None and len(rows_points) > 0:
258
            for row in rows_points:
259
                point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
260
261
        ################################################################################################################
262
        # Step 6: query child spaces
263
        ################################################################################################################
264
        child_space_list = list()
265
        cursor_system.execute(" SELECT id, name  "
266
                              " FROM tbl_spaces "
267
                              " WHERE parent_space_id = %s "
268
                              " ORDER BY id ", (space['id'],))
269
        rows_child_spaces = cursor_system.fetchall()
270
        if rows_child_spaces is not None and len(rows_child_spaces) > 0:
271
            for row in rows_child_spaces:
272
                child_space_list.append({"id": row[0], "name": row[1]})
273
274
        ################################################################################################################
275
        # Step 7: query base period energy saving
276
        ################################################################################################################
277
        base = dict()
278
        if energy_category_set is not None and len(energy_category_set) > 0:
279
            for energy_category_id in energy_category_set:
280
                kgce = energy_category_dict[energy_category_id]['kgce']
281
                kgco2e = energy_category_dict[energy_category_id]['kgco2e']
282
283
                base[energy_category_id] = dict()
284
                base[energy_category_id]['timestamps'] = list()
285
                base[energy_category_id]['values_baseline'] = list()
286
                base[energy_category_id]['values_actual'] = list()
287
                base[energy_category_id]['values_saving'] = list()
288
                base[energy_category_id]['subtotal_baseline'] = Decimal(0.0)
289
                base[energy_category_id]['subtotal_actual'] = Decimal(0.0)
290
                base[energy_category_id]['subtotal_saving'] = Decimal(0.0)
291
                base[energy_category_id]['subtotal_in_kgce_baseline'] = Decimal(0.0)
292
                base[energy_category_id]['subtotal_in_kgce_actual'] = Decimal(0.0)
293
                base[energy_category_id]['subtotal_in_kgce_saving'] = Decimal(0.0)
294
                base[energy_category_id]['subtotal_in_kgco2e_baseline'] = Decimal(0.0)
295
                base[energy_category_id]['subtotal_in_kgco2e_actual'] = Decimal(0.0)
296
                base[energy_category_id]['subtotal_in_kgco2e_saving'] = Decimal(0.0)
297
                # query base period's energy baseline
298
                cursor_energy_baseline.execute(" SELECT start_datetime_utc, actual_value "
299
                                               " FROM tbl_space_input_category_hourly "
300
                                               " WHERE space_id = %s "
301
                                               "     AND energy_category_id = %s "
302
                                               "     AND start_datetime_utc >= %s "
303
                                               "     AND start_datetime_utc < %s "
304
                                               " ORDER BY start_datetime_utc ",
305
                                               (space['id'],
306
                                                energy_category_id,
307
                                                base_start_datetime_utc,
308
                                                base_end_datetime_utc))
309
                rows_space_hourly = cursor_energy_baseline.fetchall()
310
311
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
312
                                                                                    base_start_datetime_utc,
313
                                                                                    base_end_datetime_utc,
314
                                                                                    period_type)
315
                for row_space_periodically in rows_space_periodically:
316
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
317
                                             timedelta(minutes=timezone_offset)
318
                    if period_type == 'hourly':
319
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
320
                    elif period_type == 'daily':
321
                        current_datetime = current_datetime_local.strftime('%Y-%m-%d')
322
                    elif period_type == 'monthly':
323
                        current_datetime = current_datetime_local.strftime('%Y-%m')
324
                    elif period_type == 'yearly':
325
                        current_datetime = current_datetime_local.strftime('%Y')
326
327
                    baseline_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
328
                    base[energy_category_id]['timestamps'].append(current_datetime)
0 ignored issues
show
introduced by
The variable current_datetime does not seem to be defined for all execution paths.
Loading history...
329
                    base[energy_category_id]['values_baseline'].append(baseline_value)
330
                    base[energy_category_id]['subtotal_baseline'] += baseline_value
331
                    base[energy_category_id]['subtotal_in_kgce_baseline'] += baseline_value * kgce
332
                    base[energy_category_id]['subtotal_in_kgco2e_baseline'] += baseline_value * kgco2e
333
334
                # query base period's energy actual
335
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
336
                                      " FROM tbl_space_input_category_hourly "
337
                                      " WHERE space_id = %s "
338
                                      "     AND energy_category_id = %s "
339
                                      "     AND start_datetime_utc >= %s "
340
                                      "     AND start_datetime_utc < %s "
341
                                      " ORDER BY start_datetime_utc ",
342
                                      (space['id'],
343
                                       energy_category_id,
344
                                       base_start_datetime_utc,
345
                                       base_end_datetime_utc))
346
                rows_space_hourly = cursor_energy.fetchall()
347
348
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
349
                                                                                    base_start_datetime_utc,
350
                                                                                    base_end_datetime_utc,
351
                                                                                    period_type)
352
                for row_space_periodically in rows_space_periodically:
353
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
354
                                             timedelta(minutes=timezone_offset)
355
                    if period_type == 'hourly':
356
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
357
                    elif period_type == 'daily':
358
                        current_datetime = current_datetime_local.strftime('%Y-%m-%d')
359
                    elif period_type == 'monthly':
360
                        current_datetime = current_datetime_local.strftime('%Y-%m')
361
                    elif period_type == 'yearly':
362
                        current_datetime = current_datetime_local.strftime('%Y')
363
364
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
365
                    base[energy_category_id]['values_actual'].append(actual_value)
366
                    base[energy_category_id]['subtotal_actual'] += actual_value
367
                    base[energy_category_id]['subtotal_in_kgce_actual'] += actual_value * kgce
368
                    base[energy_category_id]['subtotal_in_kgco2e_actual'] += actual_value * kgco2e
369
370
                # calculate base period's energy savings
371
                for i in range(len(base[energy_category_id]['values_baseline'])):
372
                    base[energy_category_id]['values_saving'].append(
373
                        base[energy_category_id]['values_baseline'][i] -
374
                        base[energy_category_id]['values_actual'][i])
375
376
                base[energy_category_id]['subtotal_saving'] = \
377
                    base[energy_category_id]['subtotal_baseline'] - \
378
                    base[energy_category_id]['subtotal_actual']
379
                base[energy_category_id]['subtotal_in_kgce_saving'] = \
380
                    base[energy_category_id]['subtotal_in_kgce_baseline'] - \
381
                    base[energy_category_id]['subtotal_in_kgce_actual']
382
                base[energy_category_id]['subtotal_in_kgco2e_saving'] = \
383
                    base[energy_category_id]['subtotal_in_kgco2e_baseline'] - \
384
                    base[energy_category_id]['subtotal_in_kgco2e_actual']
385
        ################################################################################################################
386
        # Step 8: query reporting period energy saving
387
        ################################################################################################################
388
        reporting = dict()
389
        if energy_category_set is not None and len(energy_category_set) > 0:
390
            for energy_category_id in energy_category_set:
391
                kgce = energy_category_dict[energy_category_id]['kgce']
392
                kgco2e = energy_category_dict[energy_category_id]['kgco2e']
393
394
                reporting[energy_category_id] = dict()
395
                reporting[energy_category_id]['timestamps'] = list()
396
                reporting[energy_category_id]['values_baseline'] = list()
397
                reporting[energy_category_id]['values_actual'] = list()
398
                reporting[energy_category_id]['values_saving'] = list()
399
                reporting[energy_category_id]['subtotal_baseline'] = Decimal(0.0)
400
                reporting[energy_category_id]['subtotal_actual'] = Decimal(0.0)
401
                reporting[energy_category_id]['subtotal_saving'] = Decimal(0.0)
402
                reporting[energy_category_id]['subtotal_in_kgce_baseline'] = Decimal(0.0)
403
                reporting[energy_category_id]['subtotal_in_kgce_actual'] = Decimal(0.0)
404
                reporting[energy_category_id]['subtotal_in_kgce_saving'] = Decimal(0.0)
405
                reporting[energy_category_id]['subtotal_in_kgco2e_baseline'] = Decimal(0.0)
406
                reporting[energy_category_id]['subtotal_in_kgco2e_actual'] = Decimal(0.0)
407
                reporting[energy_category_id]['subtotal_in_kgco2e_saving'] = Decimal(0.0)
408
                # query reporting period's energy baseline
409
                cursor_energy_baseline.execute(" SELECT start_datetime_utc, actual_value "
410
                                               " FROM tbl_space_input_category_hourly "
411
                                               " WHERE space_id = %s "
412
                                               "     AND energy_category_id = %s "
413
                                               "     AND start_datetime_utc >= %s "
414
                                               "     AND start_datetime_utc < %s "
415
                                               " ORDER BY start_datetime_utc ",
416
                                               (space['id'],
417
                                                energy_category_id,
418
                                                reporting_start_datetime_utc,
419
                                                reporting_end_datetime_utc))
420
                rows_space_hourly = cursor_energy_baseline.fetchall()
421
422
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
423
                                                                                    reporting_start_datetime_utc,
424
                                                                                    reporting_end_datetime_utc,
425
                                                                                    period_type)
426
                for row_space_periodically in rows_space_periodically:
427
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
428
                                             timedelta(minutes=timezone_offset)
429
                    if period_type == 'hourly':
430
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
431
                    elif period_type == 'daily':
432
                        current_datetime = current_datetime_local.strftime('%Y-%m-%d')
433
                    elif period_type == 'monthly':
434
                        current_datetime = current_datetime_local.strftime('%Y-%m')
435
                    elif period_type == 'yearly':
436
                        current_datetime = current_datetime_local.strftime('%Y')
437
438
                    baseline_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
439
                    reporting[energy_category_id]['timestamps'].append(current_datetime)
440
                    reporting[energy_category_id]['values_baseline'].append(baseline_value)
441
                    reporting[energy_category_id]['subtotal_baseline'] += baseline_value
442
                    reporting[energy_category_id]['subtotal_in_kgce_baseline'] += baseline_value * kgce
443
                    reporting[energy_category_id]['subtotal_in_kgco2e_baseline'] += baseline_value * kgco2e
444
445
                # query reporting period's energy actual
446
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
447
                                      " FROM tbl_space_input_category_hourly "
448
                                      " WHERE space_id = %s "
449
                                      "     AND energy_category_id = %s "
450
                                      "     AND start_datetime_utc >= %s "
451
                                      "     AND start_datetime_utc < %s "
452
                                      " ORDER BY start_datetime_utc ",
453
                                      (space['id'],
454
                                       energy_category_id,
455
                                       reporting_start_datetime_utc,
456
                                       reporting_end_datetime_utc))
457
                rows_space_hourly = cursor_energy.fetchall()
458
459
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
460
                                                                                    reporting_start_datetime_utc,
461
                                                                                    reporting_end_datetime_utc,
462
                                                                                    period_type)
463
                for row_space_periodically in rows_space_periodically:
464
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
465
                                             timedelta(minutes=timezone_offset)
466
                    if period_type == 'hourly':
467
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
468
                    elif period_type == 'daily':
469
                        current_datetime = current_datetime_local.strftime('%Y-%m-%d')
470
                    elif period_type == 'monthly':
471
                        current_datetime = current_datetime_local.strftime('%Y-%m')
472
                    elif period_type == 'yearly':
473
                        current_datetime = current_datetime_local.strftime('%Y')
474
475
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
476
                    reporting[energy_category_id]['values_actual'].append(actual_value)
477
                    reporting[energy_category_id]['subtotal_actual'] += actual_value
478
                    reporting[energy_category_id]['subtotal_in_kgce_actual'] += actual_value * kgce
479
                    reporting[energy_category_id]['subtotal_in_kgco2e_actual'] += actual_value * kgco2e
480
481
                # calculate reporting period's energy savings
482
                for i in range(len(reporting[energy_category_id]['values_baseline'])):
483
                    reporting[energy_category_id]['values_saving'].append(
484
                        reporting[energy_category_id]['values_baseline'][i] -
485
                        reporting[energy_category_id]['values_actual'][i])
486
487
                reporting[energy_category_id]['subtotal_saving'] = \
488
                    reporting[energy_category_id]['subtotal_baseline'] - \
489
                    reporting[energy_category_id]['subtotal_actual']
490
                reporting[energy_category_id]['subtotal_in_kgce_saving'] = \
491
                    reporting[energy_category_id]['subtotal_in_kgce_baseline'] - \
492
                    reporting[energy_category_id]['subtotal_in_kgce_actual']
493
                reporting[energy_category_id]['subtotal_in_kgco2e_saving'] = \
494
                    reporting[energy_category_id]['subtotal_in_kgco2e_baseline'] - \
495
                    reporting[energy_category_id]['subtotal_in_kgco2e_actual']
496
        ################################################################################################################
497
        # Step 9: query tariff data
498
        ################################################################################################################
499
        parameters_data = dict()
500
        parameters_data['names'] = list()
501
        parameters_data['timestamps'] = list()
502
        parameters_data['values'] = list()
503
        if energy_category_set is not None and len(energy_category_set) > 0:
504
            for energy_category_id in energy_category_set:
505
                energy_category_tariff_dict = utilities.get_energy_category_tariffs(space['cost_center_id'],
506
                                                                                    energy_category_id,
507
                                                                                    reporting_start_datetime_utc,
508
                                                                                    reporting_end_datetime_utc)
509
                tariff_timestamp_list = list()
510
                tariff_value_list = list()
511
                for k, v in energy_category_tariff_dict.items():
512
                    # convert k from utc to local
513
                    k = k + timedelta(minutes=timezone_offset)
514
                    tariff_timestamp_list.append(k.isoformat()[0:19][0:19])
515
                    tariff_value_list.append(v)
516
517
                parameters_data['names'].append('TARIFF-' + energy_category_dict[energy_category_id]['name'])
518
                parameters_data['timestamps'].append(tariff_timestamp_list)
519
                parameters_data['values'].append(tariff_value_list)
520
521
        ################################################################################################################
522
        # Step 10: query associated sensors and points data
523
        ################################################################################################################
524
        for point in point_list:
525
            point_values = []
526
            point_timestamps = []
527
            if point['object_type'] == 'ANALOG_VALUE':
528
                query = (" SELECT utc_date_time, actual_value "
529
                         " FROM tbl_analog_value "
530
                         " WHERE point_id = %s "
531
                         "       AND utc_date_time BETWEEN %s AND %s "
532
                         " ORDER BY utc_date_time ")
533
                cursor_historical.execute(query, (point['id'],
534
                                                  reporting_start_datetime_utc,
535
                                                  reporting_end_datetime_utc))
536
                rows = cursor_historical.fetchall()
537
538
                if rows is not None and len(rows) > 0:
539
                    for row in rows:
540
                        current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
541
                                                 timedelta(minutes=timezone_offset)
542
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
543
                        point_timestamps.append(current_datetime)
544
                        point_values.append(row[1])
545
546
            elif point['object_type'] == 'ENERGY_VALUE':
547
                query = (" SELECT utc_date_time, actual_value "
548
                         " FROM tbl_energy_value "
549
                         " WHERE point_id = %s "
550
                         "       AND utc_date_time BETWEEN %s AND %s "
551
                         " ORDER BY utc_date_time ")
552
                cursor_historical.execute(query, (point['id'],
553
                                                  reporting_start_datetime_utc,
554
                                                  reporting_end_datetime_utc))
555
                rows = cursor_historical.fetchall()
556
557
                if rows is not None and len(rows) > 0:
558
                    for row in rows:
559
                        current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
560
                                                 timedelta(minutes=timezone_offset)
561
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
562
                        point_timestamps.append(current_datetime)
563
                        point_values.append(row[1])
564
            elif point['object_type'] == 'DIGITAL_VALUE':
565
                query = (" SELECT utc_date_time, actual_value "
566
                         " FROM tbl_digital_value "
567
                         " WHERE point_id = %s "
568
                         "       AND utc_date_time BETWEEN %s AND %s ")
569
                cursor_historical.execute(query, (point['id'],
570
                                                  reporting_start_datetime_utc,
571
                                                  reporting_end_datetime_utc))
572
                rows = cursor_historical.fetchall()
573
574
                if rows is not None and len(rows) > 0:
575
                    for row in rows:
576
                        current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
577
                                                 timedelta(minutes=timezone_offset)
578
                        current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
579
                        point_timestamps.append(current_datetime)
580
                        point_values.append(row[1])
581
582
            parameters_data['names'].append(point['name'] + ' (' + point['units'] + ')')
583
            parameters_data['timestamps'].append(point_timestamps)
584
            parameters_data['values'].append(point_values)
585
586
        ################################################################################################################
587
        # Step 11: query child spaces energy saving
588
        ################################################################################################################
589
        child_space_data = dict()
590
591
        if energy_category_set is not None and len(energy_category_set) > 0:
592
            for energy_category_id in energy_category_set:
593
                child_space_data[energy_category_id] = dict()
594
                child_space_data[energy_category_id]['child_space_names'] = list()
595
                child_space_data[energy_category_id]['subtotals_saving'] = list()
596
                child_space_data[energy_category_id]['subtotals_in_kgce_saving'] = list()
597
                child_space_data[energy_category_id]['subtotals_in_kgco2e_saving'] = list()
598
                kgce = energy_category_dict[energy_category_id]['kgce']
599
                kgco2e = energy_category_dict[energy_category_id]['kgco2e']
600
                for child_space in child_space_list:
601
                    child_space_data[energy_category_id]['child_space_names'].append(child_space['name'])
602
                    # query child space's energy baseline
603
                    cursor_energy_baseline.execute(" SELECT SUM(actual_value) "
604
                                                   " FROM tbl_space_input_category_hourly "
605
                                                   " WHERE space_id = %s "
606
                                                   "     AND energy_category_id = %s "
607
                                                   "     AND start_datetime_utc >= %s "
608
                                                   "     AND start_datetime_utc < %s "
609
                                                   " ORDER BY start_datetime_utc ",
610
                                                   (child_space['id'],
611
                                                    energy_category_id,
612
                                                    reporting_start_datetime_utc,
613
                                                    reporting_end_datetime_utc))
614
                    row_subtotal = cursor_energy_baseline.fetchone()
615
616
                    subtotal = Decimal(0.0) if (row_subtotal is None or row_subtotal[0] is None) else row_subtotal[0]
617
                    subtotal_baseline = subtotal
618
                    subtotal_in_kgce_baseline = subtotal * kgce
619
                    subtotal_in_kgco2e_baseline = subtotal * kgco2e
620
                    # query child space's energy actual
621
                    cursor_energy.execute(" SELECT SUM(actual_value) "
622
                                          " FROM tbl_space_input_category_hourly "
623
                                          " WHERE space_id = %s "
624
                                          "     AND energy_category_id = %s "
625
                                          "     AND start_datetime_utc >= %s "
626
                                          "     AND start_datetime_utc < %s "
627
                                          " ORDER BY start_datetime_utc ",
628
                                          (child_space['id'],
629
                                           energy_category_id,
630
                                           reporting_start_datetime_utc,
631
                                           reporting_end_datetime_utc))
632
                    row_subtotal = cursor_energy.fetchone()
633
634
                    subtotal = Decimal(0.0) if (row_subtotal is None or row_subtotal[0] is None) else row_subtotal[0]
635
                    subtotal_actual = subtotal
636
                    subtotal_in_kgce_actual = subtotal * kgce
637
                    subtotal_in_kgco2e_actual = subtotal * kgco2e
638
639
                    # calculate child space's energy saving
640
                    child_space_data[energy_category_id]['subtotals_saving'].append(
641
                        subtotal_baseline - subtotal_actual)
642
                    child_space_data[energy_category_id]['subtotals_in_kgce_saving'].append(
643
                        subtotal_in_kgce_baseline - subtotal_in_kgce_actual)
644
                    child_space_data[energy_category_id]['subtotals_in_kgco2e_saving'].append(
645
                        subtotal_in_kgco2e_baseline - subtotal_in_kgco2e_actual)
646
        ################################################################################################################
647
        # Step 12: construct the report
648
        ################################################################################################################
649
        if cursor_system:
650
            cursor_system.close()
651
        if cnx_system:
652
            cnx_system.disconnect()
653
654
        if cursor_energy:
655
            cursor_energy.close()
656
        if cnx_energy:
657
            cnx_energy.disconnect()
658
659
        if cursor_energy_baseline:
660
            cursor_energy_baseline.close()
661
        if cnx_energy_baseline:
662
            cnx_energy_baseline.disconnect()
663
664
        result = dict()
665
666
        result['space'] = dict()
667
        result['space']['name'] = space['name']
668
        result['space']['area'] = space['area']
669
670
        result['base_period'] = dict()
671
        result['base_period']['names'] = list()
672
        result['base_period']['units'] = list()
673
        result['base_period']['timestamps'] = list()
674
        result['base_period']['values_saving'] = list()
675
        result['base_period']['subtotals_saving'] = list()
676
        result['base_period']['subtotals_in_kgce_saving'] = list()
677
        result['base_period']['subtotals_in_kgco2e_saving'] = list()
678
        result['base_period']['total_in_kgce_saving'] = Decimal(0.0)
679
        result['base_period']['total_in_kgco2e_saving'] = Decimal(0.0)
680
        if energy_category_set is not None and len(energy_category_set) > 0:
681
            for energy_category_id in energy_category_set:
682
                result['base_period']['names'].append(energy_category_dict[energy_category_id]['name'])
683
                result['base_period']['units'].append(energy_category_dict[energy_category_id]['unit_of_measure'])
684
                result['base_period']['timestamps'].append(base[energy_category_id]['timestamps'])
685
                result['base_period']['values_saving'].append(base[energy_category_id]['values_saving'])
686
                result['base_period']['subtotals_saving'].append(base[energy_category_id]['subtotal_saving'])
687
                result['base_period']['subtotals_in_kgce_saving'].append(
688
                    base[energy_category_id]['subtotal_in_kgce_saving'])
689
                result['base_period']['subtotals_in_kgco2e_saving'].append(
690
                    base[energy_category_id]['subtotal_in_kgco2e_saving'])
691
                result['base_period']['total_in_kgce_saving'] += base[energy_category_id]['subtotal_in_kgce_saving']
692
                result['base_period']['total_in_kgco2e_saving'] += base[energy_category_id]['subtotal_in_kgco2e_saving']
693
694
        result['reporting_period'] = dict()
695
        result['reporting_period']['names'] = list()
696
        result['reporting_period']['energy_category_ids'] = list()
697
        result['reporting_period']['units'] = list()
698
        result['reporting_period']['timestamps'] = list()
699
        result['reporting_period']['values_saving'] = list()
700
        result['reporting_period']['subtotals_saving'] = list()
701
        result['reporting_period']['subtotals_in_kgce_saving'] = list()
702
        result['reporting_period']['subtotals_in_kgco2e_saving'] = list()
703
        result['reporting_period']['subtotals_per_unit_area_saving'] = list()
704
        result['reporting_period']['increment_rates_saving'] = list()
705
        result['reporting_period']['total_in_kgce_saving'] = Decimal(0.0)
706
        result['reporting_period']['total_in_kgco2e_saving'] = Decimal(0.0)
707
        result['reporting_period']['increment_rate_in_kgce_saving'] = Decimal(0.0)
708
        result['reporting_period']['increment_rate_in_kgco2e_saving'] = Decimal(0.0)
709
710
        if energy_category_set is not None and len(energy_category_set) > 0:
711
            for energy_category_id in energy_category_set:
712
                result['reporting_period']['names'].append(energy_category_dict[energy_category_id]['name'])
713
                result['reporting_period']['energy_category_ids'].append(energy_category_id)
714
                result['reporting_period']['units'].append(energy_category_dict[energy_category_id]['unit_of_measure'])
715
                result['reporting_period']['timestamps'].append(reporting[energy_category_id]['timestamps'])
716
                result['reporting_period']['values_saving'].append(reporting[energy_category_id]['values_saving'])
717
                result['reporting_period']['subtotals_saving'].append(reporting[energy_category_id]['subtotal_saving'])
718
                result['reporting_period']['subtotals_in_kgce_saving'].append(
719
                    reporting[energy_category_id]['subtotal_in_kgce_saving'])
720
                result['reporting_period']['subtotals_in_kgco2e_saving'].append(
721
                    reporting[energy_category_id]['subtotal_in_kgco2e_saving'])
722
                result['reporting_period']['subtotals_per_unit_area_saving'].append(
723
                    reporting[energy_category_id]['subtotal_saving'] / space['area'] if space['area'] > 0.0 else None)
724
                result['reporting_period']['increment_rates_saving'].append(
725
                    (reporting[energy_category_id]['subtotal_saving'] - base[energy_category_id]['subtotal_saving']) /
726
                    base[energy_category_id]['subtotal_saving']
727
                    if base[energy_category_id]['subtotal_saving'] > 0.0 else None)
728
                result['reporting_period']['total_in_kgce_saving'] += \
729
                    reporting[energy_category_id]['subtotal_in_kgce_saving']
730
                result['reporting_period']['total_in_kgco2e_saving'] += \
731
                    reporting[energy_category_id]['subtotal_in_kgco2e_saving']
732
733
        result['reporting_period']['total_in_kgco2e_per_unit_area_saving'] = \
734
            result['reporting_period']['total_in_kgce_saving'] / space['area'] if space['area'] > 0.0 else None
735
736
        result['reporting_period']['increment_rate_in_kgce_saving'] = \
737
            (result['reporting_period']['total_in_kgce_saving'] - result['base_period']['total_in_kgce_saving']) / \
738
            result['base_period']['total_in_kgce_saving'] \
739
            if result['base_period']['total_in_kgce_saving'] > Decimal(0.0) else None
740
741
        result['reporting_period']['total_in_kgce_per_unit_area_saving'] = \
742
            result['reporting_period']['total_in_kgco2e_saving'] / space['area'] if space['area'] > 0.0 else None
743
744
        result['reporting_period']['increment_rate_in_kgco2e_saving'] = \
745
            (result['reporting_period']['total_in_kgco2e_saving'] - result['base_period']['total_in_kgco2e_saving']) / \
746
            result['base_period']['total_in_kgco2e_saving'] \
747
            if result['base_period']['total_in_kgco2e_saving'] > Decimal(0.0) else None
748
749
        result['parameters'] = {
750
            "names": parameters_data['names'],
751
            "timestamps": parameters_data['timestamps'],
752
            "values": parameters_data['values']
753
        }
754
755
        result['child_space'] = dict()
756
        result['child_space']['energy_category_names'] = list()  # 1D array [energy category]
757
        result['child_space']['units'] = list()  # 1D array [energy category]
758
        result['child_space']['child_space_names_array'] = list()  # 2D array [energy category][child space]
759
        result['child_space']['subtotals_saving_array'] = list()  # 2D array [energy category][child space]
760
        result['child_space']['subtotals_in_kgce_saving_array'] = list()  # 2D array [energy category][child space]
761
        result['child_space']['subtotals_in_kgco2e_saving_array'] = list()  # 2D array [energy category][child space]
762 View Code Duplication
        if energy_category_set is not None and len(energy_category_set) > 0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
763
            for energy_category_id in energy_category_set:
764
                result['child_space']['energy_category_names'].append(energy_category_dict[energy_category_id]['name'])
765
                result['child_space']['units'].append(energy_category_dict[energy_category_id]['unit_of_measure'])
766
                result['child_space']['child_space_names_array'].append(
767
                    child_space_data[energy_category_id]['child_space_names'])
768
                result['child_space']['subtotals_saving_array'].append(
769
                    child_space_data[energy_category_id]['subtotals_saving'])
770
                result['child_space']['subtotals_in_kgce_saving_array'].append(
771
                    child_space_data[energy_category_id]['subtotals_in_kgce_saving'])
772
                result['child_space']['subtotals_in_kgco2e_saving_array'].append(
773
                    child_space_data[energy_category_id]['subtotals_in_kgco2e_saving'])
774
775
        # export result to Excel file and then encode the file to base64 string
776
        result['excel_bytes_base64'] = excelexporters.spacesaving.export(result,
777
                                                                         space['name'],
778
                                                                         reporting_start_datetime_local,
779
                                                                         reporting_end_datetime_local,
780
                                                                         period_type)
781
782
        resp.body = json.dumps(result)
783