crackerjack 0.18.2__py3-none-any.whl → 0.45.2__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.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +96 -2
- crackerjack/__main__.py +637 -138
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +39 -0
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/lsp/_base.py +194 -0
- crackerjack/adapters/lsp/_client.py +358 -0
- crackerjack/adapters/lsp/_manager.py +193 -0
- crackerjack/adapters/lsp/skylos.py +283 -0
- crackerjack/adapters/lsp/zuban.py +557 -0
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +66 -0
- crackerjack/agents/architect_agent.py +238 -0
- crackerjack/agents/base.py +167 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +600 -0
- crackerjack/agents/documentation_agent.py +520 -0
- crackerjack/agents/dry_agent.py +585 -0
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +230 -0
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/import_optimization_agent.py +1181 -0
- crackerjack/agents/performance_agent.py +325 -0
- crackerjack/agents/performance_helpers.py +205 -0
- crackerjack/agents/proactive_agent.py +55 -0
- crackerjack/agents/refactoring_agent.py +511 -0
- crackerjack/agents/refactoring_helpers.py +247 -0
- crackerjack/agents/security_agent.py +793 -0
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +570 -0
- crackerjack/agents/test_specialist_agent.py +526 -0
- crackerjack/agents/tracker.py +110 -0
- crackerjack/api.py +647 -0
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/cache_handlers.py +209 -0
- crackerjack/cli/cache_handlers_enhanced.py +680 -0
- crackerjack/cli/facade.py +162 -0
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +700 -0
- crackerjack/cli/interactive.py +488 -0
- crackerjack/cli/options.py +1216 -0
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/utils.py +19 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +1307 -0
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +275 -0
- crackerjack/config/global_lock_config.py +207 -0
- crackerjack/config/hooks.py +390 -0
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +738 -0
- crackerjack/core/autofix_coordinator.py +282 -0
- crackerjack/core/container.py +105 -0
- crackerjack/core/enhanced_container.py +583 -0
- crackerjack/core/file_lifecycle.py +472 -0
- crackerjack/core/performance.py +244 -0
- crackerjack/core/performance_monitor.py +357 -0
- crackerjack/core/phase_coordinator.py +1227 -0
- crackerjack/core/proactive_workflow.py +267 -0
- crackerjack/core/resource_manager.py +425 -0
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +601 -0
- crackerjack/core/session_coordinator.py +239 -0
- crackerjack/core/timeout_manager.py +563 -0
- crackerjack/core/websocket_lifecycle.py +410 -0
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +2243 -0
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +767 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +1065 -0
- crackerjack/dynamic_config.py +678 -0
- crackerjack/errors.py +378 -0
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +13 -0
- crackerjack/executors/async_hook_executor.py +938 -0
- crackerjack/executors/cached_hook_executor.py +316 -0
- crackerjack/executors/hook_executor.py +1295 -0
- crackerjack/executors/hook_lock_manager.py +708 -0
- crackerjack/executors/individual_hook_executor.py +739 -0
- crackerjack/executors/lsp_aware_hook_executor.py +349 -0
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +433 -0
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +93 -0
- crackerjack/intelligence/README.md +557 -0
- crackerjack/intelligence/__init__.py +37 -0
- crackerjack/intelligence/adaptive_learning.py +693 -0
- crackerjack/intelligence/agent_orchestrator.py +485 -0
- crackerjack/intelligence/agent_registry.py +377 -0
- crackerjack/intelligence/agent_selector.py +439 -0
- crackerjack/intelligence/integration.py +250 -0
- crackerjack/interactive.py +719 -0
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +585 -0
- crackerjack/managers/publish_manager.py +631 -0
- crackerjack/managers/test_command_builder.py +391 -0
- crackerjack/managers/test_executor.py +474 -0
- crackerjack/managers/test_manager.py +1357 -0
- crackerjack/managers/test_progress.py +187 -0
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +352 -0
- crackerjack/mcp/client_runner.py +121 -0
- crackerjack/mcp/context.py +802 -0
- crackerjack/mcp/dashboard.py +657 -0
- crackerjack/mcp/enhanced_progress_monitor.py +493 -0
- crackerjack/mcp/file_monitor.py +394 -0
- crackerjack/mcp/progress_components.py +607 -0
- crackerjack/mcp/progress_monitor.py +1016 -0
- crackerjack/mcp/rate_limiter.py +336 -0
- crackerjack/mcp/server.py +24 -0
- crackerjack/mcp/server_core.py +526 -0
- crackerjack/mcp/service_watchdog.py +505 -0
- crackerjack/mcp/state.py +407 -0
- crackerjack/mcp/task_manager.py +259 -0
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +19 -0
- crackerjack/mcp/tools/core_tools.py +469 -0
- crackerjack/mcp/tools/error_analyzer.py +283 -0
- crackerjack/mcp/tools/execution_tools.py +384 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
- crackerjack/mcp/tools/intelligence_tools.py +264 -0
- crackerjack/mcp/tools/monitoring_tools.py +628 -0
- crackerjack/mcp/tools/proactive_tools.py +367 -0
- crackerjack/mcp/tools/progress_tools.py +222 -0
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +358 -0
- crackerjack/mcp/tools/workflow_executor.py +699 -0
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +54 -0
- crackerjack/mcp/websocket/endpoints.py +492 -0
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +406 -0
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
- crackerjack/mcp/websocket/server.py +174 -0
- crackerjack/mcp/websocket/websocket_handler.py +276 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +40 -0
- crackerjack/models/config.py +730 -0
- crackerjack/models/config_adapter.py +265 -0
- crackerjack/models/protocols.py +1535 -0
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/resource_protocols.py +299 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +173 -0
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +405 -0
- crackerjack/monitoring/metrics_collector.py +427 -0
- crackerjack/monitoring/regression_prevention.py +580 -0
- crackerjack/monitoring/websocket_server.py +406 -0
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +894 -0
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +180 -0
- crackerjack/orchestration/execution_strategies.py +361 -0
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +647 -0
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +254 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +264 -0
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/security/__init__.py +0 -0
- crackerjack/security/audit.py +197 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +9 -0
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/ai/contextual_ai_assistant.py +542 -0
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +617 -0
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +530 -0
- crackerjack/services/cache.py +369 -0
- crackerjack/services/changelog_automation.py +399 -0
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +132 -0
- crackerjack/services/config_merge.py +546 -0
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +493 -0
- crackerjack/services/coverage_badge_service.py +173 -0
- crackerjack/services/coverage_ratchet.py +381 -0
- crackerjack/services/debug.py +733 -0
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +622 -0
- crackerjack/services/documentation_generator.py +493 -0
- crackerjack/services/documentation_service.py +704 -0
- crackerjack/services/enhanced_filesystem.py +497 -0
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +149 -0
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +381 -0
- crackerjack/services/git.py +422 -0
- crackerjack/services/health_metrics.py +615 -0
- crackerjack/services/heatmap_generator.py +744 -0
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +823 -0
- crackerjack/services/input_validator.py +668 -0
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +289 -0
- crackerjack/services/logging.py +228 -0
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +414 -0
- crackerjack/services/metrics.py +587 -0
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/monitoring/performance_benchmarks.py +410 -0
- crackerjack/services/monitoring/performance_cache.py +388 -0
- crackerjack/services/monitoring/performance_monitor.py +569 -0
- crackerjack/services/parallel_executor.py +527 -0
- crackerjack/services/pattern_cache.py +333 -0
- crackerjack/services/pattern_detector.py +478 -0
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +523 -0
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/quality/quality_baseline.py +395 -0
- crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
- crackerjack/services/quality/quality_intelligence.py +949 -0
- crackerjack/services/regex_patterns.py +58 -0
- crackerjack/services/regex_utils.py +483 -0
- crackerjack/services/secure_path_utils.py +524 -0
- crackerjack/services/secure_status_formatter.py +450 -0
- crackerjack/services/secure_subprocess.py +635 -0
- crackerjack/services/security.py +239 -0
- crackerjack/services/security_logger.py +495 -0
- crackerjack/services/server_manager.py +411 -0
- crackerjack/services/smart_scheduling.py +167 -0
- crackerjack/services/status_authentication.py +460 -0
- crackerjack/services/status_security_manager.py +315 -0
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +441 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +43 -0
- crackerjack/services/unified_config.py +115 -0
- crackerjack/services/validation_rate_limiter.py +220 -0
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +461 -0
- crackerjack/services/version_checker.py +223 -0
- crackerjack/services/websocket_resource_limiter.py +438 -0
- crackerjack/services/zuban_lsp_service.py +391 -0
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/__init__.py +59 -0
- crackerjack/slash_commands/init.md +112 -0
- crackerjack/slash_commands/run.md +197 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_input_validator_patterns.py +236 -0
- crackerjack/tools/validate_regex_patterns.py +188 -0
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/__init__.py +1 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- crackerjack-0.45.2.dist-info/METADATA +1678 -0
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -14
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config.yaml +0 -91
- crackerjack/.pytest_cache/.gitignore +0 -2
- crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
- crackerjack/.pytest_cache/README.md +0 -8
- crackerjack/.pytest_cache/v/cache/nodeids +0 -1
- crackerjack/.pytest_cache/v/cache/stepwise +0 -1
- crackerjack/.ruff_cache/.gitignore +0 -1
- crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
- crackerjack/crackerjack.py +0 -855
- crackerjack/pyproject.toml +0 -214
- crackerjack-0.18.2.dist-info/METADATA +0 -420
- crackerjack-0.18.2.dist-info/RECORD +0 -59
- crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
"""Heat map visualization generator for error patterns and code quality metrics."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import typing as t
|
|
6
|
+
from collections import defaultdict
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from datetime import datetime, timedelta
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from .dependency_analyzer import DependencyGraph
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class HeatMapCell:
|
|
18
|
+
"""Individual cell in a heat map."""
|
|
19
|
+
|
|
20
|
+
x: int
|
|
21
|
+
y: int
|
|
22
|
+
value: float
|
|
23
|
+
label: str
|
|
24
|
+
metadata: dict[str, t.Any] = field(default_factory=dict[str, t.Any])
|
|
25
|
+
color_intensity: float = 0.0 # 0.0 to 1.0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class HeatMapData:
|
|
30
|
+
"""Complete heat map data structure."""
|
|
31
|
+
|
|
32
|
+
title: str
|
|
33
|
+
cells: list[HeatMapCell]
|
|
34
|
+
x_labels: list[str]
|
|
35
|
+
y_labels: list[str]
|
|
36
|
+
color_scale: dict[str, t.Any]
|
|
37
|
+
metadata: dict[str, t.Any] = field(default_factory=dict[str, t.Any])
|
|
38
|
+
generated_at: datetime = field(default_factory=datetime.now)
|
|
39
|
+
|
|
40
|
+
def to_dict(self) -> dict[str, t.Any]:
|
|
41
|
+
"""Convert to dictionary for JSON serialization."""
|
|
42
|
+
return {
|
|
43
|
+
"title": self.title,
|
|
44
|
+
"cells": [
|
|
45
|
+
{
|
|
46
|
+
"x": cell.x,
|
|
47
|
+
"y": cell.y,
|
|
48
|
+
"value": cell.value,
|
|
49
|
+
"label": cell.label,
|
|
50
|
+
"color_intensity": cell.color_intensity,
|
|
51
|
+
"metadata": cell.metadata,
|
|
52
|
+
}
|
|
53
|
+
for cell in self.cells
|
|
54
|
+
],
|
|
55
|
+
"x_labels": self.x_labels,
|
|
56
|
+
"y_labels": self.y_labels,
|
|
57
|
+
"color_scale": self.color_scale,
|
|
58
|
+
"metadata": self.metadata,
|
|
59
|
+
"generated_at": self.generated_at.isoformat(),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class HeatMapGenerator:
|
|
64
|
+
"""Generates heat map visualizations for various code quality metrics."""
|
|
65
|
+
|
|
66
|
+
def __init__(self) -> None:
|
|
67
|
+
"""Initialize heat map generator."""
|
|
68
|
+
self.error_data: dict[str, list[dict[str, t.Any]]] = defaultdict(list)
|
|
69
|
+
self.metric_data: dict[str, dict[str, t.Any]] = {}
|
|
70
|
+
|
|
71
|
+
# Color schemes
|
|
72
|
+
self.color_schemes = {
|
|
73
|
+
"error_intensity": {
|
|
74
|
+
"low": "#90EE90", # Light green
|
|
75
|
+
"medium": "#FFD700", # Gold
|
|
76
|
+
"high": "#FF6347", # Tomato
|
|
77
|
+
"critical": "#DC143C", # Crimson
|
|
78
|
+
},
|
|
79
|
+
"quality_score": {
|
|
80
|
+
"excellent": "#228B22", # Forest green
|
|
81
|
+
"good": "#32CD32", # Lime green
|
|
82
|
+
"average": "#FFD700", # Gold
|
|
83
|
+
"poor": "#FF6347", # Tomato
|
|
84
|
+
"critical": "#DC143C", # Crimson
|
|
85
|
+
},
|
|
86
|
+
"complexity": {
|
|
87
|
+
"simple": "#E6F3FF", # Very light blue
|
|
88
|
+
"moderate": "#B3D9FF", # Light blue
|
|
89
|
+
"complex": "#80BFFF", # Medium blue
|
|
90
|
+
"very_complex": "#4D9FFF", # Dark blue
|
|
91
|
+
"extremely_complex": "#1A5CFF", # Very dark blue
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
def add_error_data(
|
|
96
|
+
self,
|
|
97
|
+
file_path: str,
|
|
98
|
+
line_number: int,
|
|
99
|
+
error_type: str,
|
|
100
|
+
severity: str,
|
|
101
|
+
timestamp: datetime | None = None,
|
|
102
|
+
metadata: dict[str, t.Any] | None = None,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Add error data for heat map generation."""
|
|
105
|
+
if timestamp is None:
|
|
106
|
+
timestamp = datetime.now()
|
|
107
|
+
|
|
108
|
+
error_record = {
|
|
109
|
+
"file_path": file_path,
|
|
110
|
+
"line_number": line_number,
|
|
111
|
+
"error_type": error_type,
|
|
112
|
+
"severity": severity,
|
|
113
|
+
"timestamp": timestamp,
|
|
114
|
+
"metadata": metadata or {},
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
self.error_data[file_path].append(error_record)
|
|
118
|
+
|
|
119
|
+
def add_metric_data(
|
|
120
|
+
self,
|
|
121
|
+
identifier: str,
|
|
122
|
+
metrics: dict[str, float],
|
|
123
|
+
metadata: dict[str, t.Any] | None = None,
|
|
124
|
+
) -> None:
|
|
125
|
+
"""Add metric data for heat map generation."""
|
|
126
|
+
self.metric_data[identifier] = {
|
|
127
|
+
"metrics": metrics,
|
|
128
|
+
"metadata": metadata or {},
|
|
129
|
+
"timestamp": datetime.now(),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
def generate_error_frequency_heatmap(
|
|
133
|
+
self,
|
|
134
|
+
time_window: timedelta = timedelta(days=7),
|
|
135
|
+
granularity: str = "hourly", # hourly, daily, weekly
|
|
136
|
+
) -> HeatMapData:
|
|
137
|
+
"""Generate heat map showing error frequency patterns over time."""
|
|
138
|
+
now = datetime.now()
|
|
139
|
+
start_time = now - time_window
|
|
140
|
+
|
|
141
|
+
bucket_config = self._get_time_bucket_config(time_window, granularity)
|
|
142
|
+
file_paths = list[t.Any](self.error_data.keys())
|
|
143
|
+
time_buckets = self._create_time_buckets(start_time, bucket_config)
|
|
144
|
+
error_matrix = self._build_error_matrix(
|
|
145
|
+
start_time, now, time_buckets, bucket_config
|
|
146
|
+
)
|
|
147
|
+
cells = self._create_frequency_cells(file_paths, time_buckets, error_matrix)
|
|
148
|
+
labels = self._create_frequency_labels(
|
|
149
|
+
file_paths, time_buckets, bucket_config["format"]
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return HeatMapData(
|
|
153
|
+
title=f"Error Frequency Heat Map ({granularity.title()})",
|
|
154
|
+
cells=cells,
|
|
155
|
+
x_labels=labels[0],
|
|
156
|
+
y_labels=labels[1],
|
|
157
|
+
color_scale=self.color_schemes["error_intensity"],
|
|
158
|
+
metadata={
|
|
159
|
+
"granularity": granularity,
|
|
160
|
+
"time_window_days": time_window.days,
|
|
161
|
+
"max_errors": self._calculate_max_errors(error_matrix),
|
|
162
|
+
"total_files": len(file_paths),
|
|
163
|
+
},
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
def _get_time_bucket_config(
|
|
167
|
+
self, time_window: timedelta, granularity: str
|
|
168
|
+
) -> dict[str, t.Any]:
|
|
169
|
+
"""Get time bucket configuration based on granularity."""
|
|
170
|
+
if granularity == "hourly":
|
|
171
|
+
return {
|
|
172
|
+
"count": int(time_window.total_seconds() / 3600),
|
|
173
|
+
"size": timedelta(hours=1),
|
|
174
|
+
"format": "%H:%M",
|
|
175
|
+
}
|
|
176
|
+
elif granularity == "daily":
|
|
177
|
+
return {
|
|
178
|
+
"count": time_window.days,
|
|
179
|
+
"size": timedelta(days=1),
|
|
180
|
+
"format": "%m/%d",
|
|
181
|
+
}
|
|
182
|
+
# weekly
|
|
183
|
+
return {
|
|
184
|
+
"count": max(1, time_window.days // 7),
|
|
185
|
+
"size": timedelta(weeks=1),
|
|
186
|
+
"format": "Week %W",
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
def _create_time_buckets(
|
|
190
|
+
self, start_time: datetime, bucket_config: dict[str, t.Any]
|
|
191
|
+
) -> list[datetime]:
|
|
192
|
+
"""Create time buckets for the heatmap."""
|
|
193
|
+
time_buckets = []
|
|
194
|
+
current_time = start_time
|
|
195
|
+
for _ in range(bucket_config["count"]):
|
|
196
|
+
time_buckets.append(current_time)
|
|
197
|
+
current_time += bucket_config["size"]
|
|
198
|
+
return time_buckets
|
|
199
|
+
|
|
200
|
+
def _build_error_matrix(
|
|
201
|
+
self,
|
|
202
|
+
start_time: datetime,
|
|
203
|
+
end_time: datetime,
|
|
204
|
+
time_buckets: list[datetime],
|
|
205
|
+
bucket_config: dict[str, t.Any],
|
|
206
|
+
) -> dict[str, t.Any]:
|
|
207
|
+
"""Build error count matrix for files and time buckets."""
|
|
208
|
+
from collections import defaultdict
|
|
209
|
+
|
|
210
|
+
error_matrix: dict[str, t.Any] = defaultdict(
|
|
211
|
+
lambda: defaultdict(int) # type: ignore[call-overload]
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
for file_path, errors in self.error_data.items():
|
|
215
|
+
for error in errors:
|
|
216
|
+
error_time = error["timestamp"]
|
|
217
|
+
if start_time <= error_time <= end_time:
|
|
218
|
+
bucket_index = self._find_time_bucket_index(
|
|
219
|
+
error_time, start_time, time_buckets, bucket_config["size"]
|
|
220
|
+
)
|
|
221
|
+
error_matrix[file_path][bucket_index] += 1
|
|
222
|
+
|
|
223
|
+
return error_matrix
|
|
224
|
+
|
|
225
|
+
def _find_time_bucket_index(
|
|
226
|
+
self,
|
|
227
|
+
error_time: datetime,
|
|
228
|
+
start_time: datetime,
|
|
229
|
+
time_buckets: list[datetime],
|
|
230
|
+
bucket_size: timedelta,
|
|
231
|
+
) -> int:
|
|
232
|
+
"""Find the appropriate time bucket index for an error."""
|
|
233
|
+
return min(
|
|
234
|
+
len(time_buckets) - 1,
|
|
235
|
+
int(
|
|
236
|
+
(error_time - start_time).total_seconds() / bucket_size.total_seconds()
|
|
237
|
+
),
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def _create_frequency_cells(
|
|
241
|
+
self,
|
|
242
|
+
file_paths: list[str],
|
|
243
|
+
time_buckets: list[datetime],
|
|
244
|
+
error_matrix: dict[str, t.Any],
|
|
245
|
+
) -> list[HeatMapCell]:
|
|
246
|
+
"""Create heat map cells from error frequency data."""
|
|
247
|
+
cells = []
|
|
248
|
+
max_errors = self._calculate_max_errors(error_matrix)
|
|
249
|
+
|
|
250
|
+
for y, file_path in enumerate(file_paths):
|
|
251
|
+
for x in range(len(time_buckets)):
|
|
252
|
+
error_count = error_matrix[file_path][x]
|
|
253
|
+
intensity = error_count / max_errors
|
|
254
|
+
|
|
255
|
+
cell = HeatMapCell(
|
|
256
|
+
x=x,
|
|
257
|
+
y=y,
|
|
258
|
+
value=error_count,
|
|
259
|
+
label=f"{Path(file_path).name}: {error_count} errors",
|
|
260
|
+
color_intensity=intensity,
|
|
261
|
+
metadata={
|
|
262
|
+
"file_path": file_path,
|
|
263
|
+
"time_bucket": time_buckets[x].isoformat(),
|
|
264
|
+
"error_count": error_count,
|
|
265
|
+
},
|
|
266
|
+
)
|
|
267
|
+
cells.append(cell)
|
|
268
|
+
|
|
269
|
+
return cells
|
|
270
|
+
|
|
271
|
+
def _create_frequency_labels(
|
|
272
|
+
self, file_paths: list[str], time_buckets: list[datetime], x_label_format: str
|
|
273
|
+
) -> tuple[list[str], list[str]]:
|
|
274
|
+
"""Create x and y labels for frequency heatmap."""
|
|
275
|
+
x_labels = [bucket.strftime(x_label_format) for bucket in time_buckets]
|
|
276
|
+
y_labels = [Path(fp).name for fp in file_paths]
|
|
277
|
+
return x_labels, y_labels
|
|
278
|
+
|
|
279
|
+
def _calculate_max_errors(self, error_matrix: dict[str, t.Any]) -> int:
|
|
280
|
+
"""Calculate maximum error count for normalization."""
|
|
281
|
+
return (
|
|
282
|
+
max(
|
|
283
|
+
max(bucket_counts.values()) if bucket_counts else 0
|
|
284
|
+
for bucket_counts in error_matrix.values()
|
|
285
|
+
)
|
|
286
|
+
or 1
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
def generate_code_complexity_heatmap(self, project_root: str | Path) -> HeatMapData:
|
|
290
|
+
"""Generate heat map showing code complexity across files and functions."""
|
|
291
|
+
from .dependency_analyzer import analyze_project_dependencies
|
|
292
|
+
|
|
293
|
+
project_root = Path(project_root)
|
|
294
|
+
dependency_graph = analyze_project_dependencies(project_root)
|
|
295
|
+
|
|
296
|
+
file_complexity = self._extract_file_complexity_data(
|
|
297
|
+
dependency_graph, project_root
|
|
298
|
+
)
|
|
299
|
+
cells = self._create_complexity_cells(file_complexity)
|
|
300
|
+
x_labels, y_labels = self._create_complexity_labels(file_complexity, cells)
|
|
301
|
+
max_complexity = self._calculate_max_complexity(file_complexity)
|
|
302
|
+
|
|
303
|
+
return HeatMapData(
|
|
304
|
+
title="Code Complexity Heat Map",
|
|
305
|
+
cells=cells,
|
|
306
|
+
x_labels=x_labels,
|
|
307
|
+
y_labels=y_labels,
|
|
308
|
+
color_scale=self.color_schemes["complexity"],
|
|
309
|
+
metadata={
|
|
310
|
+
"max_complexity": max_complexity,
|
|
311
|
+
"total_files": len(file_complexity),
|
|
312
|
+
"complexity_threshold": 15,
|
|
313
|
+
},
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
def _extract_file_complexity_data(
|
|
317
|
+
self, dependency_graph: DependencyGraph, project_root: Path
|
|
318
|
+
) -> dict[str, t.Any]:
|
|
319
|
+
"""Extract complexity data grouped by file."""
|
|
320
|
+
file_complexity = defaultdict(list)
|
|
321
|
+
|
|
322
|
+
for node in dependency_graph.nodes.values():
|
|
323
|
+
if node.type in ("function", "method", "class"):
|
|
324
|
+
relative_path = str(Path(node.file_path).relative_to(project_root))
|
|
325
|
+
file_complexity[relative_path].append(
|
|
326
|
+
{
|
|
327
|
+
"name": node.name,
|
|
328
|
+
"complexity": node.complexity,
|
|
329
|
+
"type": node.type,
|
|
330
|
+
"line": node.line_number,
|
|
331
|
+
}
|
|
332
|
+
)
|
|
333
|
+
return file_complexity
|
|
334
|
+
|
|
335
|
+
def _create_complexity_cells(
|
|
336
|
+
self, file_complexity: dict[str, t.Any]
|
|
337
|
+
) -> list[HeatMapCell]:
|
|
338
|
+
"""Create heat map cells from complexity data."""
|
|
339
|
+
cells = []
|
|
340
|
+
files = list[t.Any](file_complexity.keys())
|
|
341
|
+
|
|
342
|
+
for y, file_path in enumerate(files):
|
|
343
|
+
from operator import itemgetter
|
|
344
|
+
|
|
345
|
+
functions = sorted(file_complexity[file_path], key=itemgetter("line"))
|
|
346
|
+
|
|
347
|
+
for x, func_data in enumerate(functions[:50]): # Limit to 50 functions
|
|
348
|
+
cell = self._create_complexity_cell(x, y, func_data, file_path)
|
|
349
|
+
cells.append(cell)
|
|
350
|
+
|
|
351
|
+
return cells
|
|
352
|
+
|
|
353
|
+
def _create_complexity_cell(
|
|
354
|
+
self, x: int, y: int, func_data: dict[str, t.Any], file_path: str
|
|
355
|
+
) -> HeatMapCell:
|
|
356
|
+
"""Create a single complexity heat map cell."""
|
|
357
|
+
complexity = func_data["complexity"]
|
|
358
|
+
intensity = min(1.0, complexity / 15) # Normalize to complexity threshold
|
|
359
|
+
complexity_level = self._get_complexity_level(complexity)
|
|
360
|
+
|
|
361
|
+
return HeatMapCell(
|
|
362
|
+
x=x,
|
|
363
|
+
y=y,
|
|
364
|
+
value=complexity,
|
|
365
|
+
label=f"{func_data['name']}: {complexity}",
|
|
366
|
+
color_intensity=intensity,
|
|
367
|
+
metadata={
|
|
368
|
+
"file_path": file_path,
|
|
369
|
+
"function_name": func_data["name"],
|
|
370
|
+
"function_type": func_data["type"],
|
|
371
|
+
"line_number": func_data["line"],
|
|
372
|
+
"complexity": complexity,
|
|
373
|
+
"complexity_level": complexity_level,
|
|
374
|
+
},
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
def _get_complexity_level(self, complexity: int) -> str:
|
|
378
|
+
"""Determine complexity category based on value."""
|
|
379
|
+
if complexity <= 5:
|
|
380
|
+
return "simple"
|
|
381
|
+
elif complexity <= 10:
|
|
382
|
+
return "moderate"
|
|
383
|
+
elif complexity <= 15:
|
|
384
|
+
return "complex"
|
|
385
|
+
elif complexity <= 20:
|
|
386
|
+
return "very_complex"
|
|
387
|
+
return "extremely_complex"
|
|
388
|
+
|
|
389
|
+
def _create_complexity_labels(
|
|
390
|
+
self, file_complexity: dict[str, t.Any], cells: list[HeatMapCell]
|
|
391
|
+
) -> tuple[list[str], list[str]]:
|
|
392
|
+
"""Create x and y labels for complexity heat map."""
|
|
393
|
+
files = list[t.Any](file_complexity.keys())
|
|
394
|
+
y_labels = [Path(fp).name for fp in files]
|
|
395
|
+
|
|
396
|
+
max_x = max(cell.x for cell in cells) if cells else 0
|
|
397
|
+
x_labels = [f"Func {x + 1}" for x in range(max_x + 1)]
|
|
398
|
+
|
|
399
|
+
return x_labels, y_labels
|
|
400
|
+
|
|
401
|
+
def _calculate_max_complexity(self, file_complexity: dict[str, t.Any]) -> int:
|
|
402
|
+
"""Calculate maximum complexity value across all functions."""
|
|
403
|
+
return (
|
|
404
|
+
max(
|
|
405
|
+
max(item["complexity"] for item in items)
|
|
406
|
+
for items in file_complexity.values()
|
|
407
|
+
if items
|
|
408
|
+
)
|
|
409
|
+
or 1
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
def generate_quality_metrics_heatmap(self) -> HeatMapData:
|
|
413
|
+
"""Generate heat map showing various quality metrics."""
|
|
414
|
+
if not self.metric_data:
|
|
415
|
+
return self._get_default_quality_heatmap()
|
|
416
|
+
|
|
417
|
+
metric_types = self._get_quality_metric_types()
|
|
418
|
+
identifiers = list[t.Any](self.metric_data.keys())
|
|
419
|
+
max_values = self._calculate_metric_max_values(metric_types)
|
|
420
|
+
cells = self._create_quality_metric_cells(identifiers, metric_types, max_values)
|
|
421
|
+
|
|
422
|
+
return HeatMapData(
|
|
423
|
+
title="Quality Metrics Heat Map",
|
|
424
|
+
cells=cells,
|
|
425
|
+
x_labels=metric_types,
|
|
426
|
+
y_labels=identifiers,
|
|
427
|
+
color_scale=self.color_schemes["quality_score"],
|
|
428
|
+
metadata={
|
|
429
|
+
"metric_count": len(metric_types),
|
|
430
|
+
"entity_count": len(identifiers),
|
|
431
|
+
},
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
def _get_default_quality_heatmap(self) -> HeatMapData:
|
|
435
|
+
"""Return default quality heatmap for empty data."""
|
|
436
|
+
return HeatMapData(
|
|
437
|
+
title="Quality Metrics Heat Map",
|
|
438
|
+
cells=[],
|
|
439
|
+
x_labels=[],
|
|
440
|
+
y_labels=[],
|
|
441
|
+
color_scale=self.color_schemes["quality_score"],
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
def _get_quality_metric_types(self) -> list[str]:
|
|
445
|
+
"""Define metric types to visualize."""
|
|
446
|
+
return [
|
|
447
|
+
"test_coverage",
|
|
448
|
+
"complexity_score",
|
|
449
|
+
"duplication_ratio",
|
|
450
|
+
"documentation_ratio",
|
|
451
|
+
"security_score",
|
|
452
|
+
"performance_score",
|
|
453
|
+
]
|
|
454
|
+
|
|
455
|
+
def _calculate_metric_max_values(self, metric_types: list[str]) -> dict[str, float]:
|
|
456
|
+
"""Calculate max values for normalization."""
|
|
457
|
+
max_values: dict[str, float] = {}
|
|
458
|
+
for metric_type in metric_types:
|
|
459
|
+
values = [
|
|
460
|
+
data["metrics"][metric_type]
|
|
461
|
+
for data in self.metric_data.values()
|
|
462
|
+
if metric_type in data["metrics"]
|
|
463
|
+
]
|
|
464
|
+
max_values[metric_type] = max(values) if values else 1
|
|
465
|
+
return max_values
|
|
466
|
+
|
|
467
|
+
def _create_quality_metric_cells(
|
|
468
|
+
self,
|
|
469
|
+
identifiers: list[str],
|
|
470
|
+
metric_types: list[str],
|
|
471
|
+
max_values: dict[str, float],
|
|
472
|
+
) -> list[HeatMapCell]:
|
|
473
|
+
"""Create cells for quality metrics heatmap."""
|
|
474
|
+
cells = []
|
|
475
|
+
for y, identifier in enumerate(identifiers):
|
|
476
|
+
data = self.metric_data[identifier]
|
|
477
|
+
metrics = data["metrics"]
|
|
478
|
+
|
|
479
|
+
for x, metric_type in enumerate(metric_types):
|
|
480
|
+
value = metrics.get(metric_type, 0)
|
|
481
|
+
intensity = value / max_values[metric_type]
|
|
482
|
+
quality_score = self._calculate_quality_score(metric_type, intensity)
|
|
483
|
+
quality_level = self._determine_quality_level(quality_score)
|
|
484
|
+
|
|
485
|
+
cell = HeatMapCell(
|
|
486
|
+
x=x,
|
|
487
|
+
y=y,
|
|
488
|
+
value=value,
|
|
489
|
+
label=f"{identifier}: {metric_type} = {value:.2f}",
|
|
490
|
+
color_intensity=quality_score,
|
|
491
|
+
metadata={
|
|
492
|
+
"identifier": identifier,
|
|
493
|
+
"metric_type": metric_type,
|
|
494
|
+
"raw_value": value,
|
|
495
|
+
"quality_score": quality_score,
|
|
496
|
+
"quality_level": quality_level,
|
|
497
|
+
},
|
|
498
|
+
)
|
|
499
|
+
cells.append(cell)
|
|
500
|
+
return cells
|
|
501
|
+
|
|
502
|
+
def _calculate_quality_score(self, metric_type: str, intensity: float) -> float:
|
|
503
|
+
"""Calculate quality score for a metric (higher is better for most metrics)."""
|
|
504
|
+
if metric_type in ("complexity_score", "duplication_ratio"):
|
|
505
|
+
# Lower is better for these metrics
|
|
506
|
+
return 1.0 - min(1.0, intensity)
|
|
507
|
+
# Higher is better
|
|
508
|
+
return intensity
|
|
509
|
+
|
|
510
|
+
def _determine_quality_level(self, quality_score: float) -> str:
|
|
511
|
+
"""Determine quality level from quality score."""
|
|
512
|
+
if quality_score >= 0.9:
|
|
513
|
+
return "excellent"
|
|
514
|
+
elif quality_score >= 0.7:
|
|
515
|
+
return "good"
|
|
516
|
+
elif quality_score >= 0.5:
|
|
517
|
+
return "average"
|
|
518
|
+
elif quality_score >= 0.3:
|
|
519
|
+
return "poor"
|
|
520
|
+
return "critical"
|
|
521
|
+
|
|
522
|
+
def generate_test_failure_heatmap(
|
|
523
|
+
self, time_window: timedelta = timedelta(days=14)
|
|
524
|
+
) -> HeatMapData:
|
|
525
|
+
"""Generate heat map showing test failure patterns."""
|
|
526
|
+
test_errors = self._filter_test_errors(time_window)
|
|
527
|
+
test_matrix = self._group_test_errors_by_matrix(test_errors)
|
|
528
|
+
test_files = list[t.Any](test_matrix.keys())
|
|
529
|
+
error_types = self._collect_error_types(test_matrix)
|
|
530
|
+
max_failures = self._calculate_max_test_failures(test_matrix)
|
|
531
|
+
cells = self._create_test_failure_cells(
|
|
532
|
+
test_matrix, test_files, error_types, max_failures
|
|
533
|
+
)
|
|
534
|
+
metadata = self._build_test_failure_metadata(
|
|
535
|
+
time_window, max_failures, test_files, error_types
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
return HeatMapData(
|
|
539
|
+
title="Test Failure Heat Map",
|
|
540
|
+
cells=cells,
|
|
541
|
+
x_labels=error_types,
|
|
542
|
+
y_labels=test_files,
|
|
543
|
+
color_scale=self.color_schemes["error_intensity"],
|
|
544
|
+
metadata=metadata,
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
def _filter_test_errors(self, time_window: timedelta) -> list[dict[str, t.Any]]:
|
|
548
|
+
"""Filter for test-related errors within the time window."""
|
|
549
|
+
test_errors: list[dict[str, t.Any]] = []
|
|
550
|
+
now = datetime.now()
|
|
551
|
+
start_time = now - time_window
|
|
552
|
+
|
|
553
|
+
for file_path, errors in self.error_data.items():
|
|
554
|
+
for error in errors:
|
|
555
|
+
if error["timestamp"] >= start_time and (
|
|
556
|
+
"test" in error["error_type"].lower() or "test" in file_path.lower()
|
|
557
|
+
):
|
|
558
|
+
test_errors.append(error | {"file_path": file_path})
|
|
559
|
+
return test_errors
|
|
560
|
+
|
|
561
|
+
def _group_test_errors_by_matrix(
|
|
562
|
+
self, test_errors: list[dict[str, t.Any]]
|
|
563
|
+
) -> defaultdict[str, defaultdict[str, int]]:
|
|
564
|
+
"""Group test errors by file and error type."""
|
|
565
|
+
from collections import defaultdict as dd
|
|
566
|
+
|
|
567
|
+
def make_inner_defaultdict() -> defaultdict[str, int]:
|
|
568
|
+
return dd(int)
|
|
569
|
+
|
|
570
|
+
test_matrix: defaultdict[str, defaultdict[str, int]] = defaultdict(
|
|
571
|
+
make_inner_defaultdict
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
for error in test_errors:
|
|
575
|
+
file_name = Path(error["file_path"]).name
|
|
576
|
+
error_type = error["error_type"]
|
|
577
|
+
test_matrix[file_name][error_type] += 1
|
|
578
|
+
|
|
579
|
+
return test_matrix
|
|
580
|
+
|
|
581
|
+
def _collect_error_types(
|
|
582
|
+
self, test_matrix: defaultdict[str, defaultdict[str, int]]
|
|
583
|
+
) -> list[str]:
|
|
584
|
+
"""Collect all unique error types from the test matrix."""
|
|
585
|
+
all_error_types: set[str] = set()
|
|
586
|
+
for error_types in test_matrix.values():
|
|
587
|
+
all_error_types.update(error_types.keys())
|
|
588
|
+
return list[t.Any](all_error_types)
|
|
589
|
+
|
|
590
|
+
def _calculate_max_test_failures(
|
|
591
|
+
self, test_matrix: defaultdict[str, defaultdict[str, int]]
|
|
592
|
+
) -> int:
|
|
593
|
+
"""Calculate maximum failures for normalization."""
|
|
594
|
+
return (
|
|
595
|
+
max(
|
|
596
|
+
max(error_counts.values()) if error_counts else 0
|
|
597
|
+
for error_counts in test_matrix.values()
|
|
598
|
+
)
|
|
599
|
+
or 1
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
def _create_test_failure_cells(
|
|
603
|
+
self,
|
|
604
|
+
test_matrix: defaultdict[str, defaultdict[str, int]],
|
|
605
|
+
test_files: list[str],
|
|
606
|
+
error_types: list[str],
|
|
607
|
+
max_failures: int,
|
|
608
|
+
) -> list[HeatMapCell]:
|
|
609
|
+
"""Create cells for the test failure heatmap."""
|
|
610
|
+
cells = []
|
|
611
|
+
for y, test_file in enumerate(test_files):
|
|
612
|
+
for x, error_type in enumerate(error_types):
|
|
613
|
+
failure_count = test_matrix[test_file][error_type]
|
|
614
|
+
intensity = failure_count / max_failures
|
|
615
|
+
|
|
616
|
+
cell = HeatMapCell(
|
|
617
|
+
x=x,
|
|
618
|
+
y=y,
|
|
619
|
+
value=failure_count,
|
|
620
|
+
label=f"{test_file}: {error_type} ({failure_count} failures)",
|
|
621
|
+
color_intensity=intensity,
|
|
622
|
+
metadata={
|
|
623
|
+
"test_file": test_file,
|
|
624
|
+
"error_type": error_type,
|
|
625
|
+
"failure_count": failure_count,
|
|
626
|
+
},
|
|
627
|
+
)
|
|
628
|
+
cells.append(cell)
|
|
629
|
+
return cells
|
|
630
|
+
|
|
631
|
+
def _build_test_failure_metadata(
|
|
632
|
+
self,
|
|
633
|
+
time_window: timedelta,
|
|
634
|
+
max_failures: int,
|
|
635
|
+
test_files: list[str],
|
|
636
|
+
error_types: list[str],
|
|
637
|
+
) -> dict[str, t.Any]:
|
|
638
|
+
"""Build metadata dictionary for test failure heatmap."""
|
|
639
|
+
return {
|
|
640
|
+
"time_window_days": time_window.days,
|
|
641
|
+
"max_failures": max_failures,
|
|
642
|
+
"total_test_files": len(test_files),
|
|
643
|
+
"total_error_types": len(error_types),
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
def export_heatmap_data(
|
|
647
|
+
self, heatmap: HeatMapData, output_path: str | Path, format_type: str = "json"
|
|
648
|
+
) -> None:
|
|
649
|
+
"""Export heat map data to file."""
|
|
650
|
+
if format_type.lower() == "json":
|
|
651
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
652
|
+
json.dump(heatmap.to_dict(), f, indent=2)
|
|
653
|
+
elif format_type.lower() == "csv":
|
|
654
|
+
import csv
|
|
655
|
+
|
|
656
|
+
with open(output_path, "w", newline="", encoding="utf-8") as f:
|
|
657
|
+
writer = csv.writer(f)
|
|
658
|
+
|
|
659
|
+
# Write header
|
|
660
|
+
writer.writerow(["x", "y", "value", "label", "intensity"])
|
|
661
|
+
|
|
662
|
+
# Write data
|
|
663
|
+
for cell in heatmap.cells:
|
|
664
|
+
writer.writerow(
|
|
665
|
+
[
|
|
666
|
+
cell.x,
|
|
667
|
+
cell.y,
|
|
668
|
+
cell.value,
|
|
669
|
+
cell.label,
|
|
670
|
+
cell.color_intensity,
|
|
671
|
+
]
|
|
672
|
+
)
|
|
673
|
+
else:
|
|
674
|
+
msg = f"Unsupported format: {format_type}"
|
|
675
|
+
raise ValueError(msg)
|
|
676
|
+
|
|
677
|
+
def generate_html_visualization(self, heatmap: HeatMapData) -> str:
|
|
678
|
+
"""Generate HTML visualization for the heat map."""
|
|
679
|
+
html_template = """
|
|
680
|
+
<!DOCTYPE html>
|
|
681
|
+
<html>
|
|
682
|
+
<head>
|
|
683
|
+
<title>{title}</title>
|
|
684
|
+
<style>
|
|
685
|
+
body {{ font-family: Arial, sans-serif; margin: 20px; }}
|
|
686
|
+
.heatmap {{ display: grid; gap: 1px; background: #ddd; }}
|
|
687
|
+
.cell {{
|
|
688
|
+
padding: 5px;
|
|
689
|
+
text-align: center;
|
|
690
|
+
font-size: 10px;
|
|
691
|
+
min-width: 80px;
|
|
692
|
+
min-height: 20px;
|
|
693
|
+
}}
|
|
694
|
+
.legend {{ margin-top: 20px; }}
|
|
695
|
+
.legend-item {{ display: inline-block; margin: 0 10px; }}
|
|
696
|
+
</style>
|
|
697
|
+
</head>
|
|
698
|
+
<body>
|
|
699
|
+
<h1>{title}</h1>
|
|
700
|
+
<div class="heatmap" style="grid-template-columns: repeat({cols}, 1fr);">
|
|
701
|
+
{cells_html}
|
|
702
|
+
</div>
|
|
703
|
+
<div class="legend">
|
|
704
|
+
{legend_html}
|
|
705
|
+
</div>
|
|
706
|
+
<p>Generated at: {generated_at}</p>
|
|
707
|
+
</body>
|
|
708
|
+
</html>
|
|
709
|
+
"""
|
|
710
|
+
|
|
711
|
+
# Generate cells HTML
|
|
712
|
+
cells_html = ""
|
|
713
|
+
for cell in heatmap.cells:
|
|
714
|
+
# Calculate color based on intensity
|
|
715
|
+
intensity = int(255 * (1 - cell.color_intensity))
|
|
716
|
+
color = f"rgb({255}, {intensity}, {intensity})"
|
|
717
|
+
|
|
718
|
+
cells_html += f"""
|
|
719
|
+
<div class="cell" style="background-color: {color};"
|
|
720
|
+
title="{cell.label}">
|
|
721
|
+
{cell.value:.1f}
|
|
722
|
+
</div>
|
|
723
|
+
"""
|
|
724
|
+
|
|
725
|
+
# Generate legend HTML
|
|
726
|
+
legend_html = ""
|
|
727
|
+
for level, color in heatmap.color_scale.items():
|
|
728
|
+
legend_html += f"""
|
|
729
|
+
<div class="legend-item">
|
|
730
|
+
<span style="background-color: {color}; padding: 2px 8px;">
|
|
731
|
+
{level.title()}
|
|
732
|
+
</span>
|
|
733
|
+
</div>
|
|
734
|
+
"""
|
|
735
|
+
|
|
736
|
+
max_x = max(cell.x for cell in heatmap.cells) if heatmap.cells else 1
|
|
737
|
+
|
|
738
|
+
return html_template.format(
|
|
739
|
+
title=heatmap.title,
|
|
740
|
+
cols=max_x + 1,
|
|
741
|
+
cells_html=cells_html,
|
|
742
|
+
legend_html=legend_html,
|
|
743
|
+
generated_at=heatmap.generated_at.isoformat(),
|
|
744
|
+
)
|