Issues (1577)

myems-api/reports/tenantdashboard.py (2 issues)

1
from datetime import datetime, timedelta, timezone
2
from decimal import Decimal
3
import falcon
4
import mysql.connector
5
import simplejson as json
6
import config
7
from core import utilities
8
from core.useractivity import access_control, api_key_control
9
10
11 View Code Duplication
class Reporting:
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
12
    def __init__(self):
13
        """Initializes Class"""
14
        pass
15
16
    @staticmethod
17
    def on_options(req, resp):
18
        _ = req
19
        resp.status = falcon.HTTP_200
20
21
    ####################################################################################################################
22
    # PROCEDURES
23
    # Step 1: valid parameters
24
    # Step 2: query the space
25
    # Step 3: query energy categories
26
    # Step 4: query sensor data
27
    # Step 5: query child spaces
28
    # Step 6: query base period energy input
29
    # Step 7: query base period energy cost
30
    # Step 8: query reporting period energy input
31
    # Step 9: query reporting period energy cost
32
    # Step 10: query child spaces energy input
33
    # Step 10: query child spaces energy cost
34
    # Step 12: construct the report
35
    ####################################################################################################################
36
    @staticmethod
37
    def on_get(req, resp):
38
        # todo: change this procedure from space to tenant
39
        if 'API-KEY' not in req.headers or \
40
                not isinstance(req.headers['API-KEY'], str) or \
41
                len(str.strip(req.headers['API-KEY'])) == 0:
42
            access_control(req)
43
        else:
44
            api_key_control(req)
45
        user_uuid = req.params.get('useruuid')
46
        period_type = req.params.get('periodtype')
47
        base_period_start_datetime_local = req.params.get('baseperiodstartdatetime')
48
        base_period_end_datetime_local = req.params.get('baseperiodenddatetime')
49
        reporting_period_start_datetime_local = req.params.get('reportingperiodstartdatetime')
50
        reporting_period_end_datetime_local = req.params.get('reportingperiodenddatetime')
51
52
        ################################################################################################################
53
        # Step 1: valid parameters
54
        ################################################################################################################
55
        if user_uuid is None:
56
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_USER_UUID')
57
        else:
58
            user_uuid = str.strip(user_uuid)
59
            if len(user_uuid) != 36:
60
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
61
                                       description='API.INVALID_USER_UUID')
62
63
        if period_type is None:
64
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
65
                                   description='API.INVALID_PERIOD_TYPE')
66
        else:
67
            period_type = str.strip(period_type)
68
            if period_type not in ['hourly', 'daily', 'weekly', 'monthly', 'yearly']:
69
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
70
                                       description='API.INVALID_PERIOD_TYPE')
71
72
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
73
        if config.utc_offset[0] == '-':
74
            timezone_offset = -timezone_offset
75
76
        base_start_datetime_utc = None
77
        if base_period_start_datetime_local is not None and len(str.strip(base_period_start_datetime_local)) > 0:
78
            base_period_start_datetime_local = str.strip(base_period_start_datetime_local)
79
            try:
80
                base_start_datetime_utc = datetime.strptime(base_period_start_datetime_local, '%Y-%m-%dT%H:%M:%S')
81
            except ValueError:
82
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
83
                                       description="API.INVALID_BASE_PERIOD_START_DATETIME")
84
            base_start_datetime_utc = \
85
                base_start_datetime_utc.replace(tzinfo=timezone.utc) - timedelta(minutes=timezone_offset)
86
            # nomalize the start datetime
87
            if config.minutes_to_count == 30 and base_start_datetime_utc.minute >= 30:
88
                base_start_datetime_utc = base_start_datetime_utc.replace(minute=30, second=0, microsecond=0)
89
            else:
90
                base_start_datetime_utc = base_start_datetime_utc.replace(minute=0, second=0, microsecond=0)
91
92
        base_end_datetime_utc = None
93
        if base_period_end_datetime_local is not None and len(str.strip(base_period_end_datetime_local)) > 0:
94
            base_period_end_datetime_local = str.strip(base_period_end_datetime_local)
95
            try:
96
                base_end_datetime_utc = datetime.strptime(base_period_end_datetime_local, '%Y-%m-%dT%H:%M:%S')
97
            except ValueError:
98
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
99
                                       description="API.INVALID_BASE_PERIOD_END_DATETIME")
100
            base_end_datetime_utc = \
101
                base_end_datetime_utc.replace(tzinfo=timezone.utc) - timedelta(minutes=timezone_offset)
102
103
        if base_start_datetime_utc is not None and base_end_datetime_utc is not None and \
104
                base_start_datetime_utc >= base_end_datetime_utc:
105
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
106
                                   description='API.INVALID_BASE_PERIOD_END_DATETIME')
107
108
        if reporting_period_start_datetime_local is None:
109
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
110
                                   description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
111
        else:
112
            reporting_period_start_datetime_local = str.strip(reporting_period_start_datetime_local)
113
            try:
114
                reporting_start_datetime_utc = datetime.strptime(reporting_period_start_datetime_local,
115
                                                                 '%Y-%m-%dT%H:%M:%S')
116
            except ValueError:
117
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
118
                                       description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
119
            reporting_start_datetime_utc = \
120
                reporting_start_datetime_utc.replace(tzinfo=timezone.utc) - timedelta(minutes=timezone_offset)
121
            # nomalize the start datetime
122
            if config.minutes_to_count == 30 and reporting_start_datetime_utc.minute >= 30:
123
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=30, second=0, microsecond=0)
124
            else:
125
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=0, second=0, microsecond=0)
126
127
        if reporting_period_end_datetime_local is None:
128
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
129
                                   description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
130
        else:
131
            reporting_period_end_datetime_local = str.strip(reporting_period_end_datetime_local)
132
            try:
133
                reporting_end_datetime_utc = datetime.strptime(reporting_period_end_datetime_local,
134
                                                               '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
135
                    timedelta(minutes=timezone_offset)
136
            except ValueError:
137
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
138
                                       description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
139
140
        if reporting_start_datetime_utc >= reporting_end_datetime_utc:
141
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
142
                                   description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
143
144
        ################################################################################################################
145
        # Step 2: query the space
146
        ################################################################################################################
147
148
        cnx_user = mysql.connector.connect(**config.myems_user_db)
149
        cursor_user = cnx_user.cursor()
150
151
        cursor_user.execute(" SELECT id, is_admin, privilege_id "
152
                            " FROM tbl_users "
153
                            " WHERE uuid = %s ", (user_uuid,))
154
        row_user = cursor_user.fetchone()
155
        if row_user is None:
156
            if cursor_user:
157
                cursor_user.close()
158
            if cnx_user:
159
                cnx_user.close()
160
161
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
162
                                   description='API.USER_NOT_FOUND')
163
164
        user = {'id': row_user[0], 'is_admin': row_user[1], 'privilege_id': row_user[2]}
165
        if user['is_admin']:
166
            # todo: make sure the space id is always 1 for admin
167
            space_id = 1
168
        else:
169
            cursor_user.execute(" SELECT data "
170
                                " FROM tbl_privileges "
171
                                " WHERE id = %s ", (user['privilege_id'],))
172
            row_privilege = cursor_user.fetchone()
173
            if row_privilege is None:
174
                if cursor_user:
175
                    cursor_user.close()
176
                if cnx_user:
177
                    cnx_user.close()
178
179
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
180
                                       description='API.USER_PRIVILEGE_NOT_FOUND')
181
182
            privilege_data = json.loads(row_privilege[0])
183
            if 'spaces' not in privilege_data.keys() \
184
                    or privilege_data['spaces'] is None \
185
                    or len(privilege_data['spaces']) == 0:
186
                if cursor_user:
187
                    cursor_user.close()
188
                if cnx_user:
189
                    cnx_user.close()
190
191
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
192
                                       description='API.USER_PRIVILEGE_NOT_FOUND')
193
            # todo: how to deal with multiple spaces in privilege data
194
            space_id = privilege_data['spaces'][0]
195
196
        if cursor_user:
197
            cursor_user.close()
198
        if cnx_user:
199
            cnx_user.close()
200
201
        cnx_system = mysql.connector.connect(**config.myems_system_db)
202
        cursor_system = cnx_system.cursor()
203
204
        cursor_system.execute(" SELECT id, name, area, cost_center_id "
205
                              " FROM tbl_spaces "
206
                              " WHERE id = %s ", (space_id,))
207
        row_space = cursor_system.fetchone()
208
        if row_space is None:
209
            if cursor_system:
210
                cursor_system.close()
211
            if cnx_system:
212
                cnx_system.close()
213
214
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND', description='API.SPACE_NOT_FOUND')
215
216
        space = dict()
217
        space['id'] = row_space[0]
218
        space['name'] = row_space[1]
219
        space['area'] = row_space[2]
220
        space['cost_center_id'] = row_space[3]
221
222
        ################################################################################################################
223
        # Step 3: query energy categories
224
        ################################################################################################################
225
        cnx_energy = mysql.connector.connect(**config.myems_energy_db)
226
        cursor_energy = cnx_energy.cursor()
227
228
        cnx_billing = mysql.connector.connect(**config.myems_billing_db)
229
        cursor_billing = cnx_billing.cursor()
230
231
        energy_category_set = set()
232
        # query energy categories in base period
233
        cursor_energy.execute(" SELECT DISTINCT(energy_category_id) "
234
                              " FROM tbl_space_input_category_hourly "
235
                              " WHERE space_id = %s "
236
                              "     AND start_datetime_utc >= %s "
237
                              "     AND start_datetime_utc < %s ",
238
                              (space['id'], base_start_datetime_utc, base_end_datetime_utc))
239
        rows_energy_categories = cursor_energy.fetchall()
240
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
241
            for row_energy_category in rows_energy_categories:
242
                energy_category_set.add(row_energy_category[0])
243
244
        # query energy categories in reporting period
245
        cursor_energy.execute(" SELECT DISTINCT(energy_category_id) "
246
                              " FROM tbl_space_input_category_hourly "
247
                              " WHERE space_id = %s "
248
                              "     AND start_datetime_utc >= %s "
249
                              "     AND start_datetime_utc < %s ",
250
                              (space['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
251
        rows_energy_categories = cursor_energy.fetchall()
252
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
253
            for row_energy_category in rows_energy_categories:
254
                energy_category_set.add(row_energy_category[0])
255
256
        # query all energy categories in base period and reporting period
257
        cursor_system.execute(" SELECT id, name, unit_of_measure, kgce, kgco2e "
258
                              " FROM tbl_energy_categories "
259
                              " ORDER BY id ", )
260
        rows_energy_categories = cursor_system.fetchall()
261
        if rows_energy_categories is None or len(rows_energy_categories) == 0:
262
            if cursor_system:
263
                cursor_system.close()
264
            if cnx_system:
265
                cnx_system.close()
266
267
            if cursor_energy:
268
                cursor_energy.close()
269
            if cnx_energy:
270
                cnx_energy.close()
271
272
            if cursor_billing:
273
                cursor_billing.close()
274
            if cnx_billing:
275
                cnx_billing.close()
276
277
            raise falcon.HTTPError(status=falcon.HTTP_404,
278
                                   title='API.NOT_FOUND',
279
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
280
        energy_category_dict = dict()
281
        for row_energy_category in rows_energy_categories:
282
            if row_energy_category[0] in energy_category_set:
283
                energy_category_dict[row_energy_category[0]] = {"name": row_energy_category[1],
284
                                                                "unit_of_measure": row_energy_category[2],
285
                                                                "kgce": row_energy_category[3],
286
                                                                "kgco2e": row_energy_category[4]}
287
                
288
        ################################################################################################################
289
        # Step 4: query sensor data
290
        ################################################################################################################
291
        cnx_system = mysql.connector.connect(**config.myems_system_db)
292
        cursor_system = cnx_system.cursor()
293
294
        cnx_historical = mysql.connector.connect(**config.myems_historical_db)
295
        cursor_historical = cnx_historical.cursor()
296
297
        sensor_id_list = list()
298
        sensor_dict = dict()
299
        cursor_system.execute(" SELECT s.id, s.name, s.uuid, s.description "
300
                              " FROM tbl_sensors s, tbl_spaces_sensors ss "
301
                              " WHERE ss.space_id = %s "
302
                              " AND s.id = ss.sensor_id "
303
                              , (space['id'],))
304
        rows_sensors = cursor_system.fetchall()
305
        if rows_sensors is not None and len(rows_sensors) > 0:
306
            for row in rows_sensors:
307
                sensor_id_list.append(row[0])
308
                sensor_dict[row[0]] = dict()
309
                sensor_dict[row[0]]['name'] = row[1]
310
                sensor_dict[row[0]]['description'] = row[2]
311
                sensor_dict[row[0]]['uuid'] = row[3]
312
                sensor_dict[row[0]]['point_name_list'] = list()
313
                sensor_dict[row[0]]['point_unit_list'] = list()
314
                sensor_dict[row[0]]['point_id_list'] = list()
315
        if sensor_id_list is not None and len(sensor_id_list) > 0:
316
            cursor_system.execute(" SELECT sp.sensor_id, p.id, p.name, p.units "
317
                                  " FROM tbl_sensors_points sp, tbl_points p "
318
                                  " WHERE sp.sensor_id in ({}) "
319
                                  " AND sp.point_id = p.id "
320
                                  " ORDER BY p.id ".format(','.join("{0}".format(x) for x in sensor_id_list)))
321
            rows_sensor_points = cursor_system.fetchall()
322
            if rows_sensor_points is not None and len(rows_sensor_points) > 0:
323
                for row in rows_sensor_points:
324
                    sensor_dict[row[0]]['point_id_list'].append(row[1])
325
                    sensor_dict[row[0]]['point_name_list'].append(row[2])
326
                    sensor_dict[row[0]]['point_unit_list'].append(row[3])
327
328
        point_data_dict = dict()
329
        for key in sensor_dict:
330
            if sensor_dict[key]['point_id_list'] is not None and len(sensor_dict[key]['point_id_list']) > 0:
331
                cursor_historical.execute(" SELECT point_id, actual_value "
332
                                          " FROM tbl_analog_value_latest "
333
                                          " WHERE point_id in ({}) "
334
                                          " ORDER BY point_id ".
335
                                          format(','.join("{0}".format(x) for x in sensor_dict[key]['point_id_list'])))
336
                rows_analog_values = cursor_historical.fetchall()
337
                if rows_analog_values is not None and len(rows_analog_values) > 0:
338
                    for row in rows_analog_values:
339
                        point_data_dict[row[0]] = row[1]
340
                
341
                cursor_historical.execute(" SELECT point_id, actual_value "
342
                                          " FROM tbl_digital_value_latest "
343
                                          " WHERE point_id in ({}) "
344
                                          " ORDER BY point_id ".
345
                                          format(','.join("{0}".format(x) for x in sensor_dict[key]['point_id_list'])))
346
                rows_digital_values = cursor_historical.fetchall()
347
                if rows_digital_values is not None and len(rows_digital_values) > 0:
348
                    for row in rows_digital_values:
349
                        point_data_dict[row[0]] = row[1]
350
351
        ################################################################################################################
352
        # Step 5: query child spaces
353
        ################################################################################################################
354
        child_space_list = list()
355
        cursor_system.execute(" SELECT id, name  "
356
                              " FROM tbl_spaces "
357
                              " WHERE parent_space_id = %s "
358
                              " ORDER BY id ", (space['id'], ))
359
        rows_child_spaces = cursor_system.fetchall()
360
        if rows_child_spaces is not None and len(rows_child_spaces) > 0:
361
            for row in rows_child_spaces:
362
                child_space_list.append({"id": row[0], "name": row[1]})
363
364
        ################################################################################################################
365
        # Step 6: query base period energy input
366
        ################################################################################################################
367
        base_input = dict()
368
        if energy_category_set is not None and len(energy_category_set) > 0:
369
            for energy_category_id in energy_category_set:
370
                kgce = energy_category_dict[energy_category_id]['kgce']
371
                kgco2e = energy_category_dict[energy_category_id]['kgco2e']
372
373
                base_input[energy_category_id] = dict()
374
                base_input[energy_category_id]['timestamps'] = list()
375
                base_input[energy_category_id]['values'] = list()
376
                base_input[energy_category_id]['subtotal'] = Decimal(0.0)
377
                base_input[energy_category_id]['subtotal_in_kgce'] = Decimal(0.0)
378
                base_input[energy_category_id]['subtotal_in_kgco2e'] = Decimal(0.0)
379
380
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
381
                                      " FROM tbl_space_input_category_hourly "
382
                                      " WHERE space_id = %s "
383
                                      "     AND energy_category_id = %s "
384
                                      "     AND start_datetime_utc >= %s "
385
                                      "     AND start_datetime_utc < %s "
386
                                      " ORDER BY start_datetime_utc ",
387
                                      (space['id'],
388
                                       energy_category_id,
389
                                       base_start_datetime_utc,
390
                                       base_end_datetime_utc))
391
                rows_space_hourly = cursor_energy.fetchall()
392
393
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
394
                                                                                    base_start_datetime_utc,
395
                                                                                    base_end_datetime_utc,
396
                                                                                    period_type)
397
                for row_space_periodically in rows_space_periodically:
398
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
399
                                             timedelta(minutes=timezone_offset)
400
                    if period_type == 'hourly':
401
                        current_datetime = current_datetime_local.isoformat()[0:19]
402
                    elif period_type == 'daily':
403
                        current_datetime = current_datetime_local.isoformat()[0:10]
404
                    elif period_type == 'weekly':
405
                        current_datetime = current_datetime_local.isoformat()[0:10]
406
                    elif period_type == 'monthly':
407
                        current_datetime = current_datetime_local.isoformat()[0:7]
408
                    elif period_type == 'yearly':
409
                        current_datetime = current_datetime_local.isoformat()[0:4]
410
411
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
412
                    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...
413
                    base_input[energy_category_id]['values'].append(actual_value)
414
                    base_input[energy_category_id]['subtotal'] += actual_value
415
                    base_input[energy_category_id]['subtotal_in_kgce'] += actual_value * kgce
416
                    base_input[energy_category_id]['subtotal_in_kgco2e'] += actual_value * kgco2e
417
418
        ################################################################################################################
419
        # Step 7: query base period energy cost
420
        ################################################################################################################
421
        base_cost = dict()
422
        if energy_category_set is not None and len(energy_category_set) > 0:
423
            for energy_category_id in energy_category_set:
424
                base_cost[energy_category_id] = dict()
425
                base_cost[energy_category_id]['timestamps'] = list()
426
                base_cost[energy_category_id]['values'] = list()
427
                base_cost[energy_category_id]['subtotal'] = Decimal(0.0)
428
429
                cursor_billing.execute(" SELECT start_datetime_utc, actual_value "
430
                                       " FROM tbl_space_input_category_hourly "
431
                                       " WHERE space_id = %s "
432
                                       "     AND energy_category_id = %s "
433
                                       "     AND start_datetime_utc >= %s "
434
                                       "     AND start_datetime_utc < %s "
435
                                       " ORDER BY start_datetime_utc ",
436
                                       (space['id'],
437
                                        energy_category_id,
438
                                        base_start_datetime_utc,
439
                                        base_end_datetime_utc))
440
                rows_space_hourly = cursor_billing.fetchall()
441
442
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
443
                                                                                    base_start_datetime_utc,
444
                                                                                    base_end_datetime_utc,
445
                                                                                    period_type)
446
                for row_space_periodically in rows_space_periodically:
447
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
448
                                             timedelta(minutes=timezone_offset)
449
                    if period_type == 'hourly':
450
                        current_datetime = current_datetime_local.isoformat()[0:19]
451
                    elif period_type == 'daily':
452
                        current_datetime = current_datetime_local.isoformat()[0:10]
453
                    elif period_type == 'weekly':
454
                        current_datetime = current_datetime_local.isoformat()[0:10]
455
                    elif period_type == 'monthly':
456
                        current_datetime = current_datetime_local.isoformat()[0:7]
457
                    elif period_type == 'yearly':
458
                        current_datetime = current_datetime_local.isoformat()[0:4]
459
460
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
461
                    base_cost[energy_category_id]['timestamps'].append(current_datetime)
462
                    base_cost[energy_category_id]['values'].append(actual_value)
463
                    base_cost[energy_category_id]['subtotal'] += actual_value
464
465
        ################################################################################################################
466
        # Step 8: query reporting period energy input
467
        ################################################################################################################
468
        reporting_input = dict()
469
        if energy_category_set is not None and len(energy_category_set) > 0:
470
            for energy_category_id in energy_category_set:
471
                kgce = energy_category_dict[energy_category_id]['kgce']
472
                kgco2e = energy_category_dict[energy_category_id]['kgco2e']
473
474
                reporting_input[energy_category_id] = dict()
475
                reporting_input[energy_category_id]['timestamps'] = list()
476
                reporting_input[energy_category_id]['values'] = list()
477
                reporting_input[energy_category_id]['subtotal'] = Decimal(0.0)
478
                reporting_input[energy_category_id]['subtotal_in_kgce'] = Decimal(0.0)
479
                reporting_input[energy_category_id]['subtotal_in_kgco2e'] = Decimal(0.0)
480
                reporting_input[energy_category_id]['toppeak'] = Decimal(0.0)
481
                reporting_input[energy_category_id]['onpeak'] = Decimal(0.0)
482
                reporting_input[energy_category_id]['midpeak'] = Decimal(0.0)
483
                reporting_input[energy_category_id]['offpeak'] = Decimal(0.0)
484
                reporting_input[energy_category_id]['deep'] = Decimal(0.0)
485
486
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
487
                                      " FROM tbl_space_input_category_hourly "
488
                                      " WHERE space_id = %s "
489
                                      "     AND energy_category_id = %s "
490
                                      "     AND start_datetime_utc >= %s "
491
                                      "     AND start_datetime_utc < %s "
492
                                      " ORDER BY start_datetime_utc ",
493
                                      (space['id'],
494
                                       energy_category_id,
495
                                       reporting_start_datetime_utc,
496
                                       reporting_end_datetime_utc))
497
                rows_space_hourly = cursor_energy.fetchall()
498
499
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
500
                                                                                    reporting_start_datetime_utc,
501
                                                                                    reporting_end_datetime_utc,
502
                                                                                    period_type)
503
                for row_space_periodically in rows_space_periodically:
504
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
505
                                             timedelta(minutes=timezone_offset)
506
                    if period_type == 'hourly':
507
                        current_datetime = current_datetime_local.isoformat()[0:19]
508
                    elif period_type == 'daily':
509
                        current_datetime = current_datetime_local.isoformat()[0:10]
510
                    elif period_type == 'weekly':
511
                        current_datetime = current_datetime_local.isoformat()[0:10]
512
                    elif period_type == 'monthly':
513
                        current_datetime = current_datetime_local.isoformat()[0:7]
514
                    elif period_type == 'yearly':
515
                        current_datetime = current_datetime_local.isoformat()[0:4]
516
517
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
518
                    reporting_input[energy_category_id]['timestamps'].append(current_datetime)
519
                    reporting_input[energy_category_id]['values'].append(actual_value)
520
                    reporting_input[energy_category_id]['subtotal'] += actual_value
521
                    reporting_input[energy_category_id]['subtotal_in_kgce'] += actual_value * kgce
522
                    reporting_input[energy_category_id]['subtotal_in_kgco2e'] += actual_value * kgco2e
523
524
                energy_category_tariff_dict = utilities.get_energy_category_peak_types(space['cost_center_id'],
525
                                                                                       energy_category_id,
526
                                                                                       reporting_start_datetime_utc,
527
                                                                                       reporting_end_datetime_utc)
528
                for row in rows_space_hourly:
529
                    peak_type = energy_category_tariff_dict.get(row[0], None)
530
                    if peak_type == 'toppeak':
531
                        reporting_input[energy_category_id]['toppeak'] += row[1]
532
                    elif peak_type == 'onpeak':
533
                        reporting_input[energy_category_id]['onpeak'] += row[1]
534
                    elif peak_type == 'midpeak':
535
                        reporting_input[energy_category_id]['midpeak'] += row[1]
536
                    elif peak_type == 'offpeak':
537
                        reporting_input[energy_category_id]['offpeak'] += row[1]
538
                    elif peak_type == 'deep':
539
                        reporting_input[energy_category_id]['deep'] += row[1]
540
541
        ################################################################################################################
542
        # Step 9: query reporting period energy cost
543
        ################################################################################################################
544
        reporting_cost = dict()
545
        if energy_category_set is not None and len(energy_category_set) > 0:
546
            for energy_category_id in energy_category_set:
547
548
                reporting_cost[energy_category_id] = dict()
549
                reporting_cost[energy_category_id]['timestamps'] = list()
550
                reporting_cost[energy_category_id]['values'] = list()
551
                reporting_cost[energy_category_id]['subtotal'] = Decimal(0.0)
552
                reporting_cost[energy_category_id]['toppeak'] = Decimal(0.0)
553
                reporting_cost[energy_category_id]['onpeak'] = Decimal(0.0)
554
                reporting_cost[energy_category_id]['midpeak'] = Decimal(0.0)
555
                reporting_cost[energy_category_id]['offpeak'] = Decimal(0.0)
556
                reporting_cost[energy_category_id]['deep'] = Decimal(0.0)
557
558
                cursor_billing.execute(" SELECT start_datetime_utc, actual_value "
559
                                       " FROM tbl_space_input_category_hourly "
560
                                       " WHERE space_id = %s "
561
                                       "     AND energy_category_id = %s "
562
                                       "     AND start_datetime_utc >= %s "
563
                                       "     AND start_datetime_utc < %s "
564
                                       " ORDER BY start_datetime_utc ",
565
                                       (space['id'],
566
                                        energy_category_id,
567
                                        reporting_start_datetime_utc,
568
                                        reporting_end_datetime_utc))
569
                rows_space_hourly = cursor_billing.fetchall()
570
571
                rows_space_periodically = utilities.aggregate_hourly_data_by_period(rows_space_hourly,
572
                                                                                    reporting_start_datetime_utc,
573
                                                                                    reporting_end_datetime_utc,
574
                                                                                    period_type)
575
                for row_space_periodically in rows_space_periodically:
576
                    current_datetime_local = row_space_periodically[0].replace(tzinfo=timezone.utc) + \
577
                                             timedelta(minutes=timezone_offset)
578
                    if period_type == 'hourly':
579
                        current_datetime = current_datetime_local.isoformat()[0:19]
580
                    elif period_type == 'daily':
581
                        current_datetime = current_datetime_local.isoformat()[0:10]
582
                    elif period_type == 'weekly':
583
                        current_datetime = current_datetime_local.isoformat()[0:10]
584
                    elif period_type == 'monthly':
585
                        current_datetime = current_datetime_local.isoformat()[0:7]
586
                    elif period_type == 'yearly':
587
                        current_datetime = current_datetime_local.isoformat()[0:4]
588
589
                    actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
590
                    reporting_cost[energy_category_id]['timestamps'].append(current_datetime)
591
                    reporting_cost[energy_category_id]['values'].append(actual_value)
592
                    reporting_cost[energy_category_id]['subtotal'] += actual_value
593
594
                energy_category_tariff_dict = utilities.get_energy_category_peak_types(space['cost_center_id'],
595
                                                                                       energy_category_id,
596
                                                                                       reporting_start_datetime_utc,
597
                                                                                       reporting_end_datetime_utc)
598
                for row in rows_space_hourly:
599
                    peak_type = energy_category_tariff_dict.get(row[0], None)
600
                    if peak_type == 'toppeak':
601
                        reporting_cost[energy_category_id]['toppeak'] += row[1]
602
                    elif peak_type == 'onpeak':
603
                        reporting_cost[energy_category_id]['onpeak'] += row[1]
604
                    elif peak_type == 'midpeak':
605
                        reporting_cost[energy_category_id]['midpeak'] += row[1]
606
                    elif peak_type == 'offpeak':
607
                        reporting_cost[energy_category_id]['offpeak'] += row[1]
608
                    elif peak_type == 'deep':
609
                        reporting_cost[energy_category_id]['deep'] += row[1]
610
611
        ################################################################################################################
612
        # Step 10: query child spaces energy input
613
        ################################################################################################################
614
        child_space_input = dict()
615
616
        if energy_category_set is not None and len(energy_category_set) > 0:
617
            for energy_category_id in energy_category_set:
618
                child_space_input[energy_category_id] = dict()
619
                child_space_input[energy_category_id]['child_space_names'] = list()
620
                child_space_input[energy_category_id]['subtotals'] = list()
621
                child_space_input[energy_category_id]['subtotals_in_kgce'] = list()
622
                child_space_input[energy_category_id]['subtotals_in_kgco2e'] = list()
623
                kgce = energy_category_dict[energy_category_id]['kgce']
624
                kgco2e = energy_category_dict[energy_category_id]['kgco2e']
625
                for child_space in child_space_list:
626
                    child_space_input[energy_category_id]['child_space_names'].append(child_space['name'])
627
                    subtotal = 0
628
                    subtotal_list = list()
629
630
                    cursor_energy.execute(" SELECT start_datetime_utc, actual_value"
631
                                          " FROM tbl_space_input_category_hourly "
632
                                          " WHERE space_id = %s "
633
                                          "     AND energy_category_id = %s "
634
                                          "     AND start_datetime_utc >= %s "
635
                                          "     AND start_datetime_utc < %s "
636
                                          " ORDER BY start_datetime_utc ",
637
                                          (child_space['id'],
638
                                           energy_category_id,
639
                                           reporting_start_datetime_utc,
640
                                           reporting_end_datetime_utc))
641
                    row_subtotal = cursor_energy.fetchall() 
642
                    rows_space_periodically = utilities.aggregate_hourly_data_by_period(row_subtotal,
643
                                                                                        reporting_start_datetime_utc,
644
                                                                                        reporting_end_datetime_utc,
645
                                                                                        period_type)
646
647
                    for row_space_periodically in rows_space_periodically:
648
                        actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
649
                        subtotal_list.append(actual_value)
650
                        subtotal += actual_value
651
652
                    child_space_input[energy_category_id]['subtotals'].append(subtotal_list)
653
                    child_space_input[energy_category_id]['subtotals_in_kgce'].append(subtotal * kgce)
654
                    child_space_input[energy_category_id]['subtotals_in_kgco2e'].append(subtotal * kgco2e)
655
656
        ################################################################################################################
657
        # Step 10: query child spaces energy cost
658
        ################################################################################################################
659
        child_space_cost = dict()
660
661
        if energy_category_set is not None and len(energy_category_set) > 0:
662
            for energy_category_id in energy_category_set:
663
                child_space_cost[energy_category_id] = dict()
664
                child_space_cost[energy_category_id]['child_space_names'] = list()
665
                child_space_cost[energy_category_id]['subtotals'] = list()
666
                for child_space in child_space_list:
667
                    child_space_cost[energy_category_id]['child_space_names'].append(child_space['name'])
668
                    subtotal_list = list()
669
670
                    cursor_billing.execute(" SELECT start_datetime_utc, actual_value"
671
                                           " FROM tbl_space_input_category_hourly "
672
                                           " WHERE space_id = %s "
673
                                           "     AND energy_category_id = %s "
674
                                           "     AND start_datetime_utc >= %s "
675
                                           "     AND start_datetime_utc < %s "
676
                                           " ORDER BY start_datetime_utc ",
677
                                           (child_space['id'],
678
                                            energy_category_id,
679
                                            reporting_start_datetime_utc,
680
                                            reporting_end_datetime_utc))
681
                    row_subtotal = cursor_billing.fetchall() 
682
                    rows_space_periodically = utilities.aggregate_hourly_data_by_period(row_subtotal,
683
                                                                                        reporting_start_datetime_utc,
684
                                                                                        reporting_end_datetime_utc,
685
                                                                                        period_type)
686
687
                    for row_space_periodically in rows_space_periodically:
688
                        actual_value = Decimal(0.0) if row_space_periodically[1] is None else row_space_periodically[1]
689
                        subtotal_list.append(actual_value)
690
691
                    child_space_cost[energy_category_id]['subtotals'].append(subtotal_list)
692
693
        ################################################################################################################
694
        # Step 12: construct the report
695
        ################################################################################################################
696
        if cursor_system:
697
            cursor_system.close()
698
        if cnx_system:
699
            cnx_system.close()
700
701
        if cursor_energy:
702
            cursor_energy.close()
703
        if cnx_energy:
704
            cnx_energy.close()
705
706
        if cursor_billing:
707
            cursor_billing.close()
708
        if cnx_billing:
709
            cnx_billing.close()
710
711
        result = dict()
712
713
        result['space'] = dict()
714
        result['space']['name'] = space['name']
715
        result['space']['area'] = space['area']
716
717
        result['base_period_input'] = dict()
718
        result['base_period_input']['names'] = list()
719
        result['base_period_input']['units'] = list()
720
        result['base_period_input']['timestamps'] = list()
721
        result['base_period_input']['values'] = list()
722
        result['base_period_input']['subtotals'] = list()
723
        result['base_period_input']['subtotals_in_kgce'] = list()
724
        result['base_period_input']['subtotals_in_kgco2e'] = list()
725
        result['base_period_input']['subtotals_per_unit_area'] = list()
726
        result['base_period_input']['total_in_kgce'] = Decimal(0.0)
727
        result['base_period_input']['total_in_kgco2e'] = Decimal(0.0)
728
        if energy_category_set is not None and len(energy_category_set) > 0:
729
            for energy_category_id in energy_category_set:
730
                result['base_period_input']['names'].append(
731
                    energy_category_dict[energy_category_id]['name'])
732
                result['base_period_input']['units'].append(
733
                    energy_category_dict[energy_category_id]['unit_of_measure'])
734
                result['base_period_input']['timestamps'].append(
735
                    base_input[energy_category_id]['timestamps'])
736
                result['base_period_input']['values'].append(
737
                    base_input[energy_category_id]['values'])
738
                result['base_period_input']['subtotals'].append(
739
                    base_input[energy_category_id]['subtotal'])
740
                result['base_period_input']['subtotals_in_kgce'].append(
741
                    base_input[energy_category_id]['subtotal_in_kgce'])
742
                result['base_period_input']['subtotals_in_kgco2e'].append(
743
                    base_input[energy_category_id]['subtotal_in_kgco2e'])
744
                result['base_period_input']['subtotals_per_unit_area'].append(
745
                    base_input[energy_category_id]['subtotal'] / space['area']
746
                    if space['area'] > 0.0 else None)
747
                result['base_period_input']['total_in_kgce'] += \
748
                    base_input[energy_category_id]['subtotal_in_kgce']
749
                result['base_period_input']['total_in_kgco2e'] += \
750
                    base_input[energy_category_id]['subtotal_in_kgco2e']
751
        
752
        result['sensor'] = dict()
753
        result['point'] = dict()
754
        result['sensor'] = sensor_dict
755
        result['point'] = point_data_dict
756
757
        result['base_period_cost'] = dict()
758
        result['base_period_cost']['names'] = list()
759
        result['base_period_cost']['units'] = list()
760
        result['base_period_cost']['timestamps'] = list()
761
        result['base_period_cost']['values'] = list()
762
        result['base_period_cost']['subtotals'] = list()
763
        result['base_period_cost']['subtotals_per_unit_area'] = list()
764
        result['base_period_cost']['total'] = Decimal(0.0)
765
        if energy_category_set is not None and len(energy_category_set) > 0:
766
            for energy_category_id in energy_category_set:
767
                result['base_period_cost']['names'].append(
768
                    energy_category_dict[energy_category_id]['name'])
769
                result['base_period_cost']['units'].append(config.currency_unit)
770
                result['base_period_cost']['timestamps'].append(
771
                    base_cost[energy_category_id]['timestamps'])
772
                result['base_period_cost']['values'].append(
773
                    base_cost[energy_category_id]['values'])
774
                result['base_period_cost']['subtotals'].append(
775
                    base_cost[energy_category_id]['subtotal'])
776
                result['base_period_cost']['subtotals_per_unit_area'].append(
777
                    base_cost[energy_category_id]['subtotal'] / space['area']
778
                    if space['area'] > 0.0 else None)
779
                result['base_period_cost']['total'] += base_cost[energy_category_id]['subtotal']
780
781
        result['reporting_period_input'] = dict()
782
        result['reporting_period_input']['names'] = list()
783
        result['reporting_period_input']['energy_category_ids'] = list()
784
        result['reporting_period_input']['units'] = list()
785
        result['reporting_period_input']['timestamps'] = list()
786
        result['reporting_period_input']['values'] = list()
787
        result['reporting_period_input']['subtotals'] = list()
788
        result['reporting_period_input']['subtotals_in_kgce'] = list()
789
        result['reporting_period_input']['subtotals_in_kgco2e'] = list()
790
        result['reporting_period_input']['subtotals_per_unit_area'] = list()
791
        result['reporting_period_input']['toppeaks'] = list()
792
        result['reporting_period_input']['onpeaks'] = list()
793
        result['reporting_period_input']['midpeaks'] = list()
794
        result['reporting_period_input']['offpeaks'] = list()
795
        result['reporting_period_input']['deeps'] = list()
796
        result['reporting_period_input']['increment_rates'] = list()
797
        result['reporting_period_input']['total_in_kgce'] = Decimal(0.0)
798
        result['reporting_period_input']['total_in_kgco2e'] = Decimal(0.0)
799
        result['reporting_period_input']['increment_rate_in_kgce'] = Decimal(0.0)
800
        result['reporting_period_input']['increment_rate_in_kgco2e'] = Decimal(0.0)
801
802
        if energy_category_set is not None and len(energy_category_set) > 0:
803
            for energy_category_id in energy_category_set:
804
                result['reporting_period_input']['names'].append(energy_category_dict[energy_category_id]['name'])
805
                result['reporting_period_input']['energy_category_ids'].append(energy_category_id)
806
                result['reporting_period_input']['units'].append(
807
                    energy_category_dict[energy_category_id]['unit_of_measure'])
808
                result['reporting_period_input']['timestamps'].append(
809
                    reporting_input[energy_category_id]['timestamps'])
810
                result['reporting_period_input']['values'].append(
811
                    reporting_input[energy_category_id]['values'])
812
                result['reporting_period_input']['subtotals'].append(
813
                    reporting_input[energy_category_id]['subtotal'])
814
                result['reporting_period_input']['subtotals_in_kgce'].append(
815
                    reporting_input[energy_category_id]['subtotal_in_kgce'])
816
                result['reporting_period_input']['subtotals_in_kgco2e'].append(
817
                    reporting_input[energy_category_id]['subtotal_in_kgco2e'])
818
                result['reporting_period_input']['subtotals_per_unit_area'].append(
819
                    reporting_input[energy_category_id]['subtotal'] / space['area']
820
                    if space['area'] > 0.0 else None)
821
                result['reporting_period_input']['toppeaks'].append(
822
                    reporting_input[energy_category_id]['toppeak'])
823
                result['reporting_period_input']['onpeaks'].append(
824
                    reporting_input[energy_category_id]['onpeak'])
825
                result['reporting_period_input']['midpeaks'].append(
826
                    reporting_input[energy_category_id]['midpeak'])
827
                result['reporting_period_input']['offpeaks'].append(
828
                    reporting_input[energy_category_id]['offpeak'])
829
                result['reporting_period_input']['deeps'].append(
830
                    reporting_input[energy_category_id]['deep'])
831
                result['reporting_period_input']['increment_rates'].append(
832
                    (reporting_input[energy_category_id]['subtotal'] -
833
                     base_input[energy_category_id]['subtotal']) /
834
                    base_input[energy_category_id]['subtotal']
835
                    if base_input[energy_category_id]['subtotal'] > 0.0 else None)
836
                result['reporting_period_input']['total_in_kgce'] += \
837
                    reporting_input[energy_category_id]['subtotal_in_kgce']
838
                result['reporting_period_input']['total_in_kgco2e'] += \
839
                    reporting_input[energy_category_id]['subtotal_in_kgco2e']
840
841
        result['reporting_period_input']['total_in_kgco2e_per_unit_area'] = \
842
            result['reporting_period_input']['total_in_kgce'] / space['area'] if space['area'] > 0.0 else None
843
844
        result['reporting_period_input']['increment_rate_in_kgce'] = \
845
            (result['reporting_period_input']['total_in_kgce'] - result['base_period_input']['total_in_kgce']) / \
846
            result['base_period_input']['total_in_kgce'] \
847
            if result['base_period_input']['total_in_kgce'] > Decimal(0.0) else None
848
849
        result['reporting_period_input']['total_in_kgce_per_unit_area'] = \
850
            result['reporting_period_input']['total_in_kgco2e'] / space['area'] if space['area'] > 0.0 else None
851
852
        result['reporting_period_input']['increment_rate_in_kgco2e'] = \
853
            (result['reporting_period_input']['total_in_kgco2e'] - result['base_period_input']['total_in_kgco2e']) / \
854
            result['base_period_input']['total_in_kgco2e'] \
855
            if result['base_period_input']['total_in_kgco2e'] > Decimal(0.0) else None
856
857
        result['reporting_period_cost'] = dict()
858
        result['reporting_period_cost']['names'] = list()
859
        result['reporting_period_cost']['energy_category_ids'] = list()
860
        result['reporting_period_cost']['units'] = list()
861
        result['reporting_period_cost']['timestamps'] = list()
862
        result['reporting_period_cost']['values'] = list()
863
        result['reporting_period_cost']['subtotals'] = list()
864
        result['reporting_period_cost']['subtotals_per_unit_area'] = list()
865
        result['reporting_period_cost']['toppeaks'] = list()
866
        result['reporting_period_cost']['onpeaks'] = list()
867
        result['reporting_period_cost']['midpeaks'] = list()
868
        result['reporting_period_cost']['offpeaks'] = list()
869
        result['reporting_period_cost']['deeps'] = list()
870
        result['reporting_period_cost']['increment_rates'] = list()
871
        result['reporting_period_cost']['total'] = Decimal(0.0)
872
        result['reporting_period_cost']['total_per_unit_area'] = Decimal(0.0)
873
        result['reporting_period_cost']['total_increment_rate'] = Decimal(0.0)
874
        result['reporting_period_cost']['total_unit'] = config.currency_unit
875
876
        if energy_category_set is not None and len(energy_category_set) > 0:
877
            for energy_category_id in energy_category_set:
878
                result['reporting_period_cost']['names'].append(energy_category_dict[energy_category_id]['name'])
879
                result['reporting_period_cost']['energy_category_ids'].append(energy_category_id)
880
                result['reporting_period_cost']['units'].append(config.currency_unit)
881
                result['reporting_period_cost']['timestamps'].append(
882
                    reporting_cost[energy_category_id]['timestamps'])
883
                result['reporting_period_cost']['values'].append(
884
                    reporting_cost[energy_category_id]['values'])
885
                result['reporting_period_cost']['subtotals'].append(
886
                    reporting_cost[energy_category_id]['subtotal'])
887
                result['reporting_period_cost']['subtotals_per_unit_area'].append(
888
                    reporting_cost[energy_category_id]['subtotal'] / space['area']
889
                    if space['area'] > 0.0 else None)
890
                result['reporting_period_cost']['toppeaks'].append(
891
                    reporting_cost[energy_category_id]['toppeak'])
892
                result['reporting_period_cost']['onpeaks'].append(
893
                    reporting_cost[energy_category_id]['onpeak'])
894
                result['reporting_period_cost']['midpeaks'].append(
895
                    reporting_cost[energy_category_id]['midpeak'])
896
                result['reporting_period_cost']['offpeaks'].append(
897
                    reporting_cost[energy_category_id]['offpeak'])
898
                result['reporting_period_cost']['deeps'].append(
899
                    reporting_cost[energy_category_id]['deep'])
900
                result['reporting_period_cost']['increment_rates'].append(
901
                    (reporting_cost[energy_category_id]['subtotal'] -
902
                     base_cost[energy_category_id]['subtotal']) /
903
                    base_cost[energy_category_id]['subtotal']
904
                    if base_cost[energy_category_id]['subtotal'] > 0.0 else None)
905
                result['reporting_period_cost']['total'] += reporting_cost[energy_category_id]['subtotal']
906
        result['reporting_period_cost']['total_per_unit_area'] = \
907
            result['reporting_period_cost']['total'] / space['area'] if space['area'] > 0.0 else None
908
909
        result['reporting_period_cost']['total_increment_rate'] = \
910
            (result['reporting_period_cost']['total'] - result['base_period_cost']['total']) / \
911
            result['reporting_period_cost']['total'] \
912
            if result['reporting_period_cost']['total'] > Decimal(0.0) else None
913
914
        result['child_space_input'] = dict()
915
        result['child_space_input']['energy_category_names'] = list()  # 1D array [energy category]
916
        result['child_space_input']['units'] = list()  # 1D array [energy category]
917
        result['child_space_input']['child_space_names_array'] = list()  # 2D array [energy category][child space]
918
        result['child_space_input']['subtotals_array'] = list()  # 2D array [energy category][child space]
919
        result['child_space_input']['subtotals_in_kgce_array'] = list()  # 2D array [energy category][child space]
920
        result['child_space_input']['subtotals_in_kgco2e_array'] = list()  # 2D array [energy category][child space]
921
        if energy_category_set is not None and len(energy_category_set) > 0:
922
            for energy_category_id in energy_category_set:
923
                result['child_space_input']['energy_category_names'].append(
924
                    energy_category_dict[energy_category_id]['name'])
925
                result['child_space_input']['units'].append(
926
                    energy_category_dict[energy_category_id]['unit_of_measure'])
927
                result['child_space_input']['child_space_names_array'].append(
928
                    child_space_input[energy_category_id]['child_space_names'])
929
                result['child_space_input']['subtotals_array'].append(
930
                    child_space_input[energy_category_id]['subtotals'])
931
                result['child_space_input']['subtotals_in_kgce_array'].append(
932
                    child_space_input[energy_category_id]['subtotals_in_kgce'])
933
                result['child_space_input']['subtotals_in_kgco2e_array'].append(
934
                    child_space_input[energy_category_id]['subtotals_in_kgco2e'])
935
936
        result['child_space_cost'] = dict()
937
        result['child_space_cost']['energy_category_names'] = list()  # 1D array [energy category]
938
        result['child_space_cost']['units'] = list()  # 1D array [energy category]
939
        result['child_space_cost']['child_space_names_array'] = list()  # 2D array [energy category][child space]
940
        result['child_space_cost']['subtotals_array'] = list()  # 2D array [energy category][child space]
941
        if energy_category_set is not None and len(energy_category_set) > 0:
942
            for energy_category_id in energy_category_set:
943
                result['child_space_cost']['energy_category_names'].append(
944
                    energy_category_dict[energy_category_id]['name'])
945
                result['child_space_cost']['units'].append(config.currency_unit)
946
                result['child_space_cost']['child_space_names_array'].append(
947
                    child_space_cost[energy_category_id]['child_space_names'])
948
                result['child_space_cost']['subtotals_array'].append(
949
                    child_space_cost[energy_category_id]['subtotals'])
950
951
        resp.text = json.dumps(result)
952