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,268 @@
|
|
|
1
|
+
"""Export management service for handling data exports with config integration."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from datetime import UTC, datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from structlog import get_logger
|
|
9
|
+
|
|
10
|
+
from roadmap.common.configuration import ConfigLoader
|
|
11
|
+
from roadmap.common.console import get_console
|
|
12
|
+
|
|
13
|
+
logger = get_logger()
|
|
14
|
+
console = get_console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ExportManager:
|
|
18
|
+
"""Manages data exports using configuration system for defaults and locations."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, project_root: Path | None = None):
|
|
21
|
+
"""Initialize export manager.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
project_root: Project root directory (defaults to cwd)
|
|
25
|
+
"""
|
|
26
|
+
self.project_root = project_root or Path.cwd()
|
|
27
|
+
self.config = ConfigLoader.load_config(self.project_root)
|
|
28
|
+
|
|
29
|
+
def get_export_dir(self) -> Path:
|
|
30
|
+
"""Get the export directory, creating it if needed.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Path to export directory
|
|
34
|
+
"""
|
|
35
|
+
export_dir = self.config.export.directory
|
|
36
|
+
|
|
37
|
+
# Handle relative vs absolute paths
|
|
38
|
+
if export_dir.startswith("/"):
|
|
39
|
+
# Absolute path
|
|
40
|
+
path = Path(export_dir)
|
|
41
|
+
else:
|
|
42
|
+
# Relative to project root
|
|
43
|
+
path = self.project_root / export_dir
|
|
44
|
+
|
|
45
|
+
# Create directory if it doesn't exist
|
|
46
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
|
|
48
|
+
# Add to .gitignore if configured
|
|
49
|
+
if self.config.export.auto_gitignore:
|
|
50
|
+
self._add_to_gitignore(path)
|
|
51
|
+
|
|
52
|
+
return path
|
|
53
|
+
|
|
54
|
+
def _add_to_gitignore(self, path: Path) -> None:
|
|
55
|
+
"""Add export directory to .gitignore if not already there.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
path: Path to add to gitignore
|
|
59
|
+
"""
|
|
60
|
+
gitignore_path = self.project_root / ".gitignore"
|
|
61
|
+
|
|
62
|
+
# Make path relative for .gitignore
|
|
63
|
+
try:
|
|
64
|
+
rel_path = path.relative_to(self.project_root)
|
|
65
|
+
except ValueError:
|
|
66
|
+
# Path is absolute, can't make it relative
|
|
67
|
+
rel_path = path
|
|
68
|
+
|
|
69
|
+
# Read existing gitignore
|
|
70
|
+
if gitignore_path.exists():
|
|
71
|
+
content = gitignore_path.read_text()
|
|
72
|
+
else:
|
|
73
|
+
content = ""
|
|
74
|
+
|
|
75
|
+
# Check if already present
|
|
76
|
+
gitignore_entry = str(rel_path) + "/"
|
|
77
|
+
if gitignore_entry in content:
|
|
78
|
+
return # Already there
|
|
79
|
+
|
|
80
|
+
# Add to gitignore
|
|
81
|
+
if content and not content.endswith("\n"):
|
|
82
|
+
content += "\n"
|
|
83
|
+
|
|
84
|
+
content += f"{gitignore_entry}\n"
|
|
85
|
+
gitignore_path.write_text(content)
|
|
86
|
+
logger.info("added_to_gitignore", path=str(rel_path))
|
|
87
|
+
|
|
88
|
+
def generate_export_filename(self, entity_type: str, format_type: str) -> str:
|
|
89
|
+
"""Generate a timestamped export filename.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
entity_type: Type of entity being exported (issues, milestones, projects)
|
|
93
|
+
format_type: Export format (json, csv, markdown)
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Filename with timestamp
|
|
97
|
+
"""
|
|
98
|
+
timestamp = datetime.now(UTC).strftime("%Y-%m-%d-%H%M%S")
|
|
99
|
+
return f"{entity_type}-{timestamp}.{format_type}"
|
|
100
|
+
|
|
101
|
+
def get_export_path(self, entity_type: str, format_type: str | None = None) -> Path:
|
|
102
|
+
"""Get full export path with auto-generated filename.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
entity_type: Type of entity being exported
|
|
106
|
+
format_type: Export format (uses config default if not specified)
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Full path to export file
|
|
110
|
+
"""
|
|
111
|
+
if format_type is None:
|
|
112
|
+
format_type = self.config.export.format
|
|
113
|
+
|
|
114
|
+
export_dir = self.get_export_dir()
|
|
115
|
+
filename = self.generate_export_filename(entity_type, format_type)
|
|
116
|
+
return export_dir / filename
|
|
117
|
+
|
|
118
|
+
def export_json(self, data: Any, output_path: Path | None = None) -> str:
|
|
119
|
+
"""Export data as JSON.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
data: Data to export (usually list or dict)
|
|
123
|
+
output_path: Optional custom output path
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
JSON string
|
|
127
|
+
"""
|
|
128
|
+
if isinstance(data, list):
|
|
129
|
+
# Convert objects to dicts if needed
|
|
130
|
+
serializable = [
|
|
131
|
+
item.model_dump() if hasattr(item, "model_dump") else item
|
|
132
|
+
for item in data
|
|
133
|
+
]
|
|
134
|
+
else:
|
|
135
|
+
serializable = data.model_dump() if hasattr(data, "model_dump") else data
|
|
136
|
+
|
|
137
|
+
json_str = json.dumps(serializable, indent=2, default=str)
|
|
138
|
+
|
|
139
|
+
if output_path:
|
|
140
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
141
|
+
output_path.write_text(json_str)
|
|
142
|
+
logger.info("exported_json", path=str(output_path))
|
|
143
|
+
|
|
144
|
+
return json_str
|
|
145
|
+
|
|
146
|
+
def export_csv(self, data: list[Any], output_path: Path | None = None) -> str:
|
|
147
|
+
"""Export data as CSV.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
data: List of objects to export
|
|
151
|
+
output_path: Optional custom output path
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
CSV string
|
|
155
|
+
"""
|
|
156
|
+
if not data:
|
|
157
|
+
csv_str = ""
|
|
158
|
+
else:
|
|
159
|
+
import csv
|
|
160
|
+
from io import StringIO
|
|
161
|
+
|
|
162
|
+
# Get headers from first item
|
|
163
|
+
first_item = data[0]
|
|
164
|
+
if hasattr(first_item, "model_dump"):
|
|
165
|
+
headers = first_item.model_dump().keys()
|
|
166
|
+
rows = [item.model_dump() for item in data]
|
|
167
|
+
else:
|
|
168
|
+
headers = first_item.keys() if isinstance(first_item, dict) else []
|
|
169
|
+
rows = data
|
|
170
|
+
|
|
171
|
+
# Write CSV
|
|
172
|
+
output = StringIO()
|
|
173
|
+
writer = csv.DictWriter(output, fieldnames=headers)
|
|
174
|
+
writer.writeheader()
|
|
175
|
+
writer.writerows(rows)
|
|
176
|
+
csv_str = output.getvalue()
|
|
177
|
+
|
|
178
|
+
if output_path:
|
|
179
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
180
|
+
output_path.write_text(csv_str)
|
|
181
|
+
logger.info("exported_csv", path=str(output_path))
|
|
182
|
+
|
|
183
|
+
return csv_str
|
|
184
|
+
|
|
185
|
+
def export_markdown(self, data: list[Any], output_path: Path | None = None) -> str:
|
|
186
|
+
"""Export data as Markdown table.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
data: List of objects to export
|
|
190
|
+
output_path: Optional custom output path
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Markdown string
|
|
194
|
+
"""
|
|
195
|
+
if not data:
|
|
196
|
+
md_str = ""
|
|
197
|
+
else:
|
|
198
|
+
# Get headers from first item
|
|
199
|
+
first_item = data[0]
|
|
200
|
+
if hasattr(first_item, "model_dump"):
|
|
201
|
+
headers = list(first_item.model_dump().keys())
|
|
202
|
+
rows = [item.model_dump() for item in data]
|
|
203
|
+
else:
|
|
204
|
+
headers = (
|
|
205
|
+
list(first_item.keys()) if isinstance(first_item, dict) else []
|
|
206
|
+
)
|
|
207
|
+
rows = data
|
|
208
|
+
|
|
209
|
+
# Build markdown table
|
|
210
|
+
md_lines = [
|
|
211
|
+
"| " + " | ".join(headers) + " |",
|
|
212
|
+
"| " + " | ".join(["---"] * len(headers)) + " |",
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
for row in rows:
|
|
216
|
+
values = [
|
|
217
|
+
str(row.get(h, "")) if isinstance(row, dict) else ""
|
|
218
|
+
for h in headers
|
|
219
|
+
]
|
|
220
|
+
md_lines.append("| " + " | ".join(values) + " |")
|
|
221
|
+
|
|
222
|
+
md_str = "\n".join(md_lines)
|
|
223
|
+
|
|
224
|
+
if output_path:
|
|
225
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
226
|
+
output_path.write_text(md_str)
|
|
227
|
+
logger.info("exported_markdown", path=str(output_path))
|
|
228
|
+
|
|
229
|
+
return md_str
|
|
230
|
+
|
|
231
|
+
def export_data(
|
|
232
|
+
self,
|
|
233
|
+
data: Any,
|
|
234
|
+
entity_type: str,
|
|
235
|
+
format_type: str | None = None,
|
|
236
|
+
output_path: Path | None = None,
|
|
237
|
+
) -> tuple[str, Path]:
|
|
238
|
+
"""Export data in specified format.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
data: Data to export
|
|
242
|
+
entity_type: Type of entity being exported
|
|
243
|
+
format_type: Export format (uses config default if not specified)
|
|
244
|
+
output_path: Optional custom output path
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Tuple of (exported_content, file_path)
|
|
248
|
+
"""
|
|
249
|
+
if format_type is None:
|
|
250
|
+
format_type = self.config.export.format
|
|
251
|
+
|
|
252
|
+
if output_path is None:
|
|
253
|
+
output_path = self.get_export_path(entity_type, format_type)
|
|
254
|
+
|
|
255
|
+
if format_type == "json":
|
|
256
|
+
content = self.export_json(data, output_path)
|
|
257
|
+
elif format_type == "csv":
|
|
258
|
+
content = self.export_csv(
|
|
259
|
+
data if isinstance(data, list) else [data], output_path
|
|
260
|
+
)
|
|
261
|
+
elif format_type == "markdown":
|
|
262
|
+
content = self.export_markdown(
|
|
263
|
+
data if isinstance(data, list) else [data], output_path
|
|
264
|
+
)
|
|
265
|
+
else:
|
|
266
|
+
raise ValueError(f"Unsupported export format: {format_type}")
|
|
267
|
+
|
|
268
|
+
return content, output_path
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Service layer for milestone list command.
|
|
2
|
+
|
|
3
|
+
Handles all business logic related to:
|
|
4
|
+
- Filtering milestones (overdue, open/closed, etc.)
|
|
5
|
+
- Computing milestone progress
|
|
6
|
+
- Calculating time estimates
|
|
7
|
+
- Aggregating milestone data
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from datetime import UTC, datetime
|
|
11
|
+
|
|
12
|
+
from roadmap.common.logging import get_logger
|
|
13
|
+
from roadmap.infrastructure.coordination.core import RoadmapCore
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MilestoneFilterService:
|
|
19
|
+
"""Service for filtering and selecting milestones."""
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def filter_overdue_milestones(milestones: list) -> list:
|
|
23
|
+
"""Filter to only overdue milestones.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
milestones: List of milestone objects
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
List of overdue milestones (past due date, status='open')
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
now = datetime.now(UTC).replace(tzinfo=None)
|
|
33
|
+
filtered = []
|
|
34
|
+
|
|
35
|
+
for ms in milestones:
|
|
36
|
+
if ms.due_date:
|
|
37
|
+
ms_due_date = (
|
|
38
|
+
ms.due_date.replace(tzinfo=None)
|
|
39
|
+
if ms.due_date.tzinfo
|
|
40
|
+
else ms.due_date
|
|
41
|
+
)
|
|
42
|
+
if ms_due_date < now and ms.status.value == "open":
|
|
43
|
+
filtered.append(ms)
|
|
44
|
+
|
|
45
|
+
return filtered
|
|
46
|
+
except Exception as e:
|
|
47
|
+
logger.error(
|
|
48
|
+
"failed_to_filter_overdue", error=str(e), severity="operational"
|
|
49
|
+
)
|
|
50
|
+
return []
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def filter_milestones(milestones: list, overdue_only: bool = False) -> list:
|
|
54
|
+
"""Filter milestones based on criteria.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
milestones: List of milestone objects
|
|
58
|
+
overdue_only: If True, only return overdue milestones
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Filtered list of milestones
|
|
62
|
+
"""
|
|
63
|
+
if overdue_only:
|
|
64
|
+
return MilestoneFilterService.filter_overdue_milestones(milestones)
|
|
65
|
+
return milestones
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class MilestoneProgressService:
|
|
69
|
+
"""Service for computing milestone progress."""
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def get_milestone_progress(core: RoadmapCore, milestone_name: str) -> dict:
|
|
73
|
+
"""Get progress for a specific milestone.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
core: RoadmapCore instance
|
|
77
|
+
milestone_name: Name of the milestone
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Dictionary with progress data:
|
|
81
|
+
{
|
|
82
|
+
'total': total issues,
|
|
83
|
+
'completed': completed issues,
|
|
84
|
+
'percentage': completion percentage,
|
|
85
|
+
}
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
progress = core.milestones.get_progress(milestone_name)
|
|
89
|
+
return {
|
|
90
|
+
"total": progress.get("total", 0),
|
|
91
|
+
"completed": progress.get("completed", 0),
|
|
92
|
+
"percentage": (
|
|
93
|
+
(progress.get("completed", 0) / progress.get("total", 1)) * 100
|
|
94
|
+
if progress.get("total", 0) > 0
|
|
95
|
+
else 0
|
|
96
|
+
),
|
|
97
|
+
}
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.error(
|
|
100
|
+
"failed_to_get_milestone_progress",
|
|
101
|
+
milestone=milestone_name,
|
|
102
|
+
error=str(e),
|
|
103
|
+
severity="operational",
|
|
104
|
+
)
|
|
105
|
+
return {"total": 0, "completed": 0, "percentage": 0}
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def get_all_milestones_progress(core: RoadmapCore, milestones: list) -> dict:
|
|
109
|
+
"""Get progress for all milestones.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
core: RoadmapCore instance
|
|
113
|
+
milestones: List of milestone objects
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Dictionary mapping milestone name to progress dict
|
|
117
|
+
"""
|
|
118
|
+
progress_data = {}
|
|
119
|
+
for ms in milestones:
|
|
120
|
+
progress_data[ms.name] = MilestoneProgressService.get_milestone_progress(
|
|
121
|
+
core, ms.name
|
|
122
|
+
)
|
|
123
|
+
return progress_data
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class MilestoneTimeEstimateService:
|
|
127
|
+
"""Service for calculating time estimates."""
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def get_milestone_time_estimate(milestone, all_issues: list) -> str:
|
|
131
|
+
"""Get time estimate display string for a milestone.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
milestone: Milestone object
|
|
135
|
+
all_issues: List of all issues in roadmap
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Formatted time estimate string (e.g., "32 hours", "N/A")
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
estimate = milestone.get_estimated_time_display(all_issues)
|
|
142
|
+
return estimate if estimate else "-"
|
|
143
|
+
except Exception as e:
|
|
144
|
+
logger.error(
|
|
145
|
+
"failed_to_get_time_estimate",
|
|
146
|
+
milestone=milestone.name,
|
|
147
|
+
severity="operational",
|
|
148
|
+
error=str(e),
|
|
149
|
+
)
|
|
150
|
+
return "-"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class MilestoneListService:
|
|
154
|
+
"""Service for gathering and processing milestone list data."""
|
|
155
|
+
|
|
156
|
+
def __init__(self, core: RoadmapCore):
|
|
157
|
+
"""Initialize milestone list service.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
core: RoadmapCore instance
|
|
161
|
+
"""
|
|
162
|
+
self.core = core
|
|
163
|
+
|
|
164
|
+
def get_milestones_list_data(self, overdue_only: bool = False) -> dict:
|
|
165
|
+
"""Get all data needed for milestone list display.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
overdue_only: If True, only return overdue milestones
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Dictionary with milestone list data:
|
|
172
|
+
{
|
|
173
|
+
'milestones': list of milestone objects,
|
|
174
|
+
'progress': dict mapping milestone name to progress,
|
|
175
|
+
'estimates': dict mapping milestone name to estimate,
|
|
176
|
+
'has_data': bool indicating if there are milestones,
|
|
177
|
+
'count': number of milestones,
|
|
178
|
+
}
|
|
179
|
+
"""
|
|
180
|
+
try:
|
|
181
|
+
# Get milestones
|
|
182
|
+
milestones = self.core.milestones.list()
|
|
183
|
+
|
|
184
|
+
# Apply filters
|
|
185
|
+
milestones = MilestoneFilterService.filter_milestones(
|
|
186
|
+
milestones, overdue_only=overdue_only
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Get all issues for estimates
|
|
190
|
+
all_issues = self.core.issues.list()
|
|
191
|
+
|
|
192
|
+
# Compute progress for each milestone
|
|
193
|
+
progress = MilestoneProgressService.get_all_milestones_progress(
|
|
194
|
+
self.core, milestones
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Compute time estimates
|
|
198
|
+
estimates = {}
|
|
199
|
+
for ms in milestones:
|
|
200
|
+
estimates[ms.name] = (
|
|
201
|
+
MilestoneTimeEstimateService.get_milestone_time_estimate(
|
|
202
|
+
ms, all_issues
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
"milestones": milestones,
|
|
208
|
+
"progress": progress,
|
|
209
|
+
"estimates": estimates,
|
|
210
|
+
"has_data": bool(milestones),
|
|
211
|
+
"count": len(milestones),
|
|
212
|
+
}
|
|
213
|
+
except Exception as e:
|
|
214
|
+
logger.error(
|
|
215
|
+
"failed_to_get_milestones_list_data",
|
|
216
|
+
severity="operational",
|
|
217
|
+
error=str(e),
|
|
218
|
+
)
|
|
219
|
+
return {
|
|
220
|
+
"milestones": [],
|
|
221
|
+
"progress": {},
|
|
222
|
+
"estimates": {},
|
|
223
|
+
"has_data": False,
|
|
224
|
+
"count": 0,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
def get_milestone_due_date_status(self, milestone) -> tuple[str, str | None]:
|
|
228
|
+
"""Get due date string and styling for a milestone.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
milestone: Milestone object
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Tuple of (due_date_string, style_markup or None)
|
|
235
|
+
- style_markup is None for no special styling
|
|
236
|
+
- style_markup is "bold red" for overdue
|
|
237
|
+
- style_markup is "yellow" for due within 7 days
|
|
238
|
+
"""
|
|
239
|
+
if not milestone.due_date:
|
|
240
|
+
return ("-", None)
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
now = datetime.now(UTC).replace(tzinfo=None)
|
|
244
|
+
ms_due_date = (
|
|
245
|
+
milestone.due_date.replace(tzinfo=None)
|
|
246
|
+
if milestone.due_date.tzinfo
|
|
247
|
+
else milestone.due_date
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
due_date_text = ms_due_date.strftime("%Y-%m-%d")
|
|
251
|
+
|
|
252
|
+
# Determine styling based on due date and status
|
|
253
|
+
if ms_due_date < now and milestone.status.value == "open":
|
|
254
|
+
return (due_date_text, "bold red")
|
|
255
|
+
elif (ms_due_date - now).days <= 7 and milestone.status.value == "open":
|
|
256
|
+
return (due_date_text, "yellow")
|
|
257
|
+
|
|
258
|
+
return (due_date_text, None)
|
|
259
|
+
except Exception as e:
|
|
260
|
+
logger.error(
|
|
261
|
+
"failed_to_get_due_date_status",
|
|
262
|
+
milestone=milestone.name,
|
|
263
|
+
severity="operational",
|
|
264
|
+
error=str(e),
|
|
265
|
+
)
|
|
266
|
+
return ("-", None)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Project Initialization Service - Backward Compatibility Facade.
|
|
2
|
+
|
|
3
|
+
DEPRECATED: This module is maintained for backward compatibility.
|
|
4
|
+
Use roadmap.core.services.project_init package instead.
|
|
5
|
+
|
|
6
|
+
New structure:
|
|
7
|
+
- roadmap.core.services.project_init.detection - ProjectDetectionService
|
|
8
|
+
- roadmap.core.services.project_init.context_detection - ProjectContextDetectionService
|
|
9
|
+
- roadmap.core.services.project_init.template - ProjectTemplateService
|
|
10
|
+
- roadmap.core.services.project_init.creation - ProjectCreationService
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from roadmap.adapters.persistence.parser import ProjectParser
|
|
14
|
+
from roadmap.core.services.project_init import (
|
|
15
|
+
ProjectContextDetectionService,
|
|
16
|
+
ProjectCreationService,
|
|
17
|
+
ProjectDetectionService,
|
|
18
|
+
ProjectTemplateService,
|
|
19
|
+
)
|
|
20
|
+
from roadmap.infrastructure.coordination.core import RoadmapCore
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"ProjectDetectionService",
|
|
24
|
+
"ProjectContextDetectionService",
|
|
25
|
+
"ProjectTemplateService",
|
|
26
|
+
"ProjectCreationService",
|
|
27
|
+
"ProjectParser",
|
|
28
|
+
"RoadmapCore",
|
|
29
|
+
]
|