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.
Files changed (90) hide show
  1. panelbox/__init__.py +67 -0
  2. panelbox/__version__.py +14 -0
  3. panelbox/cli/__init__.py +0 -0
  4. panelbox/cli/{commands}/__init__.py +0 -0
  5. panelbox/core/__init__.py +0 -0
  6. panelbox/core/base_model.py +164 -0
  7. panelbox/core/formula_parser.py +318 -0
  8. panelbox/core/panel_data.py +387 -0
  9. panelbox/core/results.py +366 -0
  10. panelbox/datasets/__init__.py +0 -0
  11. panelbox/datasets/{data}/__init__.py +0 -0
  12. panelbox/gmm/__init__.py +65 -0
  13. panelbox/gmm/difference_gmm.py +645 -0
  14. panelbox/gmm/estimator.py +562 -0
  15. panelbox/gmm/instruments.py +580 -0
  16. panelbox/gmm/results.py +550 -0
  17. panelbox/gmm/system_gmm.py +621 -0
  18. panelbox/gmm/tests.py +535 -0
  19. panelbox/models/__init__.py +11 -0
  20. panelbox/models/dynamic/__init__.py +0 -0
  21. panelbox/models/iv/__init__.py +0 -0
  22. panelbox/models/static/__init__.py +13 -0
  23. panelbox/models/static/fixed_effects.py +516 -0
  24. panelbox/models/static/pooled_ols.py +298 -0
  25. panelbox/models/static/random_effects.py +512 -0
  26. panelbox/report/__init__.py +61 -0
  27. panelbox/report/asset_manager.py +410 -0
  28. panelbox/report/css_manager.py +472 -0
  29. panelbox/report/exporters/__init__.py +15 -0
  30. panelbox/report/exporters/html_exporter.py +440 -0
  31. panelbox/report/exporters/latex_exporter.py +510 -0
  32. panelbox/report/exporters/markdown_exporter.py +446 -0
  33. panelbox/report/renderers/__init__.py +11 -0
  34. panelbox/report/renderers/static/__init__.py +0 -0
  35. panelbox/report/renderers/static_validation_renderer.py +341 -0
  36. panelbox/report/report_manager.py +502 -0
  37. panelbox/report/template_manager.py +337 -0
  38. panelbox/report/transformers/__init__.py +0 -0
  39. panelbox/report/transformers/static/__init__.py +0 -0
  40. panelbox/report/validation_transformer.py +449 -0
  41. panelbox/standard_errors/__init__.py +0 -0
  42. panelbox/templates/__init__.py +0 -0
  43. panelbox/templates/assets/css/base_styles.css +382 -0
  44. panelbox/templates/assets/css/report_components.css +747 -0
  45. panelbox/templates/assets/js/tab-navigation.js +161 -0
  46. panelbox/templates/assets/js/utils.js +276 -0
  47. panelbox/templates/common/footer.html +24 -0
  48. panelbox/templates/common/header.html +44 -0
  49. panelbox/templates/common/meta.html +5 -0
  50. panelbox/templates/validation/interactive/index.html +272 -0
  51. panelbox/templates/validation/interactive/partials/charts.html +58 -0
  52. panelbox/templates/validation/interactive/partials/methodology.html +201 -0
  53. panelbox/templates/validation/interactive/partials/overview.html +146 -0
  54. panelbox/templates/validation/interactive/partials/recommendations.html +101 -0
  55. panelbox/templates/validation/interactive/partials/test_results.html +231 -0
  56. panelbox/utils/__init__.py +0 -0
  57. panelbox/utils/formatting.py +172 -0
  58. panelbox/utils/matrix_ops.py +233 -0
  59. panelbox/utils/statistical.py +173 -0
  60. panelbox/validation/__init__.py +58 -0
  61. panelbox/validation/base.py +175 -0
  62. panelbox/validation/cointegration/__init__.py +0 -0
  63. panelbox/validation/cross_sectional_dependence/__init__.py +13 -0
  64. panelbox/validation/cross_sectional_dependence/breusch_pagan_lm.py +222 -0
  65. panelbox/validation/cross_sectional_dependence/frees.py +297 -0
  66. panelbox/validation/cross_sectional_dependence/pesaran_cd.py +188 -0
  67. panelbox/validation/heteroskedasticity/__init__.py +13 -0
  68. panelbox/validation/heteroskedasticity/breusch_pagan.py +222 -0
  69. panelbox/validation/heteroskedasticity/modified_wald.py +172 -0
  70. panelbox/validation/heteroskedasticity/white.py +208 -0
  71. panelbox/validation/instruments/__init__.py +0 -0
  72. panelbox/validation/robustness/__init__.py +0 -0
  73. panelbox/validation/serial_correlation/__init__.py +13 -0
  74. panelbox/validation/serial_correlation/baltagi_wu.py +220 -0
  75. panelbox/validation/serial_correlation/breusch_godfrey.py +260 -0
  76. panelbox/validation/serial_correlation/wooldridge_ar.py +200 -0
  77. panelbox/validation/specification/__init__.py +16 -0
  78. panelbox/validation/specification/chow.py +273 -0
  79. panelbox/validation/specification/hausman.py +264 -0
  80. panelbox/validation/specification/mundlak.py +331 -0
  81. panelbox/validation/specification/reset.py +273 -0
  82. panelbox/validation/unit_root/__init__.py +0 -0
  83. panelbox/validation/validation_report.py +257 -0
  84. panelbox/validation/validation_suite.py +401 -0
  85. panelbox-0.2.0.dist-info/METADATA +337 -0
  86. panelbox-0.2.0.dist-info/RECORD +90 -0
  87. panelbox-0.2.0.dist-info/WHEEL +5 -0
  88. panelbox-0.2.0.dist-info/entry_points.txt +2 -0
  89. panelbox-0.2.0.dist-info/licenses/LICENSE +21 -0
  90. 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
+ )