Passed
Push — master ( 242102...47b091 )
by Guangyu
01:52 queued 10s
created

excelexporters.spacestatistics.generate_excel()   F

Complexity

Conditions 49

Size

Total Lines 439
Code Lines 305

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 49
eloc 305
nop 5
dl 0
loc 439
rs 0
c 0
b 0
f 0

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.spacestatistics.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
from openpyxl.chart import (
5
    BarChart,
6
    Reference,
7
)
8
from openpyxl.styles import PatternFill, Border, Side, Alignment, Font
9
from openpyxl.drawing.image import Image
10
from openpyxl import Workbook
11
from openpyxl.chart.label import DataLabelList
12
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
    wb = Workbook()
69
    ws = wb.active
70
71
    # Row height
72
    ws.row_dimensions[1].height = 121
73
74
    for i in range(2, 37 + 1):
75
        ws.row_dimensions[i].height = 30
76
77
    for i in range(38, 90 + 1):
78
        ws.row_dimensions[i].height = 30
79
80
    # Col width
81
    ws.column_dimensions['A'].width = 1.5
82
    ws.column_dimensions['B'].width = 20.0
83
84
    for i in range(ord('C'), ord('I')):
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=False,
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=False,
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=False,
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=False,
124
    #                           shrink_to_fit=False,
125
    #                           indent=0)
126
127
    # Img
128
    img = Image("excelexporters/myems.png")
129
    # img = Image("myems.png")
130
    ws.add_image(img, 'B1')
131
132
    # Title
133
    ws['B3'].font = name_font
134
    ws['B3'].alignment = b_r_alignment
135
    ws['B3'] = 'Name:'
136
    ws['C3'].border = b_border
137
    ws['C3'].alignment = b_c_alignment
138
    ws['C3'].font = name_font
139
    ws['C3'] = name
140
141
    ws['D3'].font = name_font
142
    ws['D3'].alignment = b_r_alignment
143
    ws['D3'] = 'Period:'
144
    ws['E3'].border = b_border
145
    ws['E3'].alignment = b_c_alignment
146
    ws['E3'].font = name_font
147
    ws['E3'] = period_type
148
149
    ws['F3'].font = name_font
150
    ws['F3'].alignment = b_r_alignment
151
    ws['F3'] = 'Date:'
152
    ws['G3'].border = b_border
153
    ws['G3'].alignment = b_c_alignment
154
    ws['G3'].font = name_font
155
    ws['G3'] = reporting_start_datetime_local + "__" + reporting_end_datetime_local
156
    ws.merge_cells("G3:H3")
157
158
    if "reporting_period" not in report.keys() or \
159
            "names" not in report['reporting_period'].keys() or len(report['reporting_period']['names']) == 0:
160
        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...
161
        wb.save(filename)
162
163
        return filename
164
    #################################################
165
    # First: 统计分析
166
    # 6: title
167
    # 7: table title
168
    # 8~11 table_data
169
    # Total: 6 rows
170
    # if has not energy data: set low height for rows
171
    #################################################
172
    reporting_period_data = report['reporting_period']
173
174
    has_energy_data_flag = True
175
176
    if "names" not in reporting_period_data.keys() or \
177
            reporting_period_data['names'] is None or \
178
            len(reporting_period_data['names']) == 0:
179
        has_energy_data_flag = False
180
181
        filename = str(uuid.uuid4()) + '.xlsx'
182
        wb.save(filename)
183
184
        return filename
185
186
    if has_energy_data_flag:
187
        ws['B6'].font = title_font
188
        ws['B6'] = name + ' 统计分析'
189
        # ws['D6'].font = title_font
190
        # ws['D6'] = '面积' +report['space']['area']
191
192
        category = reporting_period_data['names']
193
194
        # table_title
195
        ws['B7'].fill = table_fill
196
        ws['B7'].font = title_font
197
        ws['B7'].alignment = c_c_alignment
198
        ws['B7'] = '报告期'
199
        ws['B7'].border = f_border
200
201
        ws['C7'].font = title_font
202
        ws['C7'].alignment = c_c_alignment
203
        ws['C7'] = '算术平均数'
204
        ws['C7'].border = f_border
205
206
        ws['D7'].font = title_font
207
        ws['D7'].alignment = c_c_alignment
208
        ws['D7'] = '中位数'
209
        ws['D7'].border = f_border
210
211
        ws['E7'].font = title_font
212
        ws['E7'].alignment = c_c_alignment
213
        ws['E7'] = '最小值'
214
        ws['E7'].border = f_border
215
216
        ws['F7'].font = title_font
217
        ws['F7'].alignment = c_c_alignment
218
        ws['F7'] = '最大值'
219
        ws['F7'].border = f_border
220
221
        ws['G7'].font = title_font
222
        ws['G7'].alignment = c_c_alignment
223
        ws['G7'] = '样本标准差'
224
        ws['G7'].border = f_border
225
226
        ws['H7'].font = title_font
227
        ws['H7'].alignment = c_c_alignment
228
        ws['H7'] = '样本方差'
229
        ws['H7'].border = f_border
230
231
        # table_data
232
233
        for i, value in enumerate(category):
234
            row = i + 8
235
            ws['B' + str(row)].font = name_font
236
            ws['B' + str(row)].alignment = c_c_alignment
237
            ws['B' + str(row)] = reporting_period_data['names'][i] + " (" + reporting_period_data['units'][i] + " )"
238
            ws['B' + str(row)].border = f_border
239
240
            ws['C' + str(row)].font = name_font
241
            ws['C' + str(row)].alignment = c_c_alignment
242
            if reporting_period_data['means'][i] or reporting_period_data['means'][i] == 0:
243
                ws['C' + str(row)] = round(reporting_period_data['means'][i], 2)
244
            ws['C' + str(row)].border = f_border
245
246
            ws['D' + str(row)].font = name_font
247
            ws['D' + str(row)].alignment = c_c_alignment
248
            if reporting_period_data['medians'][i] or reporting_period_data['medians'][i] == 0:
249
                ws['D' + str(row)] = round(reporting_period_data['medians'][i], 2)
250
            ws['D' + str(row)].border = f_border
251
252
            ws['E' + str(row)].font = name_font
253
            ws['E' + str(row)].alignment = c_c_alignment
254
            if reporting_period_data['minimums'][i] or reporting_period_data['minimums'][i] == 0:
255
                ws['E' + str(row)] = round(reporting_period_data['minimums'][i], 2)
256
            ws['E' + str(row)].border = f_border
257
258
            ws['F' + str(row)].font = name_font
259
            ws['F' + str(row)].alignment = c_c_alignment
260
            if reporting_period_data['maximums'][i] or reporting_period_data['maximums'][i] == 0:
261
                ws['F' + str(row)] = round(reporting_period_data['maximums'][i], 2)
262
            ws['F' + str(row)].border = f_border
263
264
            ws['G' + str(row)].font = name_font
265
            ws['G' + str(row)].alignment = c_c_alignment
266
            if reporting_period_data['stdevs'][i] or reporting_period_data['stdevs'][i] == 0:
267
                ws['G' + str(row)] = round(reporting_period_data['stdevs'][i], 2)
268
            ws['G' + str(row)].border = f_border
269
270
            ws['H' + str(row)].font = name_font
271
            ws['H' + str(row)].alignment = c_c_alignment
272
            if reporting_period_data['variances'][i] or reporting_period_data['variances'][i] == 0:
273
                ws['H' + str(row)] = round(reporting_period_data['variances'][i], 2)
274
            ws['H' + str(row)].border = f_border
275
276
    #################################################
277
    # First: 统计分析
278
    # 13: title
279
    # 14: table title
280
    # 15~18 table_data
281
    # Total: 6 rows
282
    # if has not energy data: set low height for rows
283
    #################################################
284
285
    if has_energy_data_flag:
286
        ws['B13'].font = title_font
287
        ws['B13'] = name + ' 单位面积值'
288
        ws['D13'].font = title_font
289
        ws['D13'] = str(report['space']['area']) + 'M²'
290
291
        category = reporting_period_data['names']
292
293
        # table_title
294
        ws['B14'].fill = table_fill
295
        ws['B14'].font = title_font
296
        ws['B14'].alignment = c_c_alignment
297
        ws['B14'] = '报告期'
298
        ws['B14'].border = f_border
299
300
        ws['C14'].font = title_font
301
        ws['C14'].alignment = c_c_alignment
302
        ws['C14'] = '算术平均数'
303
        ws['C14'].border = f_border
304
305
        ws['D14'].font = title_font
306
        ws['D14'].alignment = c_c_alignment
307
        ws['D14'] = '中位数'
308
        ws['D14'].border = f_border
309
310
        ws['E14'].font = title_font
311
        ws['E14'].alignment = c_c_alignment
312
        ws['E14'] = '最小值'
313
        ws['E14'].border = f_border
314
315
        ws['F14'].font = title_font
316
        ws['F14'].alignment = c_c_alignment
317
        ws['F14'] = '最大值'
318
        ws['F14'].border = f_border
319
320
        ws['G14'].font = title_font
321
        ws['G14'].alignment = c_c_alignment
322
        ws['G14'] = '样本标准差'
323
        ws['G14'].border = f_border
324
325
        ws['H14'].font = title_font
326
        ws['H14'].alignment = c_c_alignment
327
        ws['H14'] = '样本方差'
328
        ws['H14'].border = f_border
329
330
        # table_data
331
332
        for i, value in enumerate(category):
333
            row = i + 15
334
            ws['B' + str(row)].font = name_font
335
            ws['B' + str(row)].alignment = c_c_alignment
336
            ws['B' + str(row)] = reporting_period_data['names'][i] + " (" + reporting_period_data['units'][
337
                i] + "/M²)"
338
            ws['B' + str(row)].border = f_border
339
340
            ws['C' + str(row)].font = name_font
341
            ws['C' + str(row)].alignment = c_c_alignment
342
            if reporting_period_data['means_per_unit_area'][i] \
343
                    or reporting_period_data['means_per_unit_area'][i] == 0:
344
                ws['C' + str(row)] = round(reporting_period_data['means_per_unit_area'][i], 2)
345
            ws['C' + str(row)].border = f_border
346
347
            ws['D' + str(row)].font = name_font
348
            ws['D' + str(row)].alignment = c_c_alignment
349
            if reporting_period_data['medians_per_unit_area'][i] \
350
                    or reporting_period_data['medians_per_unit_area'][i] == 0:
351
                ws['D' + str(row)] = round(reporting_period_data['medians_per_unit_area'][i], 2)
352
            ws['D' + str(row)].border = f_border
353
354
            ws['E' + str(row)].font = name_font
355
            ws['E' + str(row)].alignment = c_c_alignment
356
            if reporting_period_data['minimums_per_unit_area'][i] \
357
                    or reporting_period_data['minimums_per_unit_area'][i] == 0:
358
                ws['E' + str(row)] = round(reporting_period_data['minimums_per_unit_area'][i], 2)
359
            ws['E' + str(row)].border = f_border
360
361
            ws['F' + str(row)].font = name_font
362
            ws['F' + str(row)].alignment = c_c_alignment
363
            if reporting_period_data['maximums_per_unit_area'][i] \
364
                    or reporting_period_data['maximums_per_unit_area'][i] == 0:
365
                ws['F' + str(row)] = round(reporting_period_data['maximums_per_unit_area'][i], 2)
366
            ws['F' + str(row)].border = f_border
367
368
            ws['G' + str(row)].font = name_font
369
            ws['G' + str(row)].alignment = c_c_alignment
370
            if (reporting_period_data['stdevs_per_unit_area'][i]) \
371
                    or reporting_period_data['stdevs_per_unit_area'][i] == 0:
372
                ws['G' + str(row)] = round(reporting_period_data['stdevs_per_unit_area'][i], 2)
373
            ws['G' + str(row)].border = f_border
374
375
            ws['H' + str(row)].font = name_font
376
            ws['H' + str(row)].alignment = c_c_alignment
377
            if reporting_period_data['variances_per_unit_area'][i] \
378
                    or reporting_period_data['variances_per_unit_area'][i] == 0:
379
                ws['H' + str(row)] = round(reporting_period_data['variances_per_unit_area'][i], 2)
380
            ws['H' + str(row)].border = f_border
381
382
    ################################################
383
    # Fourth:
384
    # 20: title
385
    # 21~ 26+ca_len*5-1: line
386
    # 26+ca_len*5: table title
387
    # 26+ca_len*5~: table_data
388
    ################################################
389
    reporting_period_data = report['reporting_period']
390
    times = reporting_period_data['timestamps']
391
    has_detail_data_flag = True
392
    ca_len = len(report['reporting_period']['names'])
393
    table_row = 27
394
395
    if "timestamps" not in reporting_period_data.keys() or \
396
            reporting_period_data['timestamps'] is None or \
397
            len(reporting_period_data['timestamps']) == 0:
398
        has_detail_data_flag = False
399
400
    if has_detail_data_flag:
401
        ws['B20'].font = title_font
402
        ws['B20'] = name + ' 报告期消耗'
403
        ws['B26'].font = title_font
404
        ws['B26'] = name + ' 详细数据'
405
        # table
406
407
        ws['B' + str(table_row)].fill = table_fill
408
        ws['B' + str(table_row)].border = f_border
409
        ws['B' + str(table_row)].alignment = c_c_alignment
410
        ws['B' + str(table_row)] = '日期时间'
411
        time = times[0]
412
        has_data = False
413
        max_row = 0
414
        if len(time) > 0:
415
            has_data = True
416
            max_row = table_row + len(time)
417
            # print("max_row", max_row)
418
419
        if has_data:
420
            # time
421
            time_len = len(time)
422
            for index in range(0, len(time)):
423
                col = 'B'
424
                row = str(table_row + 1 + index)
425
                # col = chr(ord('B') + i)
426
                ws[col + row].font = title_font
427
                ws[col + row].alignment = c_c_alignment
428
                ws[col + row] = time[index]
429
                ws[col + row].border = f_border
430
                if index == time_len - 1:
431
                    row = str(table_row + 2 + index)
432
                    ws['B' + row].font = title_font
433
                    ws['B' + row].alignment = c_c_alignment
434
                    ws['B' + row] = "小计"
435
                    ws['B' + row].border = f_border
436
            # data
437
            for index in range(0, ca_len):
438
                # table_title
439
                col = chr(ord('C') + index)
440
441
                ws[col + str(table_row)].fill = table_fill
442
                ws[col + str(table_row)].font = title_font
443
                ws[col + str(table_row)].alignment = c_c_alignment
444
                ws[col + str(table_row)] = (reporting_period_data['names'][index] + "(" +
445
                                            reporting_period_data['units'][index] + ")")
446
                ws[col + str(table_row)].border = f_border
447
448
                # data
449
                time = times[index]
450
                time_len = len(time)
451
452
                for j in range(0, time_len):
453
                    row = str(table_row + 1 + j)
454
                    # col = chr(ord('B') + i)
455
                    ws[col + row].font = title_font
456
                    ws[col + row].alignment = c_c_alignment
457
                    ws[col + row] = round(reporting_period_data['values'][index][j], 0)
458
                    ws[col + row].border = f_border
459
                    # subtotals
460
                    if j == time_len - 1:
461
                        row = str(table_row + 2 + j)
462
                        ws[col + row].font = title_font
463
                        ws[col + row].alignment = c_c_alignment
464
                        ws[col + row] = round(reporting_period_data['subtotals'][index], 0)
465
                        ws[col + row].border = f_border
466
467
                # bar
468
                bar = BarChart()
469
                labels = Reference(ws, min_col=2, min_row=table_row + 1, max_row=max_row)
470
                bar_data = Reference(ws, min_col=3 + index, min_row=table_row, max_row=max_row)  # openpyxl bug
471
                bar.add_data(bar_data, titles_from_data=True)
472
                bar.set_categories(labels)
473
                bar.height = 5.25  # cm 1.05*5 1.05cm = 30 pt
474
                bar.width = 18
475
                # pie.title = "Pies sold by category"
476
                bar.dLbls = DataLabelList()
477
                # bar.dLbls.showCatName = True  # label show
478
                bar.dLbls.showVal = True  # val show
479
                bar.dLbls.showPercent = True  # percent show
480
                # s1 = CharacterProperties(sz=1800)     # font size *100
481
                chart_col = 'B'
482
                chart_cell = chart_col + str(21 + 5 * index)
483
                ws.add_chart(bar, chart_cell)
484
485
    # ################################################
486
    # # Fourth: 相关参数
487
    # # table_row+2: title
488
    # # 21~ 26+ca_len*5-1: LineChart
489
    # # 26+ca_len*5: table title
490
    # # 26+ca_len*5~: table_data
491
    # ################################################
492
    #
493
    # reporting_period_data = report['parameters']
494
    # times = reporting_period_data['timestamps']
495
    # has_detail_data_flag = True
496
    # ca_len = len(reporting_period_data['names'])
497
498
    filename = str(uuid.uuid4()) + '.xlsx'
499
    wb.save(filename)
500
501
    return filename
502