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,472 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CSS Manager for PanelBox Reports.
|
|
3
|
+
|
|
4
|
+
Manages compilation and layering of CSS styles with 3-layer architecture.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Dict, List, Optional, Set
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
from .asset_manager import AssetManager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class CSSLayer:
|
|
16
|
+
"""
|
|
17
|
+
Represents a CSS layer in the compilation pipeline.
|
|
18
|
+
|
|
19
|
+
Attributes
|
|
20
|
+
----------
|
|
21
|
+
name : str
|
|
22
|
+
Layer name (e.g., 'base', 'components', 'custom')
|
|
23
|
+
files : list of str
|
|
24
|
+
CSS files to include in this layer
|
|
25
|
+
priority : int
|
|
26
|
+
Layer priority (lower = earlier in output)
|
|
27
|
+
"""
|
|
28
|
+
name: str
|
|
29
|
+
files: List[str]
|
|
30
|
+
priority: int = 0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CSSManager:
|
|
34
|
+
"""
|
|
35
|
+
Manages CSS compilation with 3-layer architecture.
|
|
36
|
+
|
|
37
|
+
Implements a layered CSS system inspired by modern design systems:
|
|
38
|
+
- Layer 1 (Base): Design tokens, reset, utilities
|
|
39
|
+
- Layer 2 (Components): Reusable UI components
|
|
40
|
+
- Layer 3 (Custom): Report-specific overrides
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
asset_manager : AssetManager, optional
|
|
45
|
+
Asset manager for loading CSS files. If None, creates default.
|
|
46
|
+
minify : bool, default=False
|
|
47
|
+
Enable CSS minification
|
|
48
|
+
|
|
49
|
+
Attributes
|
|
50
|
+
----------
|
|
51
|
+
asset_manager : AssetManager
|
|
52
|
+
Asset manager instance
|
|
53
|
+
layers : dict
|
|
54
|
+
Registered CSS layers
|
|
55
|
+
minify : bool
|
|
56
|
+
Whether to minify output
|
|
57
|
+
|
|
58
|
+
Examples
|
|
59
|
+
--------
|
|
60
|
+
>>> css_mgr = CSSManager(minify=False)
|
|
61
|
+
>>> css = css_mgr.compile()
|
|
62
|
+
>>> css_mgr.add_custom_css('custom-styles.css')
|
|
63
|
+
>>> css = css_mgr.compile()
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
# Default layer configuration
|
|
67
|
+
DEFAULT_LAYERS = {
|
|
68
|
+
'base': CSSLayer(
|
|
69
|
+
name='base',
|
|
70
|
+
files=['base_styles.css'],
|
|
71
|
+
priority=0
|
|
72
|
+
),
|
|
73
|
+
'components': CSSLayer(
|
|
74
|
+
name='components',
|
|
75
|
+
files=['report_components.css'],
|
|
76
|
+
priority=10
|
|
77
|
+
),
|
|
78
|
+
'custom': CSSLayer(
|
|
79
|
+
name='custom',
|
|
80
|
+
files=[],
|
|
81
|
+
priority=20
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
asset_manager: Optional[AssetManager] = None,
|
|
88
|
+
minify: bool = False
|
|
89
|
+
):
|
|
90
|
+
"""Initialize CSS Manager."""
|
|
91
|
+
if asset_manager is None:
|
|
92
|
+
asset_manager = AssetManager(minify=minify)
|
|
93
|
+
|
|
94
|
+
self.asset_manager = asset_manager
|
|
95
|
+
self.minify = minify
|
|
96
|
+
|
|
97
|
+
# Initialize layers with defaults
|
|
98
|
+
self.layers: Dict[str, CSSLayer] = {}
|
|
99
|
+
for name, layer in self.DEFAULT_LAYERS.items():
|
|
100
|
+
self.layers[name] = CSSLayer(
|
|
101
|
+
name=layer.name,
|
|
102
|
+
files=layer.files.copy(),
|
|
103
|
+
priority=layer.priority
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Track custom CSS snippets
|
|
107
|
+
self.custom_css: List[str] = []
|
|
108
|
+
|
|
109
|
+
# Compilation cache
|
|
110
|
+
self._compiled_css: Optional[str] = None
|
|
111
|
+
self._cache_valid = False
|
|
112
|
+
|
|
113
|
+
def add_layer(
|
|
114
|
+
self,
|
|
115
|
+
name: str,
|
|
116
|
+
files: List[str],
|
|
117
|
+
priority: int
|
|
118
|
+
) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Add a new CSS layer.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
name : str
|
|
125
|
+
Layer name
|
|
126
|
+
files : list of str
|
|
127
|
+
CSS files to include
|
|
128
|
+
priority : int
|
|
129
|
+
Layer priority (lower = earlier in output)
|
|
130
|
+
|
|
131
|
+
Examples
|
|
132
|
+
--------
|
|
133
|
+
>>> css_mgr.add_layer('theme', ['dark-theme.css'], priority=5)
|
|
134
|
+
"""
|
|
135
|
+
self.layers[name] = CSSLayer(
|
|
136
|
+
name=name,
|
|
137
|
+
files=files,
|
|
138
|
+
priority=priority
|
|
139
|
+
)
|
|
140
|
+
self._invalidate_cache()
|
|
141
|
+
|
|
142
|
+
def add_css_to_layer(self, layer_name: str, css_file: str) -> None:
|
|
143
|
+
"""
|
|
144
|
+
Add a CSS file to an existing layer.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
layer_name : str
|
|
149
|
+
Name of the layer
|
|
150
|
+
css_file : str
|
|
151
|
+
CSS file to add
|
|
152
|
+
|
|
153
|
+
Examples
|
|
154
|
+
--------
|
|
155
|
+
>>> css_mgr.add_css_to_layer('custom', 'my-styles.css')
|
|
156
|
+
"""
|
|
157
|
+
if layer_name not in self.layers:
|
|
158
|
+
raise ValueError(f"Layer '{layer_name}' does not exist")
|
|
159
|
+
|
|
160
|
+
if css_file not in self.layers[layer_name].files:
|
|
161
|
+
self.layers[layer_name].files.append(css_file)
|
|
162
|
+
self._invalidate_cache()
|
|
163
|
+
|
|
164
|
+
def remove_css_from_layer(self, layer_name: str, css_file: str) -> None:
|
|
165
|
+
"""
|
|
166
|
+
Remove a CSS file from a layer.
|
|
167
|
+
|
|
168
|
+
Parameters
|
|
169
|
+
----------
|
|
170
|
+
layer_name : str
|
|
171
|
+
Name of the layer
|
|
172
|
+
css_file : str
|
|
173
|
+
CSS file to remove
|
|
174
|
+
|
|
175
|
+
Examples
|
|
176
|
+
--------
|
|
177
|
+
>>> css_mgr.remove_css_from_layer('custom', 'old-styles.css')
|
|
178
|
+
"""
|
|
179
|
+
if layer_name not in self.layers:
|
|
180
|
+
raise ValueError(f"Layer '{layer_name}' does not exist")
|
|
181
|
+
|
|
182
|
+
if css_file in self.layers[layer_name].files:
|
|
183
|
+
self.layers[layer_name].files.remove(css_file)
|
|
184
|
+
self._invalidate_cache()
|
|
185
|
+
|
|
186
|
+
def add_custom_css(self, css_file: str) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Add a custom CSS file to the custom layer.
|
|
189
|
+
|
|
190
|
+
Convenience method for adding files to the custom layer.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
css_file : str
|
|
195
|
+
CSS file to add
|
|
196
|
+
|
|
197
|
+
Examples
|
|
198
|
+
--------
|
|
199
|
+
>>> css_mgr.add_custom_css('validation-custom.css')
|
|
200
|
+
"""
|
|
201
|
+
self.add_css_to_layer('custom', css_file)
|
|
202
|
+
|
|
203
|
+
def add_inline_css(self, css_content: str) -> None:
|
|
204
|
+
"""
|
|
205
|
+
Add inline CSS snippet.
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
css_content : str
|
|
210
|
+
CSS content to add
|
|
211
|
+
|
|
212
|
+
Examples
|
|
213
|
+
--------
|
|
214
|
+
>>> css_mgr.add_inline_css('.my-class { color: red; }')
|
|
215
|
+
"""
|
|
216
|
+
self.custom_css.append(css_content)
|
|
217
|
+
self._invalidate_cache()
|
|
218
|
+
|
|
219
|
+
def compile(self, force: bool = False) -> str:
|
|
220
|
+
"""
|
|
221
|
+
Compile all CSS layers into a single string.
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
force : bool, default=False
|
|
226
|
+
Force recompilation even if cache is valid
|
|
227
|
+
|
|
228
|
+
Returns
|
|
229
|
+
-------
|
|
230
|
+
str
|
|
231
|
+
Compiled CSS content
|
|
232
|
+
|
|
233
|
+
Examples
|
|
234
|
+
--------
|
|
235
|
+
>>> css = css_mgr.compile()
|
|
236
|
+
>>> # Use in template
|
|
237
|
+
>>> '<style>{{ css }}</style>'
|
|
238
|
+
"""
|
|
239
|
+
# Return cached version if valid
|
|
240
|
+
if self._cache_valid and not force and self._compiled_css is not None:
|
|
241
|
+
return self._compiled_css
|
|
242
|
+
|
|
243
|
+
css_parts = []
|
|
244
|
+
|
|
245
|
+
# Sort layers by priority
|
|
246
|
+
sorted_layers = sorted(
|
|
247
|
+
self.layers.values(),
|
|
248
|
+
key=lambda layer: layer.priority
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Compile each layer
|
|
252
|
+
for layer in sorted_layers:
|
|
253
|
+
if not layer.files:
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
# Add layer header
|
|
257
|
+
css_parts.append(
|
|
258
|
+
f"/* ========================================\n"
|
|
259
|
+
f" * Layer: {layer.name.upper()} (Priority: {layer.priority})\n"
|
|
260
|
+
f" * ======================================== */\n"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Collect CSS files for this layer
|
|
264
|
+
layer_css = self.asset_manager.collect_css(layer.files)
|
|
265
|
+
css_parts.append(layer_css)
|
|
266
|
+
css_parts.append("\n")
|
|
267
|
+
|
|
268
|
+
# Add custom inline CSS
|
|
269
|
+
if self.custom_css:
|
|
270
|
+
css_parts.append(
|
|
271
|
+
f"/* ========================================\n"
|
|
272
|
+
f" * INLINE CUSTOM CSS\n"
|
|
273
|
+
f" * ======================================== */\n"
|
|
274
|
+
)
|
|
275
|
+
for custom in self.custom_css:
|
|
276
|
+
css_parts.append(custom)
|
|
277
|
+
css_parts.append("\n")
|
|
278
|
+
|
|
279
|
+
# Join all parts
|
|
280
|
+
compiled = "".join(css_parts)
|
|
281
|
+
|
|
282
|
+
# Cache result
|
|
283
|
+
self._compiled_css = compiled
|
|
284
|
+
self._cache_valid = True
|
|
285
|
+
|
|
286
|
+
return compiled
|
|
287
|
+
|
|
288
|
+
def compile_for_report_type(self, report_type: str) -> str:
|
|
289
|
+
"""
|
|
290
|
+
Compile CSS with report-type-specific styles.
|
|
291
|
+
|
|
292
|
+
Automatically adds report-type CSS if available.
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
report_type : str
|
|
297
|
+
Report type (e.g., 'validation', 'regression', 'gmm')
|
|
298
|
+
|
|
299
|
+
Returns
|
|
300
|
+
-------
|
|
301
|
+
str
|
|
302
|
+
Compiled CSS content
|
|
303
|
+
|
|
304
|
+
Examples
|
|
305
|
+
--------
|
|
306
|
+
>>> css = css_mgr.compile_for_report_type('validation')
|
|
307
|
+
"""
|
|
308
|
+
# Check if report-type CSS exists
|
|
309
|
+
report_css_file = f"{report_type}_report.css"
|
|
310
|
+
|
|
311
|
+
# Temporarily add to custom layer
|
|
312
|
+
original_custom_files = self.layers['custom'].files.copy()
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
# Add report-type CSS if it exists
|
|
316
|
+
self.add_custom_css(report_css_file)
|
|
317
|
+
except FileNotFoundError:
|
|
318
|
+
# File doesn't exist, that's okay
|
|
319
|
+
pass
|
|
320
|
+
|
|
321
|
+
# Compile
|
|
322
|
+
css = self.compile(force=True)
|
|
323
|
+
|
|
324
|
+
# Restore original custom files
|
|
325
|
+
self.layers['custom'].files = original_custom_files
|
|
326
|
+
self._invalidate_cache()
|
|
327
|
+
|
|
328
|
+
return css
|
|
329
|
+
|
|
330
|
+
def get_layer_info(self) -> Dict[str, Dict]:
|
|
331
|
+
"""
|
|
332
|
+
Get information about all layers.
|
|
333
|
+
|
|
334
|
+
Returns
|
|
335
|
+
-------
|
|
336
|
+
dict
|
|
337
|
+
Dictionary mapping layer name to layer info
|
|
338
|
+
|
|
339
|
+
Examples
|
|
340
|
+
--------
|
|
341
|
+
>>> info = css_mgr.get_layer_info()
|
|
342
|
+
>>> print(info['base']['files'])
|
|
343
|
+
['base_styles.css']
|
|
344
|
+
"""
|
|
345
|
+
return {
|
|
346
|
+
name: {
|
|
347
|
+
'priority': layer.priority,
|
|
348
|
+
'files': layer.files.copy(),
|
|
349
|
+
'file_count': len(layer.files)
|
|
350
|
+
}
|
|
351
|
+
for name, layer in self.layers.items()
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
def reset_to_defaults(self) -> None:
|
|
355
|
+
"""
|
|
356
|
+
Reset CSS layers to default configuration.
|
|
357
|
+
|
|
358
|
+
Examples
|
|
359
|
+
--------
|
|
360
|
+
>>> css_mgr.add_custom_css('temp.css')
|
|
361
|
+
>>> css_mgr.reset_to_defaults()
|
|
362
|
+
>>> # All custom CSS removed
|
|
363
|
+
"""
|
|
364
|
+
self.layers.clear()
|
|
365
|
+
for name, layer in self.DEFAULT_LAYERS.items():
|
|
366
|
+
self.layers[name] = CSSLayer(
|
|
367
|
+
name=layer.name,
|
|
368
|
+
files=layer.files.copy(),
|
|
369
|
+
priority=layer.priority
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
self.custom_css.clear()
|
|
373
|
+
self._invalidate_cache()
|
|
374
|
+
|
|
375
|
+
def list_available_css(self) -> Dict[str, List[str]]:
|
|
376
|
+
"""
|
|
377
|
+
List all available CSS files from asset manager.
|
|
378
|
+
|
|
379
|
+
Returns
|
|
380
|
+
-------
|
|
381
|
+
dict
|
|
382
|
+
Dictionary with 'css' key containing list of available files
|
|
383
|
+
|
|
384
|
+
Examples
|
|
385
|
+
--------
|
|
386
|
+
>>> files = css_mgr.list_available_css()
|
|
387
|
+
>>> print(files['css'])
|
|
388
|
+
['base_styles.css', 'report_components.css', ...]
|
|
389
|
+
"""
|
|
390
|
+
return self.asset_manager.list_assets(asset_type='css')
|
|
391
|
+
|
|
392
|
+
def get_size_estimate(self) -> Dict[str, int]:
|
|
393
|
+
"""
|
|
394
|
+
Estimate size of compiled CSS.
|
|
395
|
+
|
|
396
|
+
Returns
|
|
397
|
+
-------
|
|
398
|
+
dict
|
|
399
|
+
Dictionary with size estimates in bytes
|
|
400
|
+
|
|
401
|
+
Examples
|
|
402
|
+
--------
|
|
403
|
+
>>> sizes = css_mgr.get_size_estimate()
|
|
404
|
+
>>> print(f"Total size: {sizes['total'] / 1024:.1f} KB")
|
|
405
|
+
"""
|
|
406
|
+
css = self.compile()
|
|
407
|
+
|
|
408
|
+
sizes = {
|
|
409
|
+
'total': len(css.encode('utf-8')),
|
|
410
|
+
'total_kb': len(css.encode('utf-8')) / 1024
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
# Estimate per layer
|
|
414
|
+
for name, layer in self.layers.items():
|
|
415
|
+
if not layer.files:
|
|
416
|
+
continue
|
|
417
|
+
|
|
418
|
+
layer_css = self.asset_manager.collect_css(layer.files)
|
|
419
|
+
sizes[f'{name}_layer'] = len(layer_css.encode('utf-8'))
|
|
420
|
+
|
|
421
|
+
return sizes
|
|
422
|
+
|
|
423
|
+
def validate_layers(self) -> Dict[str, List[str]]:
|
|
424
|
+
"""
|
|
425
|
+
Validate that all CSS files in layers exist.
|
|
426
|
+
|
|
427
|
+
Returns
|
|
428
|
+
-------
|
|
429
|
+
dict
|
|
430
|
+
Dictionary mapping layer name to list of missing files
|
|
431
|
+
|
|
432
|
+
Examples
|
|
433
|
+
--------
|
|
434
|
+
>>> missing = css_mgr.validate_layers()
|
|
435
|
+
>>> if missing['custom']:
|
|
436
|
+
... print(f"Missing files: {missing['custom']}")
|
|
437
|
+
"""
|
|
438
|
+
missing = {}
|
|
439
|
+
|
|
440
|
+
for name, layer in self.layers.items():
|
|
441
|
+
missing[name] = []
|
|
442
|
+
for css_file in layer.files:
|
|
443
|
+
try:
|
|
444
|
+
self.asset_manager.get_css(css_file)
|
|
445
|
+
except FileNotFoundError:
|
|
446
|
+
missing[name].append(css_file)
|
|
447
|
+
|
|
448
|
+
return missing
|
|
449
|
+
|
|
450
|
+
def clear_cache(self) -> None:
|
|
451
|
+
"""Clear compilation cache."""
|
|
452
|
+
self._invalidate_cache()
|
|
453
|
+
self.asset_manager.clear_cache()
|
|
454
|
+
|
|
455
|
+
def _invalidate_cache(self) -> None:
|
|
456
|
+
"""Invalidate compilation cache."""
|
|
457
|
+
self._cache_valid = False
|
|
458
|
+
self._compiled_css = None
|
|
459
|
+
|
|
460
|
+
def __repr__(self) -> str:
|
|
461
|
+
"""String representation."""
|
|
462
|
+
layer_count = len(self.layers)
|
|
463
|
+
file_count = sum(len(layer.files) for layer in self.layers.values())
|
|
464
|
+
custom_count = len(self.custom_css)
|
|
465
|
+
|
|
466
|
+
return (
|
|
467
|
+
f"CSSManager("
|
|
468
|
+
f"layers={layer_count}, "
|
|
469
|
+
f"files={file_count}, "
|
|
470
|
+
f"custom_snippets={custom_count}, "
|
|
471
|
+
f"minify={self.minify})"
|
|
472
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PanelBox Report Exporters.
|
|
3
|
+
|
|
4
|
+
Provides export functionality for various output formats.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .html_exporter import HTMLExporter
|
|
8
|
+
from .latex_exporter import LaTeXExporter
|
|
9
|
+
from .markdown_exporter import MarkdownExporter
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'HTMLExporter',
|
|
13
|
+
'LaTeXExporter',
|
|
14
|
+
'MarkdownExporter',
|
|
15
|
+
]
|