Passed
Push — master ( 8ba0c1...7a1547 )
by Guangyu
09:24 queued 12s
created

excelexporters.metersaving.sum_list()   A

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 2
nop 1
1
import base64
2
import uuid
3
import os
4
import re
5
from openpyxl.chart import LineChart, Reference
6
from openpyxl.styles import PatternFill, Border, Side, Alignment, Font
7
from openpyxl.drawing.image import Image
8
from openpyxl import Workbook
9
from openpyxl.chart.label import DataLabelList
10
import openpyxl.utils.cell as format_cell
11
12
13
########################################################################################################################
14
# PROCEDURES
15
# Step 1: Validate the report data
16
# Step 2: Generate excel file from the report data
17
# Step 3: Encode the excel file to Base64
18
########################################################################################################################
19
20 View Code Duplication
def export(report, name, reporting_start_datetime_local, reporting_end_datetime_local, period_type):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
21
    ####################################################################################################################
22
    # Step 1: Validate the report data
23
    ####################################################################################################################
24
    if report is None:
25
        return None
26
27
    ####################################################################################################################
28
    # Step 2: Generate excel file from the report data
29
    ####################################################################################################################
30
    filename = generate_excel(report,
31
                              name,
32
                              reporting_start_datetime_local,
33
                              reporting_end_datetime_local,
34
                              period_type)
35
    ####################################################################################################################
36
    # Step 3: Encode the excel file to Base64
37
    ####################################################################################################################
38
    binary_file_data = b''
39
    try:
40
        with open(filename, 'rb') as binary_file:
41
            binary_file_data = binary_file.read()
42
    except IOError as ex:
43
        pass
44
45
    # Base64 encode the bytes
46
    base64_encoded_data = base64.b64encode(binary_file_data)
47
    # get the Base64 encoded data using human-readable characters.
48
    base64_message = base64_encoded_data.decode('utf-8')
49
    # delete the file from server
50
    try:
51
        os.remove(filename)
52
    except NotImplementedError as ex:
53
        pass
54
    return base64_message
55
56
57 View Code Duplication
def generate_excel(report, name, reporting_start_datetime_local, reporting_end_datetime_local, period_type):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
58
59
    wb = Workbook()
60
    ws = wb.active
61
    ws.title = "MeterSaving"
62
63
    # Row height
64
    ws.row_dimensions[1].height = 102
65
    for i in range(2, 2000 + 1):
66
        ws.row_dimensions[i].height = 42
67
68
    # Col width
69
    ws.column_dimensions['A'].width = 1.5
70
71
    ws.column_dimensions['B'].width = 25.0
72
73
    for i in range(ord('C'), ord('L')):
74
        ws.column_dimensions[chr(i)].width = 15.0
75
76
    # Font
77
    name_font = Font(name='Arial', size=15, bold=True)
78
    title_font = Font(name='Arial', size=15, bold=True)
79
80
    table_fill = PatternFill(fill_type='solid', fgColor='1F497D')
81
    f_border = Border(left=Side(border_style='medium', color='00000000'),
82
                      right=Side(border_style='medium', color='00000000'),
83
                      bottom=Side(border_style='medium', color='00000000'),
84
                      top=Side(border_style='medium', color='00000000')
85
                      )
86
    b_border = Border(
87
        bottom=Side(border_style='medium', color='00000000'),
88
    )
89
90
    b_c_alignment = Alignment(vertical='bottom',
91
                              horizontal='center',
92
                              text_rotation=0,
93
                              wrap_text=True,
94
                              shrink_to_fit=False,
95
                              indent=0)
96
    c_c_alignment = Alignment(vertical='center',
97
                              horizontal='center',
98
                              text_rotation=0,
99
                              wrap_text=True,
100
                              shrink_to_fit=False,
101
                              indent=0)
102
    b_r_alignment = Alignment(vertical='bottom',
103
                              horizontal='right',
104
                              text_rotation=0,
105
                              wrap_text=True,
106
                              shrink_to_fit=False,
107
                              indent=0)
108
109
    # Img
110
    img = Image("excelexporters/myems.png")
111
    ws.add_image(img, 'A1')
112
113
    # Title
114
    ws['B3'].alignment = b_r_alignment
115
    ws['B3'] = 'Name:'
116
    ws['C3'].border = b_border
117
    ws['C3'].alignment = b_c_alignment
118
    ws['C3'] = name
119
120
    ws['D3'].alignment = b_r_alignment
121
    ws['D3'] = 'Period:'
122
    ws['E3'].border = b_border
123
    ws['E3'].alignment = b_c_alignment
124
    ws['E3'] = period_type
125
126
    ws['B4'].alignment = b_r_alignment
127
    ws['B4'] = 'Reporting Start Datetime:'
128
    ws['C4'].border = b_border
129
    ws['C4'].alignment = b_c_alignment
130
    ws['C4'] = reporting_start_datetime_local
131
132
    ws['D4'].alignment = b_r_alignment
133
    ws['D4'] = 'Reporting End Datetime:'
134
    ws['E4'].border = b_border
135
    ws['E4'].alignment = b_c_alignment
136
    ws['E4'] = reporting_end_datetime_local
137
138
    if "reporting_period" not in report.keys() or \
139
            "values_saving" not in report['reporting_period'].keys() or \
140
            len(report['reporting_period']['values_saving']) == 0:
141
        filename = str(uuid.uuid4()) + '.xlsx'
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable str does not seem to be defined.
Loading history...
142
        wb.save(filename)
143
144
        return filename
145
    ####################################################################################################################
146
    # First: Saving
147
    # 6: title
148
    # 7: table title
149
    # 8~9 table_data
150
    ####################################################################################################################
151
    has_energy_data_flag = True
152
153
    if "values_saving" not in report['reporting_period'].keys() or \
154
            len(report['reporting_period']['values_saving']) == 0:
155
        has_energy_data_flag = False
156
157
    if has_energy_data_flag:
158
        ws['B6'].font = title_font
159
        ws['B6'] = name + ' ' + 'Saving'
160
161
        reporting_period_data = report['reporting_period']
162
163
        category = report['meter']['energy_category_name']
164
        ca_len = len(category)
165
166
        ws.row_dimensions[7].height = 60
167
168
        ws['B7'].fill = table_fill
169
        ws['B7'].border = f_border
170
171
        ws['B8'].font = title_font
172
        ws['B8'].alignment = c_c_alignment
173
        ws['B8'] = 'Saving'
174
        ws['B8'].border = f_border
175
176
        ws['B9'].font = title_font
177
        ws['B9'].alignment = c_c_alignment
178
        ws['B9'] = 'Increment Rate'
179
        ws['B9'].border = f_border
180
181
        col = ''
182
183
        for i in range(0, ca_len):
184
            col = chr(ord('C') + i)
185
            row = '7'
186
            cell = col + row
187
            ws[col + '7'].fill = table_fill
188
            ws[col + '7'].font = name_font
189
            ws[col + '7'].alignment = c_c_alignment
190
            ws[col + '7'] = report['meter']['energy_category_name'] + " (" + report['meter']['unit_of_measure'] + ")"
191
            ws[col + '7'].border = f_border
192
193
            ws[col + '8'].font = name_font
194
            ws[col + '8'].alignment = c_c_alignment
195
            ws[col + '8'] = round(reporting_period_data['total_in_category_saving'], 2)
196
            ws[col + '8'].border = f_border
197
198
            ws[col + '9'].font = name_font
199
            ws[col + '9'].alignment = c_c_alignment
200
            ws[col + '9'] = str(round(reporting_period_data['increment_rate_saving'] * 100, 2)) + "%" \
201
                if reporting_period_data['increment_rate_saving'] is not None else "-"
202
            ws[col + '9'].border = f_border
203
204
        # TCE TCO2E
205
        end_col = col
206
        # TCE
207
        tce_col = chr(ord(end_col) + 1)
208
        ws[tce_col + '7'].fill = table_fill
209
        ws[tce_col + '7'].font = name_font
210
        ws[tce_col + '7'].alignment = c_c_alignment
211
        ws[tce_col + '7'] = 'Ton of Standard Coal (TCE)'
212
        ws[tce_col + '7'].border = f_border
213
214
        ws[tce_col + '8'].font = name_font
215
        ws[tce_col + '8'].alignment = c_c_alignment
216
        ws[tce_col + '8'] = round(reporting_period_data['total_in_kgce_saving'] / 1000, 2)
217
        ws[tce_col + '8'].border = f_border
218
219
        ws[tce_col + '9'].font = name_font
220
        ws[tce_col + '9'].alignment = c_c_alignment
221
        ws[tce_col + '9'] = str(round(reporting_period_data['increment_rate_saving'] * 100, 2)) + "%" \
222
            if reporting_period_data['increment_rate_saving'] is not None else "-"
223
        ws[tce_col + '9'].border = f_border
224
225
        # TCO2E
226
        tco2e_col = chr(ord(end_col) + 2)
227
        ws[tco2e_col + '7'].fill = table_fill
228
        ws[tco2e_col + '7'].font = name_font
229
        ws[tco2e_col + '7'].alignment = c_c_alignment
230
        ws[tco2e_col + '7'] = 'Ton of Carbon Dioxide Emissions (TCO2E) Saving'
231
        ws[tco2e_col + '7'].border = f_border
232
233
        ws[tco2e_col + '8'].font = name_font
234
        ws[tco2e_col + '8'].alignment = c_c_alignment
235
        ws[tco2e_col + '8'] = round(reporting_period_data['total_in_kgco2e_saving'] / 1000, 2)
236
        ws[tco2e_col + '8'].border = f_border
237
238
        ws[tco2e_col + '9'].font = name_font
239
        ws[tco2e_col + '9'].alignment = c_c_alignment
240
        ws[tco2e_col + '9'] = str(round(reporting_period_data['increment_rate_saving'] * 100, 2)) + "%" \
241
            if reporting_period_data['increment_rate_saving'] is not None else "-"
242
        ws[tco2e_col + '9'].border = f_border
243
    else:
244
        for i in range(6, 9 + 1):
245
            ws.row_dimensions[i].height = 0.1
246
    ####################################################################################################################
247
    # Second: Detailed Data
248
    # 11: title
249
    # 12 ~ 16: chart
250
    # 18: table title
251
    # 19~43: table_data
252
    ####################################################################################################################
253
    has_energy_detail_flag = True
254
    reporting_period_data = report['reporting_period']
255
    times = reporting_period_data['timestamps']
256
257
    if "values_saving" not in report['reporting_period'].keys() or \
258
            len(report['reporting_period']['values_saving']) == 0:
259
        has_energy_detail_flag = False
260
261
    if has_energy_detail_flag:
262
        reporting_period_data = report['reporting_period']
263
        category = report['meter']['energy_category_name']
264
        ca_len = len(category)
265
        parameters_names_len = len(report['parameters']['names'])
266
        parameters_data = report['parameters']
267
        parameters_parameters_datas_len = parameters_names_len
268
        start_detail_data_row_num = 13 + (parameters_parameters_datas_len + ca_len) * 6
269
        ws['B11'].font = title_font
270
        ws['B11'] = name + 'Detailed Data'
271
272
        ws.row_dimensions[start_detail_data_row_num].height = 60
273
274
        ws['B' + str(start_detail_data_row_num)].fill = table_fill
275
        ws['B' + str(start_detail_data_row_num)].font = title_font
276
        ws['B' + str(start_detail_data_row_num)].border = f_border
277
        ws['B' + str(start_detail_data_row_num)].alignment = c_c_alignment
278
        ws['B' + str(start_detail_data_row_num)] = 'Datetime'
279
        time = times
280
        has_data = False
281
        max_row = 0
282
        if len(time) > 0:
283
            has_data = True
284
            max_row = start_detail_data_row_num + len(time)
285
286
        if has_data:
287
            for i in range(0, len(time)):
288
                col = 'B'
289
                row = str(start_detail_data_row_num + 1 + i)
290
                # col = chr(ord('B') + i)
291
                ws[col + row].font = title_font
292
                ws[col + row].alignment = c_c_alignment
293
                ws[col + row] = time[i]
294
                ws[col + row].border = f_border
295
296
            for i in range(0, ca_len):
297
                # 12 title
298
                col = chr(ord('C') + i)
299
300
                ws[col + str(start_detail_data_row_num)].fill = table_fill
301
                ws[col + str(start_detail_data_row_num)].font = title_font
302
                ws[col + str(start_detail_data_row_num)].alignment = c_c_alignment
303
                ws[col + str(start_detail_data_row_num)] = report['meter']['energy_category_name'] + \
304
                    " (" + report['meter']['unit_of_measure'] + ")"
305
                ws[col + str(start_detail_data_row_num)].border = f_border
306
307
                # 13 data
308
                time = times
309
                time_len = len(time)
310
311
                for j in range(0, time_len):
312
                    row = str(start_detail_data_row_num + 1 + j)
313
                    # col = chr(ord('B') + i)
314
                    ws[col + row].font = title_font
315
                    ws[col + row].alignment = c_c_alignment
316
                    ws[col + row] = round(reporting_period_data['values_saving'][j], 2)
317
                    ws[col + row].border = f_border
318
            # line
319
            # 13~: line
320
            line = LineChart()
321
            line.title = 'Reporting Period Saving - ' + report['meter']['energy_category_name'] + \
322
                " (" + report['meter']['unit_of_measure'] + ")"
323
            labels = Reference(ws, min_col=2, min_row=start_detail_data_row_num + 1, max_row=max_row)
324
            bar_data = Reference(ws, min_col=3, min_row=start_detail_data_row_num, max_row=max_row)
325
            line.add_data(bar_data, titles_from_data=True)
326
            line.set_categories(labels)
327
            line_data = line.series[0]
328
            line_data.marker.symbol = "circle"
329
            line_data.smooth = True
330
            line.x_axis.crosses = 'min'
331
            line.height = 8.25
332
            line.width = 24
333
            line.dLbls = DataLabelList()
334
            line.dLbls = DataLabelList()
335
            line.dLbls.dLblPos = 't'
336
            line.dLbls.showVal = True  # 数量显示
337
            line.dLbls.showPercent = False  # 百分比显示
338
            ws.add_chart(line, "B12")
339
340
            col = 'B'
341
            row = str(start_detail_data_row_num + 1 + len(time))
342
343
            ws[col + row].font = title_font
344
            ws[col + row].alignment = c_c_alignment
345
            ws[col + row] = 'Total'
346
            ws[col + row].border = f_border
347
348
            for i in range(0, ca_len):
349
                col = chr(ord(col) + 1)
350
                ws[col + row].font = title_font
351
                ws[col + row].alignment = c_c_alignment
352
                ws[col + row] = round(reporting_period_data['total_in_category_saving'], 2)
353
                ws[col + row].border = f_border
354
355
    else:
356
        for i in range(11, 43 + 1):
357
            ws.row_dimensions[i].height = 0.0
358
359
    ##########################################
360
    has_parameters_names_and_timestamps_and_values_data = True
361
    # 12 is the starting line number of the last line chart in the report period
362
    category = report['meter']['energy_category_name']
363
    time_len = len(reporting_period_data['timestamps'])
364
    ca_len = len(category)
365
    current_sheet_parameters_row_number = 12 + ca_len * 6
366
    if 'parameters' not in report.keys() or \
367
            report['parameters'] is None or \
368
            'names' not in report['parameters'].keys() or \
369
            report['parameters']['names'] is None or \
370
            len(report['parameters']['names']) == 0 or \
371
            'timestamps' not in report['parameters'].keys() or \
372
            report['parameters']['timestamps'] is None or \
373
            len(report['parameters']['timestamps']) == 0 or \
374
            'values' not in report['parameters'].keys() or \
375
            report['parameters']['values'] is None or \
376
            len(report['parameters']['values']) == 0 or \
377
            timestamps_data_all_equal_0(report['parameters']['timestamps']):
378
        has_parameters_names_and_timestamps_and_values_data = False
379
    if has_parameters_names_and_timestamps_and_values_data:
380
381
        ################################################################################################################
382
        # new worksheet
383
        ################################################################################################################
384
385
        parameters_data = report['parameters']
386
387
        parameters_names_len = len(parameters_data['names'])
388
389
        file_name = (re.sub(r'[^A-Z]', '', ws.title))+'_'
390
        parameters_ws = wb.create_sheet(file_name + 'Parameters')
391
392
        parameters_timestamps_data_max_len = \
393
            get_parameters_timestamps_lists_max_len(list(parameters_data['timestamps']))
394
395
        # Row height
396
        parameters_ws.row_dimensions[1].height = 102
397
        for i in range(2, 7 + 1):
398
            parameters_ws.row_dimensions[i].height = 42
399
400
        for i in range(8, parameters_timestamps_data_max_len + 10):
401
            parameters_ws.row_dimensions[i].height = 60
402
403
        # Col width
404
        parameters_ws.column_dimensions['A'].width = 1.5
405
406
        parameters_ws.column_dimensions['B'].width = 25.0
407
408
        for i in range(3, 12 + parameters_names_len * 3):
409
            parameters_ws.column_dimensions[format_cell.get_column_letter(i)].width = 15.0
410
411
        # Img
412
        img = Image("excelexporters/myems.png")
413
        parameters_ws.add_image(img, 'A1')
414
415
        # Title
416
        parameters_ws['B3'].alignment = b_r_alignment
417
        parameters_ws['B3'] = 'Name:'
418
        parameters_ws['C3'].border = b_border
419
        parameters_ws['C3'].alignment = b_c_alignment
420
        parameters_ws['C3'] = name
421
422
        parameters_ws['D3'].alignment = b_r_alignment
423
        parameters_ws['D3'] = 'Period:'
424
        parameters_ws['E3'].border = b_border
425
        parameters_ws['E3'].alignment = b_c_alignment
426
        parameters_ws['E3'] = period_type
427
428
        parameters_ws['B4'].alignment = b_r_alignment
429
        parameters_ws['B4'] = 'Reporting Start Datetime:'
430
        parameters_ws['C4'].border = b_border
431
        parameters_ws['C4'].alignment = b_c_alignment
432
        parameters_ws['C4'] = reporting_start_datetime_local
433
434
        parameters_ws['D4'].alignment = b_r_alignment
435
        parameters_ws['D4'] = 'Reporting End Datetime:'
436
        parameters_ws['E4'].border = b_border
437
        parameters_ws['E4'].alignment = b_c_alignment
438
        parameters_ws['E4'] = reporting_end_datetime_local
439
440
        parameters_ws_current_row_number = 6
441
442
        parameters_ws['B' + str(parameters_ws_current_row_number)].font = title_font
443
        parameters_ws['B' + str(parameters_ws_current_row_number)] = name + ' ' + 'Parameters'
444
445
        parameters_ws_current_row_number += 1
446
447
        parameters_table_start_row_number = parameters_ws_current_row_number
448
449
        parameters_ws.row_dimensions[parameters_ws_current_row_number].height = 80
450
451
        parameters_ws_current_row_number += 1
452
453
        table_current_col_number = 2
454
455
        for i in range(0, parameters_names_len):
456
            col = format_cell.get_column_letter(table_current_col_number)
457
458
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].fill = table_fill
459
            col = format_cell.get_column_letter(table_current_col_number + 1)
460
461
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].fill = table_fill
462
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].font = name_font
463
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].alignment = c_c_alignment
464
            parameters_ws[col + str(parameters_ws_current_row_number - 1)] = parameters_data['names'][i]
465
466
            table_current_col_number = table_current_col_number + 3
467
468
        ################################################################################################################
469
        # Optimized insert parameter data
470
        ################################################################################################################
471
472
        timestamps_list = parameters_data['timestamps']
473
        values_list = parameters_data['values']
474
475
        timestamps_data_temp_save_start_row = parameters_timestamps_data_max_len + 10
476
477
        values_data_temp_save_start_row = parameters_timestamps_data_max_len * 2 + 10 + 1
478
479
        parameters_ws["A" + str(timestamps_data_temp_save_start_row)] = ""
480
        for i in range(parameters_timestamps_data_max_len):
481
            temp_list = []
482
            for j in range(len(timestamps_list)):
483
                try:
484
                    temp_list.append(timestamps_list[j][i])
485
                except IndexError:
486
                    temp_list.append("")
487
            parameters_ws.append(temp_list)
488
489
        parameters_ws["A" + str(values_data_temp_save_start_row)] = ""
490
        for i in range(parameters_timestamps_data_max_len):
491
            temp_list = []
492
            for j in range(len(values_list)):
493
                try:
494
                    temp_list.append(values_list[j][i])
495
                except IndexError:
496
                    temp_list.append("")
497
            parameters_ws.append(temp_list)
498
499
        parameter_current_col_number = 1
500
501
        for i in range(len(timestamps_list)):
502
            col = format_cell.get_column_letter(parameter_current_col_number)
503
            parameters_ws.move_range(
504
                "{}{}:{}{}".format(col, timestamps_data_temp_save_start_row + 1, col,
505
                                   timestamps_data_temp_save_start_row + parameters_timestamps_data_max_len),
506
                (- timestamps_data_temp_save_start_row + (parameters_ws_current_row_number - 1)), (i * 2) + 1)
507
            parameters_ws.move_range(
508
                "{}{}:{}{}".format(col, values_data_temp_save_start_row + 1, col,
509
                                   values_data_temp_save_start_row + parameters_timestamps_data_max_len),
510
                (- values_data_temp_save_start_row + (parameters_ws_current_row_number - 1)), (i * 2) + 2)
511
512
            parameter_current_col_number += 1
513
514
        ################################################################################################################
515
        # parameters chart and parameters table
516
        ################################################################################################################
517
518
        ws['B' + str(current_sheet_parameters_row_number)].font = title_font
519
        ws['B' + str(current_sheet_parameters_row_number)] = name + ' ' + 'Parameters'
520
521
        current_sheet_parameters_row_number += 1
522
523
        chart_start_row_number = current_sheet_parameters_row_number
524
525
        col_index = 0
526
527
        for i in range(0, parameters_names_len):
528
            line = LineChart()
529
            data_col = 3 + col_index * 3
530
            labels_col = 2 + col_index * 3
531
            col_index += 1
532
            line.title = 'Parameters - ' + \
533
                         parameters_ws.cell(row=parameters_table_start_row_number, column=data_col).value
534
            labels = Reference(parameters_ws, min_col=labels_col, min_row=parameters_table_start_row_number + 1,
535
                               max_row=(len(parameters_data['timestamps'][i]) + parameters_table_start_row_number))
536
            line_data = Reference(parameters_ws, min_col=data_col, min_row=parameters_table_start_row_number,
537
                                  max_row=(len(parameters_data['timestamps'][i]) + parameters_table_start_row_number))
538
            line.add_data(line_data, titles_from_data=True)
539
            line.set_categories(labels)
540
            line_data = line.series[0]
541
            line_data.marker.symbol = "circle"
542
            line_data.smooth = True
543
            line.x_axis.crosses = 'min'
544
            line.height = 8.25
545
            line.width = 24
546
            line.dLbls = DataLabelList()
547
            line.dLbls.dLblPos = 't'
548
            line.dLbls.showVal = False
549
            line.dLbls.showPercent = False
550
            chart_col = 'B'
551
            chart_cell = chart_col + str(chart_start_row_number)
552
            chart_start_row_number += 6
553
            ws.add_chart(line, chart_cell)
554
555
        current_sheet_parameters_row_number = chart_start_row_number
556
557
        current_sheet_parameters_row_number += 1
558
559
    filename = str(uuid.uuid4()) + '.xlsx'
560
    wb.save(filename)
561
562
    return filename
563
564
565
def timestamps_data_all_equal_0(lists):
566
    for i, value in enumerate(list(lists)):
567
        if len(value) > 0:
568
            return False
569
570
    return True
571
572
573
def get_parameters_timestamps_lists_max_len(parameters_timestamps_lists):
574
    max_len = 0
575
    for i, value in enumerate(list(parameters_timestamps_lists)):
576
        if len(value) > max_len:
577
            max_len = len(value)
578
579
    return max_len
580
581
582
def timestamps_data_not_equal_0(lists):
583
    number = 0
584
    for i, value in enumerate(list(lists)):
585
        if len(value) > 0:
586
            number += 1
587
    return number
588