Passed
Push — master ( 15a904...00728e )
by Guangyu
02:04 queued 13s
created

utilities.py (1 issue)

1
from datetime import datetime, timedelta
2
import mysql.connector
3
import collections
4
from decimal import *
5
import config
6
import statistics
7
8
9
########################################################################################################################
10
# Aggregate hourly data by period
11
# rows_hourly: list of (start_datetime_utc, actual_value), should belong to one energy_category_id
12
# start_datetime_utc: start datetime in utc
13
# end_datetime_utc: end datetime in utc
14
# period_type: one of the following period types, 'hourly', 'daily', 'monthly' and 'yearly'
15
# Note: this procedure doesn't work with multiple energy categories
16
########################################################################################################################
17
def aggregate_hourly_data_by_period(rows_hourly, start_datetime_utc, end_datetime_utc, period_type):
18
    # todo: validate parameters
19
    start_datetime_utc = start_datetime_utc.replace(tzinfo=None)
20
    end_datetime_utc = end_datetime_utc.replace(tzinfo=None)
21
22
    if period_type == "hourly":
23
        result_rows_hourly = list()
24
        # todo: add config.working_day_start_time_local
25
        # todo: add config.minutes_to_count
26
        current_datetime_utc = start_datetime_utc.replace(minute=0, second=0, microsecond=0, tzinfo=None)
27
        while current_datetime_utc <= end_datetime_utc:
28
            subtotal = Decimal(0.0)
29
            for row in rows_hourly:
30
                if current_datetime_utc <= row[0] < current_datetime_utc + \
31
                        timedelta(minutes=config.minutes_to_count):
32
                    subtotal += row[1]
33
            result_rows_hourly.append((current_datetime_utc, subtotal))
34
            current_datetime_utc += timedelta(minutes=config.minutes_to_count)
35
36
        return result_rows_hourly
37
38
    elif period_type == "daily":
39
        result_rows_daily = list()
40
        # todo: add config.working_day_start_time_local
41
        # todo: add config.minutes_to_count
42
        # calculate the start datetime in utc of the first day in local
43
        start_datetime_local = start_datetime_utc + timedelta(hours=int(config.utc_offset[1:3]))
44
        current_datetime_utc = start_datetime_local.replace(hour=0) - timedelta(hours=int(config.utc_offset[1:3]))
45
        while current_datetime_utc <= end_datetime_utc:
46
            subtotal = Decimal(0.0)
47
            for row in rows_hourly:
48
                if current_datetime_utc <= row[0] < current_datetime_utc + timedelta(days=1):
49
                    subtotal += row[1]
50
            result_rows_daily.append((current_datetime_utc, subtotal))
51
            current_datetime_utc += timedelta(days=1)
52
53
        return result_rows_daily
54
55
    elif period_type == "monthly":
56
        result_rows_monthly = list()
57
        # todo: add config.working_day_start_time_local
58
        # todo: add config.minutes_to_count
59
        # calculate the start datetime in utc of the first day in the first month in local
60
        start_datetime_local = start_datetime_utc + timedelta(hours=int(config.utc_offset[1:3]))
61
        current_datetime_utc = start_datetime_local.replace(day=1, hour=0) - timedelta(hours=int(config.utc_offset[1:3]))
62
63
        while current_datetime_utc <= end_datetime_utc:
64
            # calculate the next datetime in utc
65 View Code Duplication
            if current_datetime_utc.month == 1:
66
                temp_day = 28
67
                ny = current_datetime_utc.year
68
                if (ny % 100 != 0 and ny % 4 == 0) or (ny % 100 == 0 and ny % 400 == 0):
69
                    temp_day = 29
70
71
                next_datetime_utc = datetime(year=current_datetime_utc.year,
72
                                             month=current_datetime_utc.month + 1,
73
                                             day=temp_day,
74
                                             hour=current_datetime_utc.hour,
75
                                             minute=current_datetime_utc.minute,
76
                                             second=0,
77
                                             microsecond=0,
78
                                             tzinfo=None)
79
            elif current_datetime_utc.month == 2:
80
                next_datetime_utc = datetime(year=current_datetime_utc.year,
81
                                             month=current_datetime_utc.month + 1,
82
                                             day=31,
83
                                             hour=current_datetime_utc.hour,
84
                                             minute=current_datetime_utc.minute,
85
                                             second=0,
86
                                             microsecond=0,
87
                                             tzinfo=None)
88
            elif current_datetime_utc.month in [3, 5, 8, 10]:
89
                next_datetime_utc = datetime(year=current_datetime_utc.year,
90
                                             month=current_datetime_utc.month + 1,
91
                                             day=30,
92
                                             hour=current_datetime_utc.hour,
93
                                             minute=current_datetime_utc.minute,
94
                                             second=0,
95
                                             microsecond=0,
96
                                             tzinfo=None)
97
            elif current_datetime_utc.month == 7:
98
                next_datetime_utc = datetime(year=current_datetime_utc.year,
99
                                             month=current_datetime_utc.month + 1,
100
                                             day=31,
101
                                             hour=current_datetime_utc.hour,
102
                                             minute=current_datetime_utc.minute,
103
                                             second=0,
104
                                             microsecond=0,
105
                                             tzinfo=None)
106
            elif current_datetime_utc.month in [4, 6, 9, 11]:
107
                next_datetime_utc = datetime(year=current_datetime_utc.year,
108
                                             month=current_datetime_utc.month + 1,
109
                                             day=31,
110
                                             hour=current_datetime_utc.hour,
111
                                             minute=current_datetime_utc.minute,
112
                                             second=0,
113
                                             microsecond=0,
114
                                             tzinfo=None)
115
            elif current_datetime_utc.month == 12:
116
                next_datetime_utc = datetime(year=current_datetime_utc.year + 1,
117
                                             month=1,
118
                                             day=31,
119
                                             hour=current_datetime_utc.hour,
120
                                             minute=current_datetime_utc.minute,
121
                                             second=0,
122
                                             microsecond=0,
123
                                             tzinfo=None)
124
125
            subtotal = Decimal(0.0)
126
            for row in rows_hourly:
127
                if current_datetime_utc <= row[0] < next_datetime_utc:
128
                    subtotal += row[1]
129
130
            result_rows_monthly.append((current_datetime_utc, subtotal))
131
            current_datetime_utc = next_datetime_utc
132
133
        return result_rows_monthly
134
135
    elif period_type == "yearly":
136
        result_rows_yearly = list()
137
        # todo: add config.working_day_start_time_local
138
        # todo: add config.minutes_to_count
139
        # calculate the start datetime in utc of the first day in the first month in local
140
        start_datetime_local = start_datetime_utc + timedelta(hours=int(config.utc_offset[1:3]))
141
        current_datetime_utc = start_datetime_local.replace(month=1, day=1, hour=0) - timedelta(
142
            hours=int(config.utc_offset[1:3]))
143
144
        while current_datetime_utc <= end_datetime_utc:
145
            # calculate the next datetime in utc
146
            # todo: timedelta of year
147
            next_datetime_utc = datetime(year=current_datetime_utc.year + 2,
148
                                         month=1,
149
                                         day=1,
150
                                         hour=current_datetime_utc.hour,
151
                                         minute=current_datetime_utc.minute,
152
                                         second=current_datetime_utc.second,
153
                                         microsecond=current_datetime_utc.microsecond,
154
                                         tzinfo=current_datetime_utc.tzinfo) - timedelta(days=1)
155
            subtotal = Decimal(0.0)
156
            for row in rows_hourly:
157
                if current_datetime_utc <= row[0] < next_datetime_utc:
158
                    subtotal += row[1]
159
160
            result_rows_yearly.append((current_datetime_utc, subtotal))
161
            current_datetime_utc = next_datetime_utc
162
        return result_rows_yearly
163
164
165
########################################################################################################################
166
# Get tariffs by energy category
167
########################################################################################################################
168 View Code Duplication
def get_energy_category_tariffs(cost_center_id, energy_category_id, start_datetime_utc, end_datetime_utc):
169
    # todo: validate parameters
170
    if cost_center_id is None:
171
        return dict()
172
173
    start_datetime_utc = start_datetime_utc.replace(tzinfo=None)
174
    end_datetime_utc = end_datetime_utc.replace(tzinfo=None)
175
176
    # get timezone offset in minutes, this value will be returned to client
177
    timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
178
    if config.utc_offset[0] == '-':
179
        timezone_offset = -timezone_offset
180
181
    tariff_dict = collections.OrderedDict()
182
183
    cnx = None
184
    cursor = None
185
    try:
186
        cnx = mysql.connector.connect(**config.myems_system_db)
187
        cursor = cnx.cursor()
188
        query_tariffs = (" SELECT t.id, t.valid_from_datetime_utc, t.valid_through_datetime_utc "
189
                         " FROM tbl_tariffs t, tbl_cost_centers_tariffs cct "
190
                         " WHERE t.energy_category_id = %s AND "
191
                         "       t.id = cct.tariff_id AND "
192
                         "       cct.cost_center_id = %s AND "
193
                         "       t.valid_through_datetime_utc >= %s AND "
194
                         "       t.valid_from_datetime_utc <= %s "
195
                         " ORDER BY t.valid_from_datetime_utc ")
196
        cursor.execute(query_tariffs, (energy_category_id, cost_center_id, start_datetime_utc, end_datetime_utc,))
197
        rows_tariffs = cursor.fetchall()
198
    except Exception as e:
199
        print(str(e))
200
        if cnx:
201
            cnx.disconnect()
202
        if cursor:
203
            cursor.close()
204
        return dict()
205
206
    if rows_tariffs is None or len(rows_tariffs) == 0:
207
        if cursor:
208
            cursor.close()
209
        if cnx:
210
            cnx.disconnect()
211
        return dict()
212
213
    for row in rows_tariffs:
214
        tariff_dict[row[0]] = {'valid_from_datetime_utc': row[1],
215
                               'valid_through_datetime_utc': row[2],
216
                               'rates': list()}
217
218
    try:
219
        query_timeofuse_tariffs = (" SELECT tariff_id, start_time_of_day, end_time_of_day, price "
220
                                   " FROM tbl_tariffs_timeofuses "
221
                                   " WHERE tariff_id IN ( " + ', '.join(map(str, tariff_dict.keys())) + ")"
222
                                   " ORDER BY tariff_id, start_time_of_day ")
223
        cursor.execute(query_timeofuse_tariffs, )
224
        rows_timeofuse_tariffs = cursor.fetchall()
225
    except Exception as e:
226
        print(str(e))
227
        if cnx:
228
            cnx.disconnect()
229
        if cursor:
230
            cursor.close()
231
        return dict()
232
233
    if cursor:
234
        cursor.close()
235
    if cnx:
236
        cnx.disconnect()
237
238
    if rows_timeofuse_tariffs is None or len(rows_timeofuse_tariffs) == 0:
239
        return dict()
240
241
    for row in rows_timeofuse_tariffs:
242
        tariff_dict[row[0]]['rates'].append({'start_time_of_day': row[1],
243
                                             'end_time_of_day': row[2],
244
                                             'price': row[3]})
245
246
    result = dict()
247
    for tariff_id, tariff_value in tariff_dict.items():
248
        current_datetime_utc = tariff_value['valid_from_datetime_utc']
249
        while current_datetime_utc < tariff_value['valid_through_datetime_utc']:
250
            for rate in tariff_value['rates']:
251
                current_datetime_local = current_datetime_utc + timedelta(minutes=timezone_offset)
252
                seconds_since_midnight = (current_datetime_local -
253
                                          current_datetime_local.replace(hour=0,
254
                                                                         second=0,
255
                                                                         microsecond=0,
256
                                                                         tzinfo=None)).total_seconds()
257
                if rate['start_time_of_day'].total_seconds() <= \
258
                        seconds_since_midnight < rate['end_time_of_day'].total_seconds():
259
                    result[current_datetime_utc] = rate['price']
260
                    break
261
262
            # start from the next time slot
263
            current_datetime_utc += timedelta(minutes=config.minutes_to_count)
264
265
    return {k: v for k, v in result.items() if start_datetime_utc <= k <= end_datetime_utc}
266
267
268
########################################################################################################################
269
# Get peak types of tariff by energy category
270
# peak types: toppeak, onpeak, midpeak, offpeak
271
########################################################################################################################
272 View Code Duplication
def get_energy_category_peak_types(cost_center_id, energy_category_id, start_datetime_utc, end_datetime_utc):
273
    # todo: validate parameters
274
    if cost_center_id is None:
275
        return dict()
276
277
    start_datetime_utc = start_datetime_utc.replace(tzinfo=None)
278
    end_datetime_utc = end_datetime_utc.replace(tzinfo=None)
279
280
    # get timezone offset in minutes, this value will be returned to client
281
    timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
282
    if config.utc_offset[0] == '-':
283
        timezone_offset = -timezone_offset
284
285
    tariff_dict = collections.OrderedDict()
286
287
    cnx = None
288
    cursor = None
289
    try:
290
        cnx = mysql.connector.connect(**config.myems_system_db)
291
        cursor = cnx.cursor()
292
        query_tariffs = (" SELECT t.id, t.valid_from_datetime_utc, t.valid_through_datetime_utc "
293
                         " FROM tbl_tariffs t, tbl_cost_centers_tariffs cct "
294
                         " WHERE t.energy_category_id = %s AND "
295
                         "       t.id = cct.tariff_id AND "
296
                         "       cct.cost_center_id = %s AND "
297
                         "       t.valid_through_datetime_utc >= %s AND "
298
                         "       t.valid_from_datetime_utc <= %s "
299
                         " ORDER BY t.valid_from_datetime_utc ")
300
        cursor.execute(query_tariffs, (energy_category_id, cost_center_id, start_datetime_utc, end_datetime_utc,))
301
        rows_tariffs = cursor.fetchall()
302
    except Exception as e:
303
        print(str(e))
304
        if cnx:
305
            cnx.disconnect()
306
        if cursor:
307
            cursor.close()
308
        return dict()
309
310
    if rows_tariffs is None or len(rows_tariffs) == 0:
311
        if cursor:
312
            cursor.close()
313
        if cnx:
314
            cnx.disconnect()
315
        return dict()
316
317
    for row in rows_tariffs:
318
        tariff_dict[row[0]] = {'valid_from_datetime_utc': row[1],
319
                               'valid_through_datetime_utc': row[2],
320
                               'rates': list()}
321
322
    try:
323
        query_timeofuse_tariffs = (" SELECT tariff_id, start_time_of_day, end_time_of_day, peak_type "
324
                                   " FROM tbl_tariffs_timeofuses "
325
                                   " WHERE tariff_id IN ( " + ', '.join(map(str, tariff_dict.keys())) + ")"
326
                                   " ORDER BY tariff_id, start_time_of_day ")
327
        cursor.execute(query_timeofuse_tariffs, )
328
        rows_timeofuse_tariffs = cursor.fetchall()
329
    except Exception as e:
330
        print(str(e))
331
        if cnx:
332
            cnx.disconnect()
333
        if cursor:
334
            cursor.close()
335
        return dict()
336
337
    if cursor:
338
        cursor.close()
339
    if cnx:
340
        cnx.disconnect()
341
342
    if rows_timeofuse_tariffs is None or len(rows_timeofuse_tariffs) == 0:
343
        return dict()
344
345
    for row in rows_timeofuse_tariffs:
346
        tariff_dict[row[0]]['rates'].append({'start_time_of_day': row[1],
347
                                             'end_time_of_day': row[2],
348
                                             'peak_type': row[3]})
349
350
    result = dict()
351
    for tariff_id, tariff_value in tariff_dict.items():
352
        current_datetime_utc = tariff_value['valid_from_datetime_utc']
353
        while current_datetime_utc < tariff_value['valid_through_datetime_utc']:
354
            for rate in tariff_value['rates']:
355
                current_datetime_local = current_datetime_utc + timedelta(minutes=timezone_offset)
356
                seconds_since_midnight = (current_datetime_local -
357
                                          current_datetime_local.replace(hour=0,
358
                                                                         second=0,
359
                                                                         microsecond=0,
360
                                                                         tzinfo=None)).total_seconds()
361
                if rate['start_time_of_day'].total_seconds() <= \
362
                        seconds_since_midnight < rate['end_time_of_day'].total_seconds():
363
                    result[current_datetime_utc] = rate['peak_type']
364
                    break
365
366
            # start from the next time slot
367
            current_datetime_utc += timedelta(minutes=config.minutes_to_count)
368
369
    return {k: v for k, v in result.items() if start_datetime_utc <= k <= end_datetime_utc}
370
371
372
########################################################################################################################
373
# Averaging calculator of hourly data by period
374
#   rows_hourly: list of (start_datetime_utc, actual_value), should belong to one energy_category_id
375
#   start_datetime_utc: start datetime in utc
376
#   end_datetime_utc: end datetime in utc
377
#   period_type: one of the following period types, 'hourly', 'daily', 'monthly' and 'yearly'
378
# Returns: periodically data of average and maximum
379
# Note: this procedure doesn't work with multiple energy categories
380
########################################################################################################################
381
def averaging_hourly_data_by_period(rows_hourly, start_datetime_utc, end_datetime_utc, period_type):
382
    # todo: validate parameters
383
    start_datetime_utc = start_datetime_utc.replace(tzinfo=None)
384
    end_datetime_utc = end_datetime_utc.replace(tzinfo=None)
385
386
    if period_type == "hourly":
387
        result_rows_hourly = list()
388
        # todo: add config.working_day_start_time_local
389
        # todo: add config.minutes_to_count
390
        total = Decimal(0.0)
391
        maximum = None
392
        counter = 0
393
        current_datetime_utc = start_datetime_utc.replace(minute=0, second=0, microsecond=0, tzinfo=None)
394 View Code Duplication
        while current_datetime_utc <= end_datetime_utc:
395
            sub_total = Decimal(0.0)
396
            sub_maximum = None
397
            sub_counter = 0
398
            for row in rows_hourly:
399
                if current_datetime_utc <= row[0] < current_datetime_utc + \
400
                        timedelta(minutes=config.minutes_to_count):
401
                    sub_total += row[1]
402
                    if sub_maximum is None:
403
                        sub_maximum = row[1]
404
                    elif sub_maximum < row[1]:
405
                        sub_maximum = row[1]
406
                    sub_counter += 1
407
408
            sub_average = (sub_total / sub_counter) if sub_counter > 0 else None
409
            result_rows_hourly.append((current_datetime_utc, sub_average, sub_maximum))
410
411
            total += sub_total
412
            counter += sub_counter
413
            if sub_maximum is None:
414
                pass
415
            elif maximum is None:
416
                maximum = sub_maximum
417
            elif maximum < sub_maximum:
418
                maximum = sub_maximum
419
420
            current_datetime_utc += timedelta(minutes=config.minutes_to_count)
421
422
        average = total / counter if counter > 0 else None
423
        return result_rows_hourly, average, maximum
424
425
    elif period_type == "daily":
426
        result_rows_daily = list()
427
        # todo: add config.working_day_start_time_local
428
        # todo: add config.minutes_to_count
429
        total = Decimal(0.0)
430
        maximum = None
431
        counter = 0
432
        # calculate the start datetime in utc of the first day in local
433
        start_datetime_local = start_datetime_utc + timedelta(hours=int(config.utc_offset[1:3]))
434
        current_datetime_utc = start_datetime_local.replace(hour=0) - timedelta(hours=int(config.utc_offset[1:3]))
435 View Code Duplication
        while current_datetime_utc <= end_datetime_utc:
436
            sub_total = Decimal(0.0)
437
            sub_maximum = None
438
            sub_counter = 0
439
            for row in rows_hourly:
440
                if current_datetime_utc <= row[0] < current_datetime_utc + timedelta(days=1):
441
                    sub_total += row[1]
442
                    if sub_maximum is None:
443
                        sub_maximum = row[1]
444
                    elif sub_maximum < row[1]:
445
                        sub_maximum = row[1]
446
                    sub_counter += 1
447
448
            sub_average = (sub_total / sub_counter) if sub_counter > 0 else None
449
            result_rows_daily.append((current_datetime_utc, sub_average, sub_maximum))
450
            total += sub_total
451
            counter += sub_counter
452
            if sub_maximum is None:
453
                pass
454
            elif maximum is None:
455
                maximum = sub_maximum
456
            elif maximum < sub_maximum:
457
                maximum = sub_maximum
458
            current_datetime_utc += timedelta(days=1)
459
460
        return result_rows_daily, average, maximum
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable average does not seem to be defined.
Loading history...
461
462
    elif period_type == "monthly":
463
        result_rows_monthly = list()
464
        # todo: add config.working_day_start_time_local
465
        # todo: add config.minutes_to_count
466
        total = Decimal(0.0)
467
        maximum = None
468
        counter = 0
469
        # calculate the start datetime in utc of the first day in the first month in local
470
        start_datetime_local = start_datetime_utc + timedelta(hours=int(config.utc_offset[1:3]))
471
        current_datetime_utc = start_datetime_local.replace(day=1, hour=0) - timedelta(hours=int(config.utc_offset[1:3]))
472
473
        while current_datetime_utc <= end_datetime_utc:
474
            # calculate the next datetime in utc
475 View Code Duplication
            if current_datetime_utc.month == 1:
476
                temp_day = 28
477
                ny = current_datetime_utc.year
478
                if (ny % 100 != 0 and ny % 4 == 0) or (ny % 100 == 0 and ny % 400 == 0):
479
                    temp_day = 29
480
481
                next_datetime_utc = datetime(year=current_datetime_utc.year,
482
                                             month=current_datetime_utc.month + 1,
483
                                             day=temp_day,
484
                                             hour=current_datetime_utc.hour,
485
                                             minute=current_datetime_utc.minute,
486
                                             second=0,
487
                                             microsecond=0,
488
                                             tzinfo=None)
489
            elif current_datetime_utc.month == 2:
490
                next_datetime_utc = datetime(year=current_datetime_utc.year,
491
                                             month=current_datetime_utc.month + 1,
492
                                             day=31,
493
                                             hour=current_datetime_utc.hour,
494
                                             minute=current_datetime_utc.minute,
495
                                             second=0,
496
                                             microsecond=0,
497
                                             tzinfo=None)
498
            elif current_datetime_utc.month in [3, 5, 8, 10]:
499
                next_datetime_utc = datetime(year=current_datetime_utc.year,
500
                                             month=current_datetime_utc.month + 1,
501
                                             day=30,
502
                                             hour=current_datetime_utc.hour,
503
                                             minute=current_datetime_utc.minute,
504
                                             second=0,
505
                                             microsecond=0,
506
                                             tzinfo=None)
507
            elif current_datetime_utc.month == 7:
508
                next_datetime_utc = datetime(year=current_datetime_utc.year,
509
                                             month=current_datetime_utc.month + 1,
510
                                             day=31,
511
                                             hour=current_datetime_utc.hour,
512
                                             minute=current_datetime_utc.minute,
513
                                             second=0,
514
                                             microsecond=0,
515
                                             tzinfo=None)
516
            elif current_datetime_utc.month in [4, 6, 9, 11]:
517
                next_datetime_utc = datetime(year=current_datetime_utc.year,
518
                                             month=current_datetime_utc.month + 1,
519
                                             day=31,
520
                                             hour=current_datetime_utc.hour,
521
                                             minute=current_datetime_utc.minute,
522
                                             second=0,
523
                                             microsecond=0,
524
                                             tzinfo=None)
525
            elif current_datetime_utc.month == 12:
526
                next_datetime_utc = datetime(year=current_datetime_utc.year + 1,
527
                                             month=1,
528
                                             day=31,
529
                                             hour=current_datetime_utc.hour,
530
                                             minute=current_datetime_utc.minute,
531
                                             second=0,
532
                                             microsecond=0,
533
                                             tzinfo=None)
534
535
            sub_total = Decimal(0.0)
536
            sub_maximum = None
537
            sub_counter = 0
538
            for row in rows_hourly:
539
                if current_datetime_utc <= row[0] < next_datetime_utc:
540
                    sub_total += row[1]
541
                    if sub_maximum is None:
542
                        sub_maximum = row[1]
543
                    elif sub_maximum < row[1]:
544
                        sub_maximum = row[1]
545
                    sub_counter += 1
546
547
            sub_average = (sub_total / sub_counter) if sub_counter > 0 else None
548
            result_rows_monthly.append((current_datetime_utc, sub_average, sub_maximum))
549
            total += sub_total
550
            counter += sub_counter
551
            if sub_maximum is None:
552
                pass
553
            elif maximum is None:
554
                maximum = sub_maximum
555
            elif maximum < sub_maximum:
556
                maximum = sub_maximum
557
            current_datetime_utc = next_datetime_utc
558
559
        average = total / counter if counter > 0 else None
560
        return result_rows_monthly, average, maximum
561
562
    elif period_type == "yearly":
563
        result_rows_yearly = list()
564
        # todo: add config.working_day_start_time_local
565
        # todo: add config.minutes_to_count
566
        total = Decimal(0.0)
567
        maximum = None
568
        counter = 0
569
        # calculate the start datetime in utc of the first day in the first month in local
570
        start_datetime_local = start_datetime_utc + timedelta(hours=int(config.utc_offset[1:3]))
571
        current_datetime_utc = start_datetime_local.replace(month=1, day=1, hour=0) - timedelta(
572
            hours=int(config.utc_offset[1:3]))
573
574
        while current_datetime_utc <= end_datetime_utc:
575
            # calculate the next datetime in utc
576
            # todo: timedelta of year
577
            next_datetime_utc = datetime(year=current_datetime_utc.year + 2,
578
                                         month=1,
579
                                         day=1,
580
                                         hour=current_datetime_utc.hour,
581
                                         minute=current_datetime_utc.minute,
582
                                         second=current_datetime_utc.second,
583
                                         microsecond=current_datetime_utc.microsecond,
584
                                         tzinfo=current_datetime_utc.tzinfo) - timedelta(days=1)
585
            sub_total = Decimal(0.0)
586
            sub_maximum = None
587
            sub_counter = 0
588
            for row in rows_hourly:
589
                if current_datetime_utc <= row[0] < next_datetime_utc:
590
                    sub_total += row[1]
591
                    if sub_maximum is None:
592
                        sub_maximum = row[1]
593
                    elif sub_maximum < row[1]:
594
                        sub_maximum = row[1]
595
                    sub_counter += 1
596
597
            sub_average = (sub_total / sub_counter) if sub_counter > 0 else None
598
            result_rows_yearly.append((current_datetime_utc, sub_average, sub_maximum))
599
            total += sub_total
600
            counter += sub_counter
601
            if sub_maximum is None:
602
                pass
603
            elif maximum is None:
604
                maximum = sub_maximum
605
            elif maximum < sub_maximum:
606
                maximum = sub_maximum
607
            current_datetime_utc = next_datetime_utc
608
609
        average = total / counter if counter > 0 else None
610
        return result_rows_yearly, average, maximum
611
612
613
########################################################################################################################
614
# Statistics calculator of hourly data by period
615
#   rows_hourly: list of (start_datetime_utc, actual_value), should belong to one energy_category_id
616
#   start_datetime_utc: start datetime in utc
617
#   end_datetime_utc: end datetime in utc
618
#   period_type: one of the following period types, 'hourly', 'daily', 'monthly' and 'yearly'
619
# Returns: periodically data of values and statistics of mean, median, minimum, maximum, stdev and variance
620
# Note: this procedure doesn't work with multiple energy categories
621
########################################################################################################################
622
def statistics_hourly_data_by_period(rows_hourly, start_datetime_utc, end_datetime_utc, period_type):
623
    # todo: validate parameters
624
    start_datetime_utc = start_datetime_utc.replace(tzinfo=None)
625
    end_datetime_utc = end_datetime_utc.replace(tzinfo=None)
626
627
    if period_type == "hourly":
628
        result_rows_hourly = list()
629
        sample_data = list()
630
        # todo: add config.working_day_start_time_local
631
        # todo: add config.minutes_to_count
632
        counter = 0
633
        mean = None
634
        median = None
635
        minimum = None
636
        maximum = None
637
        stdev = None
638
        variance = None
639
        current_datetime_utc = start_datetime_utc.replace(minute=0, second=0, microsecond=0, tzinfo=None)
640 View Code Duplication
        while current_datetime_utc <= end_datetime_utc:
641
            sub_total = Decimal(0.0)
642
            for row in rows_hourly:
643
                if current_datetime_utc <= row[0] < current_datetime_utc + \
644
                        timedelta(minutes=config.minutes_to_count):
645
                    sub_total += row[1]
646
647
            result_rows_hourly.append((current_datetime_utc, sub_total))
648
            sample_data.append(sub_total)
649
650
            counter += 1
651
            if minimum is None:
652
                minimum = sub_total
653
            elif minimum > sub_total:
654
                minimum = sub_total
655
656
            if maximum is None:
657
                maximum = sub_total
658
            elif maximum < sub_total:
659
                maximum = sub_total
660
661
            current_datetime_utc += timedelta(minutes=config.minutes_to_count)
662
663
        if len(sample_data) > 1:
664
            mean = statistics.mean(sample_data)
665
            median = statistics.median(sample_data)
666
            stdev = statistics.stdev(sample_data)
667
            variance = statistics.variance(sample_data)
668
669
        return result_rows_hourly, mean, median, minimum, maximum, stdev, variance
670
671
    elif period_type == "daily":
672
        result_rows_daily = list()
673
        sample_data = list()
674
        # todo: add config.working_day_start_time_local
675
        # todo: add config.minutes_to_count
676
        counter = 0
677
        mean = None
678
        median = None
679
        minimum = None
680
        maximum = None
681
        stdev = None
682
        variance = None
683
        # calculate the start datetime in utc of the first day in local
684
        start_datetime_local = start_datetime_utc + timedelta(hours=int(config.utc_offset[1:3]))
685
        current_datetime_utc = start_datetime_local.replace(hour=0) - timedelta(hours=int(config.utc_offset[1:3]))
686 View Code Duplication
        while current_datetime_utc <= end_datetime_utc:
687
            sub_total = Decimal(0.0)
688
            for row in rows_hourly:
689
                if current_datetime_utc <= row[0] < current_datetime_utc + timedelta(days=1):
690
                    sub_total += row[1]
691
692
            result_rows_daily.append((current_datetime_utc, sub_total))
693
            sample_data.append(sub_total)
694
695
            counter += 1
696
            if minimum is None:
697
                minimum = sub_total
698
            elif minimum > sub_total:
699
                minimum = sub_total
700
701
            if maximum is None:
702
                maximum = sub_total
703
            elif maximum < sub_total:
704
                maximum = sub_total
705
            current_datetime_utc += timedelta(days=1)
706
707
        if len(sample_data) > 1:
708
            mean = statistics.mean(sample_data)
709
            median = statistics.median(sample_data)
710
            stdev = statistics.stdev(sample_data)
711
            variance = statistics.variance(sample_data)
712
713
        return result_rows_daily, mean, median, minimum, maximum, stdev, variance
714
715
    elif period_type == "monthly":
716
        result_rows_monthly = list()
717
        sample_data = list()
718
        # todo: add config.working_day_start_time_local
719
        # todo: add config.minutes_to_count
720
        counter = 0
721
        mean = None
722
        median = None
723
        minimum = None
724
        maximum = None
725
        stdev = None
726
        variance = None
727
        # calculate the start datetime in utc of the first day in the first month in local
728
        start_datetime_local = start_datetime_utc + timedelta(hours=int(config.utc_offset[1:3]))
729
        current_datetime_utc = start_datetime_local.replace(day=1, hour=0) - timedelta(hours=int(config.utc_offset[1:3]))
730
731
        while current_datetime_utc <= end_datetime_utc:
732
            # calculate the next datetime in utc
733 View Code Duplication
            if current_datetime_utc.month == 1:
734
                temp_day = 28
735
                ny = current_datetime_utc.year
736
                if (ny % 100 != 0 and ny % 4 == 0) or (ny % 100 == 0 and ny % 400 == 0):
737
                    temp_day = 29
738
739
                next_datetime_utc = datetime(year=current_datetime_utc.year,
740
                                             month=current_datetime_utc.month + 1,
741
                                             day=temp_day,
742
                                             hour=current_datetime_utc.hour,
743
                                             minute=current_datetime_utc.minute,
744
                                             second=0,
745
                                             microsecond=0,
746
                                             tzinfo=None)
747
            elif current_datetime_utc.month == 2:
748
                next_datetime_utc = datetime(year=current_datetime_utc.year,
749
                                             month=current_datetime_utc.month + 1,
750
                                             day=31,
751
                                             hour=current_datetime_utc.hour,
752
                                             minute=current_datetime_utc.minute,
753
                                             second=0,
754
                                             microsecond=0,
755
                                             tzinfo=None)
756
            elif current_datetime_utc.month in [3, 5, 8, 10]:
757
                next_datetime_utc = datetime(year=current_datetime_utc.year,
758
                                             month=current_datetime_utc.month + 1,
759
                                             day=30,
760
                                             hour=current_datetime_utc.hour,
761
                                             minute=current_datetime_utc.minute,
762
                                             second=0,
763
                                             microsecond=0,
764
                                             tzinfo=None)
765
            elif current_datetime_utc.month == 7:
766
                next_datetime_utc = datetime(year=current_datetime_utc.year,
767
                                             month=current_datetime_utc.month + 1,
768
                                             day=31,
769
                                             hour=current_datetime_utc.hour,
770
                                             minute=current_datetime_utc.minute,
771
                                             second=0,
772
                                             microsecond=0,
773
                                             tzinfo=None)
774
            elif current_datetime_utc.month in [4, 6, 9, 11]:
775
                next_datetime_utc = datetime(year=current_datetime_utc.year,
776
                                             month=current_datetime_utc.month + 1,
777
                                             day=31,
778
                                             hour=current_datetime_utc.hour,
779
                                             minute=current_datetime_utc.minute,
780
                                             second=0,
781
                                             microsecond=0,
782
                                             tzinfo=None)
783
            elif current_datetime_utc.month == 12:
784
                next_datetime_utc = datetime(year=current_datetime_utc.year + 1,
785
                                             month=1,
786
                                             day=31,
787
                                             hour=current_datetime_utc.hour,
788
                                             minute=current_datetime_utc.minute,
789
                                             second=0,
790
                                             microsecond=0,
791
                                             tzinfo=None)
792
793
            sub_total = Decimal(0.0)
794
            for row in rows_hourly:
795
                if current_datetime_utc <= row[0] < next_datetime_utc:
796
                    sub_total += row[1]
797
798
            result_rows_monthly.append((current_datetime_utc, sub_total))
799
            sample_data.append(sub_total)
800
801
            counter += 1
802
            if minimum is None:
803
                minimum = sub_total
804
            elif minimum > sub_total:
805
                minimum = sub_total
806
807
            if maximum is None:
808
                maximum = sub_total
809
            elif maximum < sub_total:
810
                maximum = sub_total
811
            current_datetime_utc = next_datetime_utc
812
813
        if len(sample_data) > 1:
814
            mean = statistics.mean(sample_data)
815
            median = statistics.median(sample_data)
816
            stdev = statistics.stdev(sample_data)
817
            variance = statistics.variance(sample_data)
818
819
        return result_rows_monthly, mean, median, minimum, maximum, stdev, variance
820
821
    elif period_type == "yearly":
822
        result_rows_yearly = list()
823
        sample_data = list()
824
        # todo: add config.working_day_start_time_local
825
        # todo: add config.minutes_to_count
826
        mean = None
827
        median = None
828
        minimum = None
829
        maximum = None
830
        stdev = None
831
        variance = None
832
        # calculate the start datetime in utc of the first day in the first month in local
833
        start_datetime_local = start_datetime_utc + timedelta(hours=int(config.utc_offset[1:3]))
834
        current_datetime_utc = start_datetime_local.replace(month=1, day=1, hour=0) - timedelta(
835
            hours=int(config.utc_offset[1:3]))
836
837
        while current_datetime_utc <= end_datetime_utc:
838
            # calculate the next datetime in utc
839
            # todo: timedelta of year
840
            next_datetime_utc = datetime(year=current_datetime_utc.year + 2,
841
                                         month=1,
842
                                         day=1,
843
                                         hour=current_datetime_utc.hour,
844
                                         minute=current_datetime_utc.minute,
845
                                         second=current_datetime_utc.second,
846
                                         microsecond=current_datetime_utc.microsecond,
847
                                         tzinfo=current_datetime_utc.tzinfo) - timedelta(days=1)
848
            sub_total = Decimal(0.0)
849
            for row in rows_hourly:
850
                if current_datetime_utc <= row[0] < next_datetime_utc:
851
                    sub_total += row[1]
852
853
            result_rows_yearly.append((current_datetime_utc, sub_total))
854
            sample_data.append(sub_total)
855
856
            if minimum is None:
857
                minimum = sub_total
858
            elif minimum > sub_total:
859
                minimum = sub_total
860
            if maximum is None:
861
                maximum = sub_total
862
            elif maximum < sub_total:
863
                maximum = sub_total
864
865
            current_datetime_utc = next_datetime_utc
866
867
        if len(sample_data) > 1:
868
            mean = statistics.mean(sample_data)
869
            median = statistics.median(sample_data)
870
            stdev = statistics.stdev(sample_data)
871
            variance = statistics.variance(sample_data)
872
873
        return result_rows_yearly, mean, median, minimum, maximum, stdev, variance
874