Passed
Push — master ( d49c92...35fc1e )
by Guangyu
07:19 queued 12s
created

reports.metersaving   F

Complexity

Total Complexity 97

Size/Duplication

Total Lines 628
Duplicated Lines 12.58 %

Importance

Changes 0
Metric Value
wmc 97
eloc 483
dl 79
loc 628
rs 2
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
F Reporting.on_get() 79 595 95
A Reporting.__init__() 0 4 1
A Reporting.on_options() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complexity

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

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

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

1
import re
2
import falcon
3
import simplejson as json
4
import mysql.connector
5
import config
6
from datetime import datetime, timedelta, timezone
7
from core import utilities
8
from decimal import Decimal
9
import excelexporters.metersaving
10
11
12
class Reporting:
13
    @staticmethod
14
    def __init__():
15
        """Initializes Class"""
16
        pass
17
18
    @staticmethod
19
    def on_options(req, resp):
20
        resp.status = falcon.HTTP_200
21
22
    ####################################################################################################################
23
    # PROCEDURES
24
    # Step 1: valid parameters
25
    # Step 2: query the meter and energy category
26
    # Step 3: query associated points
27
    # Step 4: query base period energy saving
28
    # Step 5: query reporting period energy saving
29
    # Step 6: query tariff data
30
    # Step 7: query associated points data
31
    # Step 8: construct the report
32
    ####################################################################################################################
33
    @staticmethod
34
    def on_get(req, resp):
35
        print(req.params)
36
        meter_id = req.params.get('meterid')
37
        meter_uuid = req.params.get('meteruuid')
38
        period_type = req.params.get('periodtype')
39
        base_start_datetime_local = req.params.get('baseperiodstartdatetime')
40
        base_end_datetime_local = req.params.get('baseperiodenddatetime')
41
        reporting_start_datetime_local = req.params.get('reportingperiodstartdatetime')
42
        reporting_end_datetime_local = req.params.get('reportingperiodenddatetime')
43
        quick_mode = req.params.get('quickmode')
44
45
        ################################################################################################################
46
        # Step 1: valid parameters
47
        ################################################################################################################
48
        if meter_id is None and meter_id is None:
49
            raise falcon.HTTPError(falcon.HTTP_400,
50
                                   title='API.BAD_REQUEST',
51
                                   description='API.INVALID_METER_ID')
52
53
        if meter_id is not None:
54
            meter_id = str.strip(meter_id)
55
            if not meter_id.isdigit() or int(meter_id) <= 0:
56
                raise falcon.HTTPError(falcon.HTTP_400,
57
                                       title='API.BAD_REQUEST',
58
                                       description='API.INVALID_METER_ID')
59
60
        if meter_uuid is not None:
61
            regex = re.compile('^[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)
62
            match = regex.match(str.strip(meter_uuid))
63
            if not bool(match):
64
                raise falcon.HTTPError(falcon.HTTP_400,
65
                                       title='API.BAD_REQUEST',
66
                                       description='API.INVALID_METER_UUID')
67
68
        if period_type is None:
69
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_PERIOD_TYPE')
70
        else:
71
            period_type = str.strip(period_type)
72
            if period_type not in ['hourly', 'daily', 'weekly', 'monthly', 'yearly']:
73
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_PERIOD_TYPE')
74
75
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
76
        if config.utc_offset[0] == '-':
77
            timezone_offset = -timezone_offset
78
79
        base_start_datetime_utc = None
80
        if base_start_datetime_local is not None and len(str.strip(base_start_datetime_local)) > 0:
81
            base_start_datetime_local = str.strip(base_start_datetime_local)
82
            try:
83
                base_start_datetime_utc = datetime.strptime(base_start_datetime_local,
84
                                                            '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
85
                                          timedelta(minutes=timezone_offset)
86
            except ValueError:
87
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
88
                                       description="API.INVALID_BASE_PERIOD_START_DATETIME")
89
90
        base_end_datetime_utc = None
91
        if base_end_datetime_local is not None and len(str.strip(base_end_datetime_local)) > 0:
92
            base_end_datetime_local = str.strip(base_end_datetime_local)
93
            try:
94
                base_end_datetime_utc = datetime.strptime(base_end_datetime_local,
95
                                                          '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
96
                                        timedelta(minutes=timezone_offset)
97
            except ValueError:
98
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
99
                                       description="API.INVALID_BASE_PERIOD_END_DATETIME")
100
101
        if base_start_datetime_utc is not None and base_end_datetime_utc is not None and \
102
                base_start_datetime_utc >= base_end_datetime_utc:
103
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
104
                                   description='API.INVALID_BASE_PERIOD_END_DATETIME')
105
106
        if reporting_start_datetime_local is None:
107
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
108
                                   description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
109
        else:
110
            reporting_start_datetime_local = str.strip(reporting_start_datetime_local)
111
            try:
112
                reporting_start_datetime_utc = datetime.strptime(reporting_start_datetime_local,
113
                                                                 '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
114
                                               timedelta(minutes=timezone_offset)
115
            except ValueError:
116
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
117
                                       description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
118
119
        if reporting_end_datetime_local is None:
120
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
121
                                   description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
122
        else:
123
            reporting_end_datetime_local = str.strip(reporting_end_datetime_local)
124
            try:
125
                reporting_end_datetime_utc = datetime.strptime(reporting_end_datetime_local,
126
                                                               '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) - \
127
                                             timedelta(minutes=timezone_offset)
128
            except ValueError:
129
                raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
130
                                       description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
131
132
        if reporting_start_datetime_utc >= reporting_end_datetime_utc:
133
            raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
134
                                   description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
135
136
        # if turn quick mode on, do not return parameters data and excel file
137
        is_quick_mode = False
138
        if quick_mode is not None and \
139
                len(str.strip(quick_mode)) > 0 and \
140
                str.lower(str.strip(quick_mode)) in ('true', 't', 'on', 'yes', 'y'):
141
            is_quick_mode = True
142
            
143
        ################################################################################################################
144
        # Step 2: query the meter and energy category
145
        ################################################################################################################
146
        cnx_system = mysql.connector.connect(**config.myems_system_db)
147
        cursor_system = cnx_system.cursor()
148
149
        cnx_energy = mysql.connector.connect(**config.myems_energy_db)
150
        cursor_energy = cnx_energy.cursor()
151
152
        cnx_energy_baseline = mysql.connector.connect(**config.myems_energy_baseline_db)
153
        cursor_energy_baseline = cnx_energy_baseline.cursor()
154
155
        cnx_historical = mysql.connector.connect(**config.myems_historical_db)
156
        cursor_historical = cnx_historical.cursor()
157
        if meter_id is not None:
158
            cursor_system.execute(" SELECT m.id, m.name, m.cost_center_id, m.energy_category_id, "
159
                                  "        ec.name, ec.unit_of_measure, ec.kgce, ec.kgco2e "
160
                                  " FROM tbl_meters m, tbl_energy_categories ec "
161
                                  " WHERE m.id = %s AND m.energy_category_id = ec.id ", (meter_id,))
162
            row_meter = cursor_system.fetchone()
163
        elif meter_uuid is not None:
164
            cursor_system.execute(" SELECT m.id, m.name, m.cost_center_id, m.energy_category_id, "
165
                                  "        ec.name, ec.unit_of_measure, ec.kgce, ec.kgco2e "
166
                                  " FROM tbl_meters m, tbl_energy_categories ec "
167
                                  " WHERE m.uuid = %s AND m.energy_category_id = ec.id ", (meter_uuid,))
168
            row_meter = cursor_system.fetchone()
169
170
        if row_meter is None:
0 ignored issues
show
introduced by
The variable row_meter does not seem to be defined for all execution paths.
Loading history...
171
            if cursor_system:
172
                cursor_system.close()
173
            if cnx_system:
174
                cnx_system.close()
175
176
            if cursor_energy:
177
                cursor_energy.close()
178
            if cnx_energy:
179
                cnx_energy.close()
180
181
            if cursor_energy_baseline:
182
                cursor_energy_baseline.close()
183
            if cnx_energy_baseline:
184
                cnx_energy_baseline.close()
185
186
            if cursor_historical:
187
                cursor_historical.close()
188
            if cnx_historical:
189
                cnx_historical.close()
190
            raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND', description='API.METER_NOT_FOUND')
191
        
192
        meter = dict()
193
        meter['id'] = row_meter[0]
194
        meter['name'] = row_meter[1]
195
        meter['cost_center_id'] = row_meter[2]
196
        meter['energy_category_id'] = row_meter[3]
197
        meter['energy_category_name'] = row_meter[4]
198
        meter['unit_of_measure'] = row_meter[5]
199
        meter['kgce'] = row_meter[6]
200
        meter['kgco2e'] = row_meter[7]
201
        ################################################################################################################
202
        # Step 3: query associated points
203
        ################################################################################################################
204
        point_list = list()
205
        cursor_system.execute(" SELECT po.id, po.name, po.units, po.object_type "
206
                              " FROM tbl_meters me, tbl_meters_points mepo, tbl_points po "
207
                              " WHERE me.id = %s AND me.id = mepo.meter_id AND mepo.point_id = po.id "
208
                              " ORDER BY po.id ", (meter['id'],))
209
        rows_points = cursor_system.fetchall()
210
        if rows_points is not None and len(rows_points) > 0:
211
            for row in rows_points:
212
                point_list.append({"id": row[0], "name": row[1], "units": row[2], "object_type": row[3]})
213
214
        ################################################################################################################
215
        # Step 4: query base period energy saving
216
        ################################################################################################################
217
        kgce = meter['kgce']
218
        kgco2e = meter['kgco2e']
219
        base = dict()
220
        base['timestamps'] = list()
221
        base['values_baseline'] = list()
222
        base['values_actual'] = list()
223
        base['values_saving'] = list()
224
        base['subtotal_baseline'] = Decimal(0.0)
225
        base['subtotal_actual'] = Decimal(0.0)
226
        base['subtotal_saving'] = Decimal(0.0)
227
        base['subtotal_in_kgce_baseline'] = Decimal(0.0)
228
        base['subtotal_in_kgce_actual'] = Decimal(0.0)
229
        base['subtotal_in_kgce_saving'] = Decimal(0.0)
230
        base['subtotal_in_kgco2e_baseline'] = Decimal(0.0)
231
        base['subtotal_in_kgco2e_actual'] = Decimal(0.0)
232
        base['subtotal_in_kgco2e_saving'] = Decimal(0.0)
233
        # query base period's energy baseline
234
        cursor_energy_baseline.execute(" SELECT start_datetime_utc, actual_value "
235
                                       " FROM tbl_meter_hourly "
236
                                       " WHERE meter_id = %s "
237
                                       " AND start_datetime_utc >= %s "
238
                                       " AND start_datetime_utc < %s "
239
                                       " ORDER BY start_datetime_utc ",
240
                                       (meter['id'],
241
                                        base_start_datetime_utc,
242
                                        base_end_datetime_utc))
243
        rows_meter_hourly = cursor_energy_baseline.fetchall()
244
245
        rows_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_meter_hourly,
246
                                                                            base_start_datetime_utc,
247
                                                                            base_end_datetime_utc,
248
                                                                            period_type)
249
        for row_meter_periodically in rows_meter_periodically:
250
            current_datetime_local = row_meter_periodically[0].replace(tzinfo=timezone.utc) + \
251
                                     timedelta(minutes=timezone_offset)
252
            if period_type == 'hourly':
253
                current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
254
            elif period_type == 'daily':
255
                current_datetime = current_datetime_local.strftime('%Y-%m-%d')
256
            elif period_type == 'weekly':
257
                current_datetime = current_datetime_local.strftime('%Y-%m-%d')
258
            elif period_type == 'monthly':
259
                current_datetime = current_datetime_local.strftime('%Y-%m')
260
            elif period_type == 'yearly':
261
                current_datetime = current_datetime_local.strftime('%Y')
262
263
            baseline_value = Decimal(0.0) if row_meter_periodically[1] is None else row_meter_periodically[1]
264
            base['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...
265
            base['values_baseline'].append(baseline_value)
266
            base['subtotal_baseline'] += baseline_value
267
            base['subtotal_in_kgce_baseline'] += baseline_value * kgce
268
            base['subtotal_in_kgco2e_baseline'] += baseline_value * kgco2e
269
270
        # query base period's energy actual
271
        cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
272
                              " FROM tbl_meter_hourly "
273
                              " WHERE meter_id = %s "
274
                              "     AND start_datetime_utc >= %s "
275
                              "     AND start_datetime_utc < %s "
276
                              " ORDER BY start_datetime_utc ",
277
                              (meter['id'],
278
                               base_start_datetime_utc,
279
                               base_end_datetime_utc))
280
        rows_meter_hourly = cursor_energy.fetchall()
281
282
        rows_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_meter_hourly,
283
                                                                            base_start_datetime_utc,
284
                                                                            base_end_datetime_utc,
285
                                                                            period_type)
286
        for row_meter_periodically in rows_meter_periodically:
287
            current_datetime_local = row_meter_periodically[0].replace(tzinfo=timezone.utc) + \
288
                                     timedelta(minutes=timezone_offset)
289
            if period_type == 'hourly':
290
                current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
291
            elif period_type == 'daily':
292
                current_datetime = current_datetime_local.strftime('%Y-%m-%d')
293
            elif period_type == 'weekly':
294
                current_datetime = current_datetime_local.strftime('%Y-%m-%d')
295
            elif period_type == 'monthly':
296
                current_datetime = current_datetime_local.strftime('%Y-%m')
297
            elif period_type == 'yearly':
298
                current_datetime = current_datetime_local.strftime('%Y')
299
300
            actual_value = Decimal(0.0) if row_meter_periodically[1] is None else row_meter_periodically[1]
301
            base['values_actual'].append(actual_value)
302
            base['subtotal_actual'] += actual_value
303
            base['subtotal_in_kgce_actual'] += actual_value * kgce
304
            base['subtotal_in_kgco2e_actual'] += actual_value * kgco2e
305
        # calculate base period's energy savings
306
        for i in range(len(base['values_baseline'])):
307
            base['values_saving'].append(
308
                base['values_baseline'][i] -
309
                base['values_actual'][i])
310
311
        base['subtotal_saving'] = \
312
            base['subtotal_baseline'] - \
313
            base['subtotal_actual']
314
        base['subtotal_in_kgce_saving'] = \
315
            base['subtotal_in_kgce_baseline'] - \
316
            base['subtotal_in_kgce_actual']
317
        base['subtotal_in_kgco2e_saving'] = \
318
            base['subtotal_in_kgco2e_baseline'] - \
319
            base['subtotal_in_kgco2e_actual']
320
        ################################################################################################################
321
        # Step 5: query reporting period energy saving
322
        ################################################################################################################
323
        reporting = dict()
324
        kgce = meter['kgce']
325
        kgco2e = meter['kgco2e']
326
327
        reporting = dict()
328
        reporting['timestamps'] = list()
329
        reporting['values_baseline'] = list()
330
        reporting['values_actual'] = list()
331
        reporting['values_saving'] = list()
332
        reporting['subtotal_baseline'] = Decimal(0.0)
333
        reporting['subtotal_actual'] = Decimal(0.0)
334
        reporting['subtotal_saving'] = Decimal(0.0)
335
        reporting['subtotal_in_kgce_baseline'] = Decimal(0.0)
336
        reporting['subtotal_in_kgce_actual'] = Decimal(0.0)
337
        reporting['subtotal_in_kgce_saving'] = Decimal(0.0)
338
        reporting['subtotal_in_kgco2e_baseline'] = Decimal(0.0)
339
        reporting['subtotal_in_kgco2e_actual'] = Decimal(0.0)
340
        reporting['subtotal_in_kgco2e_saving'] = Decimal(0.0)
341
        # query reporting period's energy baseline
342
        cursor_energy_baseline.execute(" SELECT start_datetime_utc, actual_value "
343
                                       " FROM tbl_meter_hourly "
344
                                       " WHERE meter_id = %s "
345
                                       "     AND start_datetime_utc >= %s "
346
                                       "     AND start_datetime_utc < %s "
347
                                       " ORDER BY start_datetime_utc ",
348
                                       (meter['id'],
349
                                        reporting_start_datetime_utc,
350
                                        reporting_end_datetime_utc))
351
        rows_meter_hourly = cursor_energy_baseline.fetchall()
352
353
        rows_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_meter_hourly,
354
                                                                            reporting_start_datetime_utc,
355
                                                                            reporting_end_datetime_utc,
356
                                                                            period_type)
357
        for row_meter_periodically in rows_meter_periodically:
358
            current_datetime_local = row_meter_periodically[0].replace(tzinfo=timezone.utc) + \
359
                                     timedelta(minutes=timezone_offset)
360
            if period_type == 'hourly':
361
                current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
362
            elif period_type == 'daily':
363
                current_datetime = current_datetime_local.strftime('%Y-%m-%d')
364
            elif period_type == 'weekly':
365
                current_datetime = current_datetime_local.strftime('%Y-%m-%d')
366
            elif period_type == 'monthly':
367
                current_datetime = current_datetime_local.strftime('%Y-%m')
368
            elif period_type == 'yearly':
369
                current_datetime = current_datetime_local.strftime('%Y')
370
371
            baseline_value = Decimal(0.0) if row_meter_periodically[1] is None else row_meter_periodically[1]
372
            reporting['timestamps'].append(current_datetime)
373
            reporting['values_baseline'].append(baseline_value)
374
            reporting['subtotal_baseline'] += baseline_value
375
            reporting['subtotal_in_kgce_baseline'] += baseline_value * kgce
376
            reporting['subtotal_in_kgco2e_baseline'] += baseline_value * kgco2e
377
378
        # query reporting period's energy actual
379
        cursor_energy.execute(" SELECT start_datetime_utc, actual_value "
380
                              " FROM tbl_meter_hourly "
381
                              " WHERE meter_id = %s "
382
                              "     AND start_datetime_utc >= %s "
383
                              "     AND start_datetime_utc < %s "
384
                              " ORDER BY start_datetime_utc ",
385
                              (meter['id'],
386
                               reporting_start_datetime_utc,
387
                               reporting_end_datetime_utc))
388
        rows_meter_hourly = cursor_energy.fetchall()
389
390
        rows_meter_periodically = utilities.aggregate_hourly_data_by_period(rows_meter_hourly,
391
                                                                            reporting_start_datetime_utc,
392
                                                                            reporting_end_datetime_utc,
393
                                                                            period_type)
394
        for row_meter_periodically in rows_meter_periodically:
395
            actual_value = Decimal(0.0) if row_meter_periodically[1] is None else row_meter_periodically[1]
396
            reporting['values_actual'].append(actual_value)
397
            reporting['subtotal_actual'] += actual_value
398
            reporting['subtotal_in_kgce_actual'] += actual_value * kgce
399
            reporting['subtotal_in_kgco2e_actual'] += actual_value * kgco2e
400
401
        # calculate reporting period's energy savings
402
        for i in range(len(reporting['values_baseline'])):
403
            reporting['values_saving'].append(
404
                reporting['values_baseline'][i] -
405
                reporting['values_actual'][i])
406
407
        reporting['subtotal_saving'] = \
408
            reporting['subtotal_baseline'] - \
409
            reporting['subtotal_actual']
410
        reporting['subtotal_in_kgce_saving'] = \
411
            reporting['subtotal_in_kgce_baseline'] - \
412
            reporting['subtotal_in_kgce_actual']
413
        reporting['subtotal_in_kgco2e_saving'] = \
414
            reporting['subtotal_in_kgco2e_baseline'] - \
415
            reporting['subtotal_in_kgco2e_actual']
416
        ################################################################################################################
417
        # Step 6: query tariff data
418
        ################################################################################################################
419
        parameters_data = dict()
420
        parameters_data['names'] = list()
421
        parameters_data['timestamps'] = list()
422
        parameters_data['values'] = list()
423
        energy_category_id = meter['energy_category_id']
424 View Code Duplication
        if not is_quick_mode:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
425
            energy_category_tariff_dict = utilities.get_energy_category_tariffs(meter['cost_center_id'],
426
                                                                                energy_category_id,
427
                                                                                reporting_start_datetime_utc,
428
                                                                                reporting_end_datetime_utc)
429
            tariff_timestamp_list = list()
430
            tariff_value_list = list()
431
            for key, value in energy_category_tariff_dict.items():
432
                # convert k from utc to local
433
                key = key + timedelta(minutes=timezone_offset)
434
                tariff_timestamp_list.append(key.isoformat()[0:19][0:19])
435
                tariff_value_list.append(value)
436
437
                parameters_data['names'].append('TARIFF-' + meter['name'])
438
                parameters_data['timestamps'].append(tariff_timestamp_list)
439
                parameters_data['values'].append(tariff_value_list)
440
        ################################################################################################################
441
        # Step 7: query associated points data
442
        ################################################################################################################
443 View Code Duplication
        if not is_quick_mode:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
444
            for point in point_list:
445
                point_values = []
446
                point_timestamps = []
447
                if point['object_type'] == 'ANALOG_VALUE':
448
                    query = (" SELECT utc_date_time, actual_value "
449
                             " FROM tbl_analog_value "
450
                             " WHERE point_id = %s "
451
                             "       AND utc_date_time BETWEEN %s AND %s "
452
                             " ORDER BY utc_date_time ")
453
                    cursor_historical.execute(query, (point['id'],
454
                                                      reporting_start_datetime_utc,
455
                                                      reporting_end_datetime_utc))
456
                    rows = cursor_historical.fetchall()
457
458
                    if rows is not None and len(rows) > 0:
459
                        for row in rows:
460
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
461
                                                     timedelta(minutes=timezone_offset)
462
                            current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
463
                            point_timestamps.append(current_datetime)
464
                            point_values.append(row[1])
465
466
                elif point['object_type'] == 'ENERGY_VALUE':
467
                    query = (" SELECT utc_date_time, actual_value "
468
                             " FROM tbl_energy_value "
469
                             " WHERE point_id = %s "
470
                             "       AND utc_date_time BETWEEN %s AND %s "
471
                             " ORDER BY utc_date_time ")
472
                    cursor_historical.execute(query, (point['id'],
473
                                                      reporting_start_datetime_utc,
474
                                                      reporting_end_datetime_utc))
475
                    rows = cursor_historical.fetchall()
476
477
                    if rows is not None and len(rows) > 0:
478
                        for row in rows:
479
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
480
                                                     timedelta(minutes=timezone_offset)
481
                            current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
482
                            point_timestamps.append(current_datetime)
483
                            point_values.append(row[1])
484
                elif point['object_type'] == 'DIGITAL_VALUE':
485
                    query = (" SELECT utc_date_time, actual_value "
486
                             " FROM tbl_digital_value "
487
                             " WHERE point_id = %s "
488
                             "       AND utc_date_time BETWEEN %s AND %s "
489
                             " ORDER BY utc_date_time ")
490
                    cursor_historical.execute(query, (point['id'],
491
                                                      reporting_start_datetime_utc,
492
                                                      reporting_end_datetime_utc))
493
                    rows = cursor_historical.fetchall()
494
495
                    if rows is not None and len(rows) > 0:
496
                        for row in rows:
497
                            current_datetime_local = row[0].replace(tzinfo=timezone.utc) + \
498
                                                     timedelta(minutes=timezone_offset)
499
                            current_datetime = current_datetime_local.strftime('%Y-%m-%dT%H:%M:%S')
500
                            point_timestamps.append(current_datetime)
501
                            point_values.append(row[1])
502
503
                parameters_data['names'].append(point['name'] + ' (' + point['units'] + ')')
504
                parameters_data['timestamps'].append(point_timestamps)
505
                parameters_data['values'].append(point_values)
506
        ################################################################################################################
507
        # Step 8: construct the report
508
        ################################################################################################################
509
        if cursor_system:
510
            cursor_system.close()
511
        if cnx_system:
512
            cnx_system.close()
513
514
        if cursor_energy:
515
            cursor_energy.close()
516
        if cnx_energy:
517
            cnx_energy.close()
518
519
        if cursor_energy_baseline:
520
            cursor_energy_baseline.close()
521
        if cnx_energy_baseline:
522
            cnx_energy_baseline.close()
523
524
        if cursor_historical:
525
            cursor_historical.close()
526
        if cnx_historical:
527
            cnx_historical.close()
528
529
        result = dict()
530
531
        result['meter'] = dict()
532
        result['meter']['name'] = meter['name']
533
534
        result['base_period'] = dict()
535
        result['base_period']['names'] = list()
536
        result['base_period']['units'] = list()
537
        result['base_period']['timestamps'] = list()
538
        result['base_period']['values_actual'] = list()
539
        result['base_period']['values_baseline'] = list()
540
        result['base_period']['values_saving'] = list()
541
        result['base_period']['subtotals_actual'] = list()
542
        result['base_period']['subtotals_baseline'] = list()
543
        result['base_period']['subtotals_saving'] = list()
544
        result['base_period']['subtotals_in_kgce_saving'] = list()
545
        result['base_period']['subtotals_in_kgco2e_saving'] = list()
546
        result['base_period']['total_in_kgce_saving'] = Decimal(0.0)
547
        result['base_period']['total_in_kgco2e_saving'] = Decimal(0.0)
548
549
        result['base_period']['names'] = meter['name']
550
        result['base_period']['units'] = meter['unit_of_measure']
551
        result['base_period']['timestamps'] = base['timestamps']
552
        result['base_period']['values_actual'] = base['values_actual']
553
        result['base_period']['values_baseline'] = base['values_baseline']
554
        result['base_period']['values_saving'] = base['values_saving']
555
        result['base_period']['subtotals_actual'] = base['subtotal_actual']
556
        result['base_period']['subtotals_baseline'] = base['subtotal_baseline']
557
        result['base_period']['subtotals_saving'] = base['subtotal_saving']
558
        result['base_period']['subtotals_in_kgce_saving'] = base['subtotal_in_kgce_saving']
559
        result['base_period']['subtotals_in_kgco2e_saving'] = base['subtotal_in_kgco2e_saving']
560
        result['base_period']['total_in_kgce_saving'] += base['subtotal_in_kgce_saving']
561
        result['base_period']['total_in_kgco2e_saving'] += base['subtotal_in_kgco2e_saving']
562
563
        result['reporting_period'] = dict()
564
        result['reporting_period']['names'] = list()
565
        result['reporting_period']['energy_category_ids'] = list()
566
        result['reporting_period']['units'] = list()
567
        result['reporting_period']['timestamps'] = list()
568
        result['reporting_period']['values_actual'] = list()
569
        result['reporting_period']['values_baseline'] = list()
570
        result['reporting_period']['values_saving'] = list()
571
        result['reporting_period']['subtotals_actual'] = list()
572
        result['reporting_period']['subtotals_baseline'] = list()
573
        result['reporting_period']['subtotals_saving'] = list()
574
        result['reporting_period']['subtotals_in_kgce_saving'] = list()
575
        result['reporting_period']['subtotals_in_kgco2e_saving'] = list()
576
        result['reporting_period']['increment_rates_saving'] = list()
577
        result['reporting_period']['total_in_kgce_saving'] = Decimal(0.0)
578
        result['reporting_period']['total_in_kgco2e_saving'] = Decimal(0.0)
579
        result['reporting_period']['increment_rate_in_kgce_saving'] = Decimal(0.0)
580
        result['reporting_period']['increment_rate_in_kgco2e_saving'] = Decimal(0.0)
581
582
        result['reporting_period']['names'] = meter['name']
583
        result['reporting_period']['energy_category_ids'] = energy_category_id
584
        result['reporting_period']['units'] = meter['unit_of_measure']
585
        result['reporting_period']['timestamps'] = reporting['timestamps']
586
        result['reporting_period']['values_actual'] = reporting['values_actual']
587
        result['reporting_period']['values_baseline'] = reporting['values_baseline']
588
        result['reporting_period']['values_saving'] = reporting['values_saving']
589
        result['reporting_period']['subtotals_actual'] = reporting['subtotal_actual']
590
        result['reporting_period']['subtotals_baseline'] = reporting['subtotal_baseline']
591
        result['reporting_period']['subtotals_saving'] = reporting['subtotal_saving']
592
        result['reporting_period']['subtotals_in_kgce_saving'] = reporting['subtotal_in_kgce_saving']
593
        result['reporting_period']['subtotals_in_kgco2e_saving'] = reporting['subtotal_in_kgco2e_saving']
594
        result['reporting_period']['increment_rates_saving'] = (
595
            (reporting['subtotal_saving'] - base['subtotal_saving']) /
596
            base['subtotal_saving']
597
            if base['subtotal_saving'] > 0.0 else None)
598
        result['reporting_period']['total_in_kgce_saving'] += \
599
            reporting['subtotal_in_kgce_saving']
600
        result['reporting_period']['total_in_kgco2e_saving'] += \
601
            reporting['subtotal_in_kgco2e_saving']
602
603
        result['reporting_period']['increment_rate_in_kgce_saving'] = \
604
            (result['reporting_period']['total_in_kgce_saving'] - result['base_period']['total_in_kgce_saving']) / \
605
            result['base_period']['total_in_kgce_saving'] \
606
            if result['base_period']['total_in_kgce_saving'] > Decimal(0.0) else None
607
608
        result['reporting_period']['increment_rate_in_kgco2e_saving'] = \
609
            (result['reporting_period']['total_in_kgco2e_saving'] - result['base_period']['total_in_kgco2e_saving']) / \
610
            result['base_period']['total_in_kgco2e_saving'] \
611
            if result['base_period']['total_in_kgco2e_saving'] > Decimal(0.0) else None
612
613
        result['parameters'] = {
614
            "names": parameters_data['names'],
615
            "timestamps": parameters_data['timestamps'],
616
            "values": parameters_data['values']
617
        }
618
619
        # export result to Excel file and then encode the file to base64 string
620
        if not is_quick_mode:
621
            result['excel_bytes_base64'] = excelexporters.metersaving.export(result,
622
                                                                             meter['name'],
623
                                                                             reporting_start_datetime_local,
624
                                                                             reporting_end_datetime_local,
625
                                                                             period_type)
626
627
        resp.text = json.dumps(result)
628