Passed
Push — master ( 7f4bf7...0096ee )
by Guangyu
26:42 queued 11s
created

excelexporters.metercost.column_to_decimal()   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 2
nop 1
1
import base64
2
import uuid
3
import os
4
from openpyxl.chart import (
5
    BarChart,
6
    LineChart,
7
    Reference,
8
    Series
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 excelexporters file
21
# Step 3: Encode the excelexporters file to Base64
22
####################################################################################################################
23
24 View Code Duplication
def export(report, name, reporting_start_datetime_local, reporting_end_datetime_local, period_type):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
25
    ####################################################################################################################
26
    # Step 1: Validate the report data
27
    ####################################################################################################################
28
    if report is None:
29
        return None
30
31
    if "reporting_period" not in report.keys() or \
32
            "values" not in report['reporting_period'].keys() or len(report['reporting_period']['values']) == 0:
33
        return None
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 timestamps_data_all_equal_0(lists):
64
    for i, value in enumerate(list(lists)):
65
        if len(value) > 0:
66
            return False
67
68
    return True
69
70
71
def get_parameters_timestamps_lists_max_len(parameters_timestamps_lists):
72
    max_len = 0
73
    for i, value in enumerate(list(parameters_timestamps_lists)):
74
        if len(value) > max_len:
75
            max_len = len(value)
76
77
    return max_len
78
79
80
def timestamps_data_not_equal_0(lists):
81
    number = 0
82
    for i, value in enumerate(list(lists)):
83
        if len(value) > 0:
84
            number += 1
85
    return number
86
87
88
def decimal_to_column(num=65):
89
    string = ''
90
    num = num - 64
91
    # The column number is not greater than 90
92
    if num <= 26:
93
        return chr(num + 64)
94
    # The column number is greater than 90
95
    while num // 26 > 0:
96
        if num % 26 == 0:
97
            string += 'Z'
98
            num = num // 26 - 1
99
        else:
100
            string += chr(num % 26 + 64)
101
            num //= 26
102
    # Avoid conversion errors that might occur between 741 and 766
103
    if num > 0:
104
        string += chr(num + 64)
105
106
    return string[::-1]
107
108
109
def column_to_decimal(string='A'):
110
    num = 0
111
    for index, key in enumerate(string[::-1]):
112
        num += (ord(key) - 64) * (26 ** index)
113
114
    return num + 64
115
116
117
def generate_excel(report, name, reporting_start_datetime_local, reporting_end_datetime_local, period_type):
118
    wb = Workbook()
119
120
    # todo
121
    ws = wb.active
122
123
    # Row height
124
    ws.row_dimensions[1].height = 102
125
    for i in range(2, 2000 + 1):
126
        ws.row_dimensions[i].height = 42
127
    # for i in range(2, 11 + 1):
128
    #     ws.row_dimensions[i].height = 30
129
    #
130
    # for i in range(12, 43 + 1):
131
    #     ws.row_dimensions[i].height = 30
132
133
    # Col width
134
    ws.column_dimensions['A'].width = 1.5
135
136
    ws.column_dimensions['B'].width = 25.0
137
138
    for i in range(ord('C'), ord('L')):
139
        ws.column_dimensions[chr(i)].width = 15.0
140
141
    # Font
142
    name_font = Font(name='Constantia', size=15, bold=True)
143
    title_font = Font(name='宋体', size=15, bold=True)
144
    data_font = Font(name='Franklin Gothic Book', size=11)
145
146
    table_fill = PatternFill(fill_type='solid', fgColor='1F497D')
147
    f_border = Border(left=Side(border_style='medium', color='00000000'),
148
                      right=Side(border_style='medium', color='00000000'),
149
                      bottom=Side(border_style='medium', color='00000000'),
150
                      top=Side(border_style='medium', color='00000000')
151
                      )
152
    b_border = Border(
153
        bottom=Side(border_style='medium', color='00000000'),
154
    )
155
156
    b_c_alignment = Alignment(vertical='bottom',
157
                              horizontal='center',
158
                              text_rotation=0,
159
                              wrap_text=True,
160
                              shrink_to_fit=False,
161
                              indent=0)
162
    c_c_alignment = Alignment(vertical='center',
163
                              horizontal='center',
164
                              text_rotation=0,
165
                              wrap_text=True,
166
                              shrink_to_fit=False,
167
                              indent=0)
168
    b_r_alignment = Alignment(vertical='bottom',
169
                              horizontal='right',
170
                              text_rotation=0,
171
                              wrap_text=True,
172
                              shrink_to_fit=False,
173
                              indent=0)
174
    c_r_alignment = Alignment(vertical='bottom',
175
                              horizontal='center',
176
                              text_rotation=0,
177
                              wrap_text=True,
178
                              shrink_to_fit=False,
179
                              indent=0)
180
181
    # Img
182
    img = Image("excelexporters/myems.png")
183
    img.width = img.width * 0.85
184
    img.height = img.height * 0.85
185
    # img = Image("myems.png")
186
    ws.add_image(img, 'B1')
187
188
    # Title
189
    ws.row_dimensions[3].height = 60
190
191
    ws['B3'].font = name_font
192
    ws['B3'].alignment = b_r_alignment
193
    ws['B3'] = 'Name:'
194
    ws['C3'].border = b_border
195
    ws['C3'].alignment = b_c_alignment
196
    ws['C3'].font = name_font
197
    ws['C3'] = name
198
199
    ws['D3'].font = name_font
200
    ws['D3'].alignment = b_r_alignment
201
    ws['D3'] = 'Period:'
202
    ws['E3'].border = b_border
203
    ws['E3'].alignment = b_c_alignment
204
    ws['E3'].font = name_font
205
    ws['E3'] = period_type
206
207
    ws['F3'].font = name_font
208
    ws['F3'].alignment = b_r_alignment
209
    ws['F3'] = 'Date:'
210
    ws['G3'].border = b_border
211
    ws['G3'].alignment = b_c_alignment
212
    ws['G3'].font = name_font
213
    ws['G3'] = reporting_start_datetime_local + "__" + reporting_end_datetime_local
214
    ws.merge_cells("G3:H3")
215
216
    if "reporting_period" not in report.keys() or \
217
            "values" not in report['reporting_period'].keys() or len(report['reporting_period']['values']) == 0:
218
        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...
219
        wb.save(filename)
220
221
        return filename
222
223
    ###############################
224
225
    has_cost_data_flag = True
226
227
    if "values" not in report['reporting_period'].keys() or len(report['reporting_period']['values']) == 0:
228
        has_cost_data_flag = False
229
230
    if has_cost_data_flag:
231
        ws['B6'].font = title_font
232
        ws['B6'] = name + '报告期成本'
233
234
        reporting_period_data = report['reporting_period']
235
        category = report['meter']['energy_category_name']
236
        ca_len = len(category)
237
238
        ws.row_dimensions[7].height = 60
239
        ws['B7'].fill = table_fill
240
        ws['B7'].border = f_border
241
242
        ws['B8'].font = title_font
243
        ws['B8'].alignment = c_c_alignment
244
        ws['B8'] = '成本'
245
        ws['B8'].border = f_border
246
247
        ws['B9'].font = title_font
248
        ws['B9'].alignment = c_c_alignment
249
        ws['B9'] = '环比'
250
        ws['B9'].border = f_border
251
252
        col = 'B'
253
254
        for i in range(0, ca_len):
255
            col = chr(ord('C') + i)
256
257
            ws[col + '7'].fill = table_fill
258
            ws[col + '7'].font = name_font
259
            ws[col + '7'].alignment = c_c_alignment
260
            ws[col + '7'] = report['meter']['energy_category_name'] + " (" + report['meter']['unit_of_measure'] + ")"
261
            ws[col + '7'].border = f_border
262
263
            ws[col + '8'].font = name_font
264
            ws[col + '8'].alignment = c_c_alignment
265
            ws[col + '8'] = round(reporting_period_data['total_in_category'], 2)
266
            ws[col + '8'].border = f_border
267
268
            ws[col + '9'].font = name_font
269
            ws[col + '9'].alignment = c_c_alignment
270
            ws[col + '9'] = str(round(reporting_period_data['increment_rate'] * 100, 2)) + "%" \
271
                if reporting_period_data['increment_rate'] is not None else "-"
272
            ws[col + '9'].border = f_border
273
274
        # TCE TCO2E
275
        end_col = col
276
        # TCE
277
        tce_col = chr(ord(end_col) + 1)
278
        ws[tce_col + '7'].fill = table_fill
279
        ws[tce_col + '7'].font = name_font
280
        ws[tce_col + '7'].alignment = c_c_alignment
281
        ws[tce_col + '7'] = "吨标准煤 (TCE)"
282
        ws[tce_col + '7'].border = f_border
283
284
        ws[tce_col + '8'].font = name_font
285
        ws[tce_col + '8'].alignment = c_c_alignment
286
        ws[tce_col + '8'] = round(reporting_period_data['total_in_kgce'] / 1000, 2)
287
        ws[tce_col + '8'].border = f_border
288
289
        ws[tce_col + '9'].font = name_font
290
        ws[tce_col + '9'].alignment = c_c_alignment
291
        ws[tce_col + '9'] = str(round(reporting_period_data['increment_rate'] * 100, 2)) + "%" \
292
            if reporting_period_data['increment_rate'] is not None else "-"
293
        ws[tce_col + '9'].border = f_border
294
295
        # TCO2E
296
        tco2e_col = chr(ord(end_col) + 2)
297
        ws[tco2e_col + '7'].fill = table_fill
298
        ws[tco2e_col + '7'].font = name_font
299
        ws[tco2e_col + '7'].alignment = c_c_alignment
300
        ws[tco2e_col + '7'] = "吨二氧化碳排放 (TCO2E)"
301
        ws[tco2e_col + '7'].border = f_border
302
303
        ws[tco2e_col + '8'].font = name_font
304
        ws[tco2e_col + '8'].alignment = c_c_alignment
305
        ws[tco2e_col + '8'] = round(reporting_period_data['total_in_kgco2e'] / 1000, 2)
306
        ws[tco2e_col + '8'].border = f_border
307
308
        ws[tco2e_col + '9'].font = name_font
309
        ws[tco2e_col + '9'].alignment = c_c_alignment
310
        ws[tco2e_col + '9'] = str(round(reporting_period_data['increment_rate'] * 100, 2)) + "%" \
311
            if reporting_period_data['increment_rate'] is not None else "-"
312
        ws[tco2e_col + '9'].border = f_border
313
314
    else:
315
        for i in range(6, 9 + 1):
316
            ws.rows_dimensions[i].height = 0.1
317
318
    ######################################
319
320
    has_cost_datail_flag = True
321
    reporting_period_data = report['reporting_period']
322
    category = report['meter']['energy_category_name']
323
    ca_len = len(category)
324
    times = reporting_period_data['timestamps']
325
326
    if "values" not in reporting_period_data.keys() or len(reporting_period_data['values']) == 0:
327
        has_cost_datail_flag = False
328
329
    if has_cost_datail_flag:
330
        parameters_names_len = len(report['parameters']['names'])
331
        start_detail_data_row_number = 7 + (parameters_names_len + ca_len) * 6
332
333
        ws['B11'].font = title_font
334
        ws['B11'] = name + '详细数据'
335
336
        ws.row_dimensions[start_detail_data_row_number].height = 60
337
        ws['B' + str(start_detail_data_row_number)].fill = table_fill
338
        ws['B' + str(start_detail_data_row_number)].font = title_font
339
        ws['B' + str(start_detail_data_row_number)].border = f_border
340
        ws['B' + str(start_detail_data_row_number)].alignment = c_c_alignment
341
        ws['B' + str(start_detail_data_row_number)] = '日期时间'
342
        time = times
343
        has_data = False
344
        max_row = 0
345
        if len(time) > 0:
346
            has_data = True
347
            max_row = start_detail_data_row_number + len(time)
348
349
        if has_data:
350
351
            end_data_row_number = start_detail_data_row_number
352
353
            for i in range(0, len(time)):
354
                col = 'B'
355
                end_data_row_number += 1
356
                row = str(end_data_row_number)
357
358
                ws[col + row].font = title_font
359
                ws[col + row].alignment = c_c_alignment
360
                ws[col + row] = time[i]
361
                ws[col + row].border = f_border
362
363
            ws['B' + str(end_data_row_number + 1)].font = title_font
364
            ws['B' + str(end_data_row_number + 1)].alignment = c_c_alignment
365
            ws['B' + str(end_data_row_number + 1)] = '总计'
366
            ws['B' + str(end_data_row_number + 1)].border = f_border
367
368
            for i in range(0, ca_len):
369
370
                col = chr(ord('C') + i)
371
372
                ws[col + str(start_detail_data_row_number)].fill = table_fill
373
                ws[col + str(start_detail_data_row_number)].font = title_font
374
                ws[col + str(start_detail_data_row_number)].alignment = c_c_alignment
375
                ws[col + str(start_detail_data_row_number)] = \
376
                    report['meter']['energy_category_name']+" (" + report['meter']['unit_of_measure'] + ")"
377
                ws[col + str(start_detail_data_row_number)].border = f_border
378
379
                time = times
380
                time_len = len(time)
381
382
                for j in range(0, time_len):
383
                    row = str(start_detail_data_row_number + 1 + j)
384
385
                    ws[col + row].font = title_font
386
                    ws[col + row].alignment = c_c_alignment
387
                    ws[col + row] = round(reporting_period_data['values'][j], 2)
388
                    ws[col + row].border = f_border
389
390
                ws[col + str(end_data_row_number + 1)].font = title_font
391
                ws[col + str(end_data_row_number + 1)].alignment = c_c_alignment
392
                ws[col + str(end_data_row_number + 1)] = round(reporting_period_data['total_in_category'], 2)
393
                ws[col + str(end_data_row_number + 1)].border = f_border
394
395
            line = LineChart()
396
            line.title = '报告期成本 - ' + report['meter']['energy_category_name'] + \
397
                         " (" + report['meter']['unit_of_measure'] + ")"
398
            line_data = Reference(ws, min_col=3, min_row=start_detail_data_row_number, max_row=max_row)
399
            line.series.append(Series(line_data, title_from_data=True))
400
            labels = Reference(ws, min_col=2, min_row=start_detail_data_row_number + 1, max_row=max_row)
401
            line.set_categories(labels)
402
            line_data = line.series[0]
403
            line_data.marker.symbol = "circle"
404
            line_data.smooth = True
405
            line.x_axis.crosses = 'min'
406
            line.dLbls = DataLabelList()
407
            line.dLbls.dLblPos = 't'
408
            line.dLbls.showVal = True
409
            line.height = 8.25
410
            line.width = 24
411
            ws.add_chart(line, "B12")
412
    else:
413
        for i in range(11, 43 + 1):
414
            ws.row_dimensions[i].height = 0.0
415
416
    ##########################################
417
    has_parameters_names_and_timestamps_and_values_data = True
418
    # 12 is the starting line number of the last line chart in the report period
419
    time_len = len(reporting_period_data['timestamps'])
420
    current_sheet_parameters_row_number = 12 + ca_len * 6
421
    if 'parameters' not in report.keys() or \
422
            report['parameters'] is None or \
423
            'names' not in report['parameters'].keys() or \
424
            report['parameters']['names'] is None or \
425
            len(report['parameters']['names']) == 0 or \
426
            'timestamps' not in report['parameters'].keys() or \
427
            report['parameters']['timestamps'] is None or \
428
            len(report['parameters']['timestamps']) == 0 or \
429
            'values' not in report['parameters'].keys() or \
430
            report['parameters']['values'] is None or \
431
            len(report['parameters']['values']) == 0 or \
432
            timestamps_data_all_equal_0(report['parameters']['timestamps']):
433
        has_parameters_names_and_timestamps_and_values_data = False
434
    if has_parameters_names_and_timestamps_and_values_data:
435
436
        ###############################
437
        # new worksheet
438
        ###############################
439
440
        parameters_data = report['parameters']
441
442
        parameters_names_len = len(parameters_data['names'])
443
444
        parameters_ws = wb.create_sheet('相关参数')
445
446
        parameters_timestamps_data_max_len = \
447
            get_parameters_timestamps_lists_max_len(list(parameters_data['timestamps']))
448
449
        # Row height
450
        parameters_ws.row_dimensions[1].height = 102
451
        for i in range(2, 7 + 1):
452
            parameters_ws.row_dimensions[i].height = 42
453
454
        for i in range(8, parameters_timestamps_data_max_len + 10):
455
            parameters_ws.row_dimensions[i].height = 60
456
457
        # Col width
458
        parameters_ws.column_dimensions['A'].width = 1.5
459
460
        parameters_ws.column_dimensions['B'].width = 25.0
461
462
        for i in range(3, 12 + parameters_names_len * 3):
463
            parameters_ws.column_dimensions[format_cell.get_column_letter(i)].width = 15.0
464
465
        # Img
466
        img = Image("excelexporters/myems.png")
467
        img.width = img.width * 0.85
468
        img.height = img.height * 0.85
469
        # img = Image("myems.png")
470
        parameters_ws.add_image(img, 'B1')
471
472
        # Title
473
        parameters_ws.row_dimensions[3].height = 60
474
475
        parameters_ws['B3'].font = name_font
476
        parameters_ws['B3'].alignment = b_r_alignment
477
        parameters_ws['B3'] = 'Name:'
478
        parameters_ws['C3'].border = b_border
479
        parameters_ws['C3'].alignment = b_c_alignment
480
        parameters_ws['C3'].font = name_font
481
        parameters_ws['C3'] = name
482
483
        parameters_ws['D3'].font = name_font
484
        parameters_ws['D3'].alignment = b_r_alignment
485
        parameters_ws['D3'] = 'Period:'
486
        parameters_ws['E3'].border = b_border
487
        parameters_ws['E3'].alignment = b_c_alignment
488
        parameters_ws['E3'].font = name_font
489
        parameters_ws['E3'] = period_type
490
491
        parameters_ws['F3'].font = name_font
492
        parameters_ws['F3'].alignment = b_r_alignment
493
        parameters_ws['F3'] = 'Date:'
494
        parameters_ws['G3'].border = b_border
495
        parameters_ws['G3'].alignment = b_c_alignment
496
        parameters_ws['G3'].font = name_font
497
        parameters_ws['G3'] = reporting_start_datetime_local + "__" + reporting_end_datetime_local
498
        parameters_ws.merge_cells("G3:H3")
499
500
        parameters_ws_current_row_number = 6
501
502
        parameters_ws['B' + str(parameters_ws_current_row_number)].font = title_font
503
        parameters_ws['B' + str(parameters_ws_current_row_number)] = name + ' 相关参数'
504
505
        parameters_ws_current_row_number += 1
506
507
        parameters_table_start_row_number = parameters_ws_current_row_number
508
509
        parameters_ws.row_dimensions[parameters_ws_current_row_number].height = 80
510
511
        parameters_ws_current_row_number += 1
512
513
        table_current_col_number = 'B'
514
515
        for i in range(0, parameters_names_len):
516
517
            if len(parameters_data['timestamps'][i]) == 0:
518
                continue
519
520
            parameters_ws[table_current_col_number + str(parameters_ws_current_row_number - 1)].fill = table_fill
521
            parameters_ws[table_current_col_number + str(parameters_ws_current_row_number - 1)].border = f_border
522
523
            col = decimal_to_column(column_to_decimal(table_current_col_number) + 1)
524
525
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].fill = table_fill
526
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].border = f_border
527
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].font = name_font
528
            parameters_ws[col + str(parameters_ws_current_row_number - 1)].alignment = c_c_alignment
529
            parameters_ws[col + str(parameters_ws_current_row_number - 1)] = parameters_data['names'][i]
530
531
            table_current_row_number = parameters_ws_current_row_number
532
533
            for j, value in enumerate(list(parameters_data['timestamps'][i])):
534
                col = table_current_col_number
535
536
                parameters_ws[col + str(table_current_row_number)].border = f_border
537
                parameters_ws[col + str(table_current_row_number)].font = title_font
538
                parameters_ws[col + str(table_current_row_number)].alignment = c_c_alignment
539
                parameters_ws[col + str(table_current_row_number)] = value
540
541
                col = decimal_to_column(column_to_decimal(col) + 1)
542
543
                parameters_ws[col + str(table_current_row_number)].border = f_border
544
                parameters_ws[col + str(table_current_row_number)].font = title_font
545
                parameters_ws[col + str(table_current_row_number)].alignment = c_c_alignment
546
                parameters_ws[col + str(table_current_row_number)] = round(parameters_data['values'][i][j], 2)
547
548
                table_current_row_number += 1
549
550
            table_current_col_number = decimal_to_column(column_to_decimal(table_current_col_number) + 3)
551
552
        ########################################################
553
        # parameters chart and parameters table
554
        ########################################################
555
556
        ws['B' + str(current_sheet_parameters_row_number)].font = title_font
557
        ws['B' + str(current_sheet_parameters_row_number)] = name + ' 相关参数'
558
559
        current_sheet_parameters_row_number += 1
560
561
        chart_start_row_number = current_sheet_parameters_row_number
562
563
        col_index = 0
564
565
        for i in range(0, parameters_names_len):
566
567
            if len(parameters_data['timestamps'][i]) == 0:
568
                continue
569
570
            line = LineChart()
571
            data_col = 3 + col_index * 3
572
            labels_col = 2 + col_index * 3
573
            col_index += 1
574
            line.title = '相关参数 - ' + \
575
                         parameters_ws.cell(row=parameters_table_start_row_number, column=data_col).value
576
            labels = Reference(parameters_ws, min_col=labels_col, min_row=parameters_table_start_row_number + 1,
577
                               max_row=(len(parameters_data['timestamps'][i]) + parameters_table_start_row_number))
578
            line_data = Reference(parameters_ws, min_col=data_col, min_row=parameters_table_start_row_number,
579
                                  max_row=(len(parameters_data['timestamps'][i]) + parameters_table_start_row_number))
580
            line.add_data(line_data, titles_from_data=True)
581
            line.set_categories(labels)
582
            line_data = line.series[0]
583
            line_data.marker.symbol = "circle"
584
            line_data.smooth = True
585
            line.x_axis.crosses = 'min'
586
            line.height = 8.25
587
            line.width = 24
588
            line.dLbls = DataLabelList()
589
            line.dLbls.dLblPos = 't'
590
            line.dLbls.showVal = False
591
            line.dLbls.showPercent = False
592
            chart_col = 'B'
593
            chart_cell = chart_col + str(chart_start_row_number)
594
            chart_start_row_number += 6
595
            ws.add_chart(line, chart_cell)
596
597
        current_sheet_parameters_row_number = chart_start_row_number
598
599
        current_sheet_parameters_row_number += 1
600
601
    filename = str(uuid.uuid4()) + '.xlsx'
602
    wb.save(filename)
603
604
    return filename
605