diff --git a/nc_http/core/excel/__init__.py b/nc_http/core/excel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nc_http/core/excel/constants.py b/nc_http/core/excel/constants.py new file mode 100644 index 0000000..be1775f --- /dev/null +++ b/nc_http/core/excel/constants.py @@ -0,0 +1,9 @@ +EXCEL_MIME_TYPE = [ + 'application/excel', + 'application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' +] +EXCEL_SUFFIX = [ + '.xls', + '.xlsx', +] diff --git a/nc_http/core/excel/excel_format.py b/nc_http/core/excel/excel_format.py new file mode 100644 index 0000000..cbbfd34 --- /dev/null +++ b/nc_http/core/excel/excel_format.py @@ -0,0 +1,25 @@ +class ExcelFormat: + + title = { + 'font': '黑体', 'font_size': 18, 'bold': True, 'align': 'center', 'valign': 'vcenter', + } + remark = { + 'font': '黑体', 'font_size': 8, 'bold': False, 'align': 'right', 'valign': 'vcenter', + 'bottom': 1, 'top_color': 'white', 'top': 1, + } + header = { + 'font': '宋体', 'font_size': 8, 'bold': True, 'align': 'center', 'valign': 'vcenter', + 'border': 1, 'text_wrap': True, + } + body = { + 'font': '宋体', 'font_size': 6, 'bold': False, 'align': 'center', 'valign': 'vcenter', + 'border': 1, 'text_wrap': True, + } + big_header = { + 'font': '宋体', 'font_size': 10, 'bold': False, 'align': 'left', 'valign': 'vcenter', + 'border': True, 'text_wrap': False, + } + big_body = { + 'font': '宋体', 'font_size': 10, 'bold': False, 'align': 'left', 'valign': 'vcenter', + 'border': False, 'text_wrap': False, + } diff --git a/nc_http/core/excel/excel_reader.py b/nc_http/core/excel/excel_reader.py new file mode 100644 index 0000000..50cf133 --- /dev/null +++ b/nc_http/core/excel/excel_reader.py @@ -0,0 +1,55 @@ +import os + +import xlrd +from werkzeug.datastructures import FileStorage + +from nc_http.core.excel.constants import EXCEL_SUFFIX +from nc_http.core.excel.exceptions import InvalidSuffixError + + +class ExcelReader(object): + + @staticmethod + def read_excel(file_src=None, row_index=0, file_contents=None, sheet_name=None): + if file_src: + data = xlrd.open_workbook(file_src) + elif file_contents: + data = xlrd.open_workbook(file_contents=file_contents) + else: + return [] + + sheets = data.sheets() + table = sheets[0] + # 根据 sheet 名来选择 sheet(可选) + if sheet_name: + for sheet in sheets: + if sheet.name == sheet_name: + table = sheet + + nrows = table.nrows + rows = [] + for rownum in range(row_index, nrows): + row = table.row_values(rownum) + rows.append(row) + return rows + + @staticmethod + def read(file_handler): + if isinstance(file_handler, FileStorage): + filename = file_handler.filename.strip(r'"') + suffix = os.path.splitext(filename)[-1] + if suffix not in EXCEL_SUFFIX: + raise InvalidSuffixError + result = ExcelReader.read_excel(file_contents=file_handler.read()) + else: + filename = str(file_handler) if not hasattr(file_handler, 'name') else file_handler.name + suffix = os.path.splitext(filename)[-1] + if suffix not in EXCEL_SUFFIX: + raise InvalidSuffixError + result = ExcelReader.read_excel(filename) + return result + + +if __name__ == '__main__': + s = ExcelReader.read(open(r'D:\test.xlsx')) + print(s) diff --git a/nc_http/core/excel/excel_writer.py b/nc_http/core/excel/excel_writer.py new file mode 100644 index 0000000..106979d --- /dev/null +++ b/nc_http/core/excel/excel_writer.py @@ -0,0 +1,109 @@ +from io import BytesIO + +from xlsxwriter import Workbook + +from nc_http.core.excel.excel_format import ExcelFormat + + +class ExcelWriter(object): + title_format_setting = ExcelFormat.title + remark_format_setting = ExcelFormat.remark + header_format_setting = ExcelFormat.big_header + body_format_setting = ExcelFormat.big_body + + @classmethod + def pack_excel(cls, data=None, **options): + output = BytesIO() + workbook = Workbook(output, {'in_memory': True}) + cls.write(workbook, data, **options) + workbook.close() + output.seek(0) + return output + + @classmethod + def create_excel(cls, file, data=None, **options): + data = data or [] + workbook = Workbook(file) + cls.write(workbook, data, **options) + workbook.close() + return file + + @classmethod + def write(cls, workbook, data, headers=None, merges=None, row_stretches=None, col_stretches=None, + paper=None, style_formats=None): + data = data or [] + headers = headers or [] + if headers: + data = headers + data + if not data: + return + + worksheet = workbook.add_worksheet() + worksheet.center_horizontally() + worksheet.set_margins(left=0.2, right=0.2, top=0.4, bottom=0.2) + # worksheet.set_column('A:Z', 20) + # worksheet.set_default_row(16) + if paper: + worksheet.set_paper(paper) + + style_formats = style_formats or {} + + header_format = style_formats.get('header') or cls.header_format_setting + body_format = style_formats.get('body') or cls.body_format_setting + + col_style = {} + if col_stretches: + ''' + col_stretches = [ + [0, 0, 25, {'text_wrap': True}], + [1, 1, 10], + ] + ''' + for stretch in col_stretches: + if len(stretch) != 4: + continue + if stretch[0] == stretch[1]: + col_style[stretch[0]] = stretch[3] + elif stretch[0] < stretch[1]: + for col in range(stretch[0], stretch[1] + 1): + col_style[col] = stretch[3] + + for row_num in range(len(data)): + row = data[row_num] + if isinstance(row, list): + for col_num in range(len(row)): + column = row[col_num] + if headers and row_num < len(headers): + worksheet.write(row_num, col_num, column, workbook.add_format(header_format.copy())) + else: + # 单元格样式与列样式合并 + _body_format = body_format.copy() + if col_style: + _col_style = col_style.get(col_num, {}) + _body_format.update(_col_style) + worksheet.write(row_num, col_num, column, workbook.add_format(_body_format)) + # 单元格合并 + if merges: + for merge in merges: + if len(merge) > 5: + format_item = workbook.add_format(merge[5]) + else: + format_item = workbook.add_format(header_format) + worksheet.merge_range(*merge[:5], cell_format=format_item) + # 行高指定 + if row_stretches: + for stretch in row_stretches: + worksheet.set_row(*stretch) + # 列宽指定 + if col_stretches: + for stretch in col_stretches: + stretch = stretch.copy() + if len(stretch) >= 4: + stretch[3] = workbook.add_format(stretch[3]) + worksheet.set_column(*stretch) + + +if __name__ == '__main__': + ExcelWriter.create_excel(r'E:\test.xlsx', [[1, 2, 2, 2], [1, 2, 2, 2], [1, 2, 2, 2], [1, 2, 2, 2]]) + + ExcelWriter.pack_excel([[1, 2, 2, 2], [1, 2, 2, 2], [1, 2, 2, 2], [1, 2, 2, 2]]) diff --git a/nc_http/core/excel/exceptions.py b/nc_http/core/excel/exceptions.py new file mode 100644 index 0000000..04397dc --- /dev/null +++ b/nc_http/core/excel/exceptions.py @@ -0,0 +1,6 @@ +class InvalidFileError(Exception): + """无效的 excel 文件""" + + +class InvalidSuffixError(InvalidFileError): + """无效的后缀名""" diff --git a/nc_http/tests/excel.py b/nc_http/tests/excel.py new file mode 100644 index 0000000..0d9e2c0 --- /dev/null +++ b/nc_http/tests/excel.py @@ -0,0 +1,19 @@ +import unittest + +from nc_http.core.excel.excel_reader import ExcelReader +from nc_http.core.excel.excel_writer import ExcelWriter + + +class ExcelTestCase(unittest.TestCase): + filename = r'D:\test.xlsx' + + def test_writer(self): + ExcelWriter.create_excel(self.filename, [[1, 2, 2, 2], [1, 2, 2, 2], [1, 2, 2, 2], [1, 2, 2, 2]]) + ExcelWriter.pack_excel([[1, 2, 2, 2], [1, 2, 2, 2], [1, 2, 2, 2], [1, 2, 2, 2]]) + + def test_reader(self): + ExcelReader.read(open(self.filename)) + + +if __name__ == '__main__': + unittest.main()