reports.combinedequipmentstatistics   F
last analyzed

Complexity

Total Complexity 139

Size/Duplication

Total Lines 745
Duplicated Lines 9.66 %

Importance

Changes 0
Metric Value
wmc 139
eloc 537
dl 72
loc 745
rs 2
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A Reporting.__init__() 0 3 1
F Reporting.on_get() 72 676 137
A Reporting.on_options() 0 4 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.combinedequipmentstatistics 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
"""
2
Combined Equipment Statistics Report API
3
4
This module provides REST API endpoints for generating combined equipment statistics reports.
5
It analyzes statistical data and performance metrics for combined equipment to provide
6
comprehensive insights into combined equipment performance and operational patterns.
7
8
Key Features:
9
- Combined equipment statistical analysis
10
- Performance metrics calculation
11
- Statistical trend analysis
12
- Performance benchmarking
13
- Excel export functionality
14
- Statistical insights and patterns
15
16
Report Components:
17
- Combined equipment statistics summary
18
- Performance metrics and KPIs
19
- Statistical trend analysis
20
- Benchmarking data
21
- Performance indicators
22
- Statistical patterns and insights
23
24
The module uses Falcon framework for REST API and includes:
25
- Database queries for statistical data
26
- Statistical calculation algorithms
27
- Performance analysis tools
28
- Excel export via excelexporters
29
- Multi-language support
30
- User authentication and authorization
31
"""
32
33
import re
34
from datetime import datetime, timedelta, timezone
35
from decimal import Decimal
36
import falcon
37
import mysql.connector
38
import simplejson as json
39
import config
40
import excelexporters.combinedequipmentstatistics
41
from core import utilities
42
from core.useractivity import access_control, api_key_control
43
44
45
class Reporting:
46
    def __init__(self):
47
        """Initializes Reporting"""
48
        pass
49
50
    @staticmethod
51
    def on_options(req, resp):
52
        _ = req
53
        resp.status = falcon.HTTP_200
54
55
    ####################################################################################################################
56
    # PROCEDURES
57
    # Step 1: valid parameters
58
    # Step 2: query the combined equipment
59
    # Step 3: query energy categories
60
    # Step 4: query associated points
61
    # Step 5: query associated equipments
62
    # Step 6: query base period energy input
63
    # Step 7: query reporting period energy input
64
    # Step 8: query tariff data
65
    # Step 9: query associated points data
66
    # Step 10: query associated equipments energy input
67
    # Step 11: construct the report
68
    ####################################################################################################################
69
    @staticmethod
70
    def on_get(req, resp):
71
        if 'API-KEY' not in req.headers or \
72
                not isinstance(req.headers['API-KEY'], str) or \
73
                len(str.strip(req.headers['API-KEY'])) == 0:
74
            access_control(req)
75
        else:
76
            api_key_control(req)
77
        print(req.params)
78
        combined_equipment_id = req.params.get('combinedequipmentid')
79
        combined_equipment_uuid = req.params.get('combinedequipmentuuid')
80
        period_type = req.params.get('periodtype')
81
        base_period_start_datetime_local = req.params.get('baseperiodstartdatetime')
82
        base_period_end_datetime_local = req.params.get('baseperiodenddatetime')
83
        reporting_period_start_datetime_local = req.params.get('reportingperiodstartdatetime')
84
        reporting_period_end_datetime_local = req.params.get('reportingperiodenddatetime')
85
        language = req.params.get('language')
86
        quick_mode = req.params.get('quickmode')
87
88
        ################################################################################################################
89
        # Step 1: valid parameters
90
        ################################################################################################################
91
        if combined_equipment_id is None and combined_equipment_uuid is None:
92
            raise falcon.HTTPError(status=falcon.HTTP_400,
93
                                   title='API.BAD_REQUEST',
94
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
95
96
        if combined_equipment_id is not None:
97
            combined_equipment_id = str.strip(combined_equipment_id)
98
            if not combined_equipment_id.isdigit() or int(combined_equipment_id) <= 0:
99
                raise falcon.HTTPError(status=falcon.HTTP_400,
100
                                       title='API.BAD_REQUEST',
101
                                       description='API.INVALID_COMBINED_EQUIPMENT_ID')
102
103
        if combined_equipment_uuid is not None:
104
            regex = re.compile(r'^[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}\Z', re.I)
105
            match = regex.match(str.strip(combined_equipment_uuid))
106
            if not bool(match):
107
                raise falcon.HTTPError(status=falcon.HTTP_400,
108
                                       title='API.BAD_REQUEST',
109
                                       description='API.INVALID_COMBINED_EQUIPMENT_UUID')
110
111
        if period_type is None:
112
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
113
                                   description='API.INVALID_PERIOD_TYPE')
114
        else:
115
            period_type = str.strip(period_type)
116
            if period_type not in ['hourly', 'daily', 'weekly', 'monthly', 'yearly']:
117
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
118
                                       description='API.INVALID_PERIOD_TYPE')
119
120
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
121
        if config.utc_offset[0] == '-':
122
            timezone_offset = -timezone_offset
123
124
        base_start_datetime_utc = None
125
        if base_period_start_datetime_local is not None and len(str.strip(base_period_start_datetime_local)) > 0:
126
            base_period_start_datetime_local = str.strip(base_period_start_datetime_local)
127
            try:
128
                base_start_datetime_utc = datetime.strptime(base_period_start_datetime_local, '%Y-%m-%dT%H:%M:%S')
129
            except ValueError:
130
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
131
                                       description="API.INVALID_BASE_PERIOD_START_DATETIME")
132
            base_start_datetime_utc = \
133
                base_start_datetime_utc.replace(tzinfo=timezone.utc) - timedelta(minutes=timezone_offset)
134
            # nomalize the start datetime
135
            if config.minutes_to_count == 30 and base_start_datetime_utc.minute >= 30:
136
                base_start_datetime_utc = base_start_datetime_utc.replace(minute=30, second=0, microsecond=0)
137
            else:
138
                base_start_datetime_utc = base_start_datetime_utc.replace(minute=0, second=0, microsecond=0)
139
140
        base_end_datetime_utc = None
141
        if base_period_end_datetime_local is not None and len(str.strip(base_period_end_datetime_local)) > 0:
142
            base_period_end_datetime_local = str.strip(base_period_end_datetime_local)
143
            try:
144
                base_end_datetime_utc = datetime.strptime(base_period_end_datetime_local, '%Y-%m-%dT%H:%M:%S')
145
            except ValueError:
146
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
147
                                       description="API.INVALID_BASE_PERIOD_END_DATETIME")
148
            base_end_datetime_utc = \
149
                base_end_datetime_utc.replace(tzinfo=timezone.utc) - timedelta(minutes=timezone_offset)
150
151
        if base_start_datetime_utc is not None and base_end_datetime_utc is not None and \
152
                base_start_datetime_utc >= base_end_datetime_utc:
153
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
154
                                   description='API.INVALID_BASE_PERIOD_END_DATETIME')
155
156
        if reporting_period_start_datetime_local is None:
157
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
158
                                   description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
159
        else:
160
            reporting_period_start_datetime_local = str.strip(reporting_period_start_datetime_local)
161
            try:
162
                reporting_start_datetime_utc = datetime.strptime(reporting_period_start_datetime_local,
163
                                                                 '%Y-%m-%dT%H:%M:%S')
164
            except ValueError:
165
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
166
                                       description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
167
            reporting_start_datetime_utc = \
168
                reporting_start_datetime_utc.replace(tzinfo=timezone.utc) - timedelta(minutes=timezone_offset)
169
            # nomalize the start datetime
170
            if config.minutes_to_count == 30 and reporting_start_datetime_utc.minute >= 30:
171
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=30, second=0, microsecond=0)
172
            else:
173
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=0, second=0, microsecond=0)
174
175
        if reporting_period_end_datetime_local is None:
176
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
177
                                   description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
178
        else:
179
            reporting_period_end_datetime_local = str.strip(reporting_period_end_datetime_local)
180
            try:
181
                reporting_end_datetime_utc = datetime.strptime(reporting_period_end_datetime_local,
182
                                                               '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
183
                                             timedelta(minutes=timezone_offset)
184
            except ValueError:
185
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
186
                                       description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
187
188
        if reporting_start_datetime_utc >= reporting_end_datetime_utc:
189
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
190
                                   description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
191
192
        # if turn quick mode on, do not return parameters data and excel file
193
        is_quick_mode = False
194
        if quick_mode is not None and \
195
                len(str.strip(quick_mode)) > 0 and \
196
                str.lower(str.strip(quick_mode)) in ('true', 't', 'on', 'yes', 'y'):
197
            is_quick_mode = True
198
199
        trans = utilities.get_translation(language)
200
        trans.install()
201
        _ = trans.gettext
202
203
        ################################################################################################################
204
        # Step 2: query the combined equipment
205
        ################################################################################################################
206
        cnx_system = mysql.connector.connect(**config.myems_system_db)
207
        cursor_system = cnx_system.cursor()
208
209
        cnx_energy = mysql.connector.connect(**config.myems_energy_db)
210
        cursor_energy = cnx_energy.cursor()
211
212
        cnx_historical = mysql.connector.connect(**config.myems_historical_db)
213
        cursor_historical = cnx_historical.cursor()
214
215
        if combined_equipment_id is not None:
216
            cursor_system.execute(" SELECT id, name, cost_center_id "
217
                                  " FROM tbl_combined_equipments "
218
                                  " WHERE id = %s ", (combined_equipment_id,))
219
            row_combined_equipment = cursor_system.fetchone()
220
        elif combined_equipment_uuid is not None:
221
            cursor_system.execute(" SELECT id, name, cost_center_id "
222
                                  " FROM tbl_combined_equipments "
223
                                  " WHERE uuid = %s ", (combined_equipment_uuid,))
224
            row_combined_equipment = cursor_system.fetchone()
225
226 View Code Duplication
        if row_combined_equipment is None:
0 ignored issues
show
introduced by
The variable row_combined_equipment does not seem to be defined for all execution paths.
Loading history...
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
227
            if cursor_system:
228
                cursor_system.close()
229
            if cnx_system:
230
                cnx_system.close()
231
232
            if cursor_energy:
233
                cursor_energy.close()
234
            if cnx_energy:
235
                cnx_energy.close()
236
237
            if cursor_historical:
238
                cursor_historical.close()
239
            if cnx_historical:
240
                cnx_historical.close()
241
            raise falcon.HTTPError(status=falcon.HTTP_404,
242
                                   title='API.NOT_FOUND',
243
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
244
245
        combined_equipment = dict()
246
        combined_equipment['id'] = row_combined_equipment[0]
247
        combined_equipment['name'] = row_combined_equipment[1]
248
        combined_equipment['cost_center_id'] = row_combined_equipment[2]
249
250
        ################################################################################################################
251
        # Step 3: query energy categories
252
        ################################################################################################################
253
        energy_category_set = set()
254
        # query energy categories in base period
255
        cursor_energy.execute(" SELECT DISTINCT(energy_category_id) "
256
                              " FROM tbl_combined_equipment_input_category_hourly "
257
                              " WHERE combined_equipment_id = %s "
258
                              "     AND start_datetime_utc >= %s "
259
                              "     AND start_datetime_utc < %s ",
260
                              (combined_equipment['id'], base_start_datetime_utc, base_end_datetime_utc))
261
        rows_energy_categories = cursor_energy.fetchall()
262
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
263
            for row_energy_category in rows_energy_categories:
264
                energy_category_set.add(row_energy_category[0])
265
266
        # query energy categories in reporting period
267
        cursor_energy.execute(" SELECT DISTINCT(energy_category_id) "
268
                              " FROM tbl_combined_equipment_input_category_hourly "
269
                              " WHERE combined_equipment_id = %s "
270
                              "     AND start_datetime_utc >= %s "
271
                              "     AND start_datetime_utc < %s ",
272
                              (combined_equipment['id'], reporting_start_datetime_utc, reporting_end_datetime_utc))
273
        rows_energy_categories = cursor_energy.fetchall()
274
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
275
            for row_energy_category in rows_energy_categories:
276
                energy_category_set.add(row_energy_category[0])
277
278
        # query all energy categories in base period and reporting period
279
        cursor_system.execute(" SELECT id, name, unit_of_measure, kgce, kgco2e "
280
                              " FROM tbl_energy_categories "
281
                              " ORDER BY id ", )
282
        rows_energy_categories = cursor_system.fetchall()
283
        if rows_energy_categories is None or len(rows_energy_categories) == 0:
284
            if cursor_system:
285
                cursor_system.close()
286
            if cnx_system:
287
                cnx_system.close()
288
289
            if cursor_energy:
290
                cursor_energy.close()
291
            if cnx_energy:
292
                cnx_energy.close()
293
294
            if cursor_historical:
295
                cursor_historical.close()
296
            if cnx_historical:
297
                cnx_historical.close()
298
            raise falcon.HTTPError(status=falcon.HTTP_404,
299
                                   title='API.NOT_FOUND',
300
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
301
        energy_category_dict = dict()
302
        for row_energy_category in rows_energy_categories:
303
            if row_energy_category[0] in energy_category_set:
304
                energy_category_dict[row_energy_category[0]] = {"name": row_energy_category[1],
305
                                                                "unit_of_measure": row_energy_category[2],
306
                                                                "kgce": row_energy_category[3],
307
                                                                "kgco2e": row_energy_category[4]}
308
309
        ################################################################################################################
310
        # Step 4: query associated points
311
        ################################################################################################################
312
        point_list = list()
313
        cursor_system.execute(" SELECT p.id, ep.name, p.units, p.object_type  "
314
                              " FROM tbl_combined_equipments e, tbl_combined_equipments_parameters ep, tbl_points p "
315
                              " WHERE e.id = %s AND e.id = ep.combined_equipment_id AND ep.parameter_type = 'point' "
316
                              "       AND ep.point_id = p.id "
317
                              " ORDER BY p.id ", (combined_equipment['id'],))
318
        rows_points = cursor_system.fetchall()
319
        if rows_points is not None and len(rows_points) > 0:
320
            for row in rows_points:
321
                point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
322
323
        ################################################################################################################
324
        # Step 5: query associated equipments
325
        ################################################################################################################
326
        associated_equipment_list = list()
327
        cursor_system.execute(" SELECT e.id, e.name "
328
                              " FROM tbl_equipments e,tbl_combined_equipments_equipments ee"
329
                              " WHERE ee.combined_equipment_id = %s AND e.id = ee.equipment_id"
330
                              " ORDER BY id ", (combined_equipment['id'],))
331
        rows_associated_equipments = cursor_system.fetchall()
332
        if rows_associated_equipments is not None and len(rows_associated_equipments) > 0:
333
            for row in rows_associated_equipments:
334
                associated_equipment_list.append({"id": row[0], "name": row[1]})
335
336
        ################################################################################################################
337
        # Step 6: query base period energy input
338
        ################################################################################################################
339
        base = dict()
340
        if energy_category_set is not None and len(energy_category_set) > 0:
341
            for energy_category_id in energy_category_set:
342
                base[energy_category_id] = dict()
343
                base[energy_category_id]['timestamps'] = list()
344
                base[energy_category_id]['values'] = list()
345
                base[energy_category_id]['subtotal'] = Decimal(0.0)
346
                base[energy_category_id]['mean'] = None
347
                base[energy_category_id]['median'] = None
348
                base[energy_category_id]['minimum'] = None
349
                base[energy_category_id]['maximum'] = None
350
                base[energy_category_id]['stdev'] = None
351
                base[energy_category_id]['variance'] = None
352
353
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
354
                                      " FROM tbl_combined_equipment_input_category_hourly "
355
                                      " WHERE combined_equipment_id = %s "
356
                                      "     AND energy_category_id = %s "
357
                                      "     AND start_datetime_utc >= %s "
358
                                      "     AND start_datetime_utc < %s "
359
                                      " ORDER BY start_datetime_utc ",
360
                                      (combined_equipment['id'],
361
                                       energy_category_id,
362
                                       base_start_datetime_utc,
363
                                       base_end_datetime_utc))
364
                rows_combined_equipment_hourly = cursor_energy.fetchall()
365
366
                rows_combined_equipment_periodically, \
367
                    base[energy_category_id]['mean'], \
368
                    base[energy_category_id]['median'], \
369
                    base[energy_category_id]['minimum'], \
370
                    base[energy_category_id]['maximum'], \
371
                    base[energy_category_id]['stdev'], \
372
                    base[energy_category_id]['variance'] = \
373
                    utilities.statistics_hourly_data_by_period(rows_combined_equipment_hourly,
374
                                                               base_start_datetime_utc,
375
                                                               base_end_datetime_utc,
376
                                                               period_type)
377
378
                for row_combined_equipment_periodically in rows_combined_equipment_periodically:
379
                    current_datetime_local = row_combined_equipment_periodically[0].replace(tzinfo=timezone.utc) + \
380
                                             timedelta(minutes=timezone_offset)
381
                    if period_type == 'hourly':
382
                        current_datetime = current_datetime_local.isoformat()[0:19]
383
                    elif period_type == 'daily':
384
                        current_datetime = current_datetime_local.isoformat()[0:10]
385
                    elif period_type == 'weekly':
386
                        current_datetime = current_datetime_local.isoformat()[0:10]
387
                    elif period_type == 'monthly':
388
                        current_datetime = current_datetime_local.isoformat()[0:7]
389
                    elif period_type == 'yearly':
390
                        current_datetime = current_datetime_local.isoformat()[0:4]
391
392
                    actual_value = Decimal(0.0) if row_combined_equipment_periodically[1] is None \
393
                        else row_combined_equipment_periodically[1]
394
                    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...
395
                    base[energy_category_id]['values'].append(actual_value)
396
                    base[energy_category_id]['subtotal'] += actual_value
397
398
        ################################################################################################################
399
        # Step 7: query reporting period energy input
400
        ################################################################################################################
401
        reporting = dict()
402
        if energy_category_set is not None and len(energy_category_set) > 0:
403
            for energy_category_id in energy_category_set:
404
                reporting[energy_category_id] = dict()
405
                reporting[energy_category_id]['timestamps'] = list()
406
                reporting[energy_category_id]['values'] = list()
407
                reporting[energy_category_id]['subtotal'] = Decimal(0.0)
408
                reporting[energy_category_id]['mean'] = None
409
                reporting[energy_category_id]['median'] = None
410
                reporting[energy_category_id]['minimum'] = None
411
                reporting[energy_category_id]['maximum'] = None
412
                reporting[energy_category_id]['stdev'] = None
413
                reporting[energy_category_id]['variance'] = None
414
415
                cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
416
                                      " FROM tbl_combined_equipment_input_category_hourly "
417
                                      " WHERE combined_equipment_id = %s "
418
                                      "     AND energy_category_id = %s "
419
                                      "     AND start_datetime_utc >= %s "
420
                                      "     AND start_datetime_utc < %s "
421
                                      " ORDER BY start_datetime_utc ",
422
                                      (combined_equipment['id'],
423
                                       energy_category_id,
424
                                       reporting_start_datetime_utc,
425
                                       reporting_end_datetime_utc))
426
                rows_combined_equipment_hourly = cursor_energy.fetchall()
427
428
                rows_combined_equipment_periodically, \
429
                    reporting[energy_category_id]['mean'], \
430
                    reporting[energy_category_id]['median'], \
431
                    reporting[energy_category_id]['minimum'], \
432
                    reporting[energy_category_id]['maximum'], \
433
                    reporting[energy_category_id]['stdev'], \
434
                    reporting[energy_category_id]['variance'] = \
435
                    utilities.statistics_hourly_data_by_period(rows_combined_equipment_hourly,
436
                                                               reporting_start_datetime_utc,
437
                                                               reporting_end_datetime_utc,
438
                                                               period_type)
439
440
                for row_combined_equipment_periodically in rows_combined_equipment_periodically:
441
                    current_datetime_local = row_combined_equipment_periodically[0].replace(tzinfo=timezone.utc) + \
442
                                             timedelta(minutes=timezone_offset)
443
                    if period_type == 'hourly':
444
                        current_datetime = current_datetime_local.isoformat()[0:19]
445
                    elif period_type == 'daily':
446
                        current_datetime = current_datetime_local.isoformat()[0:10]
447
                    elif period_type == 'weekly':
448
                        current_datetime = current_datetime_local.isoformat()[0:10]
449
                    elif period_type == 'monthly':
450
                        current_datetime = current_datetime_local.isoformat()[0:7]
451
                    elif period_type == 'yearly':
452
                        current_datetime = current_datetime_local.isoformat()[0:4]
453
454
                    actual_value = Decimal(0.0) if row_combined_equipment_periodically[1] is None \
455
                        else row_combined_equipment_periodically[1]
456
                    reporting[energy_category_id]['timestamps'].append(current_datetime)
457
                    reporting[energy_category_id]['values'].append(actual_value)
458
                    reporting[energy_category_id]['subtotal'] += actual_value
459
460
        ################################################################################################################
461
        # Step 8: query tariff data
462
        ################################################################################################################
463
        parameters_data = dict()
464
        parameters_data['names'] = list()
465
        parameters_data['timestamps'] = list()
466
        parameters_data['values'] = list()
467
        if not is_quick_mode:
468
            if config.is_tariff_appended and energy_category_set is not None and len(energy_category_set) > 0:
469
                for energy_category_id in energy_category_set:
470
                    energy_category_tariff_dict = \
471
                        utilities.get_energy_category_tariffs(combined_equipment['cost_center_id'],
472
                                                              energy_category_id,
473
                                                              reporting_start_datetime_utc,
474
                                                              reporting_end_datetime_utc)
475
                    tariff_timestamp_list = list()
476
                    tariff_value_list = list()
477
                    for k, v in energy_category_tariff_dict.items():
478
                        # convert k from utc to local
479
                        k = k + timedelta(minutes=timezone_offset)
480
                        tariff_timestamp_list.append(k.isoformat()[0:19])
481
                        tariff_value_list.append(v)
482
483
                    parameters_data['names'].append(
484
                        _('Tariff') + '-' + energy_category_dict[energy_category_id]['name'])
485
                    parameters_data['timestamps'].append(tariff_timestamp_list)
486
                    parameters_data['values'].append(tariff_value_list)
487
488
        ################################################################################################################
489
        # Step 9: query associated points data
490
        ################################################################################################################
491
        if not is_quick_mode:
492
            for point in point_list:
493
                point_values = []
494
                point_timestamps = []
495
                if point['object_type'] == 'ENERGY_VALUE':
496
                    query = (" SELECT utc_date_time, actual_value "
497
                             " FROM tbl_energy_value "
498
                             " WHERE point_id = %s "
499
                             "       AND utc_date_time BETWEEN %s AND %s "
500
                             " ORDER BY utc_date_time ")
501
                    cursor_historical.execute(query, (point['id'],
502
                                                      reporting_start_datetime_utc,
503
                                                      reporting_end_datetime_utc))
504
                    rows = cursor_historical.fetchall()
505
506
                    if rows is not None and len(rows) > 0:
507
                        for row in rows:
508
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
509
                                                     timedelta(minutes=timezone_offset)
510
                            current_datetime = current_datetime_local.isoformat()[0:19]
511
                            point_timestamps.append(current_datetime)
512
                            point_values.append(row[1])
513
                elif point['object_type'] == 'ANALOG_VALUE':
514
                    query = (" SELECT utc_date_time, actual_value "
515
                             " FROM tbl_analog_value "
516
                             " WHERE point_id = %s "
517
                             "       AND utc_date_time BETWEEN %s AND %s "
518
                             " ORDER BY utc_date_time ")
519
                    cursor_historical.execute(query, (point['id'],
520
                                                      reporting_start_datetime_utc,
521
                                                      reporting_end_datetime_utc))
522
                    rows = cursor_historical.fetchall()
523
524
                    if rows is not None and len(rows) > 0:
525
                        for row in rows:
526
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
527
                                                     timedelta(minutes=timezone_offset)
528
                            current_datetime = current_datetime_local.isoformat()[0:19]
529
                            point_timestamps.append(current_datetime)
530
                            point_values.append(row[1])
531
                elif point['object_type'] == 'DIGITAL_VALUE':
532
                    query = (" SELECT utc_date_time, actual_value "
533
                             " FROM tbl_digital_value "
534
                             " WHERE point_id = %s "
535
                             "       AND utc_date_time BETWEEN %s AND %s "
536
                             " ORDER BY utc_date_time ")
537
                    cursor_historical.execute(query, (point['id'],
538
                                                      reporting_start_datetime_utc,
539
                                                      reporting_end_datetime_utc))
540
                    rows = cursor_historical.fetchall()
541
542
                    if rows is not None and len(rows) > 0:
543
                        for row in rows:
544
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
545
                                                     timedelta(minutes=timezone_offset)
546
                            current_datetime = current_datetime_local.isoformat()[0:19]
547
                            point_timestamps.append(current_datetime)
548
                            point_values.append(row[1])
549
550
                parameters_data['names'].append(point['name'] + ' (' + point['units'] + ')')
551
                parameters_data['timestamps'].append(point_timestamps)
552
                parameters_data['values'].append(point_values)
553
554
        ################################################################################################################
555
        # Step 10: query associated equipments energy input
556
        ################################################################################################################
557
        associated_equipment_data = dict()
558
559
        if energy_category_set is not None and len(energy_category_set) > 0:
560
            for energy_category_id in energy_category_set:
561
                associated_equipment_data[energy_category_id] = dict()
562
                associated_equipment_data[energy_category_id]['associated_equipment_names'] = list()
563
                associated_equipment_data[energy_category_id]['subtotals'] = list()
564
                for associated_equipment in associated_equipment_list:
565
                    associated_equipment_data[energy_category_id]['associated_equipment_names'].append(
566
                        associated_equipment['name'])
567
568
                    cursor_energy.execute(" SELECT SUM(actual_value) "
569
                                          " FROM tbl_equipment_input_category_hourly "
570
                                          " WHERE equipment_id = %s "
571
                                          "     AND energy_category_id = %s "
572
                                          "     AND start_datetime_utc >= %s "
573
                                          "     AND start_datetime_utc < %s ",
574
                                          (associated_equipment['id'],
575
                                           energy_category_id,
576
                                           reporting_start_datetime_utc,
577
                                           reporting_end_datetime_utc))
578
                    row_subtotal = cursor_energy.fetchone()
579
580
                    subtotal = Decimal(0.0) if (row_subtotal is None or row_subtotal[0] is None) else row_subtotal[0]
581
                    associated_equipment_data[energy_category_id]['subtotals'].append(subtotal)
582
583
        ################################################################################################################
584
        # Step 11: construct the report
585
        ################################################################################################################
586
        if cursor_system:
587
            cursor_system.close()
588
        if cnx_system:
589
            cnx_system.close()
590
591
        if cursor_energy:
592
            cursor_energy.close()
593
        if cnx_energy:
594
            cnx_energy.close()
595
596
        if cursor_historical:
597
            cursor_historical.close()
598
        if cnx_historical:
599
            cnx_historical.close()
600
601
        result = dict()
602
603
        result['combined_equipment'] = dict()
604
        result['combined_equipment']['name'] = combined_equipment['name']
605
606
        result['base_period'] = dict()
607
        result['base_period']['names'] = list()
608
        result['base_period']['units'] = list()
609
        result['base_period']['timestamps'] = list()
610
        result['base_period']['values'] = list()
611
        result['base_period']['subtotals'] = list()
612
        result['base_period']['means'] = list()
613
        result['base_period']['medians'] = list()
614
        result['base_period']['minimums'] = list()
615
        result['base_period']['maximums'] = list()
616
        result['base_period']['stdevs'] = list()
617
        result['base_period']['variances'] = list()
618
619
        if energy_category_set is not None and len(energy_category_set) > 0:
620
            for energy_category_id in energy_category_set:
621
                result['base_period']['names'].append(energy_category_dict[energy_category_id]['name'])
622
                result['base_period']['units'].append(energy_category_dict[energy_category_id]['unit_of_measure'])
623
                result['base_period']['timestamps'].append(base[energy_category_id]['timestamps'])
624
                result['base_period']['values'].append(base[energy_category_id]['values'])
625
                result['base_period']['subtotals'].append(base[energy_category_id]['subtotal'])
626
                result['base_period']['means'].append(base[energy_category_id]['mean'])
627
                result['base_period']['medians'].append(base[energy_category_id]['median'])
628
                result['base_period']['minimums'].append(base[energy_category_id]['minimum'])
629
                result['base_period']['maximums'].append(base[energy_category_id]['maximum'])
630
                result['base_period']['stdevs'].append(base[energy_category_id]['stdev'])
631
                result['base_period']['variances'].append(base[energy_category_id]['variance'])
632
633
        result['reporting_period'] = dict()
634
        result['reporting_period']['names'] = list()
635
        result['reporting_period']['energy_category_ids'] = list()
636
        result['reporting_period']['units'] = list()
637
        result['reporting_period']['timestamps'] = list()
638
        result['reporting_period']['values'] = list()
639
        result['reporting_period']['rates'] = list()
640
        result['reporting_period']['subtotals'] = list()
641
        result['reporting_period']['means'] = list()
642
        result['reporting_period']['means_increment_rate'] = list()
643
        result['reporting_period']['medians'] = list()
644
        result['reporting_period']['medians_increment_rate'] = list()
645
        result['reporting_period']['minimums'] = list()
646
        result['reporting_period']['minimums_increment_rate'] = list()
647
        result['reporting_period']['maximums'] = list()
648
        result['reporting_period']['maximums_increment_rate'] = list()
649
        result['reporting_period']['stdevs'] = list()
650
        result['reporting_period']['stdevs_increment_rate'] = list()
651
        result['reporting_period']['variances'] = list()
652
        result['reporting_period']['variances_increment_rate'] = list()
653
654 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...
655
            for energy_category_id in energy_category_set:
656
                result['reporting_period']['names'].append(energy_category_dict[energy_category_id]['name'])
657
                result['reporting_period']['energy_category_ids'].append(energy_category_id)
658
                result['reporting_period']['units'].append(energy_category_dict[energy_category_id]['unit_of_measure'])
659
                result['reporting_period']['timestamps'].append(reporting[energy_category_id]['timestamps'])
660
                result['reporting_period']['values'].append(reporting[energy_category_id]['values'])
661
                result['reporting_period']['subtotals'].append(reporting[energy_category_id]['subtotal'])
662
                result['reporting_period']['means'].append(reporting[energy_category_id]['mean'])
663
                result['reporting_period']['means_increment_rate'].append(
664
                    (reporting[energy_category_id]['mean'] - base[energy_category_id]['mean']) /
665
                    base[energy_category_id]['mean'] if (base[energy_category_id]['mean'] is not None and
666
                                                         base[energy_category_id]['mean'] > Decimal(0.0))
667
                    else None)
668
                result['reporting_period']['medians'].append(reporting[energy_category_id]['median'])
669
                result['reporting_period']['medians_increment_rate'].append(
670
                    (reporting[energy_category_id]['median'] - base[energy_category_id]['median']) /
671
                    base[energy_category_id]['median'] if (base[energy_category_id]['median'] is not None and
672
                                                           base[energy_category_id]['median'] > Decimal(0.0))
673
                    else None)
674
                result['reporting_period']['minimums'].append(reporting[energy_category_id]['minimum'])
675
                result['reporting_period']['minimums_increment_rate'].append(
676
                    (reporting[energy_category_id]['minimum'] - base[energy_category_id]['minimum']) /
677
                    base[energy_category_id]['minimum'] if (base[energy_category_id]['minimum'] is not None and
678
                                                            base[energy_category_id]['minimum'] > Decimal(0.0))
679
                    else None)
680
                result['reporting_period']['maximums'].append(reporting[energy_category_id]['maximum'])
681
                result['reporting_period']['maximums_increment_rate'].append(
682
                    (reporting[energy_category_id]['maximum'] - base[energy_category_id]['maximum']) /
683
                    base[energy_category_id]['maximum'] if (base[energy_category_id]['maximum'] is not None and
684
                                                            base[energy_category_id]['maximum'] > Decimal(0.0))
685
                    else None)
686
                result['reporting_period']['stdevs'].append(reporting[energy_category_id]['stdev'])
687
                result['reporting_period']['stdevs_increment_rate'].append(
688
                    (reporting[energy_category_id]['stdev'] - base[energy_category_id]['stdev']) /
689
                    base[energy_category_id]['stdev'] if (base[energy_category_id]['stdev'] is not None and
690
                                                          base[energy_category_id]['stdev'] > Decimal(0.0))
691
                    else None)
692
                result['reporting_period']['variances'].append(reporting[energy_category_id]['variance'])
693
                result['reporting_period']['variances_increment_rate'].append(
694
                    (reporting[energy_category_id]['variance'] - base[energy_category_id]['variance']) /
695
                    base[energy_category_id]['variance'] if (base[energy_category_id]['variance'] is not None and
696
                                                             base[energy_category_id]['variance'] > Decimal(0.0))
697
                    else None)
698
699
                rate = list()
700
                for index, value in enumerate(reporting[energy_category_id]['values']):
701
                    if index < len(base[energy_category_id]['values']) \
702
                            and base[energy_category_id]['values'][index] != 0 and value != 0:
703
                        rate.append((value - base[energy_category_id]['values'][index])
704
                                    / base[energy_category_id]['values'][index])
705
                    else:
706
                        rate.append(None)
707
                result['reporting_period']['rates'].append(rate)
708
709
        result['parameters'] = {
710
            "names": parameters_data['names'],
711
            "timestamps": parameters_data['timestamps'],
712
            "values": parameters_data['values']
713
        }
714
715
        result['associated_equipment'] = dict()
716
        result['associated_equipment']['energy_category_names'] = list()
717
        result['associated_equipment']['units'] = list()
718
        result['associated_equipment']['associated_equipment_names_array'] = list()
719
        result['associated_equipment']['subtotals_array'] = list()
720
        if energy_category_set is not None and len(energy_category_set) > 0:
721
            for energy_category_id in energy_category_set:
722
                result['associated_equipment']['energy_category_names'].append(
723
                    energy_category_dict[energy_category_id]['name'])
724
                result['associated_equipment']['units'].append(
725
                    energy_category_dict[energy_category_id]['unit_of_measure'])
726
                result['associated_equipment']['associated_equipment_names_array'].append(
727
                    associated_equipment_data[energy_category_id]['associated_equipment_names'])
728
                result['associated_equipment']['subtotals_array'].append(
729
                    associated_equipment_data[energy_category_id]['subtotals'])
730
731
        # export result to Excel file and then encode the file to base64 string
732
        result['excel_bytes_base64'] = None
733
        if not is_quick_mode:
734
            result['excel_bytes_base64'] = \
735
                excelexporters.combinedequipmentstatistics.export(result,
736
                                                                  combined_equipment['name'],
737
                                                                  base_period_start_datetime_local,
738
                                                                  base_period_end_datetime_local,
739
                                                                  reporting_period_start_datetime_local,
740
                                                                  reporting_period_end_datetime_local,
741
                                                                  period_type,
742
                                                                  language)
743
744
        resp.text = json.dumps(result)
745