mergeron 2024.739097.4__py3-none-any.whl → 2024.739099.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mergeron might be problematic. Click here for more details.
- mergeron/__init__.py +1 -1
- {mergeron-2024.739097.4.dist-info → mergeron-2024.739099.0.dist-info}/METADATA +1 -24
- {mergeron-2024.739097.4.dist-info → mergeron-2024.739099.0.dist-info}/RECORD +4 -8
- mergeron/ext/__init__.py +0 -3
- mergeron/ext/proportions_tests.py +0 -518
- mergeron/ext/tol_colors.py +0 -848
- mergeron/ext/xlsxw_helper.py +0 -631
- {mergeron-2024.739097.4.dist-info → mergeron-2024.739099.0.dist-info}/WHEEL +0 -0
mergeron/ext/xlsxw_helper.py
DELETED
|
@@ -1,631 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Methods for writing data from Python to fresh Excel workbooks using
|
|
3
|
-
the third-party package, `xlsxwriter`.
|
|
4
|
-
|
|
5
|
-
Includes a flexible system of defining cell formats.
|
|
6
|
-
|
|
7
|
-
NOTES
|
|
8
|
-
-----
|
|
9
|
-
|
|
10
|
-
This module is designed for producing formatted summary output. For
|
|
11
|
-
writing bulk data to Excel, facilities provided in third-party packages
|
|
12
|
-
such as `polars <https://pola.rs/>`_ likely provide better performance.
|
|
13
|
-
|
|
14
|
-
License
|
|
15
|
-
========
|
|
16
|
-
|
|
17
|
-
Copyright 2017-2023 S. Murthy Kambhampaty
|
|
18
|
-
Licese: MIT
|
|
19
|
-
https://mit-license.org/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
from __future__ import annotations
|
|
25
|
-
|
|
26
|
-
from collections.abc import Sequence
|
|
27
|
-
from typing import Any, ClassVar, Literal, TypeAlias, TypedDict
|
|
28
|
-
|
|
29
|
-
import numpy as np
|
|
30
|
-
import numpy.typing as npt
|
|
31
|
-
import xlsxwriter # type: ignore
|
|
32
|
-
from aenum import Enum, extend_enum, unique # type: ignore
|
|
33
|
-
|
|
34
|
-
from .. import VERSION # noqa: TID252
|
|
35
|
-
|
|
36
|
-
__version__ = VERSION
|
|
37
|
-
|
|
38
|
-
Workbook = xlsxwriter.Workbook
|
|
39
|
-
|
|
40
|
-
XLBorderType: TypeAlias = Literal[
|
|
41
|
-
"none",
|
|
42
|
-
"thin",
|
|
43
|
-
"medium",
|
|
44
|
-
"dashed",
|
|
45
|
-
"dotted",
|
|
46
|
-
"thick",
|
|
47
|
-
"double",
|
|
48
|
-
"hair",
|
|
49
|
-
"medium_dashed",
|
|
50
|
-
"dash_dot",
|
|
51
|
-
"medium_dash_dot",
|
|
52
|
-
"dash_dot_dot",
|
|
53
|
-
"medium_dash_dot_dot",
|
|
54
|
-
"slant_dash_dot",
|
|
55
|
-
True,
|
|
56
|
-
False,
|
|
57
|
-
0,
|
|
58
|
-
1,
|
|
59
|
-
2,
|
|
60
|
-
3,
|
|
61
|
-
4,
|
|
62
|
-
5,
|
|
63
|
-
6,
|
|
64
|
-
7,
|
|
65
|
-
8,
|
|
66
|
-
9,
|
|
67
|
-
10,
|
|
68
|
-
11,
|
|
69
|
-
12,
|
|
70
|
-
13,
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class CFmtVal(TypedDict, total=False):
|
|
75
|
-
"""Keys for xlsxwriter Format objects.
|
|
76
|
-
|
|
77
|
-
This is a partial list based on formats of interest.
|
|
78
|
-
"""
|
|
79
|
-
|
|
80
|
-
font_name: str
|
|
81
|
-
font_size: int
|
|
82
|
-
font_color: str
|
|
83
|
-
align: Literal[
|
|
84
|
-
"left", "center", "right", "center_across", "top", "bottom", "vcenter"
|
|
85
|
-
]
|
|
86
|
-
text_wrap: bool
|
|
87
|
-
rotation: int # integer, 0-360
|
|
88
|
-
indent: int
|
|
89
|
-
shrink: bool
|
|
90
|
-
bold: bool
|
|
91
|
-
italic: bool
|
|
92
|
-
underline: Literal[
|
|
93
|
-
True,
|
|
94
|
-
False,
|
|
95
|
-
1,
|
|
96
|
-
2,
|
|
97
|
-
33,
|
|
98
|
-
34,
|
|
99
|
-
"single",
|
|
100
|
-
"double",
|
|
101
|
-
"accountingSingle",
|
|
102
|
-
"accountingDouble",
|
|
103
|
-
]
|
|
104
|
-
font_strikeout: bool
|
|
105
|
-
font_script: Literal[1, 2]
|
|
106
|
-
|
|
107
|
-
num_format: str
|
|
108
|
-
|
|
109
|
-
pattern: int
|
|
110
|
-
fg_color: str # html color string, no #
|
|
111
|
-
bg_color: str # html color string, no #
|
|
112
|
-
|
|
113
|
-
hidden: bool
|
|
114
|
-
locked: bool
|
|
115
|
-
|
|
116
|
-
border: XLBorderType
|
|
117
|
-
bottom: XLBorderType
|
|
118
|
-
left: XLBorderType
|
|
119
|
-
right: XLBorderType
|
|
120
|
-
top: XLBorderType
|
|
121
|
-
border_color: str # html color string, no #
|
|
122
|
-
bottom_color: str # html color string, no #
|
|
123
|
-
left_color: str # html color string, no #
|
|
124
|
-
right_color: str # html color string, no #
|
|
125
|
-
top_color: str # html color string, no #
|
|
126
|
-
|
|
127
|
-
diag_border: XLBorderType
|
|
128
|
-
diag_border_color: str # html color string, no #
|
|
129
|
-
diag_type: Literal[
|
|
130
|
-
1, 2, 3, "up", "down", "left", "right", "cross", "diagonalUp", "diagonalDown"
|
|
131
|
-
]
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
@unique
|
|
135
|
-
class CFmt(dict, Enum): # type: ignore
|
|
136
|
-
"""
|
|
137
|
-
Cell format enums for xlsxwriter Format objects.
|
|
138
|
-
|
|
139
|
-
The enums defined here, or sequences of (any of) them
|
|
140
|
-
and any added with :meth:`CFmt.add_new`, are
|
|
141
|
-
rendered as :code:`xlsxWriter.Workbook.Format` objects
|
|
142
|
-
with :meth:`CFmt.xl_fmt`.
|
|
143
|
-
|
|
144
|
-
NOTES
|
|
145
|
-
-----
|
|
146
|
-
|
|
147
|
-
For more information about xlsxwriter cell formats,
|
|
148
|
-
see, https://xlsxwriter.readthedocs.io/format.html
|
|
149
|
-
|
|
150
|
-
"""
|
|
151
|
-
|
|
152
|
-
XL_DEFAULT: ClassVar = {"font_name": "Calibri", "font_size": 11}
|
|
153
|
-
XL_DEFAULT_2003: ClassVar = {"font_name": "Arial", "font_size": 10}
|
|
154
|
-
|
|
155
|
-
A_CTR: ClassVar = {"align": "center"}
|
|
156
|
-
A_CTR_ACROSS: ClassVar = {"align": "center_across"}
|
|
157
|
-
A_LEFT: ClassVar = {"align": "left"}
|
|
158
|
-
A_RIGHT: ClassVar = {"align": "right"}
|
|
159
|
-
V_TOP: ClassVar = {"align": "top"}
|
|
160
|
-
V_BOTTOM: ClassVar = {"align": "bottom"}
|
|
161
|
-
V_CTR: ClassVar = {"align": "vcenter"}
|
|
162
|
-
|
|
163
|
-
TEXT_WRAP: ClassVar = {"text_wrap": True}
|
|
164
|
-
TEXT_ROTATE: ClassVar = {"rotation": 90}
|
|
165
|
-
IND_1: ClassVar = {"indent": 1}
|
|
166
|
-
|
|
167
|
-
BOLD: ClassVar = {"bold": True}
|
|
168
|
-
BOLD_ITALIC: ClassVar = {"bold": True, "italic": True}
|
|
169
|
-
ITALIC: ClassVar = {"italic": True}
|
|
170
|
-
ULINE: ClassVar = {"underline": "single"}
|
|
171
|
-
SOUT: ClassVar = {"font_strikeout": True}
|
|
172
|
-
# Useful with write_rich_text()
|
|
173
|
-
SUPERSCRIPT: ClassVar = {"font_script": 1}
|
|
174
|
-
SUBSCRIPT: ClassVar = {"font_script": 2}
|
|
175
|
-
|
|
176
|
-
AREA_NUM: ClassVar = {"num_format": "0.00000000"}
|
|
177
|
-
DOLLAR_NUM: ClassVar = {"num_format": "[$$-409]#,##0.00"}
|
|
178
|
-
DT_NUM: ClassVar = {"num_format": "mm/dd/yyyy"}
|
|
179
|
-
PCT_NUM: ClassVar = {"num_format": "##0%"}
|
|
180
|
-
PCT2_NUM: ClassVar = {"num_format": "##0.00%"}
|
|
181
|
-
PCT4_NUM: ClassVar = {"num_format": "##0.0000%"}
|
|
182
|
-
PCT6_NUM: ClassVar = {"num_format": "##0.000000%"}
|
|
183
|
-
PCT8_NUM: ClassVar = {"num_format": "##0.00000000%"}
|
|
184
|
-
QTY_NUM: ClassVar = {"num_format": "#,##0.0"}
|
|
185
|
-
|
|
186
|
-
BAR_FILL: ClassVar = {"pattern": 1, "bg_color": "dfeadf"}
|
|
187
|
-
HDR_FILL: ClassVar = {"pattern": 1, "bg_color": "bfbfbf"}
|
|
188
|
-
|
|
189
|
-
FULL_BORDER: ClassVar = {"border": 1, "border_color": "000000"}
|
|
190
|
-
BOTTOM_BORDER: ClassVar = {"bottom": 1, "bottom_color": "000000"}
|
|
191
|
-
LEFT_BORDER: ClassVar = {"left": 1, "left_color": "000000"}
|
|
192
|
-
RIGHT_BORDER: ClassVar = {"right": 1, "right_color": "000000"}
|
|
193
|
-
TOP_BORDER: ClassVar = {"top": 1, "top_color": "000000"}
|
|
194
|
-
HDR_BORDER: ClassVar = TOP_BORDER | BOTTOM_BORDER
|
|
195
|
-
|
|
196
|
-
@classmethod
|
|
197
|
-
def add_new(self, _fmt_name: str, _xlsx_fmt_dict: CFmtVal, /) -> CFmt:
|
|
198
|
-
"""
|
|
199
|
-
Add new :class:`CFmt` object to instance.
|
|
200
|
-
|
|
201
|
-
Parameters
|
|
202
|
-
----------
|
|
203
|
-
_fmt_name
|
|
204
|
-
Name of new member to be added to :class:`CFmt`
|
|
205
|
-
_xlsx_fmt_dict
|
|
206
|
-
Any valid argument to :code:`xlsxwriter.Workbook.add_format()`, or union of
|
|
207
|
-
same with one or more :class:`CFmt` objects, e.g.,
|
|
208
|
-
:code:`CFmt.HDR_BORDER | CFmt.HDR_FILL` or
|
|
209
|
-
:code:`CFmt.HDR_BORDER | {"pattern": 1, "bg_color": "f2f2f2"}`
|
|
210
|
-
|
|
211
|
-
Returns
|
|
212
|
-
-------
|
|
213
|
-
None
|
|
214
|
-
|
|
215
|
-
"""
|
|
216
|
-
|
|
217
|
-
return extend_enum(self, _fmt_name, _xlsx_fmt_dict) # type: ignore
|
|
218
|
-
|
|
219
|
-
@classmethod
|
|
220
|
-
def ensure_cell_format_spec_tuple(
|
|
221
|
-
self, _cell_format: Sequence[CFmt | Sequence[CFmt]], /
|
|
222
|
-
) -> bool:
|
|
223
|
-
"""
|
|
224
|
-
Test that a given format specification is a tuple of :class:`CFmt` enums
|
|
225
|
-
|
|
226
|
-
Parameters
|
|
227
|
-
----------
|
|
228
|
-
_cell_format
|
|
229
|
-
Format specification
|
|
230
|
-
|
|
231
|
-
Raises
|
|
232
|
-
------
|
|
233
|
-
ValueError
|
|
234
|
-
If format specification is not a sequence of (sequences of)
|
|
235
|
-
:class:`CFmt` enums
|
|
236
|
-
|
|
237
|
-
Returns
|
|
238
|
-
-------
|
|
239
|
-
True if format specification passes, else False
|
|
240
|
-
|
|
241
|
-
"""
|
|
242
|
-
|
|
243
|
-
for _cf in _cell_format:
|
|
244
|
-
if isinstance(_cf, tuple):
|
|
245
|
-
self.ensure_cell_format_spec_tuple(_cf)
|
|
246
|
-
|
|
247
|
-
if not (isinstance(_cf, CFmt),):
|
|
248
|
-
raise ValueError(
|
|
249
|
-
"Improperly specified format tuple for writing array."
|
|
250
|
-
" Must be tuple of :class:`CFmt` enums."
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
return True
|
|
254
|
-
|
|
255
|
-
@classmethod
|
|
256
|
-
def xl_fmt(
|
|
257
|
-
self,
|
|
258
|
-
_xl_book: xlsxwriter.Workbook,
|
|
259
|
-
_cell_fmt: Sequence[CFmt | Sequence[CFmt]] | CFmt | None,
|
|
260
|
-
/,
|
|
261
|
-
) -> xlsxwriter.format.Format:
|
|
262
|
-
"""
|
|
263
|
-
Return :code:`xlsxwriter` :code:`Format` object given a :class:`CFmt` enum, or tuple thereof.
|
|
264
|
-
|
|
265
|
-
Parameters
|
|
266
|
-
----------
|
|
267
|
-
_xl_book
|
|
268
|
-
:code:`xlsxwriter.Workbook` object
|
|
269
|
-
|
|
270
|
-
_cell_fmt
|
|
271
|
-
:class:`CFmt` enum object, or tuple thereof
|
|
272
|
-
|
|
273
|
-
Raises
|
|
274
|
-
------
|
|
275
|
-
ValueError
|
|
276
|
-
If format specification is not one of None, a :class:`CFmt` enum, or
|
|
277
|
-
a :code:`xlsxwriter.format.Format` object
|
|
278
|
-
|
|
279
|
-
Returns
|
|
280
|
-
-------
|
|
281
|
-
:code:`xlsxwriter` :code:`Format` object
|
|
282
|
-
|
|
283
|
-
"""
|
|
284
|
-
|
|
285
|
-
if isinstance(_cell_fmt, xlsxwriter.format.Format):
|
|
286
|
-
return _cell_fmt
|
|
287
|
-
elif _cell_fmt is None:
|
|
288
|
-
return _xl_book.add_format(CFmt.XL_DEFAULT.value)
|
|
289
|
-
|
|
290
|
-
_cell_fmt_dict: CFmtVal = {}
|
|
291
|
-
if isinstance(_cell_fmt, Sequence):
|
|
292
|
-
self.ensure_cell_format_spec_tuple(_cell_fmt)
|
|
293
|
-
for _cf in _cell_fmt:
|
|
294
|
-
_cell_fmt_dict = (
|
|
295
|
-
(_cell_fmt_dict | _cfi.value for _cfi in _cf)
|
|
296
|
-
if isinstance(_cf, Sequence)
|
|
297
|
-
else _cell_fmt_dict | _cf.value
|
|
298
|
-
)
|
|
299
|
-
elif isinstance(_cell_fmt, CFmt):
|
|
300
|
-
_cell_fmt_dict = _cell_fmt.value
|
|
301
|
-
else:
|
|
302
|
-
raise ValueError("Improperly specified format specification.")
|
|
303
|
-
|
|
304
|
-
return _xl_book.add_format(_cell_fmt_dict)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
def write_header(
|
|
308
|
-
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
309
|
-
/,
|
|
310
|
-
*,
|
|
311
|
-
center_header: str | None = None,
|
|
312
|
-
left_header: str | None = None,
|
|
313
|
-
right_header: str | None = None,
|
|
314
|
-
) -> None:
|
|
315
|
-
"""Write header text to given worksheet.
|
|
316
|
-
|
|
317
|
-
Parameters
|
|
318
|
-
----------
|
|
319
|
-
_xl_sheet
|
|
320
|
-
Worksheet object
|
|
321
|
-
center_header
|
|
322
|
-
Text for center header
|
|
323
|
-
left_header
|
|
324
|
-
Text for left header
|
|
325
|
-
right_header
|
|
326
|
-
Text for right header
|
|
327
|
-
|
|
328
|
-
Raises
|
|
329
|
-
------
|
|
330
|
-
ValueError
|
|
331
|
-
Must specify at least one header
|
|
332
|
-
|
|
333
|
-
Returns
|
|
334
|
-
-------
|
|
335
|
-
None
|
|
336
|
-
"""
|
|
337
|
-
if any((center_header, left_header, right_header)):
|
|
338
|
-
_xl_sheet.set_header(
|
|
339
|
-
"".join([
|
|
340
|
-
f"&L{left_header}" if left_header else "",
|
|
341
|
-
f"&C{center_header}" if center_header else "",
|
|
342
|
-
f"&R{right_header}" if right_header else "",
|
|
343
|
-
])
|
|
344
|
-
)
|
|
345
|
-
|
|
346
|
-
else:
|
|
347
|
-
raise ValueError("must specify at least one header")
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
def write_footer(
|
|
351
|
-
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
352
|
-
/,
|
|
353
|
-
*,
|
|
354
|
-
center_footer: str | None = None,
|
|
355
|
-
left_footer: str | None = None,
|
|
356
|
-
right_footer: str | None = None,
|
|
357
|
-
) -> None:
|
|
358
|
-
"""Write footer text to given worksheet.
|
|
359
|
-
|
|
360
|
-
Parameters
|
|
361
|
-
----------
|
|
362
|
-
_xl_sheet
|
|
363
|
-
Worksheet object
|
|
364
|
-
center_footer
|
|
365
|
-
Text for center footer
|
|
366
|
-
left_footer
|
|
367
|
-
Text for left footer
|
|
368
|
-
right_footer
|
|
369
|
-
Text for right footer
|
|
370
|
-
|
|
371
|
-
Raises
|
|
372
|
-
------
|
|
373
|
-
ValueError
|
|
374
|
-
Must specify at least one footer
|
|
375
|
-
|
|
376
|
-
Returns
|
|
377
|
-
-------
|
|
378
|
-
None
|
|
379
|
-
"""
|
|
380
|
-
|
|
381
|
-
if any((center_footer, left_footer, right_footer)):
|
|
382
|
-
_xl_sheet.set_footer(
|
|
383
|
-
"".join([
|
|
384
|
-
f"&L{left_footer}" if left_footer else "",
|
|
385
|
-
f"&C{center_footer}" if center_footer else "",
|
|
386
|
-
f"&R{right_footer}" if right_footer else "",
|
|
387
|
-
])
|
|
388
|
-
)
|
|
389
|
-
|
|
390
|
-
else:
|
|
391
|
-
raise ValueError("must specify at least one footer")
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
def array_to_sheet(
|
|
395
|
-
_xl_book: xlsxwriter.workbook.Workbook,
|
|
396
|
-
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
397
|
-
_data_table: Sequence[Any] | npt.NDArray[Any],
|
|
398
|
-
_row_id: int,
|
|
399
|
-
_col_id: int = 0,
|
|
400
|
-
/,
|
|
401
|
-
*,
|
|
402
|
-
cell_format: Sequence[CFmt | Sequence[CFmt]] | CFmt | None = None,
|
|
403
|
-
empty_as_blank: bool = True,
|
|
404
|
-
green_bar_flag: bool = True,
|
|
405
|
-
ragged_flag: bool = True,
|
|
406
|
-
) -> tuple[int, int]:
|
|
407
|
-
"""
|
|
408
|
-
Write a 2-D array to a worksheet.
|
|
409
|
-
|
|
410
|
-
The given array is required be a two-dimensional array, whether
|
|
411
|
-
a nested list, nested tuple, or a 2-D numpy ndarray. The array is assumed
|
|
412
|
-
to be ragged by default, i.e. not all rows are the same length, and some
|
|
413
|
-
cells may contain lists, etc. For rectangular arrays, set `ragged_flag` to
|
|
414
|
-
false if you wish to provide a format tuple with distinct formats for each
|
|
415
|
-
column in the rectangular array.
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
Parameters
|
|
419
|
-
----------
|
|
420
|
-
_xl_book
|
|
421
|
-
Workbook object
|
|
422
|
-
|
|
423
|
-
_xl_sheet
|
|
424
|
-
Worksheet object to which to write the give array
|
|
425
|
-
|
|
426
|
-
_data_table
|
|
427
|
-
Array to be written
|
|
428
|
-
|
|
429
|
-
_row_id
|
|
430
|
-
Row number of top left corner of range to write to
|
|
431
|
-
|
|
432
|
-
_col_id
|
|
433
|
-
Column number of top left corner of range to write to
|
|
434
|
-
|
|
435
|
-
cell_format
|
|
436
|
-
Format specification for range to be written
|
|
437
|
-
|
|
438
|
-
green_bar_flag
|
|
439
|
-
Whether to highlight alternating rows as in green bar paper
|
|
440
|
-
|
|
441
|
-
ragged_flag
|
|
442
|
-
Whether to write ragged array, i.e. rows not all the same length
|
|
443
|
-
or not all cells are scalar-valued
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
Raises
|
|
447
|
-
------
|
|
448
|
-
ValueError
|
|
449
|
-
If array is not two-dimensional
|
|
450
|
-
|
|
451
|
-
ValueError
|
|
452
|
-
If ragged_flag is False and array is not rectangular
|
|
453
|
-
|
|
454
|
-
ValueError
|
|
455
|
-
If array is not rectangular and cell_format is a Sequence
|
|
456
|
-
|
|
457
|
-
ValueError
|
|
458
|
-
If array is rectangular but length of format tuple does not
|
|
459
|
-
match row-length
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
Returns
|
|
463
|
-
-------
|
|
464
|
-
Tuple giving address of cell at right below and after range written
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
Notes
|
|
468
|
-
-----
|
|
469
|
-
|
|
470
|
-
The keyword argument cell_format may be passed a tuple of :class:`CFmt` enums,
|
|
471
|
-
if, and only if, ragged_flag is False. If cell_format is a tuple, it must
|
|
472
|
-
have length equal to the number of cells in each row of the passed array.
|
|
473
|
-
Further, members of cell_format must each be a :class:`CFmt` enum or a
|
|
474
|
-
tuple of :class:`CFmt` enums; in other words, :meth:`CFmt.ensure_cell_format_spec_tuple`
|
|
475
|
-
must return True for any tuple `_c` passed as `cell_format`.
|
|
476
|
-
|
|
477
|
-
"""
|
|
478
|
-
|
|
479
|
-
if not ragged_flag:
|
|
480
|
-
try:
|
|
481
|
-
if np.ndim(_data_table) != 2:
|
|
482
|
-
raise ValueError("Given array must be two-dimensional.")
|
|
483
|
-
except ValueError as _err:
|
|
484
|
-
raise ValueError(
|
|
485
|
-
"Given array must be rectangular and homogenous, with scalar members."
|
|
486
|
-
" Alternatively, try with ragged_flag=True."
|
|
487
|
-
)
|
|
488
|
-
raise _err
|
|
489
|
-
elif not (
|
|
490
|
-
isinstance(_data_table, Sequence | np.ndarray)
|
|
491
|
-
and hasattr(_data_table[0], "__len__")
|
|
492
|
-
):
|
|
493
|
-
raise ValueError("Given array must be two-dimensional array.")
|
|
494
|
-
|
|
495
|
-
# Get the array dimensions and row and column numbers for Excel
|
|
496
|
-
_num_rows = len(_data_table)
|
|
497
|
-
_bottom_row_id = _row_id + _num_rows
|
|
498
|
-
_num_cols = len(_data_table[0])
|
|
499
|
-
_right_column_id = _col_id + _num_cols
|
|
500
|
-
|
|
501
|
-
if isinstance(cell_format, Sequence):
|
|
502
|
-
if _num_rows > 1 and ragged_flag:
|
|
503
|
-
raise ValueError(
|
|
504
|
-
"It is not clear whether the sequence of formats applies to all cells,"
|
|
505
|
-
" or to each cell respectively. Please provide a single-valued cell_format."
|
|
506
|
-
" Alternatively, you can iterate over the array using scalar_to_sheet()."
|
|
507
|
-
)
|
|
508
|
-
elif not len(cell_format) == len(_data_table[0]):
|
|
509
|
-
raise ValueError("Format tuple does not match data in length.")
|
|
510
|
-
CFmt.ensure_cell_format_spec_tuple(cell_format)
|
|
511
|
-
_cell_format: Sequence[CFmt | Sequence[CFmt]] = cell_format
|
|
512
|
-
elif isinstance(cell_format, CFmt):
|
|
513
|
-
_cell_format = (cell_format,) * len(_data_table[0])
|
|
514
|
-
else:
|
|
515
|
-
_cell_format = (CFmt.XL_DEFAULT,) * len(_data_table[0])
|
|
516
|
-
|
|
517
|
-
# construct vector of xlslwrter.format.Format objects
|
|
518
|
-
_wbk_formats = tuple(CFmt.xl_fmt(_xl_book, _cf) for _cf in _cell_format)
|
|
519
|
-
if _num_rows > 1:
|
|
520
|
-
_wbk_formats_greened = (
|
|
521
|
-
tuple(
|
|
522
|
-
CFmt.xl_fmt(
|
|
523
|
-
_xl_book,
|
|
524
|
-
(*_cf, CFmt.BAR_FILL)
|
|
525
|
-
if isinstance(_cf, Sequence)
|
|
526
|
-
else (_cf, CFmt.BAR_FILL),
|
|
527
|
-
)
|
|
528
|
-
for _cf in _cell_format
|
|
529
|
-
)
|
|
530
|
-
if green_bar_flag
|
|
531
|
-
else _wbk_formats
|
|
532
|
-
)
|
|
533
|
-
|
|
534
|
-
for _ri, _rv in enumerate(_data_table):
|
|
535
|
-
_wbk_fmt_tuple = _wbk_formats_greened if _ri % 2 else _wbk_formats
|
|
536
|
-
for _ci, _cv in enumerate(_rv):
|
|
537
|
-
_cf = _wbk_fmt_tuple[_ci]
|
|
538
|
-
scalar_to_sheet(
|
|
539
|
-
_xl_sheet,
|
|
540
|
-
_row_id + _ri,
|
|
541
|
-
_col_id + _ci,
|
|
542
|
-
_cv,
|
|
543
|
-
_cf,
|
|
544
|
-
empty_as_blank=empty_as_blank,
|
|
545
|
-
)
|
|
546
|
-
|
|
547
|
-
_right_column_id = _col_id + _ci + 1 if _ci > _num_cols else _right_column_id
|
|
548
|
-
|
|
549
|
-
return _bottom_row_id, _right_column_id
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
def scalar_to_sheet(
|
|
553
|
-
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
554
|
-
_cell_addr_0: str | int = "A1",
|
|
555
|
-
/,
|
|
556
|
-
*_s2s_args: Any,
|
|
557
|
-
empty_as_blank: bool = True,
|
|
558
|
-
) -> None:
|
|
559
|
-
"""
|
|
560
|
-
Write to a single cell in a worksheet.
|
|
561
|
-
|
|
562
|
-
Parameters
|
|
563
|
-
----------
|
|
564
|
-
_xl_sheet
|
|
565
|
-
Worksheet object to which to write the give array
|
|
566
|
-
|
|
567
|
-
_cell_addr_0
|
|
568
|
-
First element of a cell address, which may be the entire address
|
|
569
|
-
in 'A1' format or the row-part in 'Row-column' format
|
|
570
|
-
|
|
571
|
-
_s2s_args
|
|
572
|
-
Other arguments, which may be just the cell value to be written and the
|
|
573
|
-
cell format, or the column-part of the 'Row-column' address along with
|
|
574
|
-
cell value and cell format.
|
|
575
|
-
|
|
576
|
-
Raises
|
|
577
|
-
------
|
|
578
|
-
ValueError
|
|
579
|
-
If too many or too few arguments
|
|
580
|
-
ValueError
|
|
581
|
-
If incorrect/incomplete specification for Excel cell data
|
|
582
|
-
|
|
583
|
-
Returns
|
|
584
|
-
-------
|
|
585
|
-
None
|
|
586
|
-
|
|
587
|
-
Notes
|
|
588
|
-
-----
|
|
589
|
-
For more information on xlsxwriter cell-address notation, see:
|
|
590
|
-
https://xlsxwriter.readthedocs.io/working_with_cell_notation.html
|
|
591
|
-
|
|
592
|
-
"""
|
|
593
|
-
|
|
594
|
-
_cell_addr: tuple[int | str, ...] = ()
|
|
595
|
-
_cell_val: Any = None
|
|
596
|
-
_cell_fmt: xlsxwriter.format.Format = CFmt.XL_DEFAULT
|
|
597
|
-
|
|
598
|
-
if isinstance(_cell_addr_0, str):
|
|
599
|
-
if len(_s2s_args) not in (1, 2):
|
|
600
|
-
raise ValueError("Incorrect number of arguments.")
|
|
601
|
-
_cell_addr = (_cell_addr_0,)
|
|
602
|
-
_cell_val = _s2s_args[0]
|
|
603
|
-
_cell_fmt = _s2s_args[1] if len(_s2s_args) == 2 else None
|
|
604
|
-
elif isinstance(_cell_addr_0, int):
|
|
605
|
-
if len(_s2s_args) not in (2, 3) or not isinstance(_s2s_args[0], int):
|
|
606
|
-
raise ValueError("Incorrect/incomplete specification for Excel cell data.")
|
|
607
|
-
_cell_addr = (_cell_addr_0, _s2s_args[0])
|
|
608
|
-
_cell_val = _s2s_args[1]
|
|
609
|
-
_cell_fmt = _s2s_args[2] if len(_s2s_args) == 3 else None
|
|
610
|
-
else:
|
|
611
|
-
raise ValueError("Incorrect/incomplete specification for Excel cell data.")
|
|
612
|
-
|
|
613
|
-
_write_args = (
|
|
614
|
-
(*_cell_addr, repr(_cell_val))
|
|
615
|
-
if np.ndim(_cell_val) or _cell_val in (np.inf, -np.inf, np.nan)
|
|
616
|
-
else (*_cell_addr, _cell_val)
|
|
617
|
-
)
|
|
618
|
-
_write_args = (*_write_args, _cell_fmt) if _cell_fmt else _write_args
|
|
619
|
-
|
|
620
|
-
if empty_as_blank and (_cell_val is None or _cell_val == ""):
|
|
621
|
-
_xl_sheet.write_blank(*_write_args)
|
|
622
|
-
elif (
|
|
623
|
-
_cell_val is None
|
|
624
|
-
or _cell_val == ""
|
|
625
|
-
or isinstance(_cell_val, str)
|
|
626
|
-
or np.ndim(_cell_val)
|
|
627
|
-
or _cell_val in (np.inf, -np.inf, np.nan)
|
|
628
|
-
):
|
|
629
|
-
_xl_sheet.write_string(*_write_args)
|
|
630
|
-
else:
|
|
631
|
-
_xl_sheet.write(*_write_args)
|
|
File without changes
|