roadmap-cli 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roadmap/__init__.py +27 -0
- roadmap/adapters/__init__.py +1 -0
- roadmap/adapters/base_paginated_adapter.py +117 -0
- roadmap/adapters/cli/__init__.py +249 -0
- roadmap/adapters/cli/analysis/__init__.py +5 -0
- roadmap/adapters/cli/analysis/commands.py +370 -0
- roadmap/adapters/cli/analysis/presenter.py +125 -0
- roadmap/adapters/cli/archive_operations.py +86 -0
- roadmap/adapters/cli/cli_command_helpers.py +109 -0
- roadmap/adapters/cli/cli_confirmations.py +74 -0
- roadmap/adapters/cli/cli_error_handlers.py +146 -0
- roadmap/adapters/cli/cli_validators.py +39 -0
- roadmap/adapters/cli/commands/sync_status.py +308 -0
- roadmap/adapters/cli/comment/__init__.py +5 -0
- roadmap/adapters/cli/comment/commands.py +106 -0
- roadmap/adapters/cli/config/__init__.py +5 -0
- roadmap/adapters/cli/config/commands.py +198 -0
- roadmap/adapters/cli/console_exports.py +10 -0
- roadmap/adapters/cli/core.py +26 -0
- roadmap/adapters/cli/crud/__init__.py +47 -0
- roadmap/adapters/cli/crud/base_archive.py +208 -0
- roadmap/adapters/cli/crud/base_create.py +154 -0
- roadmap/adapters/cli/crud/base_delete.py +155 -0
- roadmap/adapters/cli/crud/base_restore.py +283 -0
- roadmap/adapters/cli/crud/base_update.py +193 -0
- roadmap/adapters/cli/crud/crud_helpers.py +206 -0
- roadmap/adapters/cli/crud/crud_utils.py +152 -0
- roadmap/adapters/cli/crud/entity_builders.py +374 -0
- roadmap/adapters/cli/data/__init__.py +8 -0
- roadmap/adapters/cli/data/commands.py +149 -0
- roadmap/adapters/cli/decorators.py +208 -0
- roadmap/adapters/cli/dtos/__init__.py +155 -0
- roadmap/adapters/cli/exception_handler.py +105 -0
- roadmap/adapters/cli/git/__init__.py +8 -0
- roadmap/adapters/cli/git/commands.py +475 -0
- roadmap/adapters/cli/git/handlers/__init__.py +1 -0
- roadmap/adapters/cli/git/handlers/git_authentication_handler.py +124 -0
- roadmap/adapters/cli/git/handlers/git_branch_handler.py +238 -0
- roadmap/adapters/cli/git/handlers/git_connectivity_handler.py +122 -0
- roadmap/adapters/cli/git/handlers/git_hooks_handler.py +105 -0
- roadmap/adapters/cli/git/hooks_config.py +327 -0
- roadmap/adapters/cli/git/status_display.py +158 -0
- roadmap/adapters/cli/health/__init__.py +8 -0
- roadmap/adapters/cli/health/db_integrity.py +204 -0
- roadmap/adapters/cli/health/enhancer.py +227 -0
- roadmap/adapters/cli/health/fixer.py +295 -0
- roadmap/adapters/cli/health/fixers/__init__.py +10 -0
- roadmap/adapters/cli/health/fixers/corrupted_comments_fixer.py +231 -0
- roadmap/adapters/cli/health/fixers/data_integrity_fixer.py +137 -0
- roadmap/adapters/cli/health/fixers/duplicate_issues_fixer.py +138 -0
- roadmap/adapters/cli/health/fixers/folder_structure_fixer.py +114 -0
- roadmap/adapters/cli/health/fixers/label_normalization_fixer.py +162 -0
- roadmap/adapters/cli/health/fixers/milestone_name_normalization_fixer.py +181 -0
- roadmap/adapters/cli/health/fixers/milestone_naming_compliance_fixer.py +244 -0
- roadmap/adapters/cli/health/fixers/milestone_parse_error_fixer.py +162 -0
- roadmap/adapters/cli/health/fixers/milestone_validation_fixer.py +211 -0
- roadmap/adapters/cli/health/fixers/old_backups_fixer.py +135 -0
- roadmap/adapters/cli/health/fixers/orphaned_issues_fixer.py +252 -0
- roadmap/adapters/cli/health/formatter.py +250 -0
- roadmap/adapters/cli/health/formatters.py +427 -0
- roadmap/adapters/cli/health/scan.py +293 -0
- roadmap/adapters/cli/init/__init__.py +5 -0
- roadmap/adapters/cli/init/commands.py +587 -0
- roadmap/adapters/cli/init.py +9 -0
- roadmap/adapters/cli/issues/__init__.py +70 -0
- roadmap/adapters/cli/issues/archive.py +137 -0
- roadmap/adapters/cli/issues/archive_class.py +253 -0
- roadmap/adapters/cli/issues/block.py +39 -0
- roadmap/adapters/cli/issues/close.py +216 -0
- roadmap/adapters/cli/issues/comment.py +178 -0
- roadmap/adapters/cli/issues/create.py +162 -0
- roadmap/adapters/cli/issues/delete.py +31 -0
- roadmap/adapters/cli/issues/deps.py +161 -0
- roadmap/adapters/cli/issues/issue_status_helpers.py +111 -0
- roadmap/adapters/cli/issues/link.py +308 -0
- roadmap/adapters/cli/issues/list.py +402 -0
- roadmap/adapters/cli/issues/lookup.py +96 -0
- roadmap/adapters/cli/issues/progress.py +102 -0
- roadmap/adapters/cli/issues/restore.py +96 -0
- roadmap/adapters/cli/issues/restore_class.py +57 -0
- roadmap/adapters/cli/issues/start.py +165 -0
- roadmap/adapters/cli/issues/sync_status.py +374 -0
- roadmap/adapters/cli/issues/unblock.py +43 -0
- roadmap/adapters/cli/issues/unlink.py +104 -0
- roadmap/adapters/cli/issues/update.py +120 -0
- roadmap/adapters/cli/issues/view.py +36 -0
- roadmap/adapters/cli/layout.py +220 -0
- roadmap/adapters/cli/mappers.py +200 -0
- roadmap/adapters/cli/milestones/__init__.py +52 -0
- roadmap/adapters/cli/milestones/archive.py +125 -0
- roadmap/adapters/cli/milestones/archive_class.py +236 -0
- roadmap/adapters/cli/milestones/assign.py +62 -0
- roadmap/adapters/cli/milestones/close.py +177 -0
- roadmap/adapters/cli/milestones/create.py +131 -0
- roadmap/adapters/cli/milestones/delete.py +27 -0
- roadmap/adapters/cli/milestones/kanban.py +123 -0
- roadmap/adapters/cli/milestones/list.py +87 -0
- roadmap/adapters/cli/milestones/recalculate.py +79 -0
- roadmap/adapters/cli/milestones/restore.py +87 -0
- roadmap/adapters/cli/milestones/restore_class.py +99 -0
- roadmap/adapters/cli/milestones/update.py +61 -0
- roadmap/adapters/cli/milestones/view.py +142 -0
- roadmap/adapters/cli/output_manager.py +319 -0
- roadmap/adapters/cli/presentation/base_presenter.py +121 -0
- roadmap/adapters/cli/presentation/cleanup_presenter.py +235 -0
- roadmap/adapters/cli/presentation/core_initialization_presenter.py +277 -0
- roadmap/adapters/cli/presentation/crud_presenter.py +134 -0
- roadmap/adapters/cli/presentation/daily_summary_presenter.py +266 -0
- roadmap/adapters/cli/presentation/issue_presenter.py +258 -0
- roadmap/adapters/cli/presentation/milestone_list_presenter.py +135 -0
- roadmap/adapters/cli/presentation/milestone_presenter.py +297 -0
- roadmap/adapters/cli/presentation/project_presenter.py +233 -0
- roadmap/adapters/cli/presentation/project_status_presenter.py +186 -0
- roadmap/adapters/cli/presentation/table_builders.py +95 -0
- roadmap/adapters/cli/projects/__init__.py +42 -0
- roadmap/adapters/cli/projects/archive.py +127 -0
- roadmap/adapters/cli/projects/archive_class.py +96 -0
- roadmap/adapters/cli/projects/close.py +94 -0
- roadmap/adapters/cli/projects/create.py +50 -0
- roadmap/adapters/cli/projects/delete.py +27 -0
- roadmap/adapters/cli/projects/list.py +227 -0
- roadmap/adapters/cli/projects/restore.py +87 -0
- roadmap/adapters/cli/projects/restore_class.py +56 -0
- roadmap/adapters/cli/projects/update.py +57 -0
- roadmap/adapters/cli/projects/view.py +157 -0
- roadmap/adapters/cli/services/__init__.py +1 -0
- roadmap/adapters/cli/services/daily_summary_service.py +313 -0
- roadmap/adapters/cli/services/export_manager.py +268 -0
- roadmap/adapters/cli/services/milestone_list_service.py +266 -0
- roadmap/adapters/cli/services/project_initialization_service.py +29 -0
- roadmap/adapters/cli/services/project_status_service.py +462 -0
- roadmap/adapters/cli/services/sync_service.py +62 -0
- roadmap/adapters/cli/status.py +408 -0
- roadmap/adapters/cli/styling.py +21 -0
- roadmap/adapters/cli/sync.py +1117 -0
- roadmap/adapters/cli/sync_context.py +432 -0
- roadmap/adapters/cli/sync_handlers/__init__.py +77 -0
- roadmap/adapters/cli/sync_handlers/apply_ops.py +522 -0
- roadmap/adapters/cli/sync_handlers/baseline_ops.py +366 -0
- roadmap/adapters/cli/sync_handlers/conflict_ops.py +161 -0
- roadmap/adapters/cli/sync_handlers/dry_run_display.py +237 -0
- roadmap/adapters/cli/sync_handlers/interactive_resolver.py +396 -0
- roadmap/adapters/cli/sync_handlers/progress_tracker.py +128 -0
- roadmap/adapters/cli/sync_metrics_command.py +190 -0
- roadmap/adapters/cli/sync_presenter.py +58 -0
- roadmap/adapters/cli/sync_validation.py +295 -0
- roadmap/adapters/cli/today.py +52 -0
- roadmap/adapters/cli/utils/click_options.py +38 -0
- roadmap/adapters/git/__init__.py +1 -0
- roadmap/adapters/git/git.py +208 -0
- roadmap/adapters/git/git_branch_manager.py +400 -0
- roadmap/adapters/git/git_command_executor.py +56 -0
- roadmap/adapters/git/git_commit_analyzer.py +334 -0
- roadmap/adapters/git/git_hooks.py +22 -0
- roadmap/adapters/git/git_hooks_manager.py +455 -0
- roadmap/adapters/git/git_repository_info.py +65 -0
- roadmap/adapters/git/hook_installer.py +106 -0
- roadmap/adapters/git/hook_registry.py +97 -0
- roadmap/adapters/git/hook_script_generator.py +58 -0
- roadmap/adapters/git/sync_monitor.py +409 -0
- roadmap/adapters/git/workflow_automation.py +286 -0
- roadmap/adapters/github/__init__.py +1 -0
- roadmap/adapters/github/github.py +360 -0
- roadmap/adapters/github/handlers/__init__.py +17 -0
- roadmap/adapters/github/handlers/base.py +337 -0
- roadmap/adapters/github/handlers/collaborators.py +165 -0
- roadmap/adapters/github/handlers/comments.py +118 -0
- roadmap/adapters/github/handlers/issues.py +150 -0
- roadmap/adapters/github/handlers/labels.py +164 -0
- roadmap/adapters/github/handlers/milestones.py +104 -0
- roadmap/adapters/persistence/__init__.py +35 -0
- roadmap/adapters/persistence/conflict_resolver.py +190 -0
- roadmap/adapters/persistence/database_manager.py +492 -0
- roadmap/adapters/persistence/entity_sync_coordinators.py +672 -0
- roadmap/adapters/persistence/file_locking.py +348 -0
- roadmap/adapters/persistence/file_parser.py +92 -0
- roadmap/adapters/persistence/file_synchronizer.py +107 -0
- roadmap/adapters/persistence/focused_managers.py +140 -0
- roadmap/adapters/persistence/git_history.py +330 -0
- roadmap/adapters/persistence/parser/__init__.py +20 -0
- roadmap/adapters/persistence/parser/frontmatter.py +152 -0
- roadmap/adapters/persistence/parser/issue.py +245 -0
- roadmap/adapters/persistence/parser/milestone.py +146 -0
- roadmap/adapters/persistence/parser/project.py +106 -0
- roadmap/adapters/persistence/persistence.py +288 -0
- roadmap/adapters/persistence/repositories/__init__.py +15 -0
- roadmap/adapters/persistence/repositories/issue_repository.py +223 -0
- roadmap/adapters/persistence/repositories/milestone_repository.py +140 -0
- roadmap/adapters/persistence/repositories/project_repository.py +168 -0
- roadmap/adapters/persistence/repositories/remote_link_repository.py +355 -0
- roadmap/adapters/persistence/repositories/sync_state_repository.py +66 -0
- roadmap/adapters/persistence/storage/__init__.py +36 -0
- roadmap/adapters/persistence/storage/conflicts.py +101 -0
- roadmap/adapters/persistence/storage/connection_manager.py +79 -0
- roadmap/adapters/persistence/storage/issue_storage.py +89 -0
- roadmap/adapters/persistence/storage/milestone_storage.py +80 -0
- roadmap/adapters/persistence/storage/project_storage.py +102 -0
- roadmap/adapters/persistence/storage/queries.py +227 -0
- roadmap/adapters/persistence/storage/state_manager.py +652 -0
- roadmap/adapters/persistence/storage/sync_state_storage.py +207 -0
- roadmap/adapters/persistence/sync_metrics_repository.py +342 -0
- roadmap/adapters/persistence/sync_orchestrator.py +293 -0
- roadmap/adapters/persistence/sync_state_tracker.py +86 -0
- roadmap/adapters/persistence/yaml_repositories.py +711 -0
- roadmap/adapters/sync/__init__.py +21 -0
- roadmap/adapters/sync/backend_factory.py +108 -0
- roadmap/adapters/sync/backends/__init__.py +15 -0
- roadmap/adapters/sync/backends/converters.py +209 -0
- roadmap/adapters/sync/backends/github_backend_helpers.py +380 -0
- roadmap/adapters/sync/backends/github_client.py +99 -0
- roadmap/adapters/sync/backends/github_sync_backend.py +792 -0
- roadmap/adapters/sync/backends/github_sync_ops.py +908 -0
- roadmap/adapters/sync/backends/services/__init__.py +1 -0
- roadmap/adapters/sync/backends/services/github_authentication_service.py +105 -0
- roadmap/adapters/sync/backends/services/github_issue_delete_service.py +423 -0
- roadmap/adapters/sync/backends/services/github_issue_dependency_service.py +63 -0
- roadmap/adapters/sync/backends/services/github_issue_fetch_service.py +158 -0
- roadmap/adapters/sync/backends/services/github_label_sync_service.py +126 -0
- roadmap/adapters/sync/backends/services/github_local_issue_sync_service.py +229 -0
- roadmap/adapters/sync/backends/services/github_milestone_fetch_service.py +204 -0
- roadmap/adapters/sync/backends/vanilla_git_sync_backend.py +198 -0
- roadmap/adapters/sync/services/__init__.py +11 -0
- roadmap/adapters/sync/services/baseline_state_handler.py +83 -0
- roadmap/adapters/sync/services/conflict_converter.py +85 -0
- roadmap/adapters/sync/services/issue_persistence_service.py +263 -0
- roadmap/adapters/sync/services/issue_state_service.py +243 -0
- roadmap/adapters/sync/services/local_change_filter.py +129 -0
- roadmap/adapters/sync/services/pull_result_processor.py +71 -0
- roadmap/adapters/sync/services/remote_issue_creation_service.py +112 -0
- roadmap/adapters/sync/services/sync_analysis_service.py +250 -0
- roadmap/adapters/sync/services/sync_authentication_service.py +57 -0
- roadmap/adapters/sync/services/sync_data_fetch_service.py +212 -0
- roadmap/adapters/sync/services/sync_linking_service.py +329 -0
- roadmap/adapters/sync/services/sync_report_service.py +62 -0
- roadmap/adapters/sync/services/sync_state_update_service.py +51 -0
- roadmap/adapters/sync/sync_cache_orchestrator.py +454 -0
- roadmap/adapters/sync/sync_merge_engine.py +416 -0
- roadmap/adapters/sync/sync_merge_orchestrator.py +1313 -0
- roadmap/adapters/sync/sync_retrieval_orchestrator.py +810 -0
- roadmap/application/services/__init__.py +1 -0
- roadmap/application/services/deduplicate_service.py +517 -0
- roadmap/application/use_cases/__init__.py +1 -0
- roadmap/common/__init__.py +308 -0
- roadmap/common/cache.py +209 -0
- roadmap/common/cli_errors.py +147 -0
- roadmap/common/configuration/__init__.py +33 -0
- roadmap/common/configuration/config_loader.py +264 -0
- roadmap/common/configuration/config_manager.py +164 -0
- roadmap/common/configuration/config_schema.py +145 -0
- roadmap/common/configuration/github/__init__.py +9 -0
- roadmap/common/configuration/github/config_manager.py +154 -0
- roadmap/common/configuration/github/token_resolver.py +84 -0
- roadmap/common/console.py +184 -0
- roadmap/common/constants.py +134 -0
- roadmap/common/datetime_parser.py +319 -0
- roadmap/common/error_formatter.py +84 -0
- roadmap/common/errors/__init__.py +143 -0
- roadmap/common/errors/error_base.py +73 -0
- roadmap/common/errors/error_file.py +82 -0
- roadmap/common/errors/error_git.py +67 -0
- roadmap/common/errors/error_handler.py +166 -0
- roadmap/common/errors/error_network.py +90 -0
- roadmap/common/errors/error_security.py +78 -0
- roadmap/common/errors/error_standards.py +561 -0
- roadmap/common/errors/error_validation.py +111 -0
- roadmap/common/errors/exceptions.py +432 -0
- roadmap/common/formatters/__init__.py +29 -0
- roadmap/common/formatters/base_table_formatter.py +96 -0
- roadmap/common/formatters/export/__init__.py +7 -0
- roadmap/common/formatters/export/issue_exporter.py +97 -0
- roadmap/common/formatters/helpers.py +28 -0
- roadmap/common/formatters/kanban/__init__.py +9 -0
- roadmap/common/formatters/kanban/layout.py +48 -0
- roadmap/common/formatters/kanban/organizer.py +81 -0
- roadmap/common/formatters/output/__init__.py +15 -0
- roadmap/common/formatters/tables/__init__.py +11 -0
- roadmap/common/formatters/tables/column_factory.py +312 -0
- roadmap/common/formatters/tables/issue_table.py +276 -0
- roadmap/common/formatters/tables/milestone_table.py +247 -0
- roadmap/common/formatters/tables/project_table.py +195 -0
- roadmap/common/formatters/text/__init__.py +57 -0
- roadmap/common/formatters/text/basic.py +217 -0
- roadmap/common/formatters/text/display.py +42 -0
- roadmap/common/formatters/text/duration.py +36 -0
- roadmap/common/formatters/text/operations.py +325 -0
- roadmap/common/formatters/text/status_badges.py +57 -0
- roadmap/common/initialization/github/__init__.py +11 -0
- roadmap/common/initialization/github/setup_service.py +238 -0
- roadmap/common/initialization/github/setup_validator.py +78 -0
- roadmap/common/logging/__init__.py +113 -0
- roadmap/common/logging/audit_logging.py +5 -0
- roadmap/common/logging/correlation.py +45 -0
- roadmap/common/logging/decorators.py +267 -0
- roadmap/common/logging/error_logging.py +217 -0
- roadmap/common/logging/formatters.py +124 -0
- roadmap/common/logging/loggers.py +68 -0
- roadmap/common/logging/performance_tracking.py +267 -0
- roadmap/common/logging/utils.py +335 -0
- roadmap/common/models/__init__.py +44 -0
- roadmap/common/models/cli_models.py +118 -0
- roadmap/common/models/config_models.py +199 -0
- roadmap/common/models/output_models.py +381 -0
- roadmap/common/observability/__init__.py +28 -0
- roadmap/common/observability/instrumentation.py +103 -0
- roadmap/common/observability/observability.py +76 -0
- roadmap/common/observability/otel_init.py +109 -0
- roadmap/common/output_formatter.py +393 -0
- roadmap/common/progress.py +379 -0
- roadmap/common/result.py +396 -0
- roadmap/common/security/__init__.py +48 -0
- roadmap/common/security/exceptions.py +13 -0
- roadmap/common/security/export_cleanup.py +79 -0
- roadmap/common/security/file_operations.py +104 -0
- roadmap/common/security/filename_sanitization.py +54 -0
- roadmap/common/security/logging.py +27 -0
- roadmap/common/security/path_validation.py +137 -0
- roadmap/common/security/temp_files.py +41 -0
- roadmap/common/services/__init__.py +65 -0
- roadmap/common/services/decorators.py +183 -0
- roadmap/common/services/logging_utils.py +208 -0
- roadmap/common/services/metrics.py +161 -0
- roadmap/common/services/performance.py +219 -0
- roadmap/common/services/profiling.py +288 -0
- roadmap/common/services/retry.py +288 -0
- roadmap/common/status_style_manager.py +114 -0
- roadmap/common/union_find.py +88 -0
- roadmap/common/update_constants.py +13 -0
- roadmap/common/utils/__init__.py +65 -0
- roadmap/common/utils/cli_helpers.py +381 -0
- roadmap/common/utils/file_utils.py +440 -0
- roadmap/common/utils/path_utils.py +38 -0
- roadmap/common/utils/status_utils.py +130 -0
- roadmap/common/utils/timezone_utils.py +480 -0
- roadmap/common/validation/__init__.py +37 -0
- roadmap/common/validation/field_validator.py +104 -0
- roadmap/common/validation/result.py +37 -0
- roadmap/common/validation/roadmap_validator.py +310 -0
- roadmap/common/validation/schema_validator.py +36 -0
- roadmap/common/validation/validators.py +50 -0
- roadmap/core/__init__.py +1 -0
- roadmap/core/domain/__init__.py +30 -0
- roadmap/core/domain/comment.py +24 -0
- roadmap/core/domain/health.py +15 -0
- roadmap/core/domain/issue.py +274 -0
- roadmap/core/domain/milestone.py +173 -0
- roadmap/core/domain/ports/__init__.py +1 -0
- roadmap/core/domain/ports/baseline_repository.py +24 -0
- roadmap/core/domain/ports/issue_repository.py +34 -0
- roadmap/core/domain/ports/remote_backend_port.py +31 -0
- roadmap/core/domain/project.py +130 -0
- roadmap/core/interfaces/__init__.py +109 -0
- roadmap/core/interfaces/assignee_validator.py +40 -0
- roadmap/core/interfaces/backend_factory.py +175 -0
- roadmap/core/interfaces/github.py +102 -0
- roadmap/core/interfaces/parsers.py +140 -0
- roadmap/core/interfaces/persistence.py +45 -0
- roadmap/core/interfaces/repositories.py +174 -0
- roadmap/core/interfaces/state_managers.py +111 -0
- roadmap/core/interfaces/state_storage.py +64 -0
- roadmap/core/interfaces/sync_backend.py +275 -0
- roadmap/core/interfaces/sync_services.py +119 -0
- roadmap/core/interfaces/sync_validators.py +180 -0
- roadmap/core/models/__init__.py +31 -0
- roadmap/core/models/sync_models.py +162 -0
- roadmap/core/models.py +115 -0
- roadmap/core/observability/__init__.py +17 -0
- roadmap/core/observability/sync_metrics.py +699 -0
- roadmap/core/repositories.py +200 -0
- roadmap/core/services/__init__.py +313 -0
- roadmap/core/services/baseline/__init__.py +1 -0
- roadmap/core/services/baseline/baseline_builder_progress.py +274 -0
- roadmap/core/services/baseline/baseline_retriever.py +63 -0
- roadmap/core/services/baseline/baseline_selector.py +248 -0
- roadmap/core/services/baseline/baseline_state_retriever.py +284 -0
- roadmap/core/services/baseline/optimized_baseline_builder.py +425 -0
- roadmap/core/services/comment/__init__.py +1 -0
- roadmap/core/services/comment/comment_service.py +277 -0
- roadmap/core/services/git/__init__.py +1 -0
- roadmap/core/services/git/git_hook_auto_sync_service.py +403 -0
- roadmap/core/services/github/__init__.py +1 -0
- roadmap/core/services/github/github_change_detector.py +162 -0
- roadmap/core/services/github/github_config_validator.py +148 -0
- roadmap/core/services/github/github_conflict_detector.py +179 -0
- roadmap/core/services/github/github_entity_classifier.py +51 -0
- roadmap/core/services/github/github_integration_service.py +439 -0
- roadmap/core/services/github/github_issue_client.py +346 -0
- roadmap/core/services/health/__init__.py +1 -0
- roadmap/core/services/health/backup_cleanup_service.py +202 -0
- roadmap/core/services/health/data_integrity_validator_service.py +90 -0
- roadmap/core/services/health/entity_health_scanner.py +460 -0
- roadmap/core/services/health/file_repair_service.py +193 -0
- roadmap/core/services/health/health_check_service.py +166 -0
- roadmap/core/services/health/health_models.py +80 -0
- roadmap/core/services/health/infrastructure_validator_service.py +328 -0
- roadmap/core/services/health/issue_health_scanner.py +263 -0
- roadmap/core/services/helpers/__init__.py +18 -0
- roadmap/core/services/helpers/status_change_helpers.py +88 -0
- roadmap/core/services/initialization/__init__.py +19 -0
- roadmap/core/services/initialization/utils.py +104 -0
- roadmap/core/services/initialization/validator.py +78 -0
- roadmap/core/services/initialization/workflow.py +182 -0
- roadmap/core/services/initialization_service.py +111 -0
- roadmap/core/services/issue/__init__.py +1 -0
- roadmap/core/services/issue/assignee_validation_service.py +312 -0
- roadmap/core/services/issue/issue_creation_service.py +309 -0
- roadmap/core/services/issue/issue_filter_service.py +302 -0
- roadmap/core/services/issue/issue_matching_service.py +127 -0
- roadmap/core/services/issue/issue_service.py +765 -0
- roadmap/core/services/issue/issue_update_service.py +144 -0
- roadmap/core/services/issue/start_issue_service.py +174 -0
- roadmap/core/services/issue_helpers/__init__.py +18 -0
- roadmap/core/services/issue_helpers/issue_filters.py +296 -0
- roadmap/core/services/milestone_service.py +438 -0
- roadmap/core/services/project/__init__.py +1 -0
- roadmap/core/services/project/project_service.py +434 -0
- roadmap/core/services/project/project_status_service.py +125 -0
- roadmap/core/services/project_init/__init__.py +22 -0
- roadmap/core/services/project_init/context_detection.py +146 -0
- roadmap/core/services/project_init/creation.py +76 -0
- roadmap/core/services/project_init/detection.py +51 -0
- roadmap/core/services/project_init/template.py +228 -0
- roadmap/core/services/status_change_service.py +88 -0
- roadmap/core/services/sync/__init__.py +1 -0
- roadmap/core/services/sync/batch_processor.py +290 -0
- roadmap/core/services/sync/conflict_resolver.py +154 -0
- roadmap/core/services/sync/dependency_resolver.py +367 -0
- roadmap/core/services/sync/duplicate_detector.py +771 -0
- roadmap/core/services/sync/duplicate_resolver.py +315 -0
- roadmap/core/services/sync/error_classification.py +470 -0
- roadmap/core/services/sync/performance.py +263 -0
- roadmap/core/services/sync/sync_change_computer.py +211 -0
- roadmap/core/services/sync/sync_checkpoint.py +348 -0
- roadmap/core/services/sync/sync_conflict_detector.py +147 -0
- roadmap/core/services/sync/sync_conflict_resolver.py +448 -0
- roadmap/core/services/sync/sync_errors.py +383 -0
- roadmap/core/services/sync/sync_key_normalizer.py +222 -0
- roadmap/core/services/sync/sync_metadata_service.py +361 -0
- roadmap/core/services/sync/sync_plan.py +171 -0
- roadmap/core/services/sync/sync_plan_executor.py +447 -0
- roadmap/core/services/sync/sync_report.py +296 -0
- roadmap/core/services/sync/sync_state.py +173 -0
- roadmap/core/services/sync/sync_state_comparator.py +690 -0
- roadmap/core/services/sync/sync_state_manager.py +431 -0
- roadmap/core/services/sync/sync_state_normalizer.py +98 -0
- roadmap/core/services/sync/sync_three_way.py +78 -0
- roadmap/core/services/sync/three_way_merger.py +178 -0
- roadmap/core/services/utils/__init__.py +1 -0
- roadmap/core/services/utils/configuration_service.py +113 -0
- roadmap/core/services/utils/critical_path_calculator.py +418 -0
- roadmap/core/services/utils/dependency_analyzer.py +360 -0
- roadmap/core/services/utils/field_conflict_detector.py +126 -0
- roadmap/core/services/utils/remote_fetcher.py +202 -0
- roadmap/core/services/utils/remote_state_normalizer.py +110 -0
- roadmap/core/services/utils/retry_policy.py +51 -0
- roadmap/core/services/validator_base.py +130 -0
- roadmap/core/services/validators/__init__.py +28 -0
- roadmap/core/services/validators/_utils.py +20 -0
- roadmap/core/services/validators/archivable_issues_validator.py +71 -0
- roadmap/core/services/validators/archivable_milestones_validator.py +69 -0
- roadmap/core/services/validators/backup_validator.py +89 -0
- roadmap/core/services/validators/data_integrity_validator.py +84 -0
- roadmap/core/services/validators/duplicate_issues_validator.py +75 -0
- roadmap/core/services/validators/duplicate_milestones_validator.py +141 -0
- roadmap/core/services/validators/folder_structure_validator.py +165 -0
- roadmap/core/services/validators/health_status_utils.py +28 -0
- roadmap/core/services/validators/milestone_naming_validator.py +146 -0
- roadmap/core/services/validators/missing_headlines_validator.py +97 -0
- roadmap/core/services/validators/orphaned_issues_validator.py +142 -0
- roadmap/core/services/validators/orphaned_milestones_validator.py +85 -0
- roadmap/core/utils/git_remote_parser.py +83 -0
- roadmap/core/utils/github_urls.py +66 -0
- roadmap/domain/validation.py +30 -0
- roadmap/infrastructure/__init__.py +55 -0
- roadmap/infrastructure/coordination/__init__.py +8 -0
- roadmap/infrastructure/coordination/coordinator_params.py +82 -0
- roadmap/infrastructure/coordination/core.py +446 -0
- roadmap/infrastructure/coordination/git_coordinator.py +160 -0
- roadmap/infrastructure/coordination/initialization.py +348 -0
- roadmap/infrastructure/coordination/issue_coordinator.py +154 -0
- roadmap/infrastructure/coordination/issue_operations.py +456 -0
- roadmap/infrastructure/coordination/milestone_coordinator.py +135 -0
- roadmap/infrastructure/coordination/milestone_operations.py +201 -0
- roadmap/infrastructure/coordination/project_coordinator.py +113 -0
- roadmap/infrastructure/coordination/project_operations.py +144 -0
- roadmap/infrastructure/coordination/team_coordinator.py +107 -0
- roadmap/infrastructure/coordination/user_operations.py +150 -0
- roadmap/infrastructure/coordination/validation_coordinator.py +249 -0
- roadmap/infrastructure/coordination_gateway.py +152 -0
- roadmap/infrastructure/git/__init__.py +6 -0
- roadmap/infrastructure/git/git_integration_ops.py +234 -0
- roadmap/infrastructure/git_gateway.py +35 -0
- roadmap/infrastructure/github_gateway.py +52 -0
- roadmap/infrastructure/maintenance/__init__.py +5 -0
- roadmap/infrastructure/maintenance/cleanup.py +483 -0
- roadmap/infrastructure/observability/__init__.py +7 -0
- roadmap/infrastructure/observability/health.py +196 -0
- roadmap/infrastructure/observability/health_formatter.py +73 -0
- roadmap/infrastructure/observability/specialized_health_checkers.py +188 -0
- roadmap/infrastructure/persistence_gateway.py +164 -0
- roadmap/infrastructure/security/__init__.py +1 -0
- roadmap/infrastructure/security/credentials.py +489 -0
- roadmap/infrastructure/sync_gateway.py +94 -0
- roadmap/infrastructure/validation/__init__.py +7 -0
- roadmap/infrastructure/validation/file_enumeration.py +219 -0
- roadmap/infrastructure/validation/github_assignee_validator.py +47 -0
- roadmap/infrastructure/validation/github_validator.py +101 -0
- roadmap/infrastructure/validation/milestone_consistency_validator.py +96 -0
- roadmap/infrastructure/validation/vanilla_assignee_validator.py +37 -0
- roadmap/infrastructure/validation_gateway.py +96 -0
- roadmap/presentation/formatters/sync_metrics_formatter.py +355 -0
- roadmap/settings.py +345 -0
- roadmap/templates/github_workflows/ci-cd-roadmap.yml +241 -0
- roadmap/templates/github_workflows/issue-lifecycle.yml +236 -0
- roadmap/templates/github_workflows/roadmap-integration.yml +137 -0
- roadmap/templates/github_workflows/roadmap-starter.yml +59 -0
- roadmap/version.py +381 -0
- roadmap_cli-0.1.1.dist-info/METADATA +448 -0
- roadmap_cli-0.1.1.dist-info/RECORD +521 -0
- roadmap_cli-0.1.1.dist-info/WHEEL +4 -0
- roadmap_cli-0.1.1.dist-info/entry_points.txt +2 -0
- roadmap_cli-0.1.1.dist-info/licenses/LICENSE.md +21 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Git conflict detection and handling service."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from roadmap.common.logging import get_logger
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from .state_manager import StateManager
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ConflictService:
|
|
16
|
+
"""Service for detecting and managing git conflicts in .roadmap directory."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, state_manager: "StateManager"):
|
|
19
|
+
"""Initialize with reference to state manager.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
state_manager: StateManager instance for accessing sync state
|
|
23
|
+
"""
|
|
24
|
+
self.state_manager = state_manager
|
|
25
|
+
|
|
26
|
+
def check_git_conflicts(self, roadmap_dir: Path | None = None) -> list[str]:
|
|
27
|
+
"""Check for git conflicts in .roadmap directory."""
|
|
28
|
+
if roadmap_dir is None:
|
|
29
|
+
roadmap_dir = Path.cwd() / ".roadmap"
|
|
30
|
+
|
|
31
|
+
conflict_files = []
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
if not roadmap_dir.exists():
|
|
35
|
+
return conflict_files
|
|
36
|
+
|
|
37
|
+
# Check for conflict markers in .roadmap files
|
|
38
|
+
for pattern in ["**/*.md", "**/*.yaml", "**/*.yml"]:
|
|
39
|
+
for file_path in roadmap_dir.glob(pattern):
|
|
40
|
+
try:
|
|
41
|
+
with open(file_path, encoding="utf-8") as f:
|
|
42
|
+
content = f.read()
|
|
43
|
+
|
|
44
|
+
# Look for git conflict markers
|
|
45
|
+
conflict_markers = ["<<<<<<<", "=======", ">>>>>>>"]
|
|
46
|
+
if any(marker in content for marker in conflict_markers):
|
|
47
|
+
conflict_files.append(
|
|
48
|
+
str(file_path.relative_to(Path.cwd()))
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
except Exception as e:
|
|
52
|
+
logger.warning(
|
|
53
|
+
"failed_to_check_conflicts",
|
|
54
|
+
error=str(e),
|
|
55
|
+
file_path=str(file_path),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if conflict_files:
|
|
59
|
+
logger.warning(
|
|
60
|
+
"git_conflicts_detected",
|
|
61
|
+
conflict_count=len(conflict_files),
|
|
62
|
+
conflicts=conflict_files,
|
|
63
|
+
)
|
|
64
|
+
self.state_manager.set_sync_state("git_conflicts_detected", "true")
|
|
65
|
+
self.state_manager.set_sync_state(
|
|
66
|
+
"conflict_files", json.dumps(conflict_files)
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
self.state_manager.set_sync_state("git_conflicts_detected", "false")
|
|
70
|
+
self.state_manager.set_sync_state("conflict_files", "[]")
|
|
71
|
+
|
|
72
|
+
return conflict_files
|
|
73
|
+
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error(
|
|
76
|
+
"Failed to check git conflicts", error=str(e), severity="data_error"
|
|
77
|
+
)
|
|
78
|
+
return conflict_files
|
|
79
|
+
|
|
80
|
+
def has_git_conflicts(self) -> bool:
|
|
81
|
+
"""Check if there are unresolved git conflicts."""
|
|
82
|
+
try:
|
|
83
|
+
conflicts_detected = self.state_manager.get_sync_state(
|
|
84
|
+
"git_conflicts_detected"
|
|
85
|
+
)
|
|
86
|
+
return conflicts_detected == "true"
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.debug("git_conflict_check_failed", error=str(e))
|
|
89
|
+
# If we can't check, assume no conflicts to avoid blocking operations
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
def get_conflict_files(self) -> list[str]:
|
|
93
|
+
"""Get list of files with git conflicts."""
|
|
94
|
+
try:
|
|
95
|
+
conflict_files_json = self.state_manager.get_sync_state("conflict_files")
|
|
96
|
+
if conflict_files_json:
|
|
97
|
+
return json.loads(conflict_files_json)
|
|
98
|
+
return []
|
|
99
|
+
except Exception as e:
|
|
100
|
+
logger.debug("conflict_files_retrieval_failed", error=str(e))
|
|
101
|
+
return []
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Connection management for database operations."""
|
|
2
|
+
|
|
3
|
+
import sqlite3
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
|
|
6
|
+
from roadmap.common.logging import get_logger
|
|
7
|
+
|
|
8
|
+
from ..database_manager import DatabaseManager
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ConnectionManager:
|
|
14
|
+
"""Manages database connections and transactions."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, db_manager: DatabaseManager):
|
|
17
|
+
"""Initialize connection manager.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
db_manager: DatabaseManager instance handling connection pooling
|
|
21
|
+
"""
|
|
22
|
+
self._db_manager = db_manager
|
|
23
|
+
logger.debug("connection_manager_initialized")
|
|
24
|
+
|
|
25
|
+
def get_connection(self) -> sqlite3.Connection:
|
|
26
|
+
"""Get thread-local database connection.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
sqlite3.Connection: Active database connection
|
|
30
|
+
"""
|
|
31
|
+
return self._db_manager._get_connection()
|
|
32
|
+
|
|
33
|
+
@contextmanager
|
|
34
|
+
def transaction(self):
|
|
35
|
+
"""Context manager for database transactions.
|
|
36
|
+
|
|
37
|
+
Yields:
|
|
38
|
+
Connection context with automatic commit/rollback on exception
|
|
39
|
+
"""
|
|
40
|
+
with self._db_manager.transaction() as conn:
|
|
41
|
+
yield conn
|
|
42
|
+
|
|
43
|
+
def close(self):
|
|
44
|
+
"""Close all database connections."""
|
|
45
|
+
logger.debug("Closing database connections")
|
|
46
|
+
self._db_manager.close()
|
|
47
|
+
|
|
48
|
+
def vacuum(self):
|
|
49
|
+
"""Optimize database by vacuuming."""
|
|
50
|
+
logger.debug("Running database vacuum")
|
|
51
|
+
self._db_manager.vacuum()
|
|
52
|
+
|
|
53
|
+
def is_initialized(self) -> bool:
|
|
54
|
+
"""Check if database is properly initialized.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
bool: True if database exists and is initialized
|
|
58
|
+
"""
|
|
59
|
+
result = self._db_manager.is_initialized()
|
|
60
|
+
logger.debug("Database initialization check", initialized=result)
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
def database_exists(self) -> bool:
|
|
64
|
+
"""Check if database file exists and has tables.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
bool: True if database exists with tables
|
|
68
|
+
"""
|
|
69
|
+
result = self._db_manager.database_exists()
|
|
70
|
+
logger.debug("Database existence check", exists=result)
|
|
71
|
+
return result
|
|
72
|
+
|
|
73
|
+
def is_safe_for_writes(self) -> tuple[bool, str]:
|
|
74
|
+
"""Check if database is safe for write operations.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
tuple[bool, str]: (safe, message) indicating if writes are safe
|
|
78
|
+
"""
|
|
79
|
+
return self._db_manager.is_safe_for_writes()
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Issue storage operations."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from roadmap.common.errors import OperationType, safe_operation
|
|
6
|
+
from roadmap.common.logging import get_logger
|
|
7
|
+
|
|
8
|
+
from ..repositories import IssueRepository
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class IssueStorage:
|
|
14
|
+
"""Handles issue CRUD operations."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, issue_repo: IssueRepository):
|
|
17
|
+
"""Initialize issue storage.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
issue_repo: IssueRepository instance for data access
|
|
21
|
+
"""
|
|
22
|
+
self._issue_repo = issue_repo
|
|
23
|
+
logger.debug("issue_storage_initialized")
|
|
24
|
+
|
|
25
|
+
@safe_operation(OperationType.CREATE, "Issue", include_traceback=True)
|
|
26
|
+
def create(self, issue_data: dict[str, Any]) -> str:
|
|
27
|
+
"""Create a new issue.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
issue_data: Issue data dictionary
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
str: ID of created issue
|
|
34
|
+
"""
|
|
35
|
+
logger.debug("Creating issue", keys=list(issue_data.keys()))
|
|
36
|
+
return self._issue_repo.create(issue_data)
|
|
37
|
+
|
|
38
|
+
def get(self, issue_id: str) -> dict[str, Any] | None:
|
|
39
|
+
"""Get issue by ID.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
issue_id: Issue identifier
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
dict: Issue data or None if not found
|
|
46
|
+
"""
|
|
47
|
+
logger.debug("Getting issue", issue_id=issue_id)
|
|
48
|
+
return self._issue_repo.get(issue_id)
|
|
49
|
+
|
|
50
|
+
@safe_operation(OperationType.UPDATE, "Issue", include_traceback=True)
|
|
51
|
+
def update(self, issue_id: str, updates: dict[str, Any]) -> bool:
|
|
52
|
+
"""Update issue.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
issue_id: Issue identifier
|
|
56
|
+
updates: Dictionary of fields to update
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
bool: True if update successful
|
|
60
|
+
"""
|
|
61
|
+
logger.debug("Updating issue", issue_id=issue_id, keys=list(updates.keys()))
|
|
62
|
+
return self._issue_repo.update(issue_id, updates)
|
|
63
|
+
|
|
64
|
+
@safe_operation(OperationType.DELETE, "Issue", include_traceback=True)
|
|
65
|
+
def delete(self, issue_id: str) -> bool:
|
|
66
|
+
"""Delete issue.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
issue_id: Issue identifier
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
bool: True if deletion successful
|
|
73
|
+
"""
|
|
74
|
+
logger.debug("Deleting issue", issue_id=issue_id)
|
|
75
|
+
return self._issue_repo.delete(issue_id)
|
|
76
|
+
|
|
77
|
+
@safe_operation(OperationType.UPDATE, "Issue")
|
|
78
|
+
def mark_archived(self, issue_id: str, archived: bool = True) -> bool:
|
|
79
|
+
"""Mark an issue as archived or unarchived.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
issue_id: Issue identifier
|
|
83
|
+
archived: Whether to archive (True) or unarchive (False)
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
bool: True if operation successful
|
|
87
|
+
"""
|
|
88
|
+
logger.debug("Marking issue archived", issue_id=issue_id, archived=archived)
|
|
89
|
+
return self._issue_repo.mark_archived(issue_id, archived)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Milestone storage operations."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from roadmap.common.errors import OperationType, safe_operation
|
|
6
|
+
from roadmap.common.logging import get_logger
|
|
7
|
+
|
|
8
|
+
from ..repositories import MilestoneRepository
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MilestoneStorage:
|
|
14
|
+
"""Handles milestone CRUD operations."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, milestone_repo: MilestoneRepository):
|
|
17
|
+
"""Initialize milestone storage.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
milestone_repo: MilestoneRepository instance for data access
|
|
21
|
+
"""
|
|
22
|
+
self._milestone_repo = milestone_repo
|
|
23
|
+
logger.debug("milestone_storage_initialized")
|
|
24
|
+
|
|
25
|
+
@safe_operation(OperationType.CREATE, "Milestone", include_traceback=True)
|
|
26
|
+
def create(self, milestone_data: dict[str, Any]) -> str:
|
|
27
|
+
"""Create a new milestone.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
milestone_data: Milestone data dictionary
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
str: ID of created milestone
|
|
34
|
+
"""
|
|
35
|
+
logger.debug("Creating milestone", keys=list(milestone_data.keys()))
|
|
36
|
+
return self._milestone_repo.create(milestone_data)
|
|
37
|
+
|
|
38
|
+
def get(self, milestone_id: str) -> dict[str, Any] | None:
|
|
39
|
+
"""Get milestone by ID.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
milestone_id: Milestone identifier
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
dict: Milestone data or None if not found
|
|
46
|
+
"""
|
|
47
|
+
logger.debug("Getting milestone", milestone_id=milestone_id)
|
|
48
|
+
return self._milestone_repo.get(milestone_id)
|
|
49
|
+
|
|
50
|
+
@safe_operation(OperationType.UPDATE, "Milestone", include_traceback=True)
|
|
51
|
+
def update(self, milestone_id: str, updates: dict[str, Any]) -> bool:
|
|
52
|
+
"""Update milestone.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
milestone_id: Milestone identifier
|
|
56
|
+
updates: Dictionary of fields to update
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
bool: True if update successful
|
|
60
|
+
"""
|
|
61
|
+
logger.debug(
|
|
62
|
+
"Updating milestone", milestone_id=milestone_id, keys=list(updates.keys())
|
|
63
|
+
)
|
|
64
|
+
return self._milestone_repo.update(milestone_id, updates)
|
|
65
|
+
|
|
66
|
+
@safe_operation(OperationType.UPDATE, "Milestone")
|
|
67
|
+
def mark_archived(self, milestone_id: str, archived: bool = True) -> bool:
|
|
68
|
+
"""Mark a milestone as archived or unarchived.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
milestone_id: Milestone identifier
|
|
72
|
+
archived: Whether to archive (True) or unarchive (False)
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
bool: True if operation successful
|
|
76
|
+
"""
|
|
77
|
+
logger.debug(
|
|
78
|
+
"Marking milestone archived", milestone_id=milestone_id, archived=archived
|
|
79
|
+
)
|
|
80
|
+
return self._milestone_repo.mark_archived(milestone_id, archived)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Project storage operations."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from roadmap.common.errors import OperationType, safe_operation
|
|
6
|
+
from roadmap.common.logging import get_logger
|
|
7
|
+
|
|
8
|
+
from ..repositories import ProjectRepository
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ProjectStorage:
|
|
14
|
+
"""Handles project CRUD operations."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, project_repo: ProjectRepository):
|
|
17
|
+
"""Initialize project storage.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
project_repo: ProjectRepository instance for data access
|
|
21
|
+
"""
|
|
22
|
+
self._project_repo = project_repo
|
|
23
|
+
logger.debug("project_storage_initialized")
|
|
24
|
+
|
|
25
|
+
@safe_operation(OperationType.CREATE, "Project", include_traceback=True)
|
|
26
|
+
def create(self, project_data: dict[str, Any]) -> str:
|
|
27
|
+
"""Create a new project.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
project_data: Project data dictionary
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
str: ID of created project
|
|
34
|
+
"""
|
|
35
|
+
logger.debug("Creating project", keys=list(project_data.keys()))
|
|
36
|
+
return self._project_repo.create(project_data)
|
|
37
|
+
|
|
38
|
+
def get(self, project_id: str) -> dict[str, Any] | None:
|
|
39
|
+
"""Get project by ID.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
project_id: Project identifier
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
dict: Project data or None if not found
|
|
46
|
+
"""
|
|
47
|
+
logger.debug("Getting project", project_id=project_id)
|
|
48
|
+
return self._project_repo.get(project_id)
|
|
49
|
+
|
|
50
|
+
def list_all(self) -> list[dict[str, Any]]:
|
|
51
|
+
"""List all projects.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
list: List of project data dictionaries
|
|
55
|
+
"""
|
|
56
|
+
logger.debug("Listing all projects")
|
|
57
|
+
return self._project_repo.list_all()
|
|
58
|
+
|
|
59
|
+
@safe_operation(OperationType.UPDATE, "Project", include_traceback=True)
|
|
60
|
+
def update(self, project_id: str, updates: dict[str, Any]) -> bool:
|
|
61
|
+
"""Update project.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
project_id: Project identifier
|
|
65
|
+
updates: Dictionary of fields to update
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
bool: True if update successful
|
|
69
|
+
"""
|
|
70
|
+
logger.debug(
|
|
71
|
+
"Updating project", project_id=project_id, keys=list(updates.keys())
|
|
72
|
+
)
|
|
73
|
+
return self._project_repo.update(project_id, updates)
|
|
74
|
+
|
|
75
|
+
@safe_operation(OperationType.DELETE, "Project", include_traceback=True)
|
|
76
|
+
def delete(self, project_id: str) -> bool:
|
|
77
|
+
"""Delete project and all related data.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
project_id: Project identifier
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
bool: True if deletion successful
|
|
84
|
+
"""
|
|
85
|
+
logger.debug("Deleting project", project_id=project_id)
|
|
86
|
+
return self._project_repo.delete(project_id)
|
|
87
|
+
|
|
88
|
+
@safe_operation(OperationType.UPDATE, "Project")
|
|
89
|
+
def mark_archived(self, project_id: str, archived: bool = True) -> bool:
|
|
90
|
+
"""Mark a project as archived or unarchived.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
project_id: Project identifier
|
|
94
|
+
archived: Whether to archive (True) or unarchive (False)
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
bool: True if operation successful
|
|
98
|
+
"""
|
|
99
|
+
logger.debug(
|
|
100
|
+
"Marking project archived", project_id=project_id, archived=archived
|
|
101
|
+
)
|
|
102
|
+
return self._project_repo.mark_archived(project_id, archived)
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Database query service for aggregations and complex queries."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from roadmap.common.logging import get_logger
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from .state_manager import StateManager
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class QueryService:
|
|
16
|
+
"""Service for executing complex database queries and aggregations."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, state_manager: "StateManager"):
|
|
19
|
+
"""Initialize with reference to state manager.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
state_manager: StateManager instance for database access
|
|
23
|
+
"""
|
|
24
|
+
self.state_manager = state_manager
|
|
25
|
+
|
|
26
|
+
def has_file_changes(self) -> bool:
|
|
27
|
+
"""Check if .roadmap/ files have changes since last sync."""
|
|
28
|
+
try:
|
|
29
|
+
# Get roadmap directory from database path
|
|
30
|
+
roadmap_dir = self.state_manager.db_path.parent
|
|
31
|
+
|
|
32
|
+
# Check for Markdown files with YAML frontmatter in relevant directories
|
|
33
|
+
md_files = []
|
|
34
|
+
for subdir in ["issues", "milestones", "projects"]:
|
|
35
|
+
subdir_path = roadmap_dir / subdir
|
|
36
|
+
if subdir_path.exists():
|
|
37
|
+
md_files.extend(subdir_path.rglob("*.md"))
|
|
38
|
+
|
|
39
|
+
if not md_files:
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
# Check each file against stored hash
|
|
43
|
+
with self.state_manager.transaction() as conn:
|
|
44
|
+
for file_path in md_files:
|
|
45
|
+
if not file_path.exists():
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
# Calculate current hash
|
|
49
|
+
current_hash = hashlib.sha256(file_path.read_bytes()).hexdigest()
|
|
50
|
+
|
|
51
|
+
# Get stored hash
|
|
52
|
+
result = conn.execute(
|
|
53
|
+
"SELECT content_hash FROM file_sync_state WHERE file_path = ?",
|
|
54
|
+
(str(file_path.relative_to(roadmap_dir)),),
|
|
55
|
+
).fetchone()
|
|
56
|
+
|
|
57
|
+
if not result or result[0] != current_hash:
|
|
58
|
+
# File is new or changed
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.warning(
|
|
65
|
+
"Error checking file changes", error=str(e), severity="operational"
|
|
66
|
+
)
|
|
67
|
+
# If we can't check, assume changes exist to be safe
|
|
68
|
+
return True
|
|
69
|
+
|
|
70
|
+
def get_all_issues(self) -> list[dict[str, Any]]:
|
|
71
|
+
"""Get all issues from database."""
|
|
72
|
+
try:
|
|
73
|
+
with self.state_manager.transaction() as conn:
|
|
74
|
+
results = conn.execute("""
|
|
75
|
+
SELECT i.id, i.title, i.status, i.priority, i.issue_type,
|
|
76
|
+
i.assignee, i.estimate_hours, i.due_date,
|
|
77
|
+
i.project_id, i.milestone_id, i.metadata,
|
|
78
|
+
m.title as milestone_name, p.name as project_name
|
|
79
|
+
FROM issues i
|
|
80
|
+
LEFT JOIN milestones m ON i.milestone_id = m.id
|
|
81
|
+
LEFT JOIN projects p ON i.project_id = p.id
|
|
82
|
+
ORDER BY i.title
|
|
83
|
+
""").fetchall()
|
|
84
|
+
|
|
85
|
+
issues = []
|
|
86
|
+
for row in results:
|
|
87
|
+
issue = {
|
|
88
|
+
"id": row[0],
|
|
89
|
+
"title": row[1],
|
|
90
|
+
"status": row[2],
|
|
91
|
+
"priority": row[3],
|
|
92
|
+
"type": row[4],
|
|
93
|
+
"assignee": row[5],
|
|
94
|
+
"estimate_hours": row[6],
|
|
95
|
+
"due_date": row[7],
|
|
96
|
+
"project_id": row[8],
|
|
97
|
+
"milestone_id": row[9],
|
|
98
|
+
"milestone_name": row[11],
|
|
99
|
+
"project_name": row[12],
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Parse metadata
|
|
103
|
+
if row[10]:
|
|
104
|
+
try:
|
|
105
|
+
metadata = json.loads(row[10])
|
|
106
|
+
issue.update(metadata)
|
|
107
|
+
except json.JSONDecodeError:
|
|
108
|
+
logger.warning(
|
|
109
|
+
"failed_to_parse_issue_metadata", severity="data_error"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
issues.append(issue)
|
|
113
|
+
|
|
114
|
+
return issues
|
|
115
|
+
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logger.error(
|
|
118
|
+
"Failed to get issues", error=str(e), severity="infrastructure"
|
|
119
|
+
)
|
|
120
|
+
return []
|
|
121
|
+
|
|
122
|
+
def get_all_milestones(self) -> list[dict[str, Any]]:
|
|
123
|
+
"""Get all milestones from database."""
|
|
124
|
+
try:
|
|
125
|
+
with self.state_manager.transaction() as conn:
|
|
126
|
+
results = conn.execute("""
|
|
127
|
+
SELECT m.id, m.title, m.description, m.status, m.due_date,
|
|
128
|
+
m.progress_percentage, m.project_id, m.metadata,
|
|
129
|
+
p.name as project_name
|
|
130
|
+
FROM milestones m
|
|
131
|
+
LEFT JOIN projects p ON m.project_id = p.id
|
|
132
|
+
ORDER BY m.title
|
|
133
|
+
""").fetchall()
|
|
134
|
+
|
|
135
|
+
milestones = []
|
|
136
|
+
for row in results:
|
|
137
|
+
milestone = {
|
|
138
|
+
"id": row[0],
|
|
139
|
+
"name": row[1], # Use 'name' for compatibility
|
|
140
|
+
"title": row[1],
|
|
141
|
+
"description": row[2],
|
|
142
|
+
"status": row[3],
|
|
143
|
+
"due_date": row[4],
|
|
144
|
+
"progress_percentage": row[5],
|
|
145
|
+
"project_id": row[6],
|
|
146
|
+
"project_name": row[8],
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# Parse metadata
|
|
150
|
+
if row[7]:
|
|
151
|
+
try:
|
|
152
|
+
metadata = json.loads(row[7])
|
|
153
|
+
milestone.update(metadata)
|
|
154
|
+
except json.JSONDecodeError:
|
|
155
|
+
logger.warning(
|
|
156
|
+
"failed_to_parse_milestone_metadata",
|
|
157
|
+
severity="data_error",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
milestones.append(milestone)
|
|
161
|
+
|
|
162
|
+
return milestones
|
|
163
|
+
|
|
164
|
+
except Exception as e:
|
|
165
|
+
logger.error(
|
|
166
|
+
"Failed to get milestones", error=str(e), severity="infrastructure"
|
|
167
|
+
)
|
|
168
|
+
return []
|
|
169
|
+
|
|
170
|
+
def get_milestone_progress(self, milestone_name: str) -> dict[str, int]:
|
|
171
|
+
"""Get progress stats for a milestone."""
|
|
172
|
+
try:
|
|
173
|
+
with self.state_manager.transaction() as conn:
|
|
174
|
+
# Get milestone ID first
|
|
175
|
+
milestone_result = conn.execute(
|
|
176
|
+
"SELECT id FROM milestones WHERE title = ?", (milestone_name,)
|
|
177
|
+
).fetchone()
|
|
178
|
+
|
|
179
|
+
if not milestone_result:
|
|
180
|
+
return {"total": 0, "completed": 0}
|
|
181
|
+
|
|
182
|
+
milestone_id = milestone_result[0]
|
|
183
|
+
|
|
184
|
+
# Count total and done issues for this milestone
|
|
185
|
+
total_result = conn.execute(
|
|
186
|
+
"SELECT COUNT(*) FROM issues WHERE milestone_id = ?",
|
|
187
|
+
(milestone_id,),
|
|
188
|
+
).fetchone()
|
|
189
|
+
|
|
190
|
+
completed_result = conn.execute(
|
|
191
|
+
"SELECT COUNT(*) FROM issues WHERE milestone_id = ? AND status = 'closed'",
|
|
192
|
+
(milestone_id,),
|
|
193
|
+
).fetchone()
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
"total": total_result[0] if total_result else 0,
|
|
197
|
+
"completed": completed_result[0] if completed_result else 0,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
except Exception as e:
|
|
201
|
+
logger.error(
|
|
202
|
+
"failed_to_get_milestone_progress",
|
|
203
|
+
error=str(e),
|
|
204
|
+
milestone_name=milestone_name,
|
|
205
|
+
)
|
|
206
|
+
return {"total": 0, "completed": 0}
|
|
207
|
+
|
|
208
|
+
def get_issues_by_status(self) -> dict[str, int]:
|
|
209
|
+
"""Get issue counts by status."""
|
|
210
|
+
try:
|
|
211
|
+
with self.state_manager.transaction() as conn:
|
|
212
|
+
results = conn.execute("""
|
|
213
|
+
SELECT status, COUNT(*)
|
|
214
|
+
FROM issues
|
|
215
|
+
GROUP BY status
|
|
216
|
+
ORDER BY status
|
|
217
|
+
""").fetchall()
|
|
218
|
+
|
|
219
|
+
return {row[0]: row[1] for row in results}
|
|
220
|
+
|
|
221
|
+
except Exception as e:
|
|
222
|
+
logger.error(
|
|
223
|
+
"Failed to get issues by status",
|
|
224
|
+
error=str(e),
|
|
225
|
+
severity="infrastructure",
|
|
226
|
+
)
|
|
227
|
+
return {}
|