Passed
Push — master ( 78eb2a...539abd )
by Guangyu
32:42 queued 11:01
created

excelexporters.metertrend   F

Complexity

Total Complexity 67

Size/Duplication

Total Lines 507
Duplicated Lines 11.44 %

Importance

Changes 0
Metric Value
wmc 67
eloc 352
dl 58
loc 507
rs 3.04
c 0
b 0
f 0

7 Functions

Rating   Name   Duplication   Size   Complexity  
A decimal_to_column() 19 19 5
A timestamps_data_all_equal_0() 0 6 3
A get_parameters_timestamps_lists_max_len() 0 7 3
B export() 39 39 5
F generate_excel() 0 387 46
A timestamps_data_not_equal_0() 0 6 3
A column_to_decimal() 0 6 2

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.metertrend 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
import openpyxl.utils.cell as format_cell
15
16
17
####################################################################################################################
18
# PROCEDURES
19
# Step 1: Validate the report data
20
# Step 2: Generate excel file
21
# Step 3: Encode the excel file bytes to Base64
22
####################################################################################################################
23
24
25 View Code Duplication
def export(result,
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
26
           name,
27
           reporting_start_datetime_local,
28
           reporting_end_datetime_local,
29
           period_type):
30
    ####################################################################################################################
31
    # Step 1: Validate the report data
32
    ####################################################################################################################
33
    if result is None:
34
        return None
35
36
    ####################################################################################################################
37
    # Step 2: Generate excel file from the report data
38
    ####################################################################################################################
39
    filename = generate_excel(result,
40
                              name,
41
                              reporting_start_datetime_local,
42
                              reporting_end_datetime_local,
43
                              period_type)
44
45
    ####################################################################################################################
46
    # Step 3: Encode the excel file to Base64
47
    ####################################################################################################################
48
    try:
49
        with open(filename, 'rb') as binary_file:
50
            binary_file_data = binary_file.read()
51
    except IOError as ex:
52
        pass
53
54
    # Base64 encode the bytes
55
    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...
56
    # get the Base64 encoded data using human-readable characters.
57
    base64_message = base64_encoded_data.decode('utf-8')
58
    # delete the file from server
59
    try:
60
        os.remove(filename)
61
    except NotImplementedError as ex:
62
        pass
63
    return base64_message
64
65
66
def timestamps_data_all_equal_0(lists):
67
    for i, value in enumerate(list(lists)):
68
        if len(value) > 0:
69
            return False
70
71
    return True
72
73
74
def get_parameters_timestamps_lists_max_len(parameters_timestamps_lists):
75
    max_len = 0
76
    for i, value in enumerate(list(parameters_timestamps_lists)):
77
        if len(value) > max_len:
78
            max_len = len(value)
79
80
    return max_len
81
82
83
def timestamps_data_not_equal_0(lists):
84
    number = 0
85
    for i, value in enumerate(list(lists)):
86
        if len(value) > 0:
87
            number += 1
88
    return number
89
90
91 View Code Duplication
def decimal_to_column(num=65):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
92
    string = ''
93
    num = num - 64
94
# The column number is not greater than 90
95
    if num <= 26:
96
        return chr(num+64)
97
# The column number is greater than 90
98
    while num // 26 > 0:
99
        if num % 26 == 0:
100
            string += 'Z'
101
            num = num // 26 - 1
102
        else:
103
            string += chr(num % 26 + 64)
104
            num //= 26
105
# Avoid conversion errors that might occur between 741 and 766
106
    if num > 0:
107
        string += chr(num + 64)
108
109
    return string[::-1]
110
111
112
def column_to_decimal(string='A'):
113
    num = 0
114
    for index, key in enumerate(string[::-1]):
115
        num += (ord(key) - 64) * (26 ** index)
116
117
    return num + 64
118
119
120
def generate_excel(report,
121
                   name,
122
                   reporting_start_datetime_local,
123
                   reporting_end_datetime_local,
124
                   period_type):
125
    wb = Workbook()
126
    ws = wb.active
127
128
    # Row height
129
    ws.row_dimensions[1].height = 102
130
    for i in range(2, 5 + 1):
131
        ws.row_dimensions[i].height = 42
132
133
    # for i in range(2, 6 + 1):
134
    #     ws.row_dimensions[i].height = 30
135
136
    # Col width
137
    ws.column_dimensions['A'].width = 1.5
138
139
    ws.column_dimensions['B'].width = 25.0
140
141
    for i in range(ord('C'), ord('V')):
142
        ws.column_dimensions[chr(i)].width = 15.0
143
144
    # Font
145
    name_font = Font(name='Constantia', size=15, bold=True)
146
    title_font = Font(name='宋体', size=15, bold=True)
147
    data_font = Font(name='Franklin Gothic Book', size=11)
148
149
    table_fill = PatternFill(fill_type='solid', fgColor='1F497D')
150
    f_border = Border(left=Side(border_style='medium', color='00000000'),
151
                      right=Side(border_style='medium', color='00000000'),
152
                      bottom=Side(border_style='medium', color='00000000'),
153
                      top=Side(border_style='medium', color='00000000')
154
                      )
155
    b_border = Border(
156
        bottom=Side(border_style='medium', color='00000000'),
157
    )
158
159
    b_c_alignment = Alignment(vertical='bottom',
160
                              horizontal='center',
161
                              text_rotation=0,
162
                              wrap_text=True,
163
                              shrink_to_fit=False,
164
                              indent=0)
165
    c_c_alignment = Alignment(vertical='center',
166
                              horizontal='center',
167
                              text_rotation=0,
168
                              wrap_text=True,
169
                              shrink_to_fit=False,
170
                              indent=0)
171
    b_r_alignment = Alignment(vertical='bottom',
172
                              horizontal='right',
173
                              text_rotation=0,
174
                              wrap_text=True,
175
                              shrink_to_fit=False,
176
                              indent=0)
177
    c_r_alignment = Alignment(vertical='bottom',
178
                              horizontal='center',
179
                              text_rotation=0,
180
                              wrap_text=True,
181
                              shrink_to_fit=False,
182
                              indent=0)
183
184
    # Img
185
    img = Image("excelexporters/myems.png")
186
    img.width = img.width * 0.85
187
    img.height = img.height * 0.85
188
    # img = Image("myems.png")
189
    ws.add_image(img, 'B1')
190
191
    # Title
192
    ws.row_dimensions[3].height = 60
193
194
    ws['B3'].font = name_font
195
    ws['B3'].alignment = b_r_alignment
196
    ws['B3'] = 'Name:'
197
    ws['C3'].border = b_border
198
    ws['C3'].alignment = b_c_alignment
199
    ws['C3'].font = name_font
200
    ws['C3'] = name
201
202
    ws['F3'].font = name_font
203
    ws['F3'].alignment = b_r_alignment
204
    ws['F3'] = 'Date:'
205
    ws['G3'].border = b_border
206
    ws['G3'].alignment = b_c_alignment
207
    ws['G3'].font = name_font
208
    ws['G3'] = reporting_start_datetime_local + "__" + reporting_end_datetime_local
209
    ws.merge_cells("G3:H3")
210
    if "reporting_period" not in report.keys() or \
211
            "names" not in report['reporting_period'].keys() or len(report['reporting_period']['names']) == 0:
212
        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...
213
        wb.save(filename)
214
215
        return filename
216
    ################################################
217
    # First: 趋势
218
    # 6: title
219
    # 7: table title
220
    # 8~ table_data
221
    ################################################
222
    has_data_flag = True
223
    reporting_period_data = report['reporting_period']
224
    if "names" not in reporting_period_data.keys() or \
225
            reporting_period_data['names'] is None or \
226
            len(reporting_period_data['names']) == 0:
227
        has_data_flag = False
228
229
    if "timestamps" not in reporting_period_data.keys() or \
230
            reporting_period_data['timestamps'] is None or \
231
            len(reporting_period_data['timestamps']) == 0:
232
        has_data_flag = False
233
234
    if "values" not in reporting_period_data.keys() or \
235
            reporting_period_data['values'] is None or \
236
            len(reporting_period_data['values']) == 0:
237
        has_data_flag = False
238
    ca = reporting_period_data['names']
239
    ca_len = len(ca)
240
    temp_max_row = 0
241
    times = reporting_period_data['timestamps']
242
    category = report['meter']['energy_category_name']
243
    if has_data_flag:
244
        ws['B6'].font = title_font
245
        ws['B6'] = name + ' 趋势'
246
247
        ws.row_dimensions[7].height = 60
248
        ws['B7'].fill = table_fill
249
        ws['B7'].font = title_font
250
        ws['B7'].border = f_border
251
        ws['B7'].alignment = c_c_alignment
252
        ws['B7'] = '日期时间'
253
        time = times[0]
254
        for time in times:
255
            if len(time) > 0:
256
                break
257
        has_data = False
258
        max_row = 0
259
        current_sheet_parameters_row_number = 10
260
        if len(time) > 0:
261
            has_data = True
262
            max_row = 8 + len(time)
263
            # print("max_row", max_row)
264
            temp_max_row = max_row
265
            current_sheet_parameters_row_number = 2 + ca_len * 6 + temp_max_row
266
        if has_data:
267
            for i in range(0, len(time)):
268
                col = 'B'
269
                row = str(8 + i)
270
                # col = chr(ord('B') + i)
271
                ws[col + row].font = title_font
272
                ws[col + row].alignment = c_c_alignment
273
                ws[col + row] = time[i]
274
                ws[col + row].border = f_border
275
276
            for i in range(0, ca_len):
277
                # 38 title
278
                col = chr(ord('C') + i)
279
280
                ws[col + '7'].fill = table_fill
281
                ws[col + '7'].font = title_font
282
                ws[col + '7'].alignment = c_c_alignment
283
                ws[col + '7'] = reporting_period_data['names'][i]
284
                ws[col + '7'].border = f_border
285
286
                for j in range(0, len(time)):
287
288
                    row = str(8 + j)
289
                    # col = chr(ord('B') + i)
290
                    ws[col + row].font = title_font
291
                    ws[col + row].alignment = c_c_alignment
292
                    ws[col + row] = round(reporting_period_data['values'][i][j], 3) if \
293
                        len(reporting_period_data['values'][i]) > 0 is not None and \
294
                        reporting_period_data['values'][i][j] is not None else " "
295
                    ws[col + row].border = f_border
296
            # line
297
            # 39~: line
298
                line = LineChart()
299
                line.title = '趋势值 - ' + reporting_period_data['names'][i]
300
                labels = Reference(ws, min_col=2, min_row=8, max_row=max_row-1)
301
                line_data = Reference(ws, min_col=3 + i, min_row=7, max_row=max_row-1)
302
                line.add_data(line_data, titles_from_data=True)
303
                line.set_categories(labels)
304
                # line_data = line.series[0]
305
                # line_data.marker.symbol = "circle"
306
                line_data.smooth = True
307
                line.x_axis.crosses = 'min'
308
                line.height = 8.25  # cm 1.05*5 1.05cm = 30 pt
309
                line.width = 36
310
                # pie.title = "Pies sold by category"
311
                line.dLbls = DataLabelList()
312
                # line.dLbls.showCatName = True  # label show
313
                line.dLbls.dLblPos = 't'
314
                line.dLbls.showVal = False  # val show
315
                line.dLbls.showPercent = False  # percent show
316
                # s1 = CharacterProperties(sz=1800)     # font size *100
317
                chart_col = chr(ord('B'))
318
                chart_cell = chart_col + str(max_row + 2 + 6*i)
319
320
                ws.add_chart(line, chart_cell)
321
    else:
322
        pass
323
324
    for i in range(8, temp_max_row + 1 + 1 + + ca_len * 6 + len(category) * 6 + 2):
325
        ws.row_dimensions[i].height = 42
326
327
    ##########################################
328
    has_parameters_names_and_timestamps_and_values_data = True
329
    # 12 is the starting line number of the last line chart in the report period
330
331
    if 'parameters' not in report.keys() or \
332
            report['parameters'] is None or \
333
            'names' not in report['parameters'].keys() or \
334
            report['parameters']['names'] is None or \
335
            len(report['parameters']['names']) == 0 or \
336
            'timestamps' not in report['parameters'].keys() or \
337
            report['parameters']['timestamps'] is None or \
338
            len(report['parameters']['timestamps']) == 0 or \
339
            'values' not in report['parameters'].keys() or \
340
            report['parameters']['values'] is None or \
341
            len(report['parameters']['values']) == 0 or \
342
            timestamps_data_all_equal_0(report['parameters']['timestamps']):
343
        has_parameters_names_and_timestamps_and_values_data = False
344
    if has_parameters_names_and_timestamps_and_values_data:
345
346
        ###############################
347
        # new worksheet
348
        ###############################
349
350
        parameters_data = report['parameters']
351
352
        parameters_names_len = len(parameters_data['names'])
353
354
        parameters_ws = wb.create_sheet('相关参数')
355
356
        parameters_timestamps_data_max_len = \
357
            get_parameters_timestamps_lists_max_len(list(parameters_data['timestamps']))
358
359
        # Row height
360
        parameters_ws.row_dimensions[1].height = 102
361
        for i in range(2, 7 + 1):
362
            parameters_ws.row_dimensions[i].height = 42
363
364
        for i in range(8, parameters_timestamps_data_max_len + 10):
365
            parameters_ws.row_dimensions[i].height = 60
366
367
        # Col width
368
        parameters_ws.column_dimensions['A'].width = 1.5
369
370
        parameters_ws.column_dimensions['B'].width = 25.0
371
372
        for i in range(3, 12 + parameters_names_len * 3):
373
            parameters_ws.column_dimensions[format_cell.get_column_letter(i)].width = 15.0
374
375
        # Img
376
        img = Image("excelexporters/myems.png")
377
        img.width = img.width * 0.85
378
        img.height = img.height * 0.85
379
        # img = Image("myems.png")
380
        parameters_ws.add_image(img, 'B1')
381
382
        # Title
383
        parameters_ws.row_dimensions[3].height = 60
384
385
        parameters_ws['B3'].font = name_font
386
        parameters_ws['B3'].alignment = b_r_alignment
387
        parameters_ws['B3'] = 'Name:'
388
        parameters_ws['C3'].border = b_border
389
        parameters_ws['C3'].alignment = b_c_alignment
390
        parameters_ws['C3'].font = name_font
391
        parameters_ws['C3'] = name
392
393
        parameters_ws['F3'].font = name_font
394
        parameters_ws['F3'].alignment = b_r_alignment
395
        parameters_ws['F3'] = 'Date:'
396
        parameters_ws['G3'].border = b_border
397
        parameters_ws['G3'].alignment = b_c_alignment
398
        parameters_ws['G3'].font = name_font
399
        parameters_ws['G3'] = reporting_start_datetime_local + "__" + reporting_end_datetime_local
400
        parameters_ws.merge_cells("G3:H3")
401
402
        parameters_ws_current_row_number = 6
403
404
        parameters_ws['B' + str(parameters_ws_current_row_number)].font = title_font
405
        parameters_ws['B' + str(parameters_ws_current_row_number)] = name + ' 相关参数'
406
407
        parameters_ws_current_row_number += 1
408
409
        parameters_table_start_row_number = parameters_ws_current_row_number
410
411
        parameters_ws.row_dimensions[parameters_ws_current_row_number].height = 80
412
413
        parameters_ws_current_row_number += 1
414
415
        table_current_col_number = 'B'
416
417
        for i in range(0, parameters_names_len):
418
419
            if len(parameters_data['timestamps'][i]) == 0:
420
                continue
421
422
            parameters_ws[table_current_col_number + str(parameters_ws_current_row_number - 1)].fill = table_fill
423
            parameters_ws[table_current_col_number + str(parameters_ws_current_row_number - 1)].border = f_border
424
425
            col = decimal_to_column(column_to_decimal(table_current_col_number) + 1)
426
427
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].fill = table_fill
428
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].border = f_border
429
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].font = name_font
430
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].alignment = c_c_alignment
431
            parameters_ws[col + str(parameters_ws_current_row_number - 1)] = parameters_data['names'][i]
432
433
            table_current_row_number = parameters_ws_current_row_number
434
435
            for j, value in enumerate(list(parameters_data['timestamps'][i])):
436
                col = table_current_col_number
437
438
                parameters_ws[col + str(table_current_row_number)].border = f_border
439
                parameters_ws[col + str(table_current_row_number)].font = title_font
440
                parameters_ws[col + str(table_current_row_number)].alignment = c_c_alignment
441
                parameters_ws[col + str(table_current_row_number)] = value
442
443
                col = decimal_to_column(column_to_decimal(col) + 1)
444
445
                parameters_ws[col + str(table_current_row_number)].border = f_border
446
                parameters_ws[col + str(table_current_row_number)].font = title_font
447
                parameters_ws[col + str(table_current_row_number)].alignment = c_c_alignment
448
                parameters_ws[col + str(table_current_row_number)] = round(parameters_data['values'][i][j], 2)
449
450
                table_current_row_number += 1
451
452
            table_current_col_number = decimal_to_column(column_to_decimal(table_current_col_number) + 3)
453
454
        ########################################################
455
        # parameters chart and parameters table
456
        ########################################################
457
458
        ws['B' + str(current_sheet_parameters_row_number)].font = title_font
0 ignored issues
show
introduced by
The variable current_sheet_parameters_row_number does not seem to be defined for all execution paths.
Loading history...
459
        ws['B' + str(current_sheet_parameters_row_number)] = name + ' 相关参数'
460
461
        current_sheet_parameters_row_number += 1
462
463
        chart_start_row_number = current_sheet_parameters_row_number
464
465
        col_index = 0
466
467
        for i in range(0, parameters_names_len):
468
469
            if len(parameters_data['timestamps'][i]) == 0:
470
                continue
471
472
            line = LineChart()
473
            data_col = 3 + col_index * 3
474
            labels_col = 2 + col_index * 3
475
            col_index += 1
476
            line.title = '相关参数 - ' + \
477
                         parameters_ws.cell(row=parameters_table_start_row_number, column=data_col).value
478
            labels = Reference(parameters_ws, min_col=labels_col, min_row=parameters_table_start_row_number + 1,
479
                               max_row=(len(parameters_data['timestamps'][i]) + parameters_table_start_row_number))
480
            line_data = Reference(parameters_ws, min_col=data_col, min_row=parameters_table_start_row_number,
481
                                  max_row=(len(parameters_data['timestamps'][i]) + parameters_table_start_row_number))
482
            line.add_data(line_data, titles_from_data=True)
483
            line.set_categories(labels)
484
            line_data = line.series[0]
485
            line_data.marker.symbol = "circle"
486
            line_data.smooth = True
487
            line.x_axis.crosses = 'min'
488
            line.height = 8.25
489
            line.width = 24
490
            line.dLbls = DataLabelList()
491
            line.dLbls.dLblPos = 't'
492
            line.dLbls.showVal = False
493
            line.dLbls.showPercent = False
494
            chart_col = 'B'
495
            chart_cell = chart_col + str(chart_start_row_number)
496
            chart_start_row_number += 6
497
            ws.add_chart(line, chart_cell)
498
499
        current_sheet_parameters_row_number = chart_start_row_number
500
501
        current_sheet_parameters_row_number += 1
502
503
    filename = str(uuid.uuid4()) + '.xlsx'
504
    wb.save(filename)
505
506
    return filename
507