panelbox 0.2.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.
- panelbox/__init__.py +67 -0
- panelbox/__version__.py +14 -0
- panelbox/cli/__init__.py +0 -0
- panelbox/cli/{commands}/__init__.py +0 -0
- panelbox/core/__init__.py +0 -0
- panelbox/core/base_model.py +164 -0
- panelbox/core/formula_parser.py +318 -0
- panelbox/core/panel_data.py +387 -0
- panelbox/core/results.py +366 -0
- panelbox/datasets/__init__.py +0 -0
- panelbox/datasets/{data}/__init__.py +0 -0
- panelbox/gmm/__init__.py +65 -0
- panelbox/gmm/difference_gmm.py +645 -0
- panelbox/gmm/estimator.py +562 -0
- panelbox/gmm/instruments.py +580 -0
- panelbox/gmm/results.py +550 -0
- panelbox/gmm/system_gmm.py +621 -0
- panelbox/gmm/tests.py +535 -0
- panelbox/models/__init__.py +11 -0
- panelbox/models/dynamic/__init__.py +0 -0
- panelbox/models/iv/__init__.py +0 -0
- panelbox/models/static/__init__.py +13 -0
- panelbox/models/static/fixed_effects.py +516 -0
- panelbox/models/static/pooled_ols.py +298 -0
- panelbox/models/static/random_effects.py +512 -0
- panelbox/report/__init__.py +61 -0
- panelbox/report/asset_manager.py +410 -0
- panelbox/report/css_manager.py +472 -0
- panelbox/report/exporters/__init__.py +15 -0
- panelbox/report/exporters/html_exporter.py +440 -0
- panelbox/report/exporters/latex_exporter.py +510 -0
- panelbox/report/exporters/markdown_exporter.py +446 -0
- panelbox/report/renderers/__init__.py +11 -0
- panelbox/report/renderers/static/__init__.py +0 -0
- panelbox/report/renderers/static_validation_renderer.py +341 -0
- panelbox/report/report_manager.py +502 -0
- panelbox/report/template_manager.py +337 -0
- panelbox/report/transformers/__init__.py +0 -0
- panelbox/report/transformers/static/__init__.py +0 -0
- panelbox/report/validation_transformer.py +449 -0
- panelbox/standard_errors/__init__.py +0 -0
- panelbox/templates/__init__.py +0 -0
- panelbox/templates/assets/css/base_styles.css +382 -0
- panelbox/templates/assets/css/report_components.css +747 -0
- panelbox/templates/assets/js/tab-navigation.js +161 -0
- panelbox/templates/assets/js/utils.js +276 -0
- panelbox/templates/common/footer.html +24 -0
- panelbox/templates/common/header.html +44 -0
- panelbox/templates/common/meta.html +5 -0
- panelbox/templates/validation/interactive/index.html +272 -0
- panelbox/templates/validation/interactive/partials/charts.html +58 -0
- panelbox/templates/validation/interactive/partials/methodology.html +201 -0
- panelbox/templates/validation/interactive/partials/overview.html +146 -0
- panelbox/templates/validation/interactive/partials/recommendations.html +101 -0
- panelbox/templates/validation/interactive/partials/test_results.html +231 -0
- panelbox/utils/__init__.py +0 -0
- panelbox/utils/formatting.py +172 -0
- panelbox/utils/matrix_ops.py +233 -0
- panelbox/utils/statistical.py +173 -0
- panelbox/validation/__init__.py +58 -0
- panelbox/validation/base.py +175 -0
- panelbox/validation/cointegration/__init__.py +0 -0
- panelbox/validation/cross_sectional_dependence/__init__.py +13 -0
- panelbox/validation/cross_sectional_dependence/breusch_pagan_lm.py +222 -0
- panelbox/validation/cross_sectional_dependence/frees.py +297 -0
- panelbox/validation/cross_sectional_dependence/pesaran_cd.py +188 -0
- panelbox/validation/heteroskedasticity/__init__.py +13 -0
- panelbox/validation/heteroskedasticity/breusch_pagan.py +222 -0
- panelbox/validation/heteroskedasticity/modified_wald.py +172 -0
- panelbox/validation/heteroskedasticity/white.py +208 -0
- panelbox/validation/instruments/__init__.py +0 -0
- panelbox/validation/robustness/__init__.py +0 -0
- panelbox/validation/serial_correlation/__init__.py +13 -0
- panelbox/validation/serial_correlation/baltagi_wu.py +220 -0
- panelbox/validation/serial_correlation/breusch_godfrey.py +260 -0
- panelbox/validation/serial_correlation/wooldridge_ar.py +200 -0
- panelbox/validation/specification/__init__.py +16 -0
- panelbox/validation/specification/chow.py +273 -0
- panelbox/validation/specification/hausman.py +264 -0
- panelbox/validation/specification/mundlak.py +331 -0
- panelbox/validation/specification/reset.py +273 -0
- panelbox/validation/unit_root/__init__.py +0 -0
- panelbox/validation/validation_report.py +257 -0
- panelbox/validation/validation_suite.py +401 -0
- panelbox-0.2.0.dist-info/METADATA +337 -0
- panelbox-0.2.0.dist-info/RECORD +90 -0
- panelbox-0.2.0.dist-info/WHEEL +5 -0
- panelbox-0.2.0.dist-info/entry_points.txt +2 -0
- panelbox-0.2.0.dist-info/licenses/LICENSE +21 -0
- panelbox-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTML Exporter for PanelBox Reports.
|
|
3
|
+
|
|
4
|
+
Exports reports to self-contained HTML files.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Dict, Any, Optional, Union
|
|
10
|
+
import datetime
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HTMLExporter:
|
|
14
|
+
"""
|
|
15
|
+
Exports PanelBox reports to HTML format.
|
|
16
|
+
|
|
17
|
+
Creates self-contained HTML files with all assets embedded inline.
|
|
18
|
+
Suitable for sharing via email or hosting on static servers.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
minify : bool, default=False
|
|
23
|
+
Minify CSS and JavaScript for smaller file size
|
|
24
|
+
pretty_print : bool, default=False
|
|
25
|
+
Pretty-print HTML output for readability
|
|
26
|
+
|
|
27
|
+
Examples
|
|
28
|
+
--------
|
|
29
|
+
>>> from panelbox.report import ReportManager
|
|
30
|
+
>>> from panelbox.report.exporters import HTMLExporter
|
|
31
|
+
>>>
|
|
32
|
+
>>> report_mgr = ReportManager()
|
|
33
|
+
>>> html = report_mgr.generate_validation_report(...)
|
|
34
|
+
>>>
|
|
35
|
+
>>> exporter = HTMLExporter()
|
|
36
|
+
>>> exporter.export(html, 'report.html')
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
minify: bool = False,
|
|
42
|
+
pretty_print: bool = False
|
|
43
|
+
):
|
|
44
|
+
"""Initialize HTML Exporter."""
|
|
45
|
+
self.minify = minify
|
|
46
|
+
self.pretty_print = pretty_print
|
|
47
|
+
|
|
48
|
+
def export(
|
|
49
|
+
self,
|
|
50
|
+
html_content: str,
|
|
51
|
+
output_path: Union[str, Path],
|
|
52
|
+
overwrite: bool = False,
|
|
53
|
+
add_metadata: bool = True
|
|
54
|
+
) -> Path:
|
|
55
|
+
"""
|
|
56
|
+
Export HTML content to file.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
html_content : str
|
|
61
|
+
HTML content to export
|
|
62
|
+
output_path : str or Path
|
|
63
|
+
Output file path
|
|
64
|
+
overwrite : bool, default=False
|
|
65
|
+
Overwrite existing file
|
|
66
|
+
add_metadata : bool, default=True
|
|
67
|
+
Add export metadata as HTML comment
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
Path
|
|
72
|
+
Path to exported file
|
|
73
|
+
|
|
74
|
+
Examples
|
|
75
|
+
--------
|
|
76
|
+
>>> exporter = HTMLExporter()
|
|
77
|
+
>>> path = exporter.export(html, 'report.html')
|
|
78
|
+
>>> print(f"Exported to {path}")
|
|
79
|
+
"""
|
|
80
|
+
output_path = Path(output_path)
|
|
81
|
+
|
|
82
|
+
# Check if file exists
|
|
83
|
+
if output_path.exists() and not overwrite:
|
|
84
|
+
raise FileExistsError(
|
|
85
|
+
f"File already exists: {output_path}. "
|
|
86
|
+
"Use overwrite=True to replace."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Create parent directories
|
|
90
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
|
|
92
|
+
# Process HTML
|
|
93
|
+
if add_metadata:
|
|
94
|
+
html_content = self._add_metadata(html_content)
|
|
95
|
+
|
|
96
|
+
if self.pretty_print:
|
|
97
|
+
html_content = self._pretty_print_html(html_content)
|
|
98
|
+
|
|
99
|
+
# Write file
|
|
100
|
+
output_path.write_text(html_content, encoding='utf-8')
|
|
101
|
+
|
|
102
|
+
return output_path
|
|
103
|
+
|
|
104
|
+
def export_multiple(
|
|
105
|
+
self,
|
|
106
|
+
reports: Dict[str, str],
|
|
107
|
+
output_dir: Union[str, Path],
|
|
108
|
+
overwrite: bool = False
|
|
109
|
+
) -> Dict[str, Path]:
|
|
110
|
+
"""
|
|
111
|
+
Export multiple HTML reports to directory.
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
----------
|
|
115
|
+
reports : dict
|
|
116
|
+
Dictionary mapping filenames to HTML content
|
|
117
|
+
output_dir : str or Path
|
|
118
|
+
Output directory
|
|
119
|
+
overwrite : bool, default=False
|
|
120
|
+
Overwrite existing files
|
|
121
|
+
|
|
122
|
+
Returns
|
|
123
|
+
-------
|
|
124
|
+
dict
|
|
125
|
+
Dictionary mapping filenames to exported paths
|
|
126
|
+
|
|
127
|
+
Examples
|
|
128
|
+
--------
|
|
129
|
+
>>> reports = {
|
|
130
|
+
... 'validation.html': validation_html,
|
|
131
|
+
... 'regression.html': regression_html
|
|
132
|
+
... }
|
|
133
|
+
>>> paths = exporter.export_multiple(reports, 'output/')
|
|
134
|
+
"""
|
|
135
|
+
output_dir = Path(output_dir)
|
|
136
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
137
|
+
|
|
138
|
+
exported = {}
|
|
139
|
+
|
|
140
|
+
for filename, html_content in reports.items():
|
|
141
|
+
output_path = output_dir / filename
|
|
142
|
+
exported[filename] = self.export(
|
|
143
|
+
html_content,
|
|
144
|
+
output_path,
|
|
145
|
+
overwrite=overwrite
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return exported
|
|
149
|
+
|
|
150
|
+
def export_with_index(
|
|
151
|
+
self,
|
|
152
|
+
reports: Dict[str, str],
|
|
153
|
+
output_dir: Union[str, Path],
|
|
154
|
+
index_title: str = "PanelBox Reports",
|
|
155
|
+
overwrite: bool = False
|
|
156
|
+
) -> Dict[str, Path]:
|
|
157
|
+
"""
|
|
158
|
+
Export multiple reports with an index page.
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
reports : dict
|
|
163
|
+
Dictionary mapping report names to HTML content
|
|
164
|
+
output_dir : str or Path
|
|
165
|
+
Output directory
|
|
166
|
+
index_title : str, default="PanelBox Reports"
|
|
167
|
+
Title for index page
|
|
168
|
+
overwrite : bool, default=False
|
|
169
|
+
Overwrite existing files
|
|
170
|
+
|
|
171
|
+
Returns
|
|
172
|
+
-------
|
|
173
|
+
dict
|
|
174
|
+
Dictionary mapping filenames to exported paths
|
|
175
|
+
|
|
176
|
+
Examples
|
|
177
|
+
--------
|
|
178
|
+
>>> reports = {
|
|
179
|
+
... 'Validation Report': validation_html,
|
|
180
|
+
... 'Regression Results': regression_html
|
|
181
|
+
... }
|
|
182
|
+
>>> paths = exporter.export_with_index(reports, 'reports/')
|
|
183
|
+
"""
|
|
184
|
+
output_dir = Path(output_dir)
|
|
185
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
186
|
+
|
|
187
|
+
# Export individual reports
|
|
188
|
+
exported = {}
|
|
189
|
+
|
|
190
|
+
for i, (name, html_content) in enumerate(reports.items()):
|
|
191
|
+
filename = f"report_{i+1}.html"
|
|
192
|
+
output_path = output_dir / filename
|
|
193
|
+
exported[name] = self.export(
|
|
194
|
+
html_content,
|
|
195
|
+
output_path,
|
|
196
|
+
overwrite=overwrite
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Create index page
|
|
200
|
+
index_html = self._generate_index_page(
|
|
201
|
+
reports=list(reports.keys()),
|
|
202
|
+
title=index_title
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
index_path = output_dir / "index.html"
|
|
206
|
+
index_path.write_text(index_html, encoding='utf-8')
|
|
207
|
+
exported['_index'] = index_path
|
|
208
|
+
|
|
209
|
+
return exported
|
|
210
|
+
|
|
211
|
+
def _add_metadata(self, html: str) -> str:
|
|
212
|
+
"""
|
|
213
|
+
Add export metadata as HTML comment.
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
html : str
|
|
218
|
+
HTML content
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
str
|
|
223
|
+
HTML with metadata comment
|
|
224
|
+
"""
|
|
225
|
+
timestamp = datetime.datetime.now().isoformat()
|
|
226
|
+
|
|
227
|
+
metadata = f"""
|
|
228
|
+
<!--
|
|
229
|
+
PanelBox HTML Export
|
|
230
|
+
Exported: {timestamp}
|
|
231
|
+
Format: Self-contained HTML
|
|
232
|
+
Minified: {self.minify}
|
|
233
|
+
-->
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
# Insert after <!DOCTYPE html> or at beginning
|
|
237
|
+
if '<!DOCTYPE' in html:
|
|
238
|
+
parts = html.split('>', 1)
|
|
239
|
+
return parts[0] + '>' + metadata + parts[1]
|
|
240
|
+
else:
|
|
241
|
+
return metadata + html
|
|
242
|
+
|
|
243
|
+
def _pretty_print_html(self, html: str) -> str:
|
|
244
|
+
"""
|
|
245
|
+
Pretty-print HTML (basic formatting).
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
html : str
|
|
250
|
+
HTML content
|
|
251
|
+
|
|
252
|
+
Returns
|
|
253
|
+
-------
|
|
254
|
+
str
|
|
255
|
+
Formatted HTML
|
|
256
|
+
|
|
257
|
+
Note
|
|
258
|
+
----
|
|
259
|
+
This is a basic formatter. For production use, consider
|
|
260
|
+
using BeautifulSoup or lxml for proper HTML formatting.
|
|
261
|
+
"""
|
|
262
|
+
# Basic indentation - this is a simplified approach
|
|
263
|
+
# In production, you'd use BeautifulSoup or similar
|
|
264
|
+
return html
|
|
265
|
+
|
|
266
|
+
def _generate_index_page(
|
|
267
|
+
self,
|
|
268
|
+
reports: list,
|
|
269
|
+
title: str
|
|
270
|
+
) -> str:
|
|
271
|
+
"""
|
|
272
|
+
Generate HTML index page.
|
|
273
|
+
|
|
274
|
+
Parameters
|
|
275
|
+
----------
|
|
276
|
+
reports : list
|
|
277
|
+
List of report names
|
|
278
|
+
title : str
|
|
279
|
+
Page title
|
|
280
|
+
|
|
281
|
+
Returns
|
|
282
|
+
-------
|
|
283
|
+
str
|
|
284
|
+
HTML content for index page
|
|
285
|
+
"""
|
|
286
|
+
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
287
|
+
|
|
288
|
+
report_links = []
|
|
289
|
+
for i, name in enumerate(reports):
|
|
290
|
+
filename = f"report_{i+1}.html"
|
|
291
|
+
report_links.append(
|
|
292
|
+
f'<li><a href="{filename}">{name}</a></li>'
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
links_html = '\n '.join(report_links)
|
|
296
|
+
|
|
297
|
+
html = f"""<!DOCTYPE html>
|
|
298
|
+
<html lang="en">
|
|
299
|
+
<head>
|
|
300
|
+
<meta charset="UTF-8">
|
|
301
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
302
|
+
<title>{title}</title>
|
|
303
|
+
<style>
|
|
304
|
+
* {{
|
|
305
|
+
margin: 0;
|
|
306
|
+
padding: 0;
|
|
307
|
+
box-sizing: border-box;
|
|
308
|
+
}}
|
|
309
|
+
|
|
310
|
+
body {{
|
|
311
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
312
|
+
line-height: 1.6;
|
|
313
|
+
color: #333;
|
|
314
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
315
|
+
min-height: 100vh;
|
|
316
|
+
display: flex;
|
|
317
|
+
align-items: center;
|
|
318
|
+
justify-content: center;
|
|
319
|
+
padding: 2rem;
|
|
320
|
+
}}
|
|
321
|
+
|
|
322
|
+
.container {{
|
|
323
|
+
background: white;
|
|
324
|
+
border-radius: 12px;
|
|
325
|
+
padding: 3rem;
|
|
326
|
+
max-width: 800px;
|
|
327
|
+
width: 100%;
|
|
328
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
329
|
+
}}
|
|
330
|
+
|
|
331
|
+
h1 {{
|
|
332
|
+
font-size: 2.5rem;
|
|
333
|
+
color: #667eea;
|
|
334
|
+
margin-bottom: 0.5rem;
|
|
335
|
+
}}
|
|
336
|
+
|
|
337
|
+
.subtitle {{
|
|
338
|
+
color: #666;
|
|
339
|
+
margin-bottom: 2rem;
|
|
340
|
+
font-size: 0.95rem;
|
|
341
|
+
}}
|
|
342
|
+
|
|
343
|
+
.report-list {{
|
|
344
|
+
list-style: none;
|
|
345
|
+
margin: 2rem 0;
|
|
346
|
+
}}
|
|
347
|
+
|
|
348
|
+
.report-list li {{
|
|
349
|
+
margin-bottom: 1rem;
|
|
350
|
+
}}
|
|
351
|
+
|
|
352
|
+
.report-list a {{
|
|
353
|
+
display: block;
|
|
354
|
+
padding: 1rem 1.5rem;
|
|
355
|
+
background: #f8f9fa;
|
|
356
|
+
color: #667eea;
|
|
357
|
+
text-decoration: none;
|
|
358
|
+
border-radius: 8px;
|
|
359
|
+
border: 2px solid transparent;
|
|
360
|
+
transition: all 0.2s;
|
|
361
|
+
font-weight: 500;
|
|
362
|
+
}}
|
|
363
|
+
|
|
364
|
+
.report-list a:hover {{
|
|
365
|
+
background: #667eea;
|
|
366
|
+
color: white;
|
|
367
|
+
border-color: #667eea;
|
|
368
|
+
transform: translateX(5px);
|
|
369
|
+
}}
|
|
370
|
+
|
|
371
|
+
.footer {{
|
|
372
|
+
margin-top: 3rem;
|
|
373
|
+
padding-top: 2rem;
|
|
374
|
+
border-top: 1px solid #e0e0e0;
|
|
375
|
+
text-align: center;
|
|
376
|
+
color: #666;
|
|
377
|
+
font-size: 0.9rem;
|
|
378
|
+
}}
|
|
379
|
+
|
|
380
|
+
.footer strong {{
|
|
381
|
+
color: #667eea;
|
|
382
|
+
}}
|
|
383
|
+
</style>
|
|
384
|
+
</head>
|
|
385
|
+
<body>
|
|
386
|
+
<div class="container">
|
|
387
|
+
<h1>{title}</h1>
|
|
388
|
+
<p class="subtitle">Generated: {timestamp}</p>
|
|
389
|
+
|
|
390
|
+
<p>Select a report to view:</p>
|
|
391
|
+
|
|
392
|
+
<ul class="report-list">
|
|
393
|
+
{links_html}
|
|
394
|
+
</ul>
|
|
395
|
+
|
|
396
|
+
<div class="footer">
|
|
397
|
+
<p>Generated with <strong>PanelBox</strong></p>
|
|
398
|
+
<p><a href="https://github.com/panelbox/panelbox" target="_blank" style="color: #667eea;">Documentation</a></p>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
</body>
|
|
402
|
+
</html>
|
|
403
|
+
"""
|
|
404
|
+
|
|
405
|
+
return html
|
|
406
|
+
|
|
407
|
+
def get_file_size(self, html_content: str) -> Dict[str, float]:
|
|
408
|
+
"""
|
|
409
|
+
Estimate file size of HTML content.
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
html_content : str
|
|
414
|
+
HTML content
|
|
415
|
+
|
|
416
|
+
Returns
|
|
417
|
+
-------
|
|
418
|
+
dict
|
|
419
|
+
Dictionary with size estimates
|
|
420
|
+
|
|
421
|
+
Examples
|
|
422
|
+
--------
|
|
423
|
+
>>> sizes = exporter.get_file_size(html)
|
|
424
|
+
>>> print(f"Size: {sizes['kb']:.1f} KB")
|
|
425
|
+
"""
|
|
426
|
+
size_bytes = len(html_content.encode('utf-8'))
|
|
427
|
+
|
|
428
|
+
return {
|
|
429
|
+
'bytes': size_bytes,
|
|
430
|
+
'kb': size_bytes / 1024,
|
|
431
|
+
'mb': size_bytes / (1024 * 1024)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
def __repr__(self) -> str:
|
|
435
|
+
"""String representation."""
|
|
436
|
+
return (
|
|
437
|
+
f"HTMLExporter("
|
|
438
|
+
f"minify={self.minify}, "
|
|
439
|
+
f"pretty_print={self.pretty_print})"
|
|
440
|
+
)
|