Passed
Push — master ( db6f57...6c9994 )
by
unknown
09:19 queued 17s
created

reports.meterplan.Reporting.on_get()   F

Complexity

Conditions 110

Size

Total Lines 572
Code Lines 427

Duplication

Lines 572
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 427
dl 572
loc 572
rs 0
c 0
b 0
f 0
cc 110
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

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