oscura 0.0.1__py3-none-any.whl → 0.1.1__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.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.1.dist-info/METADATA +300 -0
- oscura-0.1.1.dist-info/RECORD +463 -0
- oscura-0.1.1.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"""PowerPoint presentation export for Oscura reports.
|
|
2
|
+
|
|
3
|
+
This module provides PPTX generation for stakeholder presentations
|
|
4
|
+
with automated slide layouts, embedded plots, and speaker notes.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Note:
|
|
8
|
+
This is a stub implementation. For full functionality, install python-pptx:
|
|
9
|
+
pip install python-pptx
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
>>> from oscura.reporting import export_pptx
|
|
13
|
+
>>> data = {"title": "Analysis Report", "findings": [...]}
|
|
14
|
+
>>> export_pptx(data, "presentation.pptx")
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# Check if python-pptx is available
|
|
27
|
+
try:
|
|
28
|
+
from pptx import Presentation
|
|
29
|
+
from pptx.util import Inches
|
|
30
|
+
|
|
31
|
+
PPTX_AVAILABLE = True
|
|
32
|
+
except ImportError:
|
|
33
|
+
PPTX_AVAILABLE = False
|
|
34
|
+
logger.warning("python-pptx not available. Install with: pip install python-pptx")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class PPTXSlide:
|
|
39
|
+
"""PowerPoint slide configuration.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
title: Slide title
|
|
43
|
+
content: Slide content (text, bullet points, or image path)
|
|
44
|
+
layout: Slide layout type
|
|
45
|
+
notes: Speaker notes
|
|
46
|
+
chart_data: Optional chart data to embed
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
title: str
|
|
50
|
+
content: str | list[str] | Path = ""
|
|
51
|
+
layout: str = "title_content" # title, title_content, content, blank
|
|
52
|
+
notes: str = ""
|
|
53
|
+
chart_data: dict[str, Any] | None = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class PPTXPresentation:
|
|
58
|
+
"""PowerPoint presentation configuration.
|
|
59
|
+
|
|
60
|
+
Attributes:
|
|
61
|
+
title: Presentation title
|
|
62
|
+
subtitle: Presentation subtitle
|
|
63
|
+
author: Author name
|
|
64
|
+
slides: List of slides
|
|
65
|
+
template: Optional template file path
|
|
66
|
+
|
|
67
|
+
References:
|
|
68
|
+
REPORT-023: PowerPoint/PPTX Export
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
title: str
|
|
72
|
+
subtitle: str = ""
|
|
73
|
+
author: str = ""
|
|
74
|
+
slides: list[PPTXSlide] = field(default_factory=list)
|
|
75
|
+
template: Path | None = None
|
|
76
|
+
|
|
77
|
+
def add_slide(
|
|
78
|
+
self,
|
|
79
|
+
title: str,
|
|
80
|
+
content: str | list[str] | Path = "",
|
|
81
|
+
*,
|
|
82
|
+
layout: str = "title_content",
|
|
83
|
+
notes: str = "",
|
|
84
|
+
) -> PPTXSlide:
|
|
85
|
+
"""Add slide to presentation.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
title: Slide title
|
|
89
|
+
content: Slide content
|
|
90
|
+
layout: Layout type
|
|
91
|
+
notes: Speaker notes
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Created slide object
|
|
95
|
+
"""
|
|
96
|
+
slide = PPTXSlide(title=title, content=content, layout=layout, notes=notes)
|
|
97
|
+
self.slides.append(slide)
|
|
98
|
+
return slide
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def export_pptx(
|
|
102
|
+
report_data: dict[str, Any],
|
|
103
|
+
output_path: str | Path,
|
|
104
|
+
*,
|
|
105
|
+
title: str = "Analysis Report",
|
|
106
|
+
subtitle: str = "",
|
|
107
|
+
author: str = "",
|
|
108
|
+
template: Path | None = None,
|
|
109
|
+
) -> Path:
|
|
110
|
+
"""Export report data to PowerPoint presentation.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
report_data: Report data dictionary containing:
|
|
114
|
+
- 'summary': Executive summary text
|
|
115
|
+
- 'findings': List of key findings
|
|
116
|
+
- 'measurements': Measurement results
|
|
117
|
+
- 'plots': List of plot image paths
|
|
118
|
+
output_path: Output PPTX file path
|
|
119
|
+
title: Presentation title
|
|
120
|
+
subtitle: Presentation subtitle
|
|
121
|
+
author: Author name
|
|
122
|
+
template: Optional template PPTX file
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Path to generated PPTX file
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
>>> data = {
|
|
129
|
+
... 'summary': 'All tests passed',
|
|
130
|
+
... 'findings': ['Rise time: 2.3ns', 'Fall time: 2.1ns'],
|
|
131
|
+
... 'plots': [Path('plot1.png'), Path('plot2.png')]
|
|
132
|
+
... }
|
|
133
|
+
>>> export_pptx(data, 'report.pptx', title='Signal Analysis')
|
|
134
|
+
|
|
135
|
+
References:
|
|
136
|
+
REPORT-023: PowerPoint/PPTX Export
|
|
137
|
+
"""
|
|
138
|
+
output_path = Path(output_path)
|
|
139
|
+
|
|
140
|
+
if not PPTX_AVAILABLE:
|
|
141
|
+
# Create stub file
|
|
142
|
+
logger.warning("Creating stub PPTX file (python-pptx not installed)")
|
|
143
|
+
_create_stub_pptx(output_path, title, report_data)
|
|
144
|
+
return output_path
|
|
145
|
+
|
|
146
|
+
# Create presentation
|
|
147
|
+
prs = Presentation(str(template)) if template else Presentation()
|
|
148
|
+
|
|
149
|
+
# Title slide
|
|
150
|
+
_add_title_slide(prs, title, subtitle, author)
|
|
151
|
+
|
|
152
|
+
# Summary slide
|
|
153
|
+
summary = report_data.get("summary", "")
|
|
154
|
+
if summary:
|
|
155
|
+
_add_summary_slide(prs, summary)
|
|
156
|
+
|
|
157
|
+
# Key findings slides
|
|
158
|
+
findings = report_data.get("findings", [])
|
|
159
|
+
if findings:
|
|
160
|
+
_add_findings_slide(prs, findings)
|
|
161
|
+
|
|
162
|
+
# Measurement results slides
|
|
163
|
+
measurements = report_data.get("measurements", [])
|
|
164
|
+
if measurements:
|
|
165
|
+
_add_measurement_slides(prs, measurements)
|
|
166
|
+
|
|
167
|
+
# Plot slides
|
|
168
|
+
plots = report_data.get("plots", [])
|
|
169
|
+
for i, plot_path in enumerate(plots, 1):
|
|
170
|
+
_add_plot_slide(prs, plot_path, f"Figure {i}")
|
|
171
|
+
|
|
172
|
+
# Save presentation
|
|
173
|
+
prs.save(str(output_path))
|
|
174
|
+
logger.info("Exported PPTX presentation to %s", output_path)
|
|
175
|
+
|
|
176
|
+
return output_path
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _add_title_slide(
|
|
180
|
+
prs: Any,
|
|
181
|
+
title: str,
|
|
182
|
+
subtitle: str,
|
|
183
|
+
author: str,
|
|
184
|
+
) -> None:
|
|
185
|
+
"""Add title slide to presentation."""
|
|
186
|
+
if not PPTX_AVAILABLE:
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
slide = prs.slides.add_slide(prs.slide_layouts[0]) # Title slide layout
|
|
190
|
+
|
|
191
|
+
# Set title
|
|
192
|
+
if slide.shapes.title:
|
|
193
|
+
slide.shapes.title.text = title
|
|
194
|
+
|
|
195
|
+
# Set subtitle (placeholder 1)
|
|
196
|
+
if len(slide.placeholders) > 1:
|
|
197
|
+
subtitle_text = subtitle
|
|
198
|
+
if author:
|
|
199
|
+
subtitle_text += f"\n{author}"
|
|
200
|
+
slide.placeholders[1].text = subtitle_text
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _add_summary_slide(prs: Any, summary: str) -> None:
|
|
204
|
+
"""Add executive summary slide."""
|
|
205
|
+
if not PPTX_AVAILABLE:
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title and content layout
|
|
209
|
+
|
|
210
|
+
if slide.shapes.title:
|
|
211
|
+
slide.shapes.title.text = "Executive Summary"
|
|
212
|
+
|
|
213
|
+
# Add summary text
|
|
214
|
+
if len(slide.placeholders) > 1:
|
|
215
|
+
text_frame = slide.placeholders[1].text_frame
|
|
216
|
+
text_frame.text = summary
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _add_findings_slide(prs: Any, findings: list[str]) -> None:
|
|
220
|
+
"""Add key findings slide with bullet points."""
|
|
221
|
+
if not PPTX_AVAILABLE:
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title and content layout
|
|
225
|
+
|
|
226
|
+
if slide.shapes.title:
|
|
227
|
+
slide.shapes.title.text = "Key Findings"
|
|
228
|
+
|
|
229
|
+
# Add bullet points
|
|
230
|
+
if len(slide.placeholders) > 1:
|
|
231
|
+
text_frame = slide.placeholders[1].text_frame
|
|
232
|
+
text_frame.clear()
|
|
233
|
+
|
|
234
|
+
for finding in findings:
|
|
235
|
+
p = text_frame.add_paragraph()
|
|
236
|
+
p.text = finding
|
|
237
|
+
p.level = 0
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _add_measurement_slides(prs: Any, measurements: list[dict[str, Any]]) -> None:
|
|
241
|
+
"""Add measurement results slides."""
|
|
242
|
+
if not PPTX_AVAILABLE:
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
# Group measurements into slides (5 per slide)
|
|
246
|
+
chunk_size = 5
|
|
247
|
+
for i in range(0, len(measurements), chunk_size):
|
|
248
|
+
chunk = measurements[i : i + chunk_size]
|
|
249
|
+
|
|
250
|
+
slide = prs.slides.add_slide(prs.slide_layouts[1])
|
|
251
|
+
|
|
252
|
+
if slide.shapes.title:
|
|
253
|
+
slide.shapes.title.text = f"Measurement Results ({i + 1}-{i + len(chunk)})"
|
|
254
|
+
|
|
255
|
+
# Add measurements as bullet points
|
|
256
|
+
if len(slide.placeholders) > 1:
|
|
257
|
+
text_frame = slide.placeholders[1].text_frame
|
|
258
|
+
text_frame.clear()
|
|
259
|
+
|
|
260
|
+
for meas in chunk:
|
|
261
|
+
p = text_frame.add_paragraph()
|
|
262
|
+
name = meas.get("name", "Unknown")
|
|
263
|
+
value = meas.get("value", "")
|
|
264
|
+
unit = meas.get("unit", "")
|
|
265
|
+
status = meas.get("status", "")
|
|
266
|
+
|
|
267
|
+
p.text = f"{name}: {value} {unit} {status}"
|
|
268
|
+
p.level = 0
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _add_plot_slide(prs: Any, plot_path: Path, caption: str) -> None:
|
|
272
|
+
"""Add slide with embedded plot image."""
|
|
273
|
+
if not PPTX_AVAILABLE:
|
|
274
|
+
return
|
|
275
|
+
|
|
276
|
+
slide = prs.slides.add_slide(prs.slide_layouts[5]) # Blank layout
|
|
277
|
+
|
|
278
|
+
# Add title
|
|
279
|
+
if slide.shapes.title:
|
|
280
|
+
slide.shapes.title.text = caption
|
|
281
|
+
|
|
282
|
+
# Add image (centered)
|
|
283
|
+
if plot_path.exists():
|
|
284
|
+
left = Inches(1.5)
|
|
285
|
+
top = Inches(2)
|
|
286
|
+
width = Inches(7)
|
|
287
|
+
|
|
288
|
+
slide.shapes.add_picture(str(plot_path), left, top, width=width)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _create_stub_pptx(
|
|
292
|
+
output_path: Path,
|
|
293
|
+
title: str,
|
|
294
|
+
report_data: dict[str, Any],
|
|
295
|
+
) -> None:
|
|
296
|
+
"""Create stub PPTX file when python-pptx not available."""
|
|
297
|
+
# Write text file with .pptx extension as placeholder
|
|
298
|
+
with output_path.open("w") as f:
|
|
299
|
+
f.write("PowerPoint Export Stub\n")
|
|
300
|
+
f.write("======================\n\n")
|
|
301
|
+
f.write(f"Title: {title}\n\n")
|
|
302
|
+
f.write("Install python-pptx for full PPTX export:\n")
|
|
303
|
+
f.write(" pip install python-pptx\n\n")
|
|
304
|
+
f.write("Report Data Summary:\n")
|
|
305
|
+
f.write(f" - Summary: {report_data.get('summary', 'N/A')}\n")
|
|
306
|
+
f.write(f" - Findings: {len(report_data.get('findings', []))} items\n")
|
|
307
|
+
f.write(f" - Measurements: {len(report_data.get('measurements', []))} items\n")
|
|
308
|
+
f.write(f" - Plots: {len(report_data.get('plots', []))} items\n")
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def generate_presentation_from_report(
|
|
312
|
+
report: dict[str, Any],
|
|
313
|
+
output_path: str | Path,
|
|
314
|
+
*,
|
|
315
|
+
presentation_config: PPTXPresentation | None = None,
|
|
316
|
+
) -> Path:
|
|
317
|
+
"""Generate PowerPoint presentation from report structure.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
report: Report dictionary with standard structure
|
|
321
|
+
output_path: Output file path
|
|
322
|
+
presentation_config: Optional presentation configuration
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Path to generated presentation
|
|
326
|
+
|
|
327
|
+
References:
|
|
328
|
+
REPORT-023: PowerPoint/PPTX Export
|
|
329
|
+
"""
|
|
330
|
+
if presentation_config is None:
|
|
331
|
+
presentation_config = PPTXPresentation(
|
|
332
|
+
title=report.get("title", "Analysis Report"),
|
|
333
|
+
subtitle=report.get("subtitle", ""),
|
|
334
|
+
author=report.get("author", ""),
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
# Extract data from report
|
|
338
|
+
report_data = {
|
|
339
|
+
"summary": report.get("executive_summary", ""),
|
|
340
|
+
"findings": report.get("key_findings", []),
|
|
341
|
+
"measurements": report.get("measurements", []),
|
|
342
|
+
"plots": report.get("plot_paths", []),
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return export_pptx(
|
|
346
|
+
report_data,
|
|
347
|
+
output_path,
|
|
348
|
+
title=presentation_config.title,
|
|
349
|
+
subtitle=presentation_config.subtitle,
|
|
350
|
+
author=presentation_config.author,
|
|
351
|
+
template=presentation_config.template,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
__all__ = [
|
|
356
|
+
"PPTXPresentation",
|
|
357
|
+
"PPTXSlide",
|
|
358
|
+
"export_pptx",
|
|
359
|
+
"generate_presentation_from_report",
|
|
360
|
+
]
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Enhanced PDF report generation.
|
|
2
|
+
|
|
3
|
+
High-quality PDF report generation with embedded plots, metadata,
|
|
4
|
+
and PDF/A compliance for archival (enhancements to existing PDF module).
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
References:
|
|
8
|
+
- REPORT-008: PDF Report Generation
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import TYPE_CHECKING, Any
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from oscura.reporting.core import Report
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class PDFRenderer:
|
|
22
|
+
"""PDF report renderer.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
dpi: Plot rendering DPI (default 300 for print quality).
|
|
26
|
+
embed_fonts: Embed fonts for consistency (default True).
|
|
27
|
+
vector_graphics: Use vector graphics for plots (default True).
|
|
28
|
+
table_of_contents: Include TOC (default True).
|
|
29
|
+
pdfa_compliance: Generate PDF/A-1b output (default False).
|
|
30
|
+
page_numbering: Include page numbers (default True).
|
|
31
|
+
|
|
32
|
+
References:
|
|
33
|
+
REPORT-008: PDF Report Generation Engine
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
dpi: int = 300
|
|
37
|
+
embed_fonts: bool = True
|
|
38
|
+
vector_graphics: bool = True
|
|
39
|
+
table_of_contents: bool = True
|
|
40
|
+
pdfa_compliance: bool = False
|
|
41
|
+
page_numbering: bool = True
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def render_to_pdf(
|
|
45
|
+
report: Report,
|
|
46
|
+
output_path: str | None = None,
|
|
47
|
+
**kwargs: Any,
|
|
48
|
+
) -> bytes:
|
|
49
|
+
"""Render report to PDF.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
report: Report object to render.
|
|
53
|
+
output_path: Optional output path (if None, returns bytes).
|
|
54
|
+
**kwargs: Additional PDF rendering options.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
PDF data as bytes (if output_path is None).
|
|
58
|
+
|
|
59
|
+
Example:
|
|
60
|
+
>>> from oscura.reporting.core import Report, ReportConfig
|
|
61
|
+
>>> report = Report(config=ReportConfig(title="Test Report"))
|
|
62
|
+
>>> pdf_bytes = render_to_pdf(report)
|
|
63
|
+
|
|
64
|
+
References:
|
|
65
|
+
REPORT-008: PDF Report Generation Engine
|
|
66
|
+
"""
|
|
67
|
+
# Import existing PDF module
|
|
68
|
+
from oscura.reporting.pdf import generate_pdf_report
|
|
69
|
+
|
|
70
|
+
# Merge kwargs with defaults
|
|
71
|
+
renderer = PDFRenderer(**kwargs)
|
|
72
|
+
|
|
73
|
+
# Generate PDF
|
|
74
|
+
pdf_bytes = generate_pdf_report(
|
|
75
|
+
report,
|
|
76
|
+
dpi=renderer.dpi,
|
|
77
|
+
embed_fonts=renderer.embed_fonts,
|
|
78
|
+
vector_graphics=renderer.vector_graphics,
|
|
79
|
+
table_of_contents=renderer.table_of_contents,
|
|
80
|
+
pdfa_compliance=renderer.pdfa_compliance,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Save if path provided
|
|
84
|
+
if output_path:
|
|
85
|
+
with open(output_path, "wb") as f:
|
|
86
|
+
f.write(pdf_bytes)
|
|
87
|
+
|
|
88
|
+
return pdf_bytes
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
__all__ = [
|
|
92
|
+
"PDFRenderer",
|
|
93
|
+
"render_to_pdf",
|
|
94
|
+
]
|