oscura 0.0.1__py3-none-any.whl → 0.1.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.
- 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.0.dist-info/METADATA +300 -0
- oscura-0.1.0.dist-info/RECORD +463 -0
- oscura-0.1.0.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.0.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.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
"""Professional formatting standards and visual emphasis for reports.
|
|
2
|
+
|
|
3
|
+
This module provides professional formatting standards, visual emphasis
|
|
4
|
+
systems, and executive summary generation for TraceKit reports.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from oscura.reporting.standards import FormatStandards, VisualEmphasis
|
|
9
|
+
>>> standards = FormatStandards()
|
|
10
|
+
>>> emphasis = VisualEmphasis()
|
|
11
|
+
>>> formatted_text = emphasis.format_pass_fail(True)
|
|
12
|
+
|
|
13
|
+
References:
|
|
14
|
+
REPORT-001, REPORT-002,
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from enum import Enum
|
|
21
|
+
from typing import Any, Literal
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Severity(Enum):
|
|
25
|
+
"""Severity levels for findings and violations."""
|
|
26
|
+
|
|
27
|
+
CRITICAL = "critical"
|
|
28
|
+
WARNING = "warning"
|
|
29
|
+
INFO = "info"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ColorScheme(Enum):
|
|
33
|
+
"""Available colorblind-safe color schemes."""
|
|
34
|
+
|
|
35
|
+
VIRIDIS = "viridis"
|
|
36
|
+
CIVIDIS = "cividis"
|
|
37
|
+
PLASMA = "plasma"
|
|
38
|
+
INFERNO = "inferno"
|
|
39
|
+
OKABE_ITO = "okabe_ito" # Colorblind-safe palette
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class FormatStandards:
|
|
44
|
+
"""Professional formatting standards configuration.
|
|
45
|
+
|
|
46
|
+
Defines typography, page layout, color schemes, and section hierarchy
|
|
47
|
+
for professional report output.
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
heading_font: Font family for headings.
|
|
51
|
+
body_font: Font family for body text.
|
|
52
|
+
code_font: Font family for code and data.
|
|
53
|
+
page_size: Page size (letter or A4).
|
|
54
|
+
margins_inches: Page margins in inches.
|
|
55
|
+
color_scheme: Colorblind-safe color palette.
|
|
56
|
+
line_spacing: Line spacing multiplier.
|
|
57
|
+
logo_max_height_inches: Maximum logo height.
|
|
58
|
+
watermark_opacity: Watermark opacity (0.0-1.0).
|
|
59
|
+
|
|
60
|
+
References:
|
|
61
|
+
REPORT-001: Professional Formatting Standards
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
heading_font: str = "Arial, Helvetica, sans-serif"
|
|
65
|
+
body_font: str = "Georgia, Times New Roman, serif"
|
|
66
|
+
code_font: str = "Consolas, Courier New, monospace"
|
|
67
|
+
page_size: Literal["letter", "A4"] = "letter"
|
|
68
|
+
margins_inches: float = 1.0
|
|
69
|
+
color_scheme: ColorScheme = ColorScheme.VIRIDIS
|
|
70
|
+
line_spacing: float = 1.5
|
|
71
|
+
logo_max_height_inches: float = 2.0
|
|
72
|
+
watermark_opacity: float = 0.3
|
|
73
|
+
|
|
74
|
+
# Font sizes in points
|
|
75
|
+
title_size: int = 24
|
|
76
|
+
h1_size: int = 18
|
|
77
|
+
h2_size: int = 14
|
|
78
|
+
h3_size: int = 12
|
|
79
|
+
body_size: int = 10
|
|
80
|
+
|
|
81
|
+
def to_css(self) -> str:
|
|
82
|
+
"""Generate CSS stylesheet from format standards.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
CSS stylesheet string.
|
|
86
|
+
|
|
87
|
+
References:
|
|
88
|
+
REPORT-001: Professional Formatting Standards
|
|
89
|
+
"""
|
|
90
|
+
return f"""
|
|
91
|
+
/* TraceKit Professional Report Styles - REPORT-001 */
|
|
92
|
+
:root {{
|
|
93
|
+
--heading-font: {self.heading_font};
|
|
94
|
+
--body-font: {self.body_font};
|
|
95
|
+
--code-font: {self.code_font};
|
|
96
|
+
--title-size: {self.title_size}pt;
|
|
97
|
+
--h1-size: {self.h1_size}pt;
|
|
98
|
+
--h2-size: {self.h2_size}pt;
|
|
99
|
+
--h3-size: {self.h3_size}pt;
|
|
100
|
+
--body-size: {self.body_size}pt;
|
|
101
|
+
--line-spacing: {self.line_spacing};
|
|
102
|
+
--margin: {self.margins_inches}in;
|
|
103
|
+
|
|
104
|
+
/* Colorblind-safe palette */
|
|
105
|
+
--color-pass: #2e7d32;
|
|
106
|
+
--color-fail: #c62828;
|
|
107
|
+
--color-warning: #f57c00;
|
|
108
|
+
--color-info: #1565c0;
|
|
109
|
+
--color-critical-bg: #ffebee;
|
|
110
|
+
--color-warning-bg: #fff3e0;
|
|
111
|
+
--color-info-bg: #e3f2fd;
|
|
112
|
+
}}
|
|
113
|
+
|
|
114
|
+
body {{
|
|
115
|
+
font-family: var(--body-font);
|
|
116
|
+
font-size: var(--body-size);
|
|
117
|
+
line-height: var(--line-spacing);
|
|
118
|
+
margin: var(--margin);
|
|
119
|
+
max-width: 8.5in;
|
|
120
|
+
color: #333;
|
|
121
|
+
}}
|
|
122
|
+
|
|
123
|
+
h1, h2, h3, h4 {{
|
|
124
|
+
font-family: var(--heading-font);
|
|
125
|
+
line-height: 1.2;
|
|
126
|
+
margin-top: 1.5em;
|
|
127
|
+
margin-bottom: 0.5em;
|
|
128
|
+
}}
|
|
129
|
+
|
|
130
|
+
h1 {{ font-size: var(--h1-size); }}
|
|
131
|
+
h2 {{ font-size: var(--h2-size); }}
|
|
132
|
+
h3 {{ font-size: var(--h3-size); }}
|
|
133
|
+
|
|
134
|
+
.report-title {{
|
|
135
|
+
font-size: var(--title-size);
|
|
136
|
+
text-align: center;
|
|
137
|
+
margin-bottom: 2em;
|
|
138
|
+
}}
|
|
139
|
+
|
|
140
|
+
code, pre {{
|
|
141
|
+
font-family: var(--code-font);
|
|
142
|
+
font-size: 0.9em;
|
|
143
|
+
background-color: #f5f5f5;
|
|
144
|
+
padding: 2px 4px;
|
|
145
|
+
border-radius: 3px;
|
|
146
|
+
}}
|
|
147
|
+
|
|
148
|
+
/* Table styles */
|
|
149
|
+
table {{
|
|
150
|
+
border-collapse: collapse;
|
|
151
|
+
width: 100%;
|
|
152
|
+
margin: 1em 0;
|
|
153
|
+
}}
|
|
154
|
+
|
|
155
|
+
th, td {{
|
|
156
|
+
border: 1px solid #ddd;
|
|
157
|
+
padding: 8px;
|
|
158
|
+
text-align: left;
|
|
159
|
+
}}
|
|
160
|
+
|
|
161
|
+
th {{
|
|
162
|
+
background-color: #f2f2f2;
|
|
163
|
+
font-weight: bold;
|
|
164
|
+
}}
|
|
165
|
+
|
|
166
|
+
tr:nth-child(even) {{
|
|
167
|
+
background-color: #f9f9f9;
|
|
168
|
+
}}
|
|
169
|
+
|
|
170
|
+
/* Pass/Fail indicators (REPORT-002) */
|
|
171
|
+
.pass {{
|
|
172
|
+
color: var(--color-pass);
|
|
173
|
+
}}
|
|
174
|
+
|
|
175
|
+
.fail {{
|
|
176
|
+
color: var(--color-fail);
|
|
177
|
+
}}
|
|
178
|
+
|
|
179
|
+
.warning {{
|
|
180
|
+
color: var(--color-warning);
|
|
181
|
+
}}
|
|
182
|
+
|
|
183
|
+
/* Severity indicators (REPORT-002) */
|
|
184
|
+
.severity-critical {{
|
|
185
|
+
background-color: var(--color-critical-bg);
|
|
186
|
+
border-left: 4px solid var(--color-fail);
|
|
187
|
+
padding: 10px;
|
|
188
|
+
margin: 10px 0;
|
|
189
|
+
}}
|
|
190
|
+
|
|
191
|
+
.severity-warning {{
|
|
192
|
+
background-color: var(--color-warning-bg);
|
|
193
|
+
border-left: 4px solid var(--color-warning);
|
|
194
|
+
padding: 10px;
|
|
195
|
+
margin: 10px 0;
|
|
196
|
+
}}
|
|
197
|
+
|
|
198
|
+
.severity-info {{
|
|
199
|
+
background-color: var(--color-info-bg);
|
|
200
|
+
border-left: 4px solid var(--color-info);
|
|
201
|
+
padding: 10px;
|
|
202
|
+
margin: 10px 0;
|
|
203
|
+
}}
|
|
204
|
+
|
|
205
|
+
/* Callout box (REPORT-002) */
|
|
206
|
+
.callout {{
|
|
207
|
+
border: 1px solid #ddd;
|
|
208
|
+
border-radius: 4px;
|
|
209
|
+
padding: 15px;
|
|
210
|
+
margin: 15px 0;
|
|
211
|
+
background-color: #fafafa;
|
|
212
|
+
}}
|
|
213
|
+
|
|
214
|
+
.callout.key-finding {{
|
|
215
|
+
border-color: var(--color-info);
|
|
216
|
+
background-color: var(--color-info-bg);
|
|
217
|
+
}}
|
|
218
|
+
|
|
219
|
+
/* Highlighting for out-of-spec values */
|
|
220
|
+
.out-of-spec {{
|
|
221
|
+
background-color: rgba(255, 235, 59, 0.15);
|
|
222
|
+
padding: 2px 4px;
|
|
223
|
+
border-radius: 2px;
|
|
224
|
+
}}
|
|
225
|
+
|
|
226
|
+
/* Executive summary styles (REPORT-004) */
|
|
227
|
+
.executive-summary {{
|
|
228
|
+
background-color: #f5f5f5;
|
|
229
|
+
padding: 20px;
|
|
230
|
+
margin: 20px 0;
|
|
231
|
+
border-radius: 4px;
|
|
232
|
+
}}
|
|
233
|
+
|
|
234
|
+
.executive-summary h2 {{
|
|
235
|
+
margin-top: 0;
|
|
236
|
+
}}
|
|
237
|
+
|
|
238
|
+
.key-findings {{
|
|
239
|
+
list-style-type: none;
|
|
240
|
+
padding-left: 0;
|
|
241
|
+
}}
|
|
242
|
+
|
|
243
|
+
.key-findings li {{
|
|
244
|
+
padding: 5px 0;
|
|
245
|
+
padding-left: 25px;
|
|
246
|
+
position: relative;
|
|
247
|
+
}}
|
|
248
|
+
|
|
249
|
+
.key-findings li::before {{
|
|
250
|
+
content: "";
|
|
251
|
+
position: absolute;
|
|
252
|
+
left: 0;
|
|
253
|
+
top: 8px;
|
|
254
|
+
width: 16px;
|
|
255
|
+
height: 16px;
|
|
256
|
+
}}
|
|
257
|
+
|
|
258
|
+
.key-findings li.critical::before {{
|
|
259
|
+
content: "!";
|
|
260
|
+
color: var(--color-fail);
|
|
261
|
+
font-weight: bold;
|
|
262
|
+
}}
|
|
263
|
+
|
|
264
|
+
/* Watermark */
|
|
265
|
+
.watermark {{
|
|
266
|
+
position: fixed;
|
|
267
|
+
top: 50%;
|
|
268
|
+
left: 50%;
|
|
269
|
+
transform: translate(-50%, -50%) rotate(-45deg);
|
|
270
|
+
font-size: 72pt;
|
|
271
|
+
color: rgba(0, 0, 0, {self.watermark_opacity});
|
|
272
|
+
pointer-events: none;
|
|
273
|
+
z-index: 1000;
|
|
274
|
+
}}
|
|
275
|
+
|
|
276
|
+
/* Print styles */
|
|
277
|
+
@media print {{
|
|
278
|
+
body {{
|
|
279
|
+
margin: 0;
|
|
280
|
+
}}
|
|
281
|
+
.page-break {{
|
|
282
|
+
page-break-before: always;
|
|
283
|
+
}}
|
|
284
|
+
.no-print {{
|
|
285
|
+
display: none;
|
|
286
|
+
}}
|
|
287
|
+
}}
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@dataclass
|
|
292
|
+
class VisualEmphasis:
|
|
293
|
+
"""Visual emphasis system for pass/fail indicators and severity levels.
|
|
294
|
+
|
|
295
|
+
Provides WCAG-compliant visual indicators using both symbols and colors
|
|
296
|
+
for accessibility.
|
|
297
|
+
|
|
298
|
+
Attributes:
|
|
299
|
+
use_unicode_symbols: Use Unicode check/X marks.
|
|
300
|
+
colorblind_safe: Always use symbols + colors (never color alone).
|
|
301
|
+
highlight_violations: Highlight out-of-spec values.
|
|
302
|
+
severity_icons: Show icons for severity levels.
|
|
303
|
+
|
|
304
|
+
References:
|
|
305
|
+
REPORT-002: Visual Emphasis System
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
use_unicode_symbols: bool = True
|
|
309
|
+
colorblind_safe: bool = True
|
|
310
|
+
highlight_violations: bool = True
|
|
311
|
+
severity_icons: bool = True
|
|
312
|
+
|
|
313
|
+
# Unicode symbols for status indicators
|
|
314
|
+
CHECK_SYMBOL = "\u2713" # Check mark
|
|
315
|
+
CROSS_SYMBOL = "\u2717" # X mark
|
|
316
|
+
WARNING_SYMBOL = "\u26a0" # Warning triangle
|
|
317
|
+
INFO_SYMBOL = "\u2139" # Info circle
|
|
318
|
+
CRITICAL_SYMBOL = "\u2757" # Exclamation mark
|
|
319
|
+
|
|
320
|
+
def format_pass_fail(
|
|
321
|
+
self,
|
|
322
|
+
passed: bool,
|
|
323
|
+
*,
|
|
324
|
+
with_text: bool = True,
|
|
325
|
+
html: bool = False,
|
|
326
|
+
) -> str:
|
|
327
|
+
"""Format pass/fail status with visual emphasis.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
passed: Whether the test passed.
|
|
331
|
+
with_text: Include PASS/FAIL text.
|
|
332
|
+
html: Output as HTML with styling.
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
Formatted status string.
|
|
336
|
+
|
|
337
|
+
References:
|
|
338
|
+
REPORT-002: Visual Emphasis System
|
|
339
|
+
"""
|
|
340
|
+
if passed:
|
|
341
|
+
symbol = self.CHECK_SYMBOL if self.use_unicode_symbols else "[PASS]"
|
|
342
|
+
text = "PASS" if with_text else ""
|
|
343
|
+
css_class = "pass"
|
|
344
|
+
else:
|
|
345
|
+
symbol = self.CROSS_SYMBOL if self.use_unicode_symbols else "[FAIL]"
|
|
346
|
+
text = "FAIL" if with_text else ""
|
|
347
|
+
css_class = "fail"
|
|
348
|
+
|
|
349
|
+
result = f"{symbol} {text}".strip() if with_text else symbol
|
|
350
|
+
|
|
351
|
+
if html:
|
|
352
|
+
return f'<span class="{css_class}">{result}</span>'
|
|
353
|
+
return result
|
|
354
|
+
|
|
355
|
+
def format_severity(
|
|
356
|
+
self,
|
|
357
|
+
severity: Severity | str,
|
|
358
|
+
message: str,
|
|
359
|
+
*,
|
|
360
|
+
html: bool = False,
|
|
361
|
+
) -> str:
|
|
362
|
+
"""Format message with severity indicator.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
severity: Severity level.
|
|
366
|
+
message: Message text.
|
|
367
|
+
html: Output as HTML with styling.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
Formatted message string.
|
|
371
|
+
|
|
372
|
+
References:
|
|
373
|
+
REPORT-002: Visual Emphasis System
|
|
374
|
+
"""
|
|
375
|
+
if isinstance(severity, str):
|
|
376
|
+
severity = Severity(severity.lower())
|
|
377
|
+
|
|
378
|
+
if severity == Severity.CRITICAL:
|
|
379
|
+
symbol = self.CRITICAL_SYMBOL if self.severity_icons else ""
|
|
380
|
+
css_class = "severity-critical"
|
|
381
|
+
elif severity == Severity.WARNING:
|
|
382
|
+
symbol = self.WARNING_SYMBOL if self.severity_icons else ""
|
|
383
|
+
css_class = "severity-warning"
|
|
384
|
+
else:
|
|
385
|
+
symbol = self.INFO_SYMBOL if self.severity_icons else ""
|
|
386
|
+
css_class = "severity-info"
|
|
387
|
+
|
|
388
|
+
text = f"{symbol} {message}".strip() if symbol else message
|
|
389
|
+
|
|
390
|
+
if html:
|
|
391
|
+
return f'<div class="{css_class}">{text}</div>'
|
|
392
|
+
return text
|
|
393
|
+
|
|
394
|
+
def format_margin(
|
|
395
|
+
self,
|
|
396
|
+
value: float,
|
|
397
|
+
limit: float,
|
|
398
|
+
*,
|
|
399
|
+
limit_type: Literal["upper", "lower"] = "upper",
|
|
400
|
+
html: bool = False,
|
|
401
|
+
) -> str:
|
|
402
|
+
"""Format margin with color-coded indicator.
|
|
403
|
+
|
|
404
|
+
Color coding:
|
|
405
|
+
- Green: margin > 20%
|
|
406
|
+
- Yellow: 10% < margin <= 20%
|
|
407
|
+
- Red: margin <= 10%
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
value: Measured value.
|
|
411
|
+
limit: Limit value.
|
|
412
|
+
limit_type: Whether limit is upper or lower bound.
|
|
413
|
+
html: Output as HTML with styling.
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
Formatted margin string.
|
|
417
|
+
|
|
418
|
+
References:
|
|
419
|
+
REPORT-002: Visual Emphasis System
|
|
420
|
+
"""
|
|
421
|
+
if limit_type == "upper":
|
|
422
|
+
margin = limit - value
|
|
423
|
+
margin_pct = (margin / limit * 100) if limit != 0 else 0
|
|
424
|
+
else:
|
|
425
|
+
margin = value - limit
|
|
426
|
+
margin_pct = (margin / limit * 100) if limit != 0 else 0
|
|
427
|
+
|
|
428
|
+
# Determine status
|
|
429
|
+
if margin_pct > 20:
|
|
430
|
+
status = "good"
|
|
431
|
+
css_class = "pass"
|
|
432
|
+
symbol = self.PASS_SYMBOL # type: ignore[attr-defined]
|
|
433
|
+
elif margin_pct > 10:
|
|
434
|
+
status = "marginal"
|
|
435
|
+
css_class = "warning"
|
|
436
|
+
symbol = self.WARNING_SYMBOL
|
|
437
|
+
elif margin_pct > 0:
|
|
438
|
+
status = "tight"
|
|
439
|
+
css_class = "warning"
|
|
440
|
+
symbol = self.WARNING_SYMBOL
|
|
441
|
+
else:
|
|
442
|
+
status = "violation"
|
|
443
|
+
css_class = "fail"
|
|
444
|
+
symbol = self.CROSS_SYMBOL
|
|
445
|
+
|
|
446
|
+
text = f"{symbol} margin: {margin_pct:.1f}% ({status})"
|
|
447
|
+
|
|
448
|
+
if html:
|
|
449
|
+
return f'<span class="{css_class}">{text}</span>'
|
|
450
|
+
return text
|
|
451
|
+
|
|
452
|
+
def create_callout_box(
|
|
453
|
+
self,
|
|
454
|
+
title: str,
|
|
455
|
+
content: str,
|
|
456
|
+
*,
|
|
457
|
+
is_key_finding: bool = False,
|
|
458
|
+
) -> str:
|
|
459
|
+
"""Create a callout box for key findings.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
title: Box title.
|
|
463
|
+
content: Box content.
|
|
464
|
+
is_key_finding: Style as key finding.
|
|
465
|
+
|
|
466
|
+
Returns:
|
|
467
|
+
HTML callout box string.
|
|
468
|
+
|
|
469
|
+
References:
|
|
470
|
+
REPORT-002: Visual Emphasis System
|
|
471
|
+
"""
|
|
472
|
+
css_class = "callout key-finding" if is_key_finding else "callout"
|
|
473
|
+
return f"""<div class="{css_class}">
|
|
474
|
+
<h4>{title}</h4>
|
|
475
|
+
<p>{content}</p>
|
|
476
|
+
</div>"""
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
@dataclass
|
|
480
|
+
class ExecutiveSummary:
|
|
481
|
+
"""Executive summary of analysis results.
|
|
482
|
+
|
|
483
|
+
Attributes:
|
|
484
|
+
overall_status: Pass/fail status.
|
|
485
|
+
pass_count: Number of passing tests.
|
|
486
|
+
total_count: Total number of tests.
|
|
487
|
+
key_findings: List of key findings.
|
|
488
|
+
critical_violations: List of critical violations.
|
|
489
|
+
min_margin_pct: Minimum margin percentage.
|
|
490
|
+
summary_text: Generated summary text.
|
|
491
|
+
|
|
492
|
+
References:
|
|
493
|
+
REPORT-004: Executive Summary Auto-Generation
|
|
494
|
+
"""
|
|
495
|
+
|
|
496
|
+
overall_status: bool
|
|
497
|
+
pass_count: int
|
|
498
|
+
total_count: int
|
|
499
|
+
key_findings: list[str] = field(default_factory=list)
|
|
500
|
+
critical_violations: list[str] = field(default_factory=list)
|
|
501
|
+
min_margin_pct: float | None = None
|
|
502
|
+
summary_text: str = ""
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _extract_key_findings(
|
|
506
|
+
results: dict[str, Any],
|
|
507
|
+
critical_violations: list[Any],
|
|
508
|
+
max_findings: int,
|
|
509
|
+
) -> tuple[list[str], float | None]:
|
|
510
|
+
"""Extract key findings from results."""
|
|
511
|
+
key_findings: list[str] = []
|
|
512
|
+
violations = results.get("violations", [])
|
|
513
|
+
|
|
514
|
+
# Add violation summary
|
|
515
|
+
if critical_violations:
|
|
516
|
+
key_findings.append(
|
|
517
|
+
f"{len(critical_violations)} critical violation(s) require immediate attention"
|
|
518
|
+
)
|
|
519
|
+
elif violations:
|
|
520
|
+
key_findings.append(f"{len(violations)} violation(s) detected")
|
|
521
|
+
|
|
522
|
+
# Add margin information
|
|
523
|
+
margins = results.get("margins", [])
|
|
524
|
+
min_margin = min(margins) if margins else results.get("min_margin")
|
|
525
|
+
|
|
526
|
+
if min_margin is not None and min_margin < 20:
|
|
527
|
+
status = "critical" if min_margin < 10 else "marginal"
|
|
528
|
+
key_findings.append(f"Minimum margin is {min_margin:.1f}% ({status})")
|
|
529
|
+
|
|
530
|
+
# Extract from failed measurements
|
|
531
|
+
for name, meas in results.get("measurements", {}).items():
|
|
532
|
+
if not meas.get("passed", True):
|
|
533
|
+
key_findings.append(f"{name}: FAIL - {meas.get('message', 'violation')}")
|
|
534
|
+
|
|
535
|
+
return key_findings[:max_findings], min_margin
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def _build_summary_text(
|
|
539
|
+
overall_status: bool,
|
|
540
|
+
total_count: int,
|
|
541
|
+
fail_count: int,
|
|
542
|
+
critical_violations: list[Any],
|
|
543
|
+
min_margin: float | None,
|
|
544
|
+
key_findings: list[str],
|
|
545
|
+
length: str,
|
|
546
|
+
) -> str:
|
|
547
|
+
"""Build the summary text."""
|
|
548
|
+
parts: list[str] = []
|
|
549
|
+
pass_count = total_count - fail_count
|
|
550
|
+
|
|
551
|
+
# First sentence: overall status
|
|
552
|
+
if overall_status and total_count > 0:
|
|
553
|
+
parts.append(f"All {pass_count} tests passed.")
|
|
554
|
+
elif overall_status:
|
|
555
|
+
parts.append("Analysis completed successfully.")
|
|
556
|
+
elif total_count > 0:
|
|
557
|
+
pct = fail_count / total_count * 100
|
|
558
|
+
parts.append(f"{fail_count} of {total_count} tests failed ({pct:.0f}% failure rate).")
|
|
559
|
+
else:
|
|
560
|
+
parts.append("Analysis completed with failures.")
|
|
561
|
+
|
|
562
|
+
# Add critical violations
|
|
563
|
+
if critical_violations:
|
|
564
|
+
parts.append(f"Critical: {len(critical_violations)} violation(s) require immediate action.")
|
|
565
|
+
|
|
566
|
+
# Add margin note
|
|
567
|
+
if min_margin is not None and min_margin < 10:
|
|
568
|
+
parts.append(f"Warning: Minimum margin is only {min_margin:.1f}%.")
|
|
569
|
+
|
|
570
|
+
# Key findings (for detailed mode)
|
|
571
|
+
if length == "detailed" and key_findings:
|
|
572
|
+
parts.append("\nKey Findings:")
|
|
573
|
+
parts.extend(f" - {finding}" for finding in key_findings)
|
|
574
|
+
|
|
575
|
+
return " ".join(parts)
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def generate_executive_summary(
|
|
579
|
+
results: dict[str, Any],
|
|
580
|
+
*,
|
|
581
|
+
max_findings: int = 5,
|
|
582
|
+
length: Literal["short", "detailed"] = "short",
|
|
583
|
+
) -> ExecutiveSummary:
|
|
584
|
+
"""Generate executive summary from analysis results.
|
|
585
|
+
|
|
586
|
+
Automatically extracts key findings, pass/fail status, and critical
|
|
587
|
+
violations from analysis results.
|
|
588
|
+
|
|
589
|
+
Args:
|
|
590
|
+
results: Analysis results dictionary.
|
|
591
|
+
max_findings: Maximum number of key findings to include.
|
|
592
|
+
length: Summary length (short = 1 paragraph, detailed = 1 page).
|
|
593
|
+
|
|
594
|
+
Returns:
|
|
595
|
+
ExecutiveSummary with generated content.
|
|
596
|
+
|
|
597
|
+
Example:
|
|
598
|
+
>>> results = {"pass_count": 10, "total_count": 12, "violations": [...]}
|
|
599
|
+
>>> summary = generate_executive_summary(results)
|
|
600
|
+
>>> print(summary.summary_text)
|
|
601
|
+
|
|
602
|
+
References:
|
|
603
|
+
REPORT-004: Executive Summary Auto-Generation
|
|
604
|
+
"""
|
|
605
|
+
# Extract basic counts
|
|
606
|
+
pass_count = results.get("pass_count", 0)
|
|
607
|
+
total_count = results.get("total_count", 0)
|
|
608
|
+
fail_count = total_count - pass_count if total_count else 0
|
|
609
|
+
overall_status = fail_count == 0
|
|
610
|
+
|
|
611
|
+
# Extract violations
|
|
612
|
+
violations = results.get("violations", [])
|
|
613
|
+
critical_violations = [v for v in violations if v.get("severity", "").lower() == "critical"]
|
|
614
|
+
|
|
615
|
+
# Extract key findings
|
|
616
|
+
key_findings, min_margin = _extract_key_findings(results, critical_violations, max_findings)
|
|
617
|
+
|
|
618
|
+
# Generate summary text
|
|
619
|
+
summary_text = _build_summary_text(
|
|
620
|
+
overall_status,
|
|
621
|
+
total_count,
|
|
622
|
+
fail_count,
|
|
623
|
+
critical_violations,
|
|
624
|
+
min_margin,
|
|
625
|
+
key_findings,
|
|
626
|
+
length,
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
return ExecutiveSummary(
|
|
630
|
+
overall_status=overall_status,
|
|
631
|
+
pass_count=pass_count,
|
|
632
|
+
total_count=total_count,
|
|
633
|
+
key_findings=key_findings,
|
|
634
|
+
critical_violations=[str(v) for v in critical_violations],
|
|
635
|
+
min_margin_pct=min_margin,
|
|
636
|
+
summary_text=summary_text,
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def format_executive_summary_html(summary: ExecutiveSummary) -> str:
|
|
641
|
+
"""Format executive summary as HTML.
|
|
642
|
+
|
|
643
|
+
Args:
|
|
644
|
+
summary: ExecutiveSummary to format.
|
|
645
|
+
|
|
646
|
+
Returns:
|
|
647
|
+
HTML string.
|
|
648
|
+
|
|
649
|
+
References:
|
|
650
|
+
REPORT-004: Executive Summary Auto-Generation
|
|
651
|
+
"""
|
|
652
|
+
emphasis = VisualEmphasis()
|
|
653
|
+
|
|
654
|
+
status_html = emphasis.format_pass_fail(summary.overall_status, html=True)
|
|
655
|
+
|
|
656
|
+
findings_html = ""
|
|
657
|
+
if summary.key_findings:
|
|
658
|
+
items = []
|
|
659
|
+
for finding in summary.key_findings:
|
|
660
|
+
css_class = "critical" if "critical" in finding.lower() else ""
|
|
661
|
+
items.append(f'<li class="{css_class}">{finding}</li>')
|
|
662
|
+
findings_html = f'<ul class="key-findings">{"".join(items)}</ul>'
|
|
663
|
+
|
|
664
|
+
return f"""<div class="executive-summary">
|
|
665
|
+
<h2>Executive Summary</h2>
|
|
666
|
+
<p><strong>Overall Status:</strong> {status_html}</p>
|
|
667
|
+
<p>{summary.summary_text}</p>
|
|
668
|
+
{findings_html}
|
|
669
|
+
</div>"""
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
__all__ = [
|
|
673
|
+
"ColorScheme",
|
|
674
|
+
"ExecutiveSummary",
|
|
675
|
+
"FormatStandards",
|
|
676
|
+
"Severity",
|
|
677
|
+
"VisualEmphasis",
|
|
678
|
+
"format_executive_summary_html",
|
|
679
|
+
"generate_executive_summary",
|
|
680
|
+
]
|