feat: excel
This commit is contained in:
0
nc_http/core/excel/__init__.py
Normal file
0
nc_http/core/excel/__init__.py
Normal file
9
nc_http/core/excel/constants.py
Normal file
9
nc_http/core/excel/constants.py
Normal file
@@ -0,0 +1,9 @@
|
||||
EXCEL_MIME_TYPE = [
|
||||
'application/excel',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
]
|
||||
EXCEL_SUFFIX = [
|
||||
'.xls',
|
||||
'.xlsx',
|
||||
]
|
25
nc_http/core/excel/excel_format.py
Normal file
25
nc_http/core/excel/excel_format.py
Normal file
@@ -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,
|
||||
}
|
55
nc_http/core/excel/excel_reader.py
Normal file
55
nc_http/core/excel/excel_reader.py
Normal file
@@ -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)
|
109
nc_http/core/excel/excel_writer.py
Normal file
109
nc_http/core/excel/excel_writer.py
Normal file
@@ -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]])
|
6
nc_http/core/excel/exceptions.py
Normal file
6
nc_http/core/excel/exceptions.py
Normal file
@@ -0,0 +1,6 @@
|
||||
class InvalidFileError(Exception):
|
||||
"""无效的 excel 文件"""
|
||||
|
||||
|
||||
class InvalidSuffixError(InvalidFileError):
|
||||
"""无效的后缀名"""
|
19
nc_http/tests/excel.py
Normal file
19
nc_http/tests/excel.py
Normal file
@@ -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()
|
Reference in New Issue
Block a user