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,410 @@
1
+ """
2
+ Asset Manager for PanelBox Reports.
3
+
4
+ Manages collection, minification, and embedding of assets (CSS, JS, images).
5
+ """
6
+
7
+ import base64
8
+ from pathlib import Path
9
+ from typing import Dict, List, Optional
10
+ import mimetypes
11
+
12
+
13
+ class AssetManager:
14
+ """
15
+ Manages assets for report generation.
16
+
17
+ Handles CSS, JavaScript, and image files. Can embed assets
18
+ inline for self-contained HTML reports.
19
+
20
+ Parameters
21
+ ----------
22
+ asset_dir : str or Path, optional
23
+ Directory containing assets. If None, uses package assets.
24
+ minify : bool, default=False
25
+ Enable minification of CSS/JS (requires additional packages)
26
+
27
+ Attributes
28
+ ----------
29
+ asset_dir : Path
30
+ Directory containing assets
31
+ minify : bool
32
+ Whether to minify assets
33
+ asset_cache : dict
34
+ Cache for loaded assets
35
+
36
+ Examples
37
+ --------
38
+ >>> manager = AssetManager()
39
+ >>> css = manager.get_css('base_styles.css')
40
+ >>> js = manager.get_js('tab-navigation.js')
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ asset_dir: Optional[Path] = None,
46
+ minify: bool = False
47
+ ):
48
+ """Initialize Asset Manager."""
49
+ # Determine asset directory
50
+ if asset_dir is None:
51
+ # Use package assets
52
+ package_dir = Path(__file__).parent.parent
53
+ asset_dir = package_dir / 'templates' / 'assets'
54
+ else:
55
+ asset_dir = Path(asset_dir)
56
+
57
+ if not asset_dir.exists():
58
+ raise ValueError(f"Asset directory does not exist: {asset_dir}")
59
+
60
+ self.asset_dir = asset_dir
61
+ self.minify = minify
62
+ self.asset_cache: Dict[str, str] = {}
63
+
64
+ def get_css(self, css_path: str) -> str:
65
+ """
66
+ Load CSS file content.
67
+
68
+ Parameters
69
+ ----------
70
+ css_path : str
71
+ Relative path to CSS file from assets/css directory.
72
+ Example: 'base_styles.css' or 'components/buttons.css'
73
+
74
+ Returns
75
+ -------
76
+ str
77
+ CSS content
78
+
79
+ Examples
80
+ --------
81
+ >>> css = manager.get_css('base_styles.css')
82
+ >>> css = manager.get_css('components/buttons.css')
83
+ """
84
+ cache_key = f"css:{css_path}"
85
+
86
+ if cache_key in self.asset_cache:
87
+ return self.asset_cache[cache_key]
88
+
89
+ css_file = self.asset_dir / 'css' / css_path
90
+
91
+ if not css_file.exists():
92
+ raise FileNotFoundError(f"CSS file not found: {css_file}")
93
+
94
+ content = css_file.read_text(encoding='utf-8')
95
+
96
+ if self.minify:
97
+ content = self._minify_css(content)
98
+
99
+ self.asset_cache[cache_key] = content
100
+ return content
101
+
102
+ def get_js(self, js_path: str) -> str:
103
+ """
104
+ Load JavaScript file content.
105
+
106
+ Parameters
107
+ ----------
108
+ js_path : str
109
+ Relative path to JS file from assets/js directory.
110
+ Example: 'tab-navigation.js' or 'components/charts.js'
111
+
112
+ Returns
113
+ -------
114
+ str
115
+ JavaScript content
116
+
117
+ Examples
118
+ --------
119
+ >>> js = manager.get_js('tab-navigation.js')
120
+ >>> js = manager.get_js('components/charts.js')
121
+ """
122
+ cache_key = f"js:{js_path}"
123
+
124
+ if cache_key in self.asset_cache:
125
+ return self.asset_cache[cache_key]
126
+
127
+ js_file = self.asset_dir / 'js' / js_path
128
+
129
+ if not js_file.exists():
130
+ raise FileNotFoundError(f"JavaScript file not found: {js_file}")
131
+
132
+ content = js_file.read_text(encoding='utf-8')
133
+
134
+ if self.minify:
135
+ content = self._minify_js(content)
136
+
137
+ self.asset_cache[cache_key] = content
138
+ return content
139
+
140
+ def get_image_base64(self, image_path: str) -> str:
141
+ """
142
+ Load image and encode as base64 data URI.
143
+
144
+ Parameters
145
+ ----------
146
+ image_path : str
147
+ Relative path to image from assets directory
148
+
149
+ Returns
150
+ -------
151
+ str
152
+ Base64-encoded data URI
153
+
154
+ Examples
155
+ --------
156
+ >>> data_uri = manager.get_image_base64('images/logo.png')
157
+ >>> # Returns: 'data:image/png;base64,iVBORw0KG...'
158
+ """
159
+ cache_key = f"img:{image_path}"
160
+
161
+ if cache_key in self.asset_cache:
162
+ return self.asset_cache[cache_key]
163
+
164
+ img_file = self.asset_dir / image_path
165
+
166
+ if not img_file.exists():
167
+ raise FileNotFoundError(f"Image file not found: {img_file}")
168
+
169
+ # Read binary data
170
+ img_data = img_file.read_bytes()
171
+
172
+ # Encode as base64
173
+ b64_data = base64.b64encode(img_data).decode('utf-8')
174
+
175
+ # Determine MIME type
176
+ mime_type, _ = mimetypes.guess_type(str(img_file))
177
+ if mime_type is None:
178
+ mime_type = 'application/octet-stream'
179
+
180
+ # Create data URI
181
+ data_uri = f"data:{mime_type};base64,{b64_data}"
182
+
183
+ self.asset_cache[cache_key] = data_uri
184
+ return data_uri
185
+
186
+ def collect_css(self, css_files: List[str]) -> str:
187
+ """
188
+ Collect multiple CSS files into one string.
189
+
190
+ Parameters
191
+ ----------
192
+ css_files : list of str
193
+ List of CSS file paths to collect
194
+
195
+ Returns
196
+ -------
197
+ str
198
+ Combined CSS content
199
+
200
+ Examples
201
+ --------
202
+ >>> css = manager.collect_css([
203
+ ... 'base_styles.css',
204
+ ... 'report_components.css',
205
+ ... 'components/tables.css'
206
+ ... ])
207
+ """
208
+ css_parts = []
209
+
210
+ for css_file in css_files:
211
+ try:
212
+ content = self.get_css(css_file)
213
+ css_parts.append(f"/* ========== {css_file} ========== */\n")
214
+ css_parts.append(content)
215
+ css_parts.append("\n\n")
216
+ except FileNotFoundError as e:
217
+ print(f"Warning: {e}")
218
+ continue
219
+
220
+ return "".join(css_parts)
221
+
222
+ def collect_js(self, js_files: List[str]) -> str:
223
+ """
224
+ Collect multiple JavaScript files into one string.
225
+
226
+ Parameters
227
+ ----------
228
+ js_files : list of str
229
+ List of JS file paths to collect
230
+
231
+ Returns
232
+ -------
233
+ str
234
+ Combined JavaScript content
235
+
236
+ Examples
237
+ --------
238
+ >>> js = manager.collect_js([
239
+ ... 'utils.js',
240
+ ... 'tab-navigation.js'
241
+ ... ])
242
+ """
243
+ js_parts = []
244
+
245
+ for js_file in js_files:
246
+ try:
247
+ content = self.get_js(js_file)
248
+ js_parts.append(f"/* ========== {js_file} ========== */\n")
249
+ js_parts.append(content)
250
+ js_parts.append("\n\n")
251
+ except FileNotFoundError as e:
252
+ print(f"Warning: {e}")
253
+ continue
254
+
255
+ return "".join(js_parts)
256
+
257
+ def embed_plotly(self, include_plotly: bool = True) -> str:
258
+ """
259
+ Get Plotly.js library for embedding.
260
+
261
+ Parameters
262
+ ----------
263
+ include_plotly : bool, default=True
264
+ Whether to include full Plotly library
265
+
266
+ Returns
267
+ -------
268
+ str
269
+ Plotly.js library content or CDN link
270
+
271
+ Examples
272
+ --------
273
+ >>> plotly_js = manager.embed_plotly()
274
+ """
275
+ if not include_plotly:
276
+ return ""
277
+
278
+ # Use CDN link for now
279
+ # In production, you might want to embed the full library
280
+ return (
281
+ '<script src="https://cdn.plot.ly/plotly-2.27.0.min.js" '
282
+ 'charset="utf-8"></script>'
283
+ )
284
+
285
+ def _minify_css(self, css: str) -> str:
286
+ """
287
+ Minify CSS content.
288
+
289
+ Basic minification without external dependencies.
290
+ For production, consider using cssmin package.
291
+
292
+ Parameters
293
+ ----------
294
+ css : str
295
+ CSS content to minify
296
+
297
+ Returns
298
+ -------
299
+ str
300
+ Minified CSS
301
+ """
302
+ if not self.minify:
303
+ return css
304
+
305
+ # Basic minification
306
+ # Remove comments
307
+ import re
308
+ css = re.sub(r'/\*.*?\*/', '', css, flags=re.DOTALL)
309
+
310
+ # Remove extra whitespace
311
+ css = re.sub(r'\s+', ' ', css)
312
+ css = re.sub(r'\s*([{}:;,])\s*', r'\1', css)
313
+
314
+ return css.strip()
315
+
316
+ def _minify_js(self, js: str) -> str:
317
+ """
318
+ Minify JavaScript content.
319
+
320
+ Basic minification without external dependencies.
321
+ For production, consider using jsmin package.
322
+
323
+ Parameters
324
+ ----------
325
+ js : str
326
+ JavaScript content to minify
327
+
328
+ Returns
329
+ -------
330
+ str
331
+ Minified JavaScript
332
+ """
333
+ if not self.minify:
334
+ return js
335
+
336
+ # Basic minification
337
+ # Remove comments (simple approach)
338
+ import re
339
+ js = re.sub(r'//.*?$', '', js, flags=re.MULTILINE)
340
+ js = re.sub(r'/\*.*?\*/', '', js, flags=re.DOTALL)
341
+
342
+ # Remove extra whitespace (but preserve newlines in strings)
343
+ # This is a very basic approach
344
+ js = re.sub(r'\s+', ' ', js)
345
+
346
+ return js.strip()
347
+
348
+ def clear_cache(self) -> None:
349
+ """Clear asset cache."""
350
+ self.asset_cache.clear()
351
+
352
+ def list_assets(self, asset_type: str = "all") -> Dict[str, List[str]]:
353
+ """
354
+ List available assets.
355
+
356
+ Parameters
357
+ ----------
358
+ asset_type : str, default="all"
359
+ Type of assets to list: "css", "js", "images", or "all"
360
+
361
+ Returns
362
+ -------
363
+ dict
364
+ Dictionary mapping asset type to list of paths
365
+
366
+ Examples
367
+ --------
368
+ >>> assets = manager.list_assets()
369
+ >>> print(assets['css'])
370
+ ['base_styles.css', 'report_components.css', ...]
371
+ """
372
+ assets = {}
373
+
374
+ if asset_type in ("css", "all"):
375
+ css_dir = self.asset_dir / 'css'
376
+ if css_dir.exists():
377
+ css_files = [
378
+ str(p.relative_to(css_dir))
379
+ for p in css_dir.rglob("*.css")
380
+ ]
381
+ assets['css'] = sorted(css_files)
382
+
383
+ if asset_type in ("js", "all"):
384
+ js_dir = self.asset_dir / 'js'
385
+ if js_dir.exists():
386
+ js_files = [
387
+ str(p.relative_to(js_dir))
388
+ for p in js_dir.rglob("*.js")
389
+ ]
390
+ assets['js'] = sorted(js_files)
391
+
392
+ if asset_type in ("images", "all"):
393
+ img_dir = self.asset_dir / 'images'
394
+ if img_dir.exists():
395
+ img_files = [
396
+ str(p.relative_to(self.asset_dir))
397
+ for p in img_dir.rglob("*")
398
+ if p.is_file()
399
+ ]
400
+ assets['images'] = sorted(img_files)
401
+
402
+ return assets
403
+
404
+ def __repr__(self) -> str:
405
+ """String representation."""
406
+ return (
407
+ f"AssetManager(asset_dir={self.asset_dir}, "
408
+ f"minify={self.minify}, "
409
+ f"cached_assets={len(self.asset_cache)})"
410
+ )