Total Complexity | 43 |
Total Lines | 331 |
Duplicated Lines | 11.78 % |
Changes | 0 |
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:
Complex classes like excelexporters.tenantbill 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 | import datetime |
||
5 | from openpyxl.chart import ( |
||
6 | PieChart, |
||
7 | BarChart, |
||
8 | Reference, |
||
9 | ) |
||
10 | from openpyxl.styles import PatternFill, Border, Side, Alignment, Font |
||
11 | from decimal import Decimal |
||
12 | from openpyxl.drawing.image import Image |
||
13 | from openpyxl import Workbook |
||
14 | from openpyxl.chart.label import DataLabelList |
||
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(report, |
|
|
|||
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 report is None: |
||
34 | return None |
||
35 | print(report) |
||
36 | |||
37 | #################################################################################################################### |
||
38 | # Step 2: Generate excel file from the report data |
||
39 | #################################################################################################################### |
||
40 | filename = generate_excel(report, |
||
41 | name, |
||
42 | reporting_start_datetime_local, |
||
43 | reporting_end_datetime_local, |
||
44 | period_type) |
||
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) |
||
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 generate_excel(report, |
||
67 | name, |
||
68 | reporting_start_datetime_local, |
||
69 | reporting_end_datetime_local, |
||
70 | period_type): |
||
71 | wb = Workbook() |
||
72 | ws = wb.active |
||
73 | |||
74 | # Row height |
||
75 | for i in range(1, 11 + 1): |
||
76 | ws.row_dimensions[i].height = 0.1 |
||
77 | ws.row_dimensions[12].height = 45.0 |
||
78 | ws.row_dimensions[13].height = 10.0 |
||
79 | ws.merge_cells('B13:I13') |
||
80 | for i in range(14, 23 + 1): |
||
81 | ws.row_dimensions[i].height = 0.1 |
||
82 | ws.row_dimensions[24].height = 30.0 |
||
83 | ws.row_dimensions[25].height = 10.0 |
||
84 | ws.merge_cells('B25:I25') |
||
85 | for i in range(26, 35 + 1): |
||
86 | ws.row_dimensions[i].height = 0.1 |
||
87 | for i in range(36, 41 + 1): |
||
88 | ws.row_dimensions[i].height = 30.0 |
||
89 | ws.row_dimensions[42].height = 10.0 |
||
90 | ws.merge_cells('B42:I42') |
||
91 | for i in range(43, 52 + 1): |
||
92 | ws.row_dimensions[i].height = 0.1 |
||
93 | |||
94 | # Col width |
||
95 | ws.column_dimensions['A'].width = 1.5 |
||
96 | for i in range(ord('B'), ord('J')): |
||
97 | ws.column_dimensions[chr(i)].width = 17 |
||
98 | ws.column_dimensions['J'].width = 1.5 |
||
99 | ws.column_dimensions['E'].width = 14 |
||
100 | ws.column_dimensions['F'].width = 14 |
||
101 | |||
102 | # merge cell |
||
103 | ws.merge_cells('C12:H12') |
||
104 | |||
105 | ws.merge_cells('C24:I24') |
||
106 | |||
107 | for i in range(36, 41 + 1): |
||
108 | ws.merge_cells('C{}:D{}'.format(i, i)) |
||
109 | ws.merge_cells('E{}:F{}'.format(i, i)) |
||
110 | ws.merge_cells('H{}:I{}'.format(i, i)) |
||
111 | |||
112 | # Font |
||
113 | notice_font = Font(name='宋体', size=25, bold=True) |
||
114 | name_font = Font(name='Constantia', size=14, bold=True) |
||
115 | title_font = Font(name='宋体', size=14, bold=True) |
||
116 | data_font = Font(name='Franklin Gothic Book', size=11) |
||
117 | |||
118 | table_fill = PatternFill(fill_type='solid', fgColor='1F497D') |
||
119 | f_border = Border(left=Side(border_style='medium', color='00000000'), |
||
120 | right=Side(border_style='medium', color='00000000'), |
||
121 | bottom=Side(border_style='medium', color='00000000'), |
||
122 | top=Side(border_style='medium', color='00000000') |
||
123 | ) |
||
124 | b_border = Border( |
||
125 | bottom=Side(border_style='medium', color='00000000'), |
||
126 | ) |
||
127 | |||
128 | b_c_alignment = Alignment(vertical='bottom', |
||
129 | horizontal='center', |
||
130 | text_rotation=0, |
||
131 | wrap_text=False, |
||
132 | shrink_to_fit=False, |
||
133 | indent=0) |
||
134 | c_c_alignment = Alignment(vertical='center', |
||
135 | horizontal='center', |
||
136 | text_rotation=0, |
||
137 | wrap_text=False, |
||
138 | shrink_to_fit=False, |
||
139 | indent=0) |
||
140 | b_r_alignment = Alignment(vertical='bottom', |
||
141 | horizontal='right', |
||
142 | text_rotation=0, |
||
143 | wrap_text=False, |
||
144 | shrink_to_fit=False, |
||
145 | indent=0) |
||
146 | c_r_alignment = Alignment(vertical='bottom', |
||
147 | horizontal='center', |
||
148 | text_rotation=0, |
||
149 | wrap_text=False, |
||
150 | shrink_to_fit=False, |
||
151 | indent=0) |
||
152 | b_l_alignment = Alignment(vertical='bottom', |
||
153 | horizontal='left', |
||
154 | text_rotation=0, |
||
155 | wrap_text=False, |
||
156 | shrink_to_fit=False, |
||
157 | indent=0) |
||
158 | |||
159 | ws['C12'].font = notice_font |
||
160 | ws['C12'].alignment = c_c_alignment |
||
161 | ws['C12'] = '付款通知书' |
||
162 | |||
163 | # img |
||
164 | img = Image("excelexporters/myemslogo.png") |
||
165 | img.width = 160 |
||
166 | img.height = 160 |
||
167 | ws.add_image(img, 'I12') |
||
168 | |||
169 | has_lease_number_data_flag = True |
||
170 | if "tenant" not in report.keys() or \ |
||
171 | report['tenant'] is None or \ |
||
172 | 'lease_number' not in report['tenant'].keys() or \ |
||
173 | report['tenant']['lease_number'] is None: |
||
174 | has_lease_number_data_flag = False |
||
175 | ws.row_dimensions[24].height = 0.1 |
||
176 | |||
177 | if has_lease_number_data_flag: |
||
178 | ws['B24'].font = name_font |
||
179 | ws['B24'].alignment = b_r_alignment |
||
180 | ws['B24'] = '租赁合同号码:' |
||
181 | ws['C24'].alignment = b_l_alignment |
||
182 | ws['C24'].font = name_font |
||
183 | ws['C24'] = report['tenant']['lease_number'] |
||
184 | |||
185 | has_tenant_data_flag = True |
||
186 | if "tenant" not in report.keys() or \ |
||
187 | report['tenant'] is None: |
||
188 | has_tenant_data_flag = False |
||
189 | for i in range(36, 41 + 1): |
||
190 | ws.row_dimensions[i].height = 0.1 |
||
191 | |||
192 | if has_tenant_data_flag: |
||
193 | report_tenant_data = report['tenant'] |
||
194 | for i in range(36, 41 + 1): |
||
195 | ws['C' + str(i)].alignment = b_l_alignment |
||
196 | ws['C' + str(i)].font = name_font |
||
197 | |||
198 | ws['C36'] = report_tenant_data['name'] |
||
199 | ws.merge_cells('E36:I36') |
||
200 | |||
201 | ws['C37'] = report_tenant_data['rooms'] |
||
202 | |||
203 | ws['C38'] = report_tenant_data['floors'] |
||
204 | |||
205 | ws['C39'] = report_tenant_data['buildings'] |
||
206 | |||
207 | ws['C40'] = report_tenant_data['email'] |
||
208 | |||
209 | ws['C41'] = report_tenant_data['phone'] |
||
210 | |||
211 | for i in range(36, 41 + 1): |
||
212 | ws['G' + str(i)].alignment = b_r_alignment |
||
213 | ws['G' + str(i)].font = name_font |
||
214 | ws['H' + str(i)].alignment = b_l_alignment |
||
215 | ws['H' + str(i)].font = name_font |
||
216 | |||
217 | ws['G37'] = '账单号码:' |
||
218 | ws['G38'] = '租赁合同号码:' |
||
219 | ws['G39'] = '账单日期:' |
||
220 | ws['G40'] = '付款到期日:' |
||
221 | ws['G41'] = '应付款金额:' |
||
222 | |||
223 | # Simulated data |
||
224 | ws['H37'] = '' |
||
225 | ws['H38'] = report_tenant_data['lease_number'] |
||
226 | ws['H39'] = datetime.datetime.strptime(reporting_start_datetime_local, '%Y-%m-%dT%H:%M:%S').strftime('%Y-%m-%d') |
||
227 | ws['H40'] = datetime.datetime.strptime(reporting_end_datetime_local, '%Y-%m-%dT%H:%M:%S').strftime('%Y-%m-%d') |
||
228 | ws['H41'] = report['reporting_period']['currency_unit'] + \ |
||
229 | str(round(report['reporting_period']['total_cost'] |
||
230 | if 'reporting_period' in report.keys() and 'total_cost' in report['reporting_period'].keys() |
||
231 | and report['reporting_period']['total_cost'] is not None |
||
232 | else 0, 2)) |
||
233 | |||
234 | has_reporting_period_data_flag = True |
||
235 | |||
236 | if 'reporting_period' not in report.keys() \ |
||
237 | or report['reporting_period'] is None: |
||
238 | has_reporting_period_data_flag = False |
||
239 | |||
240 | if has_reporting_period_data_flag: |
||
241 | ws.row_dimensions[53].height = 30.0 |
||
242 | for i in range(ord('B'), ord('J')): |
||
243 | ws[chr(i) + '53'].fill = table_fill |
||
244 | ws[chr(i) + '53'].font = title_font |
||
245 | ws[chr(i) + '53'].alignment = c_c_alignment |
||
246 | ws[chr(i) + '53'].border = f_border |
||
247 | |||
248 | ws['B53'] = '能耗分类' |
||
249 | ws['C53'] = '结算期开始日期' |
||
250 | ws['D53'] = '结算期结束日期' |
||
251 | ws['E53'] = '数量' |
||
252 | ws['F53'] = '单位' |
||
253 | ws['G53'] = '金额' |
||
254 | ws['H53'] = '税率' |
||
255 | ws['I53'] = '税额' |
||
256 | |||
257 | reporting_period_data = report['reporting_period'] |
||
258 | names = reporting_period_data['names'] |
||
259 | ca_len = len(names) if names is not None else 0 |
||
260 | |||
261 | for i in range(54, 54 + ca_len): |
||
262 | ws.row_dimensions[i].height = 30.0 |
||
263 | for j in range(ord('B'), ord('J')): |
||
264 | ws[chr(j) + str(i)].font = title_font |
||
265 | ws[chr(j) + str(i)].alignment = c_c_alignment |
||
266 | ws[chr(j) + str(i)].border = f_border |
||
267 | |||
268 | if chr(j) == 'B': |
||
269 | ws[chr(j) + str(i)] = reporting_period_data['names'][i - 54] |
||
270 | elif chr(j) == 'C': |
||
271 | ws[chr(j) + str(i)] = datetime.datetime.strptime(reporting_start_datetime_local, |
||
272 | '%Y-%m-%dT%H:%M:%S').strftime('%Y-%m-%d') |
||
273 | elif chr(j) == 'D': |
||
274 | ws[chr(j) + str(i)] = datetime.datetime.strptime(reporting_end_datetime_local, |
||
275 | '%Y-%m-%dT%H:%M:%S').strftime('%Y-%m-%d') |
||
276 | elif chr(j) == 'E': |
||
277 | ws[chr(j) + str(i)] = round(reporting_period_data['subtotals_input'][i - 54], 3) |
||
278 | elif chr(j) == 'F': |
||
279 | ws[chr(j) + str(i)] = reporting_period_data['units'][i - 54] |
||
280 | elif chr(j) == 'G': |
||
281 | ws[chr(j) + str(i)] = round(reporting_period_data['subtotals_cost'][i - 54], 2) |
||
282 | elif chr(j) == 'H': |
||
283 | # Simulated data |
||
284 | ws[chr(j) + str(i)] = 0 |
||
285 | elif chr(j) == 'I': |
||
286 | # Simulated data |
||
287 | ws[chr(j) + str(i)] = 0 |
||
288 | |||
289 | ws.row_dimensions[54 + ca_len].height = 10.0 |
||
290 | ws.merge_cells('B{}:H{}'.format((54 + ca_len), (54 + ca_len))) |
||
291 | |||
292 | current_row_number = 54 + ca_len + 1 |
||
293 | for i in range(current_row_number, current_row_number + 3): |
||
294 | ws.row_dimensions[i].height = 30.0 |
||
295 | ws['B' + str(i)].alignment = b_r_alignment |
||
296 | ws['B' + str(i)].font = name_font |
||
297 | ws['H' + str(i)].alignment = b_l_alignment |
||
298 | ws['H' + str(i)].font = name_font |
||
299 | ws.merge_cells('B{}:G{}'.format(i, i)) |
||
300 | ws.merge_cells('H{}:I{}'.format(i, i)) |
||
301 | |||
302 | ws['B' + str(current_row_number)] = '小计:' |
||
303 | ws['H' + str(current_row_number)] = report['reporting_period']['currency_unit'] + str( |
||
304 | round(report['reporting_period']['total_cost'] |
||
305 | if 'reporting_period' in report.keys() and 'total_cost' in report['reporting_period'].keys() |
||
306 | and report['reporting_period']['total_cost'] is not None |
||
307 | else 0, 2)) |
||
308 | |||
309 | current_row_number += 1 |
||
310 | |||
311 | # Simulated data |
||
312 | taxes = Decimal(0.00) |
||
313 | |||
314 | ws['B' + str(current_row_number)] = '增值税销项税金:' |
||
315 | ws['H' + str(current_row_number)] = report['reporting_period']['currency_unit'] + str(round(taxes, 2)) |
||
316 | |||
317 | current_row_number += 1 |
||
318 | |||
319 | ws['B' + str(current_row_number)] = '应付金额合计:' |
||
320 | ws['H' + str(current_row_number)] = report['reporting_period']['currency_unit'] + str( |
||
321 | round(report['reporting_period']['total_cost'] + taxes |
||
322 | if 'reporting_period' in report.keys() |
||
323 | and 'total_cost' in report['reporting_period'].keys() |
||
324 | and report['reporting_period']['total_cost'] is not None |
||
325 | else 0 + taxes, 2)) |
||
326 | |||
327 | filename = str(uuid.uuid4()) + '.xlsx' |
||
328 | wb.save(filename) |
||
329 | |||
330 | return filename |
||
331 |