mergeron 2024.739079.13__tar.gz → 2024.739088.0__tar.gz
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-2024.739079.13 → mergeron-2024.739088.0}/PKG-INFO +1 -1
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/pyproject.toml +2 -1
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/__init__.py +1 -1
- mergeron-2024.739088.0/src/mergeron/ext/xlsxw_helper.py +513 -0
- mergeron-2024.739079.13/src/mergeron/core/excel_helper.py +0 -373
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/README.rst +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/License.txt +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/__init__.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/damodaran_margin_data.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/ftc_merger_investigations_data.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/guidelines_boundaries.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/guidelines_boundary_functions.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/guidelines_boundary_functions_extra.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/proportions_tests.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/pseudorandom_numbers.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/__init__.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/damodaran_margin_data.xls +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/damodaran_margin_data_dict.msgpack +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/ftc_invdata.msgpack +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/jinja2_LaTeX_templates/clrrate_cis_summary_table_template.tex.jinja2 +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_byhhianddelta_table_template.tex.jinja2 +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_summary_table_template.tex.jinja2 +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_summarypaired_table_template.tex.jinja2 +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/jinja2_LaTeX_templates/mergeron.cls +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/jinja2_LaTeX_templates/mergeron_table_collection_template.tex.jinja2 +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/jinja2_LaTeX_templates/setup_tikz_tables.tex +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/demo/__init__.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/demo/visualize_empirical_margin_distribution.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/ext/__init__.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/ext/tol_colors.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/gen/__init__.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/gen/_data_generation_functions.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/gen/data_generation.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/gen/enforcement_stats.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/gen/market_sample.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/gen/upp_tests.py +0 -0
- {mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mergeron
|
|
3
|
-
Version: 2024.
|
|
3
|
+
Version: 2024.739088.0
|
|
4
4
|
Summary: Merger Policy Analysis using Python
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: merger policy analysis,merger guidelines,merger screening,policy presumptions,concentration standards,upward pricing pressure,GUPPI
|
|
@@ -13,7 +13,7 @@ keywords = [
|
|
|
13
13
|
"upward pricing pressure",
|
|
14
14
|
"GUPPI",
|
|
15
15
|
]
|
|
16
|
-
version = "2024.
|
|
16
|
+
version = "2024.739088.0"
|
|
17
17
|
|
|
18
18
|
# Classifiers list: https://pypi.org/classifiers/
|
|
19
19
|
classifiers = [
|
|
@@ -184,6 +184,7 @@ allow_redefinition = true
|
|
|
184
184
|
plugins = "numpy.typing.mypy_plugin"
|
|
185
185
|
|
|
186
186
|
[tool.pytest.ini_options]
|
|
187
|
+
log_auto_indent = 4
|
|
187
188
|
minversion = "8.0"
|
|
188
189
|
testpaths = ["tests"]
|
|
189
190
|
addopts = ["--import-mode=importlib"]
|
|
@@ -0,0 +1,513 @@
|
|
|
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 desinged for producing formatted summary output. For
|
|
11
|
+
writing bulk data to Excel, facilities provided in third-party packages
|
|
12
|
+
such as `polars` likely provide better performance.
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from collections.abc import Mapping, Sequence
|
|
19
|
+
from typing import Any, ClassVar
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
import numpy.typing as npt
|
|
23
|
+
import xlsxwriter # type: ignore
|
|
24
|
+
from aenum import Enum, extend_enum, unique # type: ignore
|
|
25
|
+
|
|
26
|
+
from .. import VERSION # noqa: TID252
|
|
27
|
+
|
|
28
|
+
__version__ = VERSION
|
|
29
|
+
|
|
30
|
+
Workbook = xlsxwriter.Workbook
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@unique
|
|
34
|
+
class CFmt(dict, Enum): # type: ignore
|
|
35
|
+
"""
|
|
36
|
+
Initialize cell formats for xlsxwriter.
|
|
37
|
+
|
|
38
|
+
The mappings included here, and unions, etc. of them
|
|
39
|
+
and any others added at runtime, are rendered
|
|
40
|
+
as xlsxWriter.Workbook.Format objects for writing
|
|
41
|
+
cell values to formatted cells in a spreadsheet.
|
|
42
|
+
|
|
43
|
+
NOTES
|
|
44
|
+
-----
|
|
45
|
+
|
|
46
|
+
For more information about xlsxwriter's cell formats,
|
|
47
|
+
see, https://xlsxwriter.readthedocs.io/format.html
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
XL_DEFAULT: ClassVar = {"font_name": "Calibri", "font_size": 11}
|
|
52
|
+
XL_DEFAULT_2003: ClassVar = {"font_name": "Arial", "font_size": 10}
|
|
53
|
+
|
|
54
|
+
A_CTR: ClassVar = {"align": "center"}
|
|
55
|
+
A_CTR_ACROSS: ClassVar = {"align": "center_across"}
|
|
56
|
+
A_LEFT: ClassVar = {"align": "left"}
|
|
57
|
+
A_RIGHT: ClassVar = {"align": "right"}
|
|
58
|
+
V_TOP: ClassVar = {"align": "top"}
|
|
59
|
+
V_BOTTOM: ClassVar = {"align": "bottom"}
|
|
60
|
+
V_CTR: ClassVar = {"align": "vcenter"}
|
|
61
|
+
|
|
62
|
+
TEXT_WRAP: ClassVar = {"text_wrap": True}
|
|
63
|
+
TEXT_ROTATE: ClassVar = {"rotation": 90}
|
|
64
|
+
IND_1: ClassVar = {"indent": 1}
|
|
65
|
+
|
|
66
|
+
BOLD: ClassVar = {"bold": True}
|
|
67
|
+
BOLD_ITALIC: ClassVar = {"bold": True, "italic": True}
|
|
68
|
+
ITALIC: ClassVar = {"italic": True}
|
|
69
|
+
ULINE: ClassVar = {"underline": True}
|
|
70
|
+
SOUT: ClassVar = {"font_strikeout": True}
|
|
71
|
+
# Useful with write_rich_text()
|
|
72
|
+
SUPERSCRIPT: ClassVar = {"font_script": 1}
|
|
73
|
+
SUBSCRIPT: ClassVar = {"font_script": 2}
|
|
74
|
+
|
|
75
|
+
AREA_NUM: ClassVar = ({"num_format": "0.00000000"},)
|
|
76
|
+
DOLLAR_NUM: ClassVar = {"num_format": "[$$-409]#,##0.00"}
|
|
77
|
+
DT_NUM: ClassVar = {"num_format": "mm/dd/yyyy"}
|
|
78
|
+
PCT_NUM: ClassVar = {"num_format": "##0%"}
|
|
79
|
+
PCT2_NUM: ClassVar = {"num_format": "##0.00%"}
|
|
80
|
+
PCT4_NUM: ClassVar = {"num_format": "##0.0000%"}
|
|
81
|
+
PCT6_NUM: ClassVar = {"num_format": "##0.000000%"}
|
|
82
|
+
PCT8_NUM: ClassVar = {"num_format": "##0.00000000%"}
|
|
83
|
+
QTY_NUM: ClassVar = {"num_format": "#,##0.0"}
|
|
84
|
+
|
|
85
|
+
BAR_FILL: ClassVar = {"pattern": 1, "bg_color": "dfeadf"}
|
|
86
|
+
HDR_FILL: ClassVar = {"pattern": 1, "bg_color": "e9e9e9"}
|
|
87
|
+
|
|
88
|
+
LEFT_BORDER: ClassVar = {"left": 1, "left_color": "000000"}
|
|
89
|
+
RIGHT_BORDER: ClassVar = {"right": 1, "right_color": "000000"}
|
|
90
|
+
BOT_BORDER: ClassVar = {"bottom": 1, "bottom_color": "000000"}
|
|
91
|
+
TOP_BORDER: ClassVar = {"top": 1, "top_color": "000000"}
|
|
92
|
+
HDR_BORDER: ClassVar = TOP_BORDER | BOT_BORDER
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def add_new(self, _fmt_name: str, _xlsx_fmt_dict: dict[str, Any], /) -> CFmt:
|
|
96
|
+
"""
|
|
97
|
+
Add new CFmt object to instance.
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
_fmt_name
|
|
102
|
+
Name of new member to be added to CFmt
|
|
103
|
+
_xlsx_fmt_dict
|
|
104
|
+
Any valid argument to xlsxwriter.Workbook.add_format(), or union of
|
|
105
|
+
same with one or more CFmt objects with same, e.g.,
|
|
106
|
+
CFmt.HDR_BORDER | CFmt.HDR_FILL or
|
|
107
|
+
CFmt.HDR_BORDER | {"pattern": 1, "bg_color": "f2f2f2"}
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
None
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
return extend_enum(self, _fmt_name, _xlsx_fmt_dict) # type: ignore
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def ensure_cell_format_spec_tuple(
|
|
119
|
+
self, _cell_formats: Sequence[CFmt | Sequence[CFmt]], /
|
|
120
|
+
) -> bool:
|
|
121
|
+
"""
|
|
122
|
+
Test that a given format specification is a tuple of CFmt enums
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
_cell_formats
|
|
127
|
+
Format specification
|
|
128
|
+
|
|
129
|
+
Raises
|
|
130
|
+
------
|
|
131
|
+
ValueError
|
|
132
|
+
If format specification is not tuple of CFmt enums
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
True if format specification passes, else False
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
for _cell_format in _cell_formats:
|
|
141
|
+
if isinstance(_cell_format, tuple):
|
|
142
|
+
self.ensure_cell_format_spec_tuple(_cell_format)
|
|
143
|
+
|
|
144
|
+
if not (isinstance(_cell_format, CFmt),):
|
|
145
|
+
raise ValueError(
|
|
146
|
+
"Improperly specified format tuple for writing array."
|
|
147
|
+
" Must be tuple of CFmt enums."
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return True
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def xl_fmt(
|
|
154
|
+
self,
|
|
155
|
+
_xl_book: xlsxwriter.Workbook,
|
|
156
|
+
_cell_fmt: Sequence[CFmt | Sequence[CFmt]] | CFmt | None,
|
|
157
|
+
/,
|
|
158
|
+
) -> xlsxwriter.format.Format:
|
|
159
|
+
"""
|
|
160
|
+
Return :code:`xlsxwriter` `Format` object given a CFmt aenum, or tuple thereof.
|
|
161
|
+
|
|
162
|
+
Parameters
|
|
163
|
+
----------
|
|
164
|
+
_xl_book
|
|
165
|
+
:code:`xlsxwriter.Workbook` object
|
|
166
|
+
|
|
167
|
+
_cell_fmt
|
|
168
|
+
:code:`CFmt` aenum object, or tuple thereof
|
|
169
|
+
|
|
170
|
+
Raises
|
|
171
|
+
------
|
|
172
|
+
ValueError
|
|
173
|
+
If format specification is not one of None, a CFmt aenum, or
|
|
174
|
+
a xlsxwriter.format.Format object
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
:code:`xlsxwriter` `Format` object
|
|
179
|
+
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
if isinstance(_cell_fmt, xlsxwriter.format.Format):
|
|
183
|
+
return _cell_fmt
|
|
184
|
+
elif _cell_fmt is None:
|
|
185
|
+
return _xl_book.add_format(CFmt.XL_DEFAULT.value)
|
|
186
|
+
|
|
187
|
+
_cell_fmt_dict: Mapping[str, Any] = {}
|
|
188
|
+
if isinstance(_cell_fmt, Sequence):
|
|
189
|
+
self.ensure_cell_format_spec_tuple(_cell_fmt)
|
|
190
|
+
for _cf in _cell_fmt:
|
|
191
|
+
_cell_fmt_dict = (
|
|
192
|
+
(_cell_fmt_dict | _cfi.value for _cfi in _cf)
|
|
193
|
+
if isinstance(_cf, Sequence)
|
|
194
|
+
else _cell_fmt_dict | _cf.value
|
|
195
|
+
)
|
|
196
|
+
elif isinstance(_cell_fmt, CFmt):
|
|
197
|
+
_cell_fmt_dict = _cell_fmt.value
|
|
198
|
+
else:
|
|
199
|
+
raise ValueError("Improperly specified format specification.")
|
|
200
|
+
|
|
201
|
+
return _xl_book.add_format(_cell_fmt_dict)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def write_header(
|
|
205
|
+
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
206
|
+
/,
|
|
207
|
+
*,
|
|
208
|
+
center_header: str | None = None,
|
|
209
|
+
left_header: str | None = None,
|
|
210
|
+
right_header: str | None = None,
|
|
211
|
+
) -> None:
|
|
212
|
+
"""Write header text to given worksheet.
|
|
213
|
+
|
|
214
|
+
Parameters
|
|
215
|
+
----------
|
|
216
|
+
_xl_sheet
|
|
217
|
+
Worksheet object
|
|
218
|
+
center_header
|
|
219
|
+
Text for center header
|
|
220
|
+
left_header
|
|
221
|
+
Text for left header
|
|
222
|
+
right_header
|
|
223
|
+
Text for right header
|
|
224
|
+
|
|
225
|
+
Raises
|
|
226
|
+
------
|
|
227
|
+
ValueError
|
|
228
|
+
Must specify at least one header
|
|
229
|
+
|
|
230
|
+
Returns
|
|
231
|
+
-------
|
|
232
|
+
None
|
|
233
|
+
"""
|
|
234
|
+
if any((center_header, left_header, right_header)):
|
|
235
|
+
_xl_sheet.set_header(
|
|
236
|
+
"".join([
|
|
237
|
+
f"&L{left_header}" if left_header else "",
|
|
238
|
+
f"&C{center_header}" if center_header else "",
|
|
239
|
+
f"&R{right_header}" if right_header else "",
|
|
240
|
+
])
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
else:
|
|
244
|
+
raise ValueError("must specify at least one header")
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def write_footer(
|
|
248
|
+
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
249
|
+
/,
|
|
250
|
+
*,
|
|
251
|
+
center_footer: str | None = None,
|
|
252
|
+
left_footer: str | None = None,
|
|
253
|
+
right_footer: str | None = None,
|
|
254
|
+
) -> None:
|
|
255
|
+
"""Write footer text to given worksheet.
|
|
256
|
+
|
|
257
|
+
Parameters
|
|
258
|
+
----------
|
|
259
|
+
_xl_sheet
|
|
260
|
+
Worksheet object
|
|
261
|
+
center_footer
|
|
262
|
+
Text for center footer
|
|
263
|
+
left_footer
|
|
264
|
+
Text for left footer
|
|
265
|
+
right_footer
|
|
266
|
+
Text for right footer
|
|
267
|
+
|
|
268
|
+
Raises
|
|
269
|
+
------
|
|
270
|
+
ValueError
|
|
271
|
+
Must specify at least one footer
|
|
272
|
+
|
|
273
|
+
Returns
|
|
274
|
+
-------
|
|
275
|
+
None
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
if not any((center_footer, left_footer, right_footer)):
|
|
279
|
+
_xl_sheet.set_footer(
|
|
280
|
+
"".join([
|
|
281
|
+
f"&L{left_footer}" if left_footer else "",
|
|
282
|
+
f"&C{center_footer}" if center_footer else "",
|
|
283
|
+
f"&R{right_footer}" if right_footer else "",
|
|
284
|
+
])
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
else:
|
|
288
|
+
raise ValueError("must specify at least one footer")
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def array_to_sheet(
|
|
292
|
+
_xl_book: xlsxwriter.workbook.Workbook,
|
|
293
|
+
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
294
|
+
_data_table: Sequence[Any] | npt.NDArray[Any],
|
|
295
|
+
_row_id: int,
|
|
296
|
+
_col_id: int = 0,
|
|
297
|
+
/,
|
|
298
|
+
*,
|
|
299
|
+
cell_format: Sequence[CFmt | Sequence[CFmt]] | CFmt | None = None,
|
|
300
|
+
green_bar_flag: bool = True,
|
|
301
|
+
ragged_flag: bool = True,
|
|
302
|
+
) -> tuple[int, int]:
|
|
303
|
+
"""
|
|
304
|
+
Write a 2-D array to a worksheet.
|
|
305
|
+
|
|
306
|
+
The given array is required be a two-dimensional array, whether
|
|
307
|
+
a nested list, nested tuple, or a 2-D numpy ndarray.
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
Parameters
|
|
311
|
+
----------
|
|
312
|
+
_xl_book
|
|
313
|
+
Workbook object
|
|
314
|
+
|
|
315
|
+
_xl_sheet
|
|
316
|
+
Worksheet object to which to write the give array
|
|
317
|
+
|
|
318
|
+
_data_table
|
|
319
|
+
Array to be written
|
|
320
|
+
|
|
321
|
+
_row_id
|
|
322
|
+
Row number of top left corner of range to write to
|
|
323
|
+
|
|
324
|
+
_col_id
|
|
325
|
+
Column number of top left corner of range to write to
|
|
326
|
+
|
|
327
|
+
cell_format
|
|
328
|
+
Format specification for range to be written
|
|
329
|
+
|
|
330
|
+
green_bar_flag
|
|
331
|
+
Whether to highlight alternating rows as in green bar paper
|
|
332
|
+
|
|
333
|
+
ragged_flag
|
|
334
|
+
Whether to write ragged array, i.e. rows not all the same length
|
|
335
|
+
or not all cells are scalar-valued
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
Raises
|
|
339
|
+
------
|
|
340
|
+
ValueError
|
|
341
|
+
If array is not two-dimensional
|
|
342
|
+
|
|
343
|
+
ValueError
|
|
344
|
+
If ragged_flag is False and array is not rectangular
|
|
345
|
+
|
|
346
|
+
ValueError
|
|
347
|
+
If array is not rectangular and cell_format is a Sequence
|
|
348
|
+
|
|
349
|
+
ValueError
|
|
350
|
+
If array is rectangular format tuple does not match data in length
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
Returns
|
|
354
|
+
-------
|
|
355
|
+
Tuple giving address of cell at right below and after range written
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
Notes
|
|
359
|
+
-----
|
|
360
|
+
|
|
361
|
+
The keyword argument cell_format may be passed a tuple of CFmt enums,
|
|
362
|
+
if, and only if, ragged_flag is False. If cell_format is a tuple, it must
|
|
363
|
+
have length equal to the number of cells in the range to be written. Further,
|
|
364
|
+
members of cell_format must each be a CFmt enum or a tuple of CFmt enums; in
|
|
365
|
+
other words, `CFmt.ensure_cell_format_spec_tuple(_c)` must return True for
|
|
366
|
+
any tuple `_c` passed as `cell_format`.
|
|
367
|
+
|
|
368
|
+
"""
|
|
369
|
+
|
|
370
|
+
if not ragged_flag:
|
|
371
|
+
try:
|
|
372
|
+
if np.ndim(_data_table) != 2:
|
|
373
|
+
raise ValueError("Given array must be two-dimensional.")
|
|
374
|
+
except ValueError as _err:
|
|
375
|
+
raise ValueError(
|
|
376
|
+
"Given array must be rectangular and homogenous, with scalar members."
|
|
377
|
+
" Alternatively, try with ragged_flag=True."
|
|
378
|
+
)
|
|
379
|
+
raise _err
|
|
380
|
+
elif not (
|
|
381
|
+
isinstance(_data_table, Sequence | np.ndarray)
|
|
382
|
+
and hasattr(_data_table[0], "__len__")
|
|
383
|
+
):
|
|
384
|
+
raise ValueError("Given array must be two-dimensional array.")
|
|
385
|
+
|
|
386
|
+
# Get the array dimensions and row and column numbers for Excel
|
|
387
|
+
_num_rows = len(_data_table)
|
|
388
|
+
_bottom_row_id = _row_id + _num_rows
|
|
389
|
+
_num_cols = len(_data_table[0])
|
|
390
|
+
_right_column_id = _col_id + _num_cols
|
|
391
|
+
|
|
392
|
+
if isinstance(cell_format, Sequence):
|
|
393
|
+
if ragged_flag:
|
|
394
|
+
raise ValueError(
|
|
395
|
+
"It is not clear whether the sequence of formats applies to all cells,"
|
|
396
|
+
" or to each cell respectively. Please provide a single-valued cell_format."
|
|
397
|
+
" Alternatively, you can iterate over the array using scalar_to_sheet()."
|
|
398
|
+
)
|
|
399
|
+
elif not len(cell_format) == len(_data_table[0]):
|
|
400
|
+
raise ValueError("Format tuple does not match data in length.")
|
|
401
|
+
CFmt.ensure_cell_format_spec_tuple(cell_format)
|
|
402
|
+
_cell_format: Sequence[CFmt | Sequence[CFmt]] = cell_format
|
|
403
|
+
elif isinstance(cell_format, CFmt):
|
|
404
|
+
_cell_format = (cell_format,) * len(_data_table[0])
|
|
405
|
+
else:
|
|
406
|
+
_cell_format = (CFmt.XL_DEFAULT,) * len(_data_table[0])
|
|
407
|
+
|
|
408
|
+
# construct vector of xlslwrter.format.Format objects
|
|
409
|
+
_wbk_formats = tuple(CFmt.xl_fmt(_xl_book, _cf) for _cf in _cell_format)
|
|
410
|
+
if _num_rows > 1:
|
|
411
|
+
_wbk_formats_greened = (
|
|
412
|
+
tuple(
|
|
413
|
+
CFmt.xl_fmt(
|
|
414
|
+
_xl_book,
|
|
415
|
+
(*_cf, CFmt.BAR_FILL)
|
|
416
|
+
if isinstance(_cf, Sequence)
|
|
417
|
+
else (_cf, CFmt.BAR_FILL),
|
|
418
|
+
)
|
|
419
|
+
for _cf in _cell_format
|
|
420
|
+
)
|
|
421
|
+
if green_bar_flag
|
|
422
|
+
else _wbk_formats
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
for _ri, _rv in enumerate(_data_table):
|
|
426
|
+
_wbk_fmt_tuple = _wbk_formats_greened if _ri % 2 else _wbk_formats
|
|
427
|
+
for _ci, _cv in enumerate(_rv):
|
|
428
|
+
_cf = _wbk_fmt_tuple[_ci]
|
|
429
|
+
scalar_to_sheet(_xl_sheet, _row_id + _ri, _col_id + _ci, _cv, _cf)
|
|
430
|
+
|
|
431
|
+
_right_column_id = _col_id + _ci + 1 if _ci > _num_cols else _right_column_id
|
|
432
|
+
|
|
433
|
+
return _bottom_row_id, _right_column_id
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def scalar_to_sheet(
|
|
437
|
+
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
438
|
+
_cell_addr_0: str | int = "A1",
|
|
439
|
+
/,
|
|
440
|
+
*_s2s_args: Any,
|
|
441
|
+
empty_as_blank: bool = True,
|
|
442
|
+
) -> None:
|
|
443
|
+
"""
|
|
444
|
+
Write to a single cell in a worksheet.
|
|
445
|
+
|
|
446
|
+
Parameters
|
|
447
|
+
----------
|
|
448
|
+
_xl_book
|
|
449
|
+
Workbook object
|
|
450
|
+
|
|
451
|
+
_xl_sheet
|
|
452
|
+
Worksheet object to which to write the give array
|
|
453
|
+
|
|
454
|
+
_cell_addr_0
|
|
455
|
+
First element of a cell address, which may be the entire address
|
|
456
|
+
in 'A1' format or the row-part in 'R1,C1' format
|
|
457
|
+
|
|
458
|
+
_s2s_args
|
|
459
|
+
Other arguments, which may be just the cell value to be written and the
|
|
460
|
+
cell format, or the column-part of the 'R1,C1' address along with
|
|
461
|
+
cell value and cell format.
|
|
462
|
+
|
|
463
|
+
Raises
|
|
464
|
+
------
|
|
465
|
+
ValueError
|
|
466
|
+
If too many or too few arguments
|
|
467
|
+
ValueError
|
|
468
|
+
If incorrect/incomplete specification for Excel cell data
|
|
469
|
+
|
|
470
|
+
Returns
|
|
471
|
+
-------
|
|
472
|
+
None
|
|
473
|
+
|
|
474
|
+
"""
|
|
475
|
+
|
|
476
|
+
_cell_addr: tuple[int | str, ...] = ()
|
|
477
|
+
_cell_val: Any = None
|
|
478
|
+
_cell_fmt: xlsxwriter.format.Format = CFmt.XL_DEFAULT
|
|
479
|
+
|
|
480
|
+
if isinstance(_cell_addr_0, str):
|
|
481
|
+
if len(_s2s_args) not in (1, 2):
|
|
482
|
+
raise ValueError("Incorrect number of arguments.")
|
|
483
|
+
_cell_addr = (_cell_addr_0,)
|
|
484
|
+
_cell_val = _s2s_args[0]
|
|
485
|
+
_cell_fmt = _s2s_args[1] if len(_s2s_args) == 2 else None # type: ignore
|
|
486
|
+
elif isinstance(_cell_addr_0, int):
|
|
487
|
+
if len(_s2s_args) not in (2, 3) or not isinstance(_s2s_args[0], int):
|
|
488
|
+
raise ValueError("Incorrect/incomplete specification for Excel cell data.")
|
|
489
|
+
_cell_addr = (_cell_addr_0, _s2s_args[0])
|
|
490
|
+
_cell_val = _s2s_args[1]
|
|
491
|
+
_cell_fmt = _s2s_args[2] if len(_s2s_args) == 3 else None # type: ignore
|
|
492
|
+
else:
|
|
493
|
+
raise ValueError("Incorrect/incomplete specification for Excel cell data.")
|
|
494
|
+
|
|
495
|
+
_write_args = (
|
|
496
|
+
(*_cell_addr, repr(_cell_val))
|
|
497
|
+
if np.ndim(_cell_val) or _cell_val in (np.inf, -np.inf, np.nan)
|
|
498
|
+
else (*_cell_addr, _cell_val)
|
|
499
|
+
)
|
|
500
|
+
_write_args = (*_write_args, _cell_fmt) if _cell_fmt else _write_args
|
|
501
|
+
|
|
502
|
+
if empty_as_blank and (_cell_val is None or _cell_val == ""):
|
|
503
|
+
_xl_sheet.write_blank(*_write_args)
|
|
504
|
+
elif (
|
|
505
|
+
_cell_val is None
|
|
506
|
+
or _cell_val == ""
|
|
507
|
+
or isinstance(_cell_val, str)
|
|
508
|
+
or np.ndim(_cell_val)
|
|
509
|
+
or _cell_val in (np.inf, -np.inf, np.nan)
|
|
510
|
+
):
|
|
511
|
+
_xl_sheet.write_string(*_write_args)
|
|
512
|
+
else:
|
|
513
|
+
_xl_sheet.write(*_write_args)
|
|
@@ -1,373 +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 desinged for producing formatted summary output. For
|
|
11
|
-
writing bulk data to Excel, facilities provided in third-party packages
|
|
12
|
-
such as `polars` likely provide better performance.
|
|
13
|
-
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
from __future__ import annotations
|
|
17
|
-
|
|
18
|
-
from collections.abc import Mapping, Sequence
|
|
19
|
-
from typing import Any, ClassVar
|
|
20
|
-
|
|
21
|
-
import numpy as np
|
|
22
|
-
import numpy.typing as npt
|
|
23
|
-
import xlsxwriter # type: ignore
|
|
24
|
-
from aenum import Enum, unique # type: ignore
|
|
25
|
-
|
|
26
|
-
from .. import VERSION # noqa: TID252
|
|
27
|
-
|
|
28
|
-
__version__ = VERSION
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@unique
|
|
32
|
-
class CFmt(dict, Enum): # type: ignore
|
|
33
|
-
"""
|
|
34
|
-
Initialize cell formats for xlsxwriter.
|
|
35
|
-
|
|
36
|
-
The mappings included here, and unions, etc. of them
|
|
37
|
-
and any others added at runtime, will be rendered
|
|
38
|
-
as xlsxWriter.Workbook.Format objects for writing
|
|
39
|
-
cell values to formatted cells in a spreadsheet.
|
|
40
|
-
|
|
41
|
-
NOTES
|
|
42
|
-
-----
|
|
43
|
-
|
|
44
|
-
For more information about xlsxwriter's cell formats,
|
|
45
|
-
see, https://xlsxwriter.readthedocs.io/format.html
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
XL_DEFAULT: ClassVar = {"font_name": "Calibri", "font_size": 11}
|
|
49
|
-
XL_DEFAULT_2003: ClassVar = {"font_name": "Arial", "font_size": 10}
|
|
50
|
-
|
|
51
|
-
A_CTR: ClassVar = {"align": "center"}
|
|
52
|
-
A_CTR_ACROSS: ClassVar = {"align": "center_across"}
|
|
53
|
-
A_LEFT: ClassVar = {"align": "left"}
|
|
54
|
-
A_RIGHT: ClassVar = {"align": "right"}
|
|
55
|
-
|
|
56
|
-
BOLD: ClassVar = {"bold": True}
|
|
57
|
-
BOLD_ITALIC: ClassVar = {"bold": True, "italic": True}
|
|
58
|
-
ITALIC: ClassVar = {"italic": True}
|
|
59
|
-
ULINE: ClassVar = {"underline": True}
|
|
60
|
-
|
|
61
|
-
TEXT_WRAP: ClassVar = {"text_wrap": True}
|
|
62
|
-
TEXT_ROTATE: ClassVar = {"rotation": 90}
|
|
63
|
-
IND_1: ClassVar = {"indent": 1}
|
|
64
|
-
|
|
65
|
-
DOLLAR_NUM: ClassVar = {"num_format": "[$$-409]#,##0.00"}
|
|
66
|
-
DT_NUM: ClassVar = {"num_format": "mm/dd/yyyy"}
|
|
67
|
-
QTY_NUM: ClassVar = {"num_format": "#,##0.0"}
|
|
68
|
-
PCT_NUM: ClassVar = {"num_format": "##0%"}
|
|
69
|
-
PCT2_NUM: ClassVar = {"num_format": "##0.00%"}
|
|
70
|
-
PCT4_NUM: ClassVar = {"num_format": "##0.0000%"}
|
|
71
|
-
PCT6_NUM: ClassVar = {"num_format": "##0.000000%"}
|
|
72
|
-
PCT8_NUM: ClassVar = {"num_format": "##0.00000000%"}
|
|
73
|
-
AREA_NUM: ClassVar = {"num_format": "0.00000000"}
|
|
74
|
-
|
|
75
|
-
BAR_FILL: ClassVar = {"pattern": 1, "bg_color": "dfeadf"}
|
|
76
|
-
HDR_FILL: ClassVar = {"pattern": 1, "bg_color": "999999"}
|
|
77
|
-
|
|
78
|
-
LEFT_BORDER: ClassVar = {"left": 1, "left_color": "000000"}
|
|
79
|
-
RIGHT_BORDER: ClassVar = {"right": 1, "right_color": "000000"}
|
|
80
|
-
BOT_BORDER: ClassVar = {"bottom": 1, "bottom_color": "000000"}
|
|
81
|
-
TOP_BORDER: ClassVar = {"top": 1, "top_color": "000000"}
|
|
82
|
-
HDR_BORDER: ClassVar = TOP_BORDER | BOT_BORDER
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def write_header(
|
|
86
|
-
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
87
|
-
/,
|
|
88
|
-
*,
|
|
89
|
-
center_header: str | None = None,
|
|
90
|
-
left_header: str | None = None,
|
|
91
|
-
right_header: str | None = None,
|
|
92
|
-
) -> None:
|
|
93
|
-
"""Write header text to given worksheet.
|
|
94
|
-
|
|
95
|
-
Parameters
|
|
96
|
-
----------
|
|
97
|
-
_xl_sheet
|
|
98
|
-
Worksheet object
|
|
99
|
-
center_header
|
|
100
|
-
Text for center header
|
|
101
|
-
left_header
|
|
102
|
-
Text for left header
|
|
103
|
-
right_header
|
|
104
|
-
Text for right header
|
|
105
|
-
|
|
106
|
-
Raises
|
|
107
|
-
------
|
|
108
|
-
ValueError
|
|
109
|
-
Must specify at least one header
|
|
110
|
-
|
|
111
|
-
Returns
|
|
112
|
-
-------
|
|
113
|
-
None
|
|
114
|
-
"""
|
|
115
|
-
if not any((center_header, left_header, right_header)):
|
|
116
|
-
raise ValueError("must specify at least one header")
|
|
117
|
-
_xl_sheet.set_footer(
|
|
118
|
-
"".join([
|
|
119
|
-
f"&L{left_header}" if left_header else "",
|
|
120
|
-
f"&C{center_header}" if center_header else "",
|
|
121
|
-
f"&R{right_header}" if right_header else "",
|
|
122
|
-
])
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def write_footer(
|
|
127
|
-
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
128
|
-
/,
|
|
129
|
-
*,
|
|
130
|
-
center_footer: str | None = None,
|
|
131
|
-
left_footer: str | None = None,
|
|
132
|
-
right_footer: str | None = None,
|
|
133
|
-
) -> None:
|
|
134
|
-
"""Write footer text to given worksheet.
|
|
135
|
-
|
|
136
|
-
Parameters
|
|
137
|
-
----------
|
|
138
|
-
_xl_sheet
|
|
139
|
-
Worksheet object
|
|
140
|
-
center_footer
|
|
141
|
-
Text for center footer
|
|
142
|
-
left_footer
|
|
143
|
-
Text for left footer
|
|
144
|
-
right_footer
|
|
145
|
-
Text for right footer
|
|
146
|
-
|
|
147
|
-
Raises
|
|
148
|
-
------
|
|
149
|
-
ValueError
|
|
150
|
-
Must specify at least one footer
|
|
151
|
-
|
|
152
|
-
Returns
|
|
153
|
-
-------
|
|
154
|
-
None
|
|
155
|
-
"""
|
|
156
|
-
|
|
157
|
-
if not any((center_footer, left_footer, right_footer)):
|
|
158
|
-
raise ValueError("must specify at least one footer")
|
|
159
|
-
|
|
160
|
-
_xl_sheet.set_footer(
|
|
161
|
-
"".join([
|
|
162
|
-
f"&L{left_footer}" if left_footer else "",
|
|
163
|
-
f"&C{center_footer}" if center_footer else "",
|
|
164
|
-
f"&R{right_footer}" if right_footer else "",
|
|
165
|
-
])
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
def array_to_sheet(
|
|
170
|
-
_xl_book: xlsxwriter.workbook.Workbook,
|
|
171
|
-
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
172
|
-
_data_table: Sequence[Any] | npt.NDArray[Any],
|
|
173
|
-
_row_id: int,
|
|
174
|
-
_col_id: int = 0,
|
|
175
|
-
/,
|
|
176
|
-
*,
|
|
177
|
-
cell_format: Sequence[CFmt] | CFmt | None = None,
|
|
178
|
-
green_bar_flag: bool = True,
|
|
179
|
-
ragged_flag: bool = True,
|
|
180
|
-
) -> tuple[int, int]:
|
|
181
|
-
"""
|
|
182
|
-
Write a 2-D array to a worksheet.
|
|
183
|
-
|
|
184
|
-
The given array is required be a two-dimensional array, whether
|
|
185
|
-
a nested list, nested tuple, or a 2-D numpy ndarray.
|
|
186
|
-
|
|
187
|
-
Parameters
|
|
188
|
-
----------
|
|
189
|
-
_xl_book
|
|
190
|
-
Workbook object
|
|
191
|
-
|
|
192
|
-
_xl_sheet
|
|
193
|
-
Worksheet object to which to write the give array
|
|
194
|
-
|
|
195
|
-
_data_table
|
|
196
|
-
Array to be written
|
|
197
|
-
|
|
198
|
-
_row_id
|
|
199
|
-
Row number of top left corner of range to write to
|
|
200
|
-
|
|
201
|
-
_col_id
|
|
202
|
-
Column number of top left corner of range to write to
|
|
203
|
-
|
|
204
|
-
cell_format
|
|
205
|
-
Format specification for range to be written
|
|
206
|
-
|
|
207
|
-
green_bar_flag
|
|
208
|
-
Whether to highlight alternating rows as in green bar paper
|
|
209
|
-
|
|
210
|
-
Raises
|
|
211
|
-
------
|
|
212
|
-
ValueError
|
|
213
|
-
If format tuple does not match data in length
|
|
214
|
-
|
|
215
|
-
Returns
|
|
216
|
-
-------
|
|
217
|
-
Tuple giving address of cell at right below and after range written
|
|
218
|
-
|
|
219
|
-
"""
|
|
220
|
-
|
|
221
|
-
# Get the array dimensions and row and column numbers for Excel
|
|
222
|
-
_num_rows = len(_data_table)
|
|
223
|
-
_bottom_row_id = _row_id + _num_rows
|
|
224
|
-
_num_cols = len(_data_table[0])
|
|
225
|
-
_right_column_id = _col_id + _num_cols
|
|
226
|
-
|
|
227
|
-
if isinstance(cell_format, tuple):
|
|
228
|
-
ensure_cell_format_spec_tuple(cell_format)
|
|
229
|
-
if not len(cell_format) == len(_data_table[0]):
|
|
230
|
-
raise ValueError("Format tuple does not match data in length.")
|
|
231
|
-
_cell_format: Sequence[CFmt] = cell_format
|
|
232
|
-
elif isinstance(cell_format, CFmt):
|
|
233
|
-
_cell_format = (cell_format,) * len(_data_table[0])
|
|
234
|
-
else:
|
|
235
|
-
_cell_format = (CFmt.XL_DEFAULT,) * len(_data_table[0])
|
|
236
|
-
|
|
237
|
-
for _ri, _rv in enumerate(_data_table):
|
|
238
|
-
for _ci, _cv in enumerate(_rv):
|
|
239
|
-
_cell_fmt = _cell_format[_ci] | (
|
|
240
|
-
CFmt.BAR_FILL if green_bar_flag and _ri % 2 else {}
|
|
241
|
-
)
|
|
242
|
-
scalar_to_sheet(
|
|
243
|
-
_xl_book, _xl_sheet, _row_id + _ri, _col_id + _ci, _cv, _cell_fmt
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
_right_column_id = _col_id + _ci + 1 if _ci > _num_cols else _right_column_id
|
|
247
|
-
|
|
248
|
-
return _bottom_row_id, _right_column_id
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
def scalar_to_sheet(
|
|
252
|
-
_xl_book: xlsxwriter.workbook.Workbook,
|
|
253
|
-
_xl_sheet: xlsxwriter.worksheet.Worksheet,
|
|
254
|
-
_cell_addr_0: str | int | float = "A1",
|
|
255
|
-
/,
|
|
256
|
-
*_s2s_args: Any,
|
|
257
|
-
) -> None:
|
|
258
|
-
"""
|
|
259
|
-
Write to a single cell in a worksheet.
|
|
260
|
-
|
|
261
|
-
Parameters
|
|
262
|
-
----------
|
|
263
|
-
_xl_book
|
|
264
|
-
Workbook object
|
|
265
|
-
|
|
266
|
-
_xl_sheet
|
|
267
|
-
Worksheet object to which to write the give array
|
|
268
|
-
|
|
269
|
-
_cell_addr_0
|
|
270
|
-
First element of a cell address, which may be the entire address
|
|
271
|
-
in 'A1' format or the row-part in 'R1,C1' format
|
|
272
|
-
|
|
273
|
-
_s2s_args
|
|
274
|
-
Other arguments, which may be just the cell value to be written and the
|
|
275
|
-
cell format, or the column-part of the 'R1,C1' address along with
|
|
276
|
-
cell value and cell format.
|
|
277
|
-
|
|
278
|
-
Raises
|
|
279
|
-
------
|
|
280
|
-
ValueError
|
|
281
|
-
If too many or too few arguments
|
|
282
|
-
ValueError
|
|
283
|
-
If incorrect/incomplete specification for Excel cell data
|
|
284
|
-
|
|
285
|
-
Returns
|
|
286
|
-
-------
|
|
287
|
-
None
|
|
288
|
-
|
|
289
|
-
"""
|
|
290
|
-
|
|
291
|
-
if isinstance(_cell_addr_0, str):
|
|
292
|
-
if len(_s2s_args) not in (1, 2):
|
|
293
|
-
raise ValueError("Too many or too few arguments.")
|
|
294
|
-
_cell_addr: tuple[int | str, ...] = (_cell_addr_0,)
|
|
295
|
-
_cell_val: Any = _s2s_args[0]
|
|
296
|
-
_cell_fmt: CFmt | Sequence[CFmt] = _s2s_args[1] if len(_s2s_args) == 2 else None # type: ignore
|
|
297
|
-
elif isinstance(_cell_addr_0, int):
|
|
298
|
-
if len(_s2s_args) not in (2, 3):
|
|
299
|
-
raise ValueError("Too many or too few arguments.")
|
|
300
|
-
_cell_addr = (_cell_addr_0, _s2s_args[0])
|
|
301
|
-
_cell_val = _s2s_args[1]
|
|
302
|
-
_cell_fmt = _s2s_args[2] if len(_s2s_args) == 3 else None # type: ignore
|
|
303
|
-
else:
|
|
304
|
-
raise ValueError("Incorrect/incomplete specification for Excel cell data.")
|
|
305
|
-
|
|
306
|
-
if isinstance(_cell_val, str):
|
|
307
|
-
_xl_sheet.write_string(*_cell_addr, _cell_val, xl_fmt(_xl_book, _cell_fmt))
|
|
308
|
-
else:
|
|
309
|
-
_xl_sheet.write(
|
|
310
|
-
*_cell_addr,
|
|
311
|
-
repr(_cell_val) if np.ndim(_cell_val) else _cell_val,
|
|
312
|
-
xl_fmt(_xl_book, _cell_fmt),
|
|
313
|
-
)
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
def xl_fmt(
|
|
317
|
-
_xl_book: xlsxwriter.Workbook, _cell_fmt: Sequence[CFmt] | CFmt | None, /
|
|
318
|
-
) -> xlsxwriter.format.Format:
|
|
319
|
-
"""
|
|
320
|
-
Return :code:`xlsxwriter` `Format` object given a CFmt aenum, or tuple thereof.
|
|
321
|
-
|
|
322
|
-
Parameters
|
|
323
|
-
----------
|
|
324
|
-
_xl_book
|
|
325
|
-
:code:`xlsxwriter.Workbook` object
|
|
326
|
-
|
|
327
|
-
_cell_fmt
|
|
328
|
-
:code:`CFmt` aenum object, or tuple thereof
|
|
329
|
-
|
|
330
|
-
Returns
|
|
331
|
-
-------
|
|
332
|
-
:code:`xlsxwriter` `Format` object
|
|
333
|
-
|
|
334
|
-
"""
|
|
335
|
-
_cell_fmt_dict: Mapping[str, Any] = {}
|
|
336
|
-
if isinstance(_cell_fmt, tuple):
|
|
337
|
-
ensure_cell_format_spec_tuple(_cell_fmt)
|
|
338
|
-
for _cf in _cell_fmt:
|
|
339
|
-
_cell_fmt_dict = _cell_fmt_dict | _cf.value
|
|
340
|
-
elif isinstance(_cell_fmt, CFmt):
|
|
341
|
-
_cell_fmt_dict = _cell_fmt.value
|
|
342
|
-
else:
|
|
343
|
-
_cell_fmt_dict = CFmt.XL_DEFAULT.value
|
|
344
|
-
|
|
345
|
-
return _xl_book.add_format(_cell_fmt_dict)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
def ensure_cell_format_spec_tuple(_cell_formats: Sequence[CFmt], /) -> None:
|
|
349
|
-
"""
|
|
350
|
-
Test that a given format specification is tuple of CFmt enums
|
|
351
|
-
|
|
352
|
-
Parameters
|
|
353
|
-
----------
|
|
354
|
-
_cell_formats
|
|
355
|
-
Format specification
|
|
356
|
-
|
|
357
|
-
Raises
|
|
358
|
-
------
|
|
359
|
-
ValueError
|
|
360
|
-
If format specification is not tuple of CFmt aenums
|
|
361
|
-
|
|
362
|
-
Returns
|
|
363
|
-
-------
|
|
364
|
-
True if format specification passes, else False
|
|
365
|
-
|
|
366
|
-
"""
|
|
367
|
-
|
|
368
|
-
for _cell_format in _cell_formats:
|
|
369
|
-
if isinstance(_cell_format, tuple):
|
|
370
|
-
ensure_cell_format_spec_tuple(_cell_format)
|
|
371
|
-
|
|
372
|
-
if not (isinstance(_cell_format, CFmt),):
|
|
373
|
-
raise ValueError("Improperly specified format tuple.")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/damodaran_margin_data.py
RENAMED
|
File without changes
|
|
File without changes
|
{mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/guidelines_boundaries.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/core/pseudorandom_numbers.py
RENAMED
|
File without changes
|
|
File without changes
|
{mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/data/damodaran_margin_data.xls
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mergeron-2024.739079.13 → mergeron-2024.739088.0}/src/mergeron/gen/_data_generation_functions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|