Passed
Push — master ( a065be...58905b )
by Guangyu
02:13 queued 12s
created

excelexporters.spaceenergycategory   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 532
Duplicated Lines 7.33 %

Importance

Changes 0
Metric Value
eloc 382
dl 39
loc 532
rs 8.8
c 0
b 0
f 0
wmc 45

2 Functions

Rating   Name   Duplication   Size   Complexity  
B export() 39 39 5
F generate_excel() 0 469 40

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