Passed
Push — master ( aba09b...96c57b )
by Guangyu
08:00 queued 14s
created

excelexporters.equipmentload.generate_excel()   F

Complexity

Conditions 45

Size

Total Lines 468
Code Lines 341

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 341
dl 0
loc 468
rs 0
c 0
b 0
f 0
cc 45
nop 5

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