Passed
Push — master ( 553349...ae5231 )
by Guangyu
16:23 queued 11s
created

excelexporters.combinedequipmentenergycategory   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 573
Duplicated Lines 88.83 %

Importance

Changes 0
Metric Value
wmc 49
eloc 407
dl 509
loc 573
rs 8.48
c 0
b 0
f 0

2 Functions

Rating   Name   Duplication   Size   Complexity  
B export() 0 39 5
F generate_excel() 509 509 44

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complexity

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

Complex classes like excelexporters.combinedequipmentenergycategory 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
from openpyxl.chart import (
5
        PieChart,
6
        LineChart,
7
        BarChart,
8
        Reference,
9
    )
10
from openpyxl.styles import PatternFill, Border, Side, Alignment, Font
11
from openpyxl.drawing.image import Image
12
from openpyxl import Workbook
13
from openpyxl.chart.label import DataLabelList
14
15
####################################################################################################################
16
# PROCEDURES
17
# Step 1: Validate the report data
18
# Step 2: Generate excel file
19
# Step 3: Encode the excel file bytes to Base64
20
####################################################################################################################
21
22
23
def export(report,
24
           name,
25
           reporting_start_datetime_local,
26
           reporting_end_datetime_local,
27
           period_type):
28
    ####################################################################################################################
29
    # Step 1: Validate the report data
30
    ####################################################################################################################
31
    if report is None:
32
        return None
33
    print(report)
34
35
    ####################################################################################################################
36
    # Step 2: Generate excel file from the report data
37
    ####################################################################################################################
38
    filename = generate_excel(report,
39
                              name,
40
                              reporting_start_datetime_local,
41
                              reporting_end_datetime_local,
42
                              period_type)
43
    ####################################################################################################################
44
    # Step 3: Encode the excel file to Base64
45
    ####################################################################################################################
46
    try:
47
        with open(filename, 'rb') as binary_file:
48
            binary_file_data = binary_file.read()
49
    except IOError as ex:
50
        pass
51
52
    # Base64 encode the bytes
53
    base64_encoded_data = base64.b64encode(binary_file_data)
0 ignored issues
show
introduced by
The variable binary_file_data does not seem to be defined for all execution paths.
Loading history...
54
    # get the Base64 encoded data using human-readable characters.
55
    base64_message = base64_encoded_data.decode('utf-8')
56
    # delete the file from server
57
    try:
58
        os.remove(filename)
59
    except NotImplementedError as ex:
60
        pass
61
    return base64_message
62
63
64 View Code Duplication
def generate_excel(report,
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
65
                   name,
66
                   reporting_start_datetime_local,
67
                   reporting_end_datetime_local,
68
                   period_type):
69
70
    wb = Workbook()
71
    ws = wb.active
72
73
    # Row height
74
    ws.row_dimensions[1].height = 102
75
76
    for i in range(2, 2000 + 1):
77
        ws.row_dimensions[i].height = 42
78
79
    # Col width
80
    ws.column_dimensions['A'].width = 1.5
81
82
    ws.column_dimensions['B'].width = 25.0
83
84
    for i in range(ord('C'), ord('L')):
85
        ws.column_dimensions[chr(i)].width = 15.0
86
87
    # Font
88
    name_font = Font(name='Constantia', size=15, bold=True)
89
    title_font = Font(name='宋体', size=15, bold=True)
90
    data_font = Font(name='Franklin Gothic Book', size=11)
91
92
    table_fill = PatternFill(fill_type='solid', fgColor='1F497D')
93
    f_border = Border(left=Side(border_style='medium', color='00000000'),
94
                      right=Side(border_style='medium', color='00000000'),
95
                      bottom=Side(border_style='medium', color='00000000'),
96
                      top=Side(border_style='medium', color='00000000')
97
                      )
98
    b_border = Border(
99
        bottom=Side(border_style='medium', color='00000000'),
100
    )
101
102
    b_c_alignment = Alignment(vertical='bottom',
103
                              horizontal='center',
104
                              text_rotation=0,
105
                              wrap_text=True,
106
                              shrink_to_fit=False,
107
                              indent=0)
108
    c_c_alignment = Alignment(vertical='center',
109
                              horizontal='center',
110
                              text_rotation=0,
111
                              wrap_text=True,
112
                              shrink_to_fit=False,
113
                              indent=0)
114
    b_r_alignment = Alignment(vertical='bottom',
115
                              horizontal='right',
116
                              text_rotation=0,
117
                              wrap_text=True,
118
                              shrink_to_fit=False,
119
                              indent=0)
120
    c_r_alignment = Alignment(vertical='bottom',
121
                              horizontal='center',
122
                              text_rotation=0,
123
                              wrap_text=True,
124
                              shrink_to_fit=False,
125
                              indent=0)
126
127
    # Img
128
    img = Image("excelexporters/myems.png")
129
    # img = Image("myems.png")
130
    img.width = img.width * 1.06
131
    img.height = img.height * 1.06
132
    ws.add_image(img, 'B1')
133
134
    # Title
135
    ws.row_dimensions[3].height = 60
136
137
    ws['B3'].font = name_font
138
    ws['B3'].alignment = b_r_alignment
139
    ws['B3'] = 'Name:'
140
    ws['C3'].border = b_border
141
    ws['C3'].alignment = b_c_alignment
142
    ws['C3'].font = name_font
143
    ws['C3'] = name
144
145
    ws['D3'].font = name_font
146
    ws['D3'].alignment = b_r_alignment
147
    ws['D3'] = 'Period:'
148
    ws['E3'].border = b_border
149
    ws['E3'].alignment = b_c_alignment
150
    ws['E3'].font = name_font
151
    ws['E3'] = period_type
152
153
    ws['F3'].font = name_font
154
    ws['F3'].alignment = b_r_alignment
155
    ws['F3'] = 'Date:'
156
    ws['G3'].border = b_border
157
    ws['G3'].alignment = b_c_alignment
158
    ws['G3'].font = name_font
159
    ws['G3'] = reporting_start_datetime_local[:10] + "__" + reporting_end_datetime_local[:10]
160
    ws.merge_cells("G3:H3")
161
162
    if "reporting_period" not in report.keys() or \
163
            "names" not in report['reporting_period'].keys() or len(report['reporting_period']['names']) == 0:
164
        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...
165
        wb.save(filename)
166
167
        return filename
168
    #################################################
169
    # First: 能耗分析
170
    # 6: title
171
    # 7: table title
172
    # 8~10 table_data
173
    # Total: 5 rows
174
    # if has not energy data: set low height for rows
175
    #################################################
176
    reporting_period_data = report['reporting_period']
177
178
    has_energy_data_flag = True
179
    if "names" not in reporting_period_data.keys() or \
180
            reporting_period_data['names'] is None or \
181
            len(reporting_period_data['names']) == 0:
182
        has_energy_data_flag = False
183
184
    if has_energy_data_flag:
185
        ws['B6'].font = title_font
186
        ws['B6'] = name+' 能耗分析'
187
188
        category = reporting_period_data['names']
189
        ca_len = len(category)
190
191
        ws.row_dimensions[7].height = 60
192
        ws['B7'].fill = table_fill
193
        ws['B7'].border = f_border
194
195
        ws['B8'].font = title_font
196
        ws['B8'].alignment = c_c_alignment
197
        ws['B8'] = '能耗'
198
        ws['B8'].border = f_border
199
200
        ws['B9'].font = title_font
201
        ws['B9'].alignment = c_c_alignment
202
        ws['B9'] = '环比'
203
        ws['B9'].border = f_border
204
205
        col = ''
206
207
        for i in range(0, ca_len):
208
            col = chr(ord('C') + i)
209
            row = '7'
210
            cell = col + row
211
            ws[col + '7'].fill = table_fill
212
            ws[col + '7'].font = name_font
213
            ws[col + '7'].alignment = c_c_alignment
214
            ws[col + '7'] = reporting_period_data['names'][i] + " (" + reporting_period_data['units'][i] + ")"
215
            ws[col + '7'].border = f_border
216
217
            ws[col + '8'].font = name_font
218
            ws[col + '8'].alignment = c_c_alignment
219
            ws[col + '8'] = round(reporting_period_data['subtotals'][i], 2)
220
            ws[col + '8'].border = f_border
221
222
            ws[col + '9'].font = name_font
223
            ws[col + '9'].alignment = c_c_alignment
224
            ws[col + '9'] = str(round(reporting_period_data['increment_rates'][i] * 100, 2)) + "%" \
225
                if reporting_period_data['increment_rates'][i] is not None else "-"
226
            ws[col + '9'].border = f_border
227
228
        # TCE TCO2E
229
        end_col = col
230
        # TCE
231
        tce_col = chr(ord(end_col) + 1)
232
        ws[tce_col + '7'].fill = table_fill
233
        ws[tce_col + '7'].font = name_font
234
        ws[tce_col + '7'].alignment = c_c_alignment
235
        ws[tce_col + '7'] = "吨标准煤 (TCE)"
236
        ws[tce_col + '7'].border = f_border
237
238
        ws[tce_col + '8'].font = name_font
239
        ws[tce_col + '8'].alignment = c_c_alignment
240
        ws[tce_col + '8'] = round(reporting_period_data['total_in_kgce'] / 1000, 2)
241
        ws[tce_col + '8'].border = f_border
242
243
        ws[tce_col + '9'].font = name_font
244
        ws[tce_col + '9'].alignment = c_c_alignment
245
        ws[tce_col + '9'] = str(round(reporting_period_data['increment_rate_in_kgce'] * 100, 2)) + "%" \
246
            if reporting_period_data['increment_rate_in_kgce'] is not None else "-"
247
        ws[tce_col + '9'].border = f_border
248
249
        # TCO2E
250
        tco2e_col = chr(ord(end_col) + 2)
251
        ws[tco2e_col + '7'].fill = table_fill
252
        ws[tco2e_col + '7'].font = name_font
253
        ws[tco2e_col + '7'].alignment = c_c_alignment
254
        ws[tco2e_col + '7'] = "吨二氧化碳排放 (TCO2E)"
255
        ws[tco2e_col + '7'].border = f_border
256
257
        ws[tco2e_col + '8'].font = name_font
258
        ws[tco2e_col + '8'].alignment = c_c_alignment
259
        ws[tco2e_col + '8'] = round(reporting_period_data['total_in_kgco2e'] / 1000, 2)
260
        ws[tco2e_col + '8'].border = f_border
261
262
        ws[tco2e_col + '9'].font = name_font
263
        ws[tco2e_col + '9'].alignment = c_c_alignment
264
        ws[tco2e_col + '9'] = str(round(reporting_period_data['increment_rate_in_kgco2e'] * 100, 2)) + "%" \
265
            if reporting_period_data['increment_rate_in_kgco2e'] is not None else "-"
266
        ws[tco2e_col + '9'].border = f_border
267
    else:
268
        for i in range(6, 9 + 1):
269
            ws.row_dimensions[i].height = 0.1
270
    #################################################
271
    # Second: 分时电耗
272
    # 12: title
273
    # 13: table title
274
    # 14~17 table_data
275
    # Total: 6 rows
276
    ################################################
277
    has_ele_peak_flag = True
278
    if "toppeaks" not in reporting_period_data.keys() or \
279
            reporting_period_data['toppeaks'] is None or \
280
            len(reporting_period_data['toppeaks']) == 0:
281
        has_ele_peak_flag = False
282
283
    if has_ele_peak_flag:
284
        ws['B12'].font = title_font
285
        ws['B12'] = name+' 分时电耗'
286
287
        ws.row_dimensions[13].height = 60
288
        ws['B13'].fill = table_fill
289
        ws['B13'].font = name_font
290
        ws['B13'].alignment = c_c_alignment
291
        ws['B13'].border = f_border
292
293
        ws['C13'].fill = table_fill
294
        ws['C13'].font = name_font
295
        ws['C13'].alignment = c_c_alignment
296
        ws['C13'].border = f_border
297
        ws['C13'] = '分时电耗'
298
299
        ws['B14'].font = title_font
300
        ws['B14'].alignment = c_c_alignment
301
        ws['B14'] = '尖'
302
        ws['B14'].border = f_border
303
304
        ws['C14'].font = title_font
305
        ws['C14'].alignment = c_c_alignment
306
        ws['C14'].border = f_border
307
        ws['C14'] = round(reporting_period_data['toppeaks'][0], 2)
308
309
        ws['B15'].font = title_font
310
        ws['B15'].alignment = c_c_alignment
311
        ws['B15'] = '峰'
312
        ws['B15'].border = f_border
313
314
        ws['C15'].font = title_font
315
        ws['C15'].alignment = c_c_alignment
316
        ws['C15'].border = f_border
317
        ws['C15'] = round(reporting_period_data['onpeaks'][0], 2)
318
319
        ws['B16'].font = title_font
320
        ws['B16'].alignment = c_c_alignment
321
        ws['B16'] = '平'
322
        ws['B16'].border = f_border
323
324
        ws['C16'].font = title_font
325
        ws['C16'].alignment = c_c_alignment
326
        ws['C16'].border = f_border
327
        ws['C16'] = round(reporting_period_data['midpeaks'][0], 2)
328
329
        ws['B17'].font = title_font
330
        ws['B17'].alignment = c_c_alignment
331
        ws['B17'] = '谷'
332
        ws['B17'].border = f_border
333
334
        ws['C17'].font = title_font
335
        ws['C17'].alignment = c_c_alignment
336
        ws['C17'].border = f_border
337
        ws['C17'] = round(reporting_period_data['offpeaks'][0], 2)
338
339
        pie = PieChart()
340
        pie.title = name+' 分时电耗'
341
        labels = Reference(ws, min_col=2, min_row=14, max_row=17)
342
        pie_data = Reference(ws, min_col=3, min_row=13, max_row=17)
343
        pie.add_data(pie_data, titles_from_data=True)
344
        pie.set_categories(labels)
345
        pie.height = 7.25  # cm 1.05*5 1.05cm = 30 pt
346
        pie.width = 9
347
        # pie.title = "Pies sold by category"
348
        s1 = pie.series[0]
349
        s1.dLbls = DataLabelList()
350
        s1.dLbls.showCatName = False  # 标签显示
351
        s1.dLbls.showVal = True  # 数量显示
352
        s1.dLbls.showPercent = True  # 百分比显示
353
        # s1 = CharacterProperties(sz=1800)     # 图表中字体大小 *100
354
355
        ws.add_chart(pie, "D13")
356
357
    else:
358
        for i in range(12, 18 + 1):
359
            ws.row_dimensions[i].height = 0.1
360
        # end_row 10
361
        # start_row 12
362
    ################################################
363
    # Third: 子空间能耗
364
    # 19: title
365
    # 20: table title
366
    # 21~24 table_data
367
    # Total: 6 rows
368
    ################################################
369
    has_child_flag = True
370
    # Judge if the space has child space, if not, delete it.
371
    if "child_space" not in report.keys() or "energy_category_names" not in report['child_space'].keys() or \
372
            len(report['child_space']["energy_category_names"]) == 0 \
373
            or 'child_space_names_array' not in report['child_space'].keys() \
374
            or report['child_space']['energy_category_names'] is None \
375
            or len(report['child_space']['child_space_names_array']) == 0 \
376
            or len(report['child_space']['child_space_nchild_space_names_arrayames_array'][0]) == 0:
377
378
        has_child_flag = False
379
380
    current_row_number = 19
381
382
    if has_child_flag:
383
        child = report['child_space']
384
        child_spaces = child['child_space_names_array'][0]
385
        child_subtotals = child['subtotals_array'][0]
386
387
        ws['B19'].font = title_font
388
        ws['B19'] = name+' 子空间能耗'
389
390
        ws.row_dimensions[20].height = 60
391
        ws['B20'].fill = table_fill
392
        ws['B20'].border = f_border
393
        ca_len = len(child['energy_category_names'])
394
395
        table_start_row_number = 20
396
397
        for i in range(0, ca_len):
398
            row = chr(ord('C') + i)
399
            ws[row + '20'].fill = table_fill
400
            ws[row + '20'].font = title_font
401
            ws[row + '20'].alignment = c_c_alignment
402
            ws[row + '20'].border = f_border
403
            ws[row + '20'] = child['energy_category_names'][i] + ' (' + child['units'][i] + ')'
404
405
        space_len = len(child['child_space_names_array'][0])
406
407
        for i in range(0, space_len):
408
            row = str(i + 21)
409
410
            ws['B' + row].font = name_font
411
            ws['B' + row].alignment = c_c_alignment
412
            ws['B' + row] = child['child_space_names_array'][0][i]
413
            ws['B' + row].border = f_border
414
415
            for j in range(0, ca_len):
416
                col = chr(ord('C') + j)
417
                ws[col + row].font = name_font
418
                ws[col + row].alignment = c_c_alignment
419
                ws[col + row] = round(child['subtotals_array'][j][i], 2)
420
                ws[col + row].border = f_border
421
422
        table_end_row_number = 20 + space_len
423
        chart_start_row_number = 20 + space_len + 1
424
425
        for i in range(0, ca_len):
426
            # pie
427
            # 25~30: pie
428
            pie = PieChart()
429
            pie.title = ws.cell(column=3 + i, row=table_start_row_number).value
430
            labels = Reference(ws, min_col=2, min_row=table_start_row_number + 1, max_row=table_end_row_number)
431
            pie_data = Reference(ws, min_col=3 + i, min_row=table_start_row_number, max_row=table_end_row_number)
432
            pie.add_data(pie_data, titles_from_data=True)
433
            pie.set_categories(labels)
434
            pie.height = 6.6  # cm 1.05*5 1.05cm = 30 pt
435
            pie.width = 8
436
            # pie.title = "Pies sold by category"
437
            s1 = pie.series[0]
438
            s1.dLbls = DataLabelList()
439
            s1.dLbls.showCatName = False  # 标签显示
440
            s1.dLbls.showVal = True  # 数量显示
441
            s1.dLbls.showPercent = True  # 百分比显示
442
            # s1 = CharacterProperties(sz=1800)     # 图表中字体大小 *100
443
            chart_cell = ''
444
            if i % 2 == 0:
445
                chart_cell = 'B' + str(chart_start_row_number)
446
            else:
447
                chart_cell = 'E' + str(chart_start_row_number)
448
                chart_start_row_number += 5
449
            # ws.add_chart(pie, chart_cell)
450
            # chart_col = chr(ord('B') + 2 * j)
451
            # chart_cell = chart_col + '25'
452
            ws.add_chart(pie, chart_cell)
453
454
        current_row_number = chart_start_row_number
455
456
        if ca_len % 2 == 1:
457
            current_row_number += 5
458
459
        current_row_number += 1
460
461
    ################################################
462
    # Fourth: 能耗详情
463
    # current_row_number: title
464
    # current_row_number+1 ~ current_row_number+1+ca_len*6-1: line
465
    # current_row_number+1+ca_len*6: table title
466
    # current_row_number+1+ca_len*6~: table_data
467
    ################################################
468
    reporting_period_data = report['reporting_period']
469
    times = reporting_period_data['timestamps']
470
    has_detail_data_flag = True
471
    ca_len = len(report['reporting_period']['names'])
472
    table_row = current_row_number + 1 + ca_len*6
473
    chart_start_row_number = current_row_number + 1
474
    if "timestamps" not in reporting_period_data.keys() or \
475
            reporting_period_data['timestamps'] is None or \
476
            len(reporting_period_data['timestamps']) == 0:
477
        has_detail_data_flag = False
478
479
    if has_detail_data_flag:
480
        ws['B' + str(current_row_number)].font = title_font
481
        ws['B' + str(current_row_number)] = name+' 详细数据'
482
483
        ws.row_dimensions[table_row].height = 60
484
        ws['B'+str(table_row)].fill = table_fill
485
        ws['B' + str(table_row)].font = title_font
486
        ws['B'+str(table_row)].border = f_border
487
        ws['B'+str(table_row)].alignment = c_c_alignment
488
        ws['B'+str(table_row)] = '日期时间'
489
        time = times[0]
490
        has_data = False
491
        max_row = 0
492
        if len(time) > 0:
493
            has_data = True
494
            max_row = table_row + len(time)
495
            print("max_row", max_row)
496
497
        if has_data:
498
            for i in range(0, len(time)):
499
                col = 'B'
500
                row = str(table_row+1 + i)
501
                # col = chr(ord('B') + i)
502
                ws[col + row].font = title_font
503
                ws[col + row].alignment = c_c_alignment
504
                ws[col + row] = time[i]
505
                ws[col + row].border = f_border
506
507
            for i in range(0, ca_len):
508
                # 38 title
509
                col = chr(ord('C') + i)
510
511
                ws[col + str(table_row)].fill = table_fill
512
                ws[col + str(table_row)].font = title_font
513
                ws[col + str(table_row)].alignment = c_c_alignment
514
                ws[col + str(table_row)] = reporting_period_data['names'][i] + \
515
                    " (" + reporting_period_data['units'][i] + ")"
516
                ws[col + str(table_row)].border = f_border
517
518
                # 39 data
519
                time = times[i]
520
                time_len = len(time)
521
522
                for j in range(0, time_len):
523
                    row = str(table_row+1 + j)
524
                    # col = chr(ord('B') + i)
525
                    ws[col + row].font = title_font
526
                    ws[col + row].alignment = c_c_alignment
527
                    ws[col + row] = round(reporting_period_data['values'][i][j], 2)
528
                    ws[col + row].border = f_border
529
530
            current_row_number = table_row + 1 + len(times[0])
531
532
            ws['B' + str(current_row_number)].font = title_font
533
            ws['B' + str(current_row_number)].alignment = c_c_alignment
534
            ws['B' + str(current_row_number)].border = f_border
535
            ws['B' + str(current_row_number)] = '小计'
536
537
            for i in range(0, ca_len):
538
                col = chr(ord('C') + i)
539
                ws[col + str(current_row_number)].font = title_font
540
                ws[col + str(current_row_number)].alignment = c_c_alignment
541
                ws[col + str(current_row_number)].border = f_border
542
                ws[col + str(current_row_number)] = round(reporting_period_data['subtotals'][i], 2)
543
544
                # line
545
                # 39~: line
546
                line = LineChart()
547
                line.title = '报告期消耗 - ' + ws.cell(column=3+i, row=table_row).value
548
                labels = Reference(ws, min_col=2, min_row=table_row+1, max_row=max_row)
549
                line_data = Reference(ws, min_col=3 + i, min_row=table_row, max_row=max_row)  # openpyxl bug
550
                line.add_data(line_data, titles_from_data=True)
551
                line.set_categories(labels)
552
                line_data = line.series[0]
553
                line_data.marker.symbol = "circle"
554
                line_data.smooth = True
555
                line.x_axis.crosses = 'min'
556
                line.height = 8.25  # cm 1.05*5 1.05cm = 30 pt
557
                line.width = 24
558
                # pie.title = "Pies sold by category"
559
                line.dLbls = DataLabelList()
560
                line.dLbls.dLblPos = 't'
561
                # line.dLbls.showCatName = True  # label show
562
                line.dLbls.showVal = True  # val show
563
                line.dLbls.showPercent = True  # percent show
564
                # s1 = CharacterProperties(sz=1800)     # font size *100
565
                chart_col = 'B'
566
                chart_cell = chart_col + str(chart_start_row_number + 6*i)
567
                ws.add_chart(line, chart_cell)
568
569
    filename = str(uuid.uuid4()) + '.xlsx'
570
    wb.save(filename)
571
572
    return filename
573