Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/tablib/formats/_xlsx.py: 20%
100 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1""" Tablib - XLSX Support.
2"""
4import re
5from io import BytesIO
7from openpyxl.reader.excel import ExcelReader, load_workbook
8from openpyxl.styles import Alignment, Font
9from openpyxl.utils import get_column_letter
10from openpyxl.workbook import Workbook
11from openpyxl.writer.excel import ExcelWriter
13import tablib
15INVALID_TITLE_REGEX = re.compile(r'[\\*?:/\[\]]')
17def safe_xlsx_sheet_title(s, replace="-"):
18 return re.sub(INVALID_TITLE_REGEX, replace, s)[:31]
21class XLSXFormat:
22 title = 'xlsx'
23 extensions = ('xlsx',)
25 @classmethod
26 def detect(cls, stream):
27 """Returns True if given stream is a readable excel file."""
28 try:
29 # No need to fully load the file, it should be enough to be able to
30 # read the manifest.
31 reader = ExcelReader(stream, read_only=False)
32 reader.read_manifest()
33 return True
34 except Exception:
35 return False
37 @classmethod
38 def export_set(cls, dataset, freeze_panes=True, invalid_char_subst="-"):
39 """Returns XLSX representation of Dataset.
41 If dataset.title contains characters which are considered invalid for an XLSX file
42 sheet name (http://www.excelcodex.com/2012/06/worksheets-naming-conventions/), they will
43 be replaced with `invalid_char_subst`.
44 """
45 wb = Workbook()
46 ws = wb.worksheets[0]
48 ws.title = (
49 safe_xlsx_sheet_title(dataset.title, invalid_char_subst)
50 if dataset.title else 'Tablib Dataset'
51 )
53 cls.dset_sheet(dataset, ws, freeze_panes=freeze_panes)
55 stream = BytesIO()
56 wb.save(stream)
57 return stream.getvalue()
59 @classmethod
60 def export_book(cls, databook, freeze_panes=True, invalid_char_subst="-"):
61 """Returns XLSX representation of DataBook.
63 If dataset.title contains characters which are considered invalid for an XLSX file
64 sheet name (http://www.excelcodex.com/2012/06/worksheets-naming-conventions/), they will
65 be replaced with `invalid_char_subst`.
66 """
68 wb = Workbook()
69 for sheet in wb.worksheets:
70 wb.remove(sheet)
71 for i, dset in enumerate(databook._datasets):
72 ws = wb.create_sheet()
73 ws.title = (
74 safe_xlsx_sheet_title(dset.title, invalid_char_subst)
75 if dset.title else 'Sheet%s' % (i)
76 )
78 cls.dset_sheet(dset, ws, freeze_panes=freeze_panes)
80 stream = BytesIO()
81 wb.save(stream)
82 return stream.getvalue()
84 @classmethod
85 def import_set(cls, dset, in_stream, headers=True, read_only=True, skip_lines=0):
86 """Returns databook from XLS stream."""
88 dset.wipe()
90 xls_book = load_workbook(in_stream, read_only=read_only, data_only=True)
91 sheet = xls_book.active
93 dset.title = sheet.title
95 for i, row in enumerate(sheet.rows):
96 if i < skip_lines:
97 continue
98 row_vals = [c.value for c in row]
99 if i == skip_lines and headers:
100 dset.headers = row_vals
101 else:
102 dset.append(row_vals)
104 @classmethod
105 def import_book(cls, dbook, in_stream, headers=True, read_only=True):
106 """Returns databook from XLS stream."""
108 dbook.wipe()
110 xls_book = load_workbook(in_stream, read_only=read_only, data_only=True)
112 for sheet in xls_book.worksheets:
113 data = tablib.Dataset()
114 data.title = sheet.title
116 for i, row in enumerate(sheet.rows):
117 row_vals = [c.value for c in row]
118 if (i == 0) and (headers):
119 data.headers = row_vals
120 else:
121 if i > 0 and len(row_vals) < data.width:
122 row_vals += [''] * (data.width - len(row_vals))
123 data.append(row_vals)
125 dbook.add_sheet(data)
127 @classmethod
128 def dset_sheet(cls, dataset, ws, freeze_panes=True):
129 """Completes given worksheet from given Dataset."""
130 _package = dataset._package(dicts=False)
132 for i, sep in enumerate(dataset._separators):
133 _offset = i
134 _package.insert((sep[0] + _offset), (sep[1],))
136 bold = Font(bold=True)
137 wrap_text = Alignment(wrap_text=True)
139 for i, row in enumerate(_package):
140 row_number = i + 1
141 for j, col in enumerate(row):
142 col_idx = get_column_letter(j + 1)
143 cell = ws[f'{col_idx}{row_number}']
145 # bold headers
146 if (row_number == 1) and dataset.headers:
147 cell.font = bold
148 if freeze_panes:
149 # Export Freeze only after first Line
150 ws.freeze_panes = 'A2'
152 # bold separators
153 elif len(row) < dataset.width:
154 cell.font = bold
156 # wrap the rest
157 else:
158 try:
159 str_col_value = str(col)
160 except TypeError:
161 str_col_value = ''
162 if '\n' in str_col_value:
163 cell.alignment = wrap_text
165 try:
166 cell.value = col
167 except (ValueError, TypeError):
168 cell.value = str(col)