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,277 @@
|
|
|
1
|
+
"""Presenter for core initialization command output."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Union
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from roadmap.core.domain.health import HealthStatus
|
|
7
|
+
|
|
8
|
+
import click # type: ignore[import]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CoreInitializationPresenter:
|
|
12
|
+
"""Handles all console output for core initialization commands."""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
"""Initialize presenter."""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
# ========== Init Command Output ==========
|
|
19
|
+
|
|
20
|
+
def present_initialization_header(self) -> None:
|
|
21
|
+
"""Show initialization header."""
|
|
22
|
+
click.secho("š Roadmap CLI Initialization", fg="cyan", bold=True)
|
|
23
|
+
click.echo()
|
|
24
|
+
|
|
25
|
+
def present_force_reinitialize_warning(self, name: str) -> None:
|
|
26
|
+
"""Show warning about force re-initialization."""
|
|
27
|
+
click.secho(
|
|
28
|
+
f"ā ļø --force specified: removing existing {name}/",
|
|
29
|
+
fg="yellow",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def present_already_initialized_info(self, name: str) -> None:
|
|
33
|
+
"""Show info that roadmap is already initialized."""
|
|
34
|
+
click.secho(
|
|
35
|
+
f"ā¹ļø Roadmap already initialized in {name}/",
|
|
36
|
+
fg="cyan",
|
|
37
|
+
)
|
|
38
|
+
click.echo(
|
|
39
|
+
" Updating configuration while preserving existing data.", err=False
|
|
40
|
+
)
|
|
41
|
+
click.echo()
|
|
42
|
+
|
|
43
|
+
def present_initialization_error(self, error: str) -> None:
|
|
44
|
+
"""Show initialization error."""
|
|
45
|
+
click.secho(f"ā {error}", fg="red", bold=True)
|
|
46
|
+
|
|
47
|
+
def present_initialization_tip(self) -> None:
|
|
48
|
+
"""Show helpful tip for initialization errors."""
|
|
49
|
+
click.secho(
|
|
50
|
+
"Tip: use --force to reinitialize or --dry-run to preview.",
|
|
51
|
+
fg="yellow",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def present_already_in_progress_error(self) -> None:
|
|
55
|
+
"""Show error that initialization is already in progress."""
|
|
56
|
+
click.secho(
|
|
57
|
+
"ā Initialization already in progress. Try again later.",
|
|
58
|
+
fg="red",
|
|
59
|
+
bold=True,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def present_creating_structure(self, name: str) -> None:
|
|
63
|
+
"""Show status while creating structure."""
|
|
64
|
+
click.secho(f"šļø Creating roadmap structure in {name}/...", fg="white")
|
|
65
|
+
|
|
66
|
+
def present_existing_projects_found(self, count: int) -> None:
|
|
67
|
+
"""Show info about existing projects found."""
|
|
68
|
+
click.secho(
|
|
69
|
+
f"\n š” {count} projects found. All will be available.",
|
|
70
|
+
fg="white",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def present_project_created(self, project_name: str) -> None:
|
|
74
|
+
"""Show that a project was created."""
|
|
75
|
+
click.secho(f"ā
Created project: {project_name}", fg="green")
|
|
76
|
+
|
|
77
|
+
def present_project_joined(self, project_name: str) -> None:
|
|
78
|
+
"""Show that team member joined existing project."""
|
|
79
|
+
click.secho(f"ā
Joined existing project: {project_name}", fg="green")
|
|
80
|
+
|
|
81
|
+
def present_projects_joined(self, main_project: str, count: int) -> None:
|
|
82
|
+
"""Show that team member joined with multiple projects available."""
|
|
83
|
+
click.secho(
|
|
84
|
+
f"ā
Joined {main_project} ({count} projects available)", fg="green"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def present_initialization_warning(self, message: str) -> None:
|
|
88
|
+
"""Show warning message after initialization."""
|
|
89
|
+
click.secho(
|
|
90
|
+
"ā ļø Initialization completed with warnings; see above.",
|
|
91
|
+
fg="yellow",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def present_initialization_failed(self, error: str) -> None:
|
|
95
|
+
"""Show initialization failure message."""
|
|
96
|
+
click.secho(f"ā Failed to initialize roadmap: {error}", fg="red", bold=True)
|
|
97
|
+
|
|
98
|
+
# ========== Status Command Output ==========
|
|
99
|
+
|
|
100
|
+
def present_status_header(self) -> None:
|
|
101
|
+
"""Show status command header."""
|
|
102
|
+
click.secho("š Roadmap Status Report", fg="cyan", bold=True)
|
|
103
|
+
click.echo()
|
|
104
|
+
|
|
105
|
+
def present_status_section(self, title: str) -> None:
|
|
106
|
+
"""Show status section header."""
|
|
107
|
+
click.secho(f"\n{title}", fg="cyan", bold=True)
|
|
108
|
+
click.secho("ā" * (len(title)), fg="white")
|
|
109
|
+
|
|
110
|
+
def present_status_item(self, label: str, value: str, ok: bool = True) -> None:
|
|
111
|
+
"""Show a status item."""
|
|
112
|
+
color = "green" if ok else "yellow"
|
|
113
|
+
icon = "ā" if ok else "ā "
|
|
114
|
+
click.secho(f"{icon} {label}: {value}", fg=color)
|
|
115
|
+
|
|
116
|
+
def present_status_not_initialized(self) -> None:
|
|
117
|
+
"""Show that roadmap is not initialized."""
|
|
118
|
+
click.secho("ā Roadmap not initialized in .roadmap/", fg="red")
|
|
119
|
+
click.echo("Run 'roadmap init' to get started.")
|
|
120
|
+
|
|
121
|
+
# ========== Health Check Output ==========
|
|
122
|
+
|
|
123
|
+
def present_health_header(self) -> None:
|
|
124
|
+
"""Show health check header."""
|
|
125
|
+
click.secho("š„ Roadmap Health Check", fg="cyan", bold=True)
|
|
126
|
+
click.echo()
|
|
127
|
+
|
|
128
|
+
def present_health_section(self, title: str) -> None:
|
|
129
|
+
"""Show health section header."""
|
|
130
|
+
click.secho(f"\n{title}", fg="cyan", bold=True)
|
|
131
|
+
click.secho("ā" * (len(title)), fg="white")
|
|
132
|
+
|
|
133
|
+
def present_health_check(
|
|
134
|
+
self, check_name: str, status: Union[str, "HealthStatus"], message: str = ""
|
|
135
|
+
) -> None:
|
|
136
|
+
"""Show individual health check result.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
check_name: Name of the health check
|
|
140
|
+
status: HealthStatus enum value or string
|
|
141
|
+
message: Optional details about the check
|
|
142
|
+
"""
|
|
143
|
+
from roadmap.infrastructure.observability.health import HealthStatus
|
|
144
|
+
|
|
145
|
+
# Convert status to string if it's an enum
|
|
146
|
+
status_str = status.name if isinstance(status, HealthStatus) else str(status)
|
|
147
|
+
|
|
148
|
+
# Map status to display values
|
|
149
|
+
status_map = {
|
|
150
|
+
"HEALTHY": ("ā
", "green"),
|
|
151
|
+
"DEGRADED": ("ā ļø", "yellow"),
|
|
152
|
+
"UNHEALTHY": ("ā", "red"),
|
|
153
|
+
"healthy": ("ā
", "green"),
|
|
154
|
+
"warning": ("ā ļø", "yellow"),
|
|
155
|
+
"error": ("ā", "red"),
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
icon, color = status_map.get(status_str, ("?", "white"))
|
|
159
|
+
display_name = check_name.replace("_", " ").title()
|
|
160
|
+
|
|
161
|
+
click.secho(f"{icon} {display_name}: {status_str}", fg=color)
|
|
162
|
+
if message:
|
|
163
|
+
click.echo(f" {message}")
|
|
164
|
+
|
|
165
|
+
def present_overall_health(self, status: Union[str, "HealthStatus"]) -> None:
|
|
166
|
+
"""Show overall health status.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
status: HealthStatus enum value or string
|
|
170
|
+
"""
|
|
171
|
+
from roadmap.infrastructure.observability.health import HealthStatus
|
|
172
|
+
|
|
173
|
+
# Convert status to string if it's an enum
|
|
174
|
+
status_str = status.name if isinstance(status, HealthStatus) else str(status)
|
|
175
|
+
|
|
176
|
+
status_map = {
|
|
177
|
+
"HEALTHY": ("ā
", "green"),
|
|
178
|
+
"DEGRADED": ("ā ļø", "yellow"),
|
|
179
|
+
"UNHEALTHY": ("ā", "red"),
|
|
180
|
+
"healthy": ("ā
", "green"),
|
|
181
|
+
"warning": ("ā ļø", "yellow"),
|
|
182
|
+
"error": ("ā", "red"),
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
icon, color = status_map.get(status_str, ("?", "white"))
|
|
186
|
+
|
|
187
|
+
click.echo()
|
|
188
|
+
click.secho(
|
|
189
|
+
f"{icon} Overall Status: {status_str}",
|
|
190
|
+
fg=color,
|
|
191
|
+
bold=True,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
def present_health_warning(self, message: str) -> None:
|
|
195
|
+
"""Show health warning."""
|
|
196
|
+
click.secho(f"ā ļø {message}", fg="yellow")
|
|
197
|
+
|
|
198
|
+
# ========== GitHub Integration Output ==========
|
|
199
|
+
|
|
200
|
+
def present_github_testing(self) -> None:
|
|
201
|
+
"""Show message while testing GitHub connection."""
|
|
202
|
+
click.secho("š Testing GitHub connection...", fg="yellow")
|
|
203
|
+
|
|
204
|
+
def present_github_credentials_stored(self) -> None:
|
|
205
|
+
"""Show message that credentials were stored."""
|
|
206
|
+
click.secho("š Credentials stored securely", fg="green")
|
|
207
|
+
|
|
208
|
+
def present_github_unavailable(self, reason: str) -> None:
|
|
209
|
+
"""Show GitHub integration unavailable message."""
|
|
210
|
+
click.secho(
|
|
211
|
+
f"ā ļø GitHub integration not available: {reason}",
|
|
212
|
+
fg="yellow",
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def present_github_setup_failed(self, error: str) -> None:
|
|
216
|
+
"""Show GitHub setup failure."""
|
|
217
|
+
click.secho(f"ā GitHub setup failed: {error}", fg="red")
|
|
218
|
+
|
|
219
|
+
# ========== Generic Output Methods ==========
|
|
220
|
+
|
|
221
|
+
def present_error(self, message: str) -> None:
|
|
222
|
+
"""Show generic error message."""
|
|
223
|
+
click.secho(f"ā {message}", fg="red", bold=True)
|
|
224
|
+
|
|
225
|
+
def present_warning(self, message: str) -> None:
|
|
226
|
+
"""Show generic warning message."""
|
|
227
|
+
click.secho(f"ā ļø {message}", fg="yellow")
|
|
228
|
+
|
|
229
|
+
def present_info(self, message: str) -> None:
|
|
230
|
+
"""Show generic info message."""
|
|
231
|
+
click.secho(f"ā¹ļø {message}", fg="cyan")
|
|
232
|
+
|
|
233
|
+
def present_success(self, message: str) -> None:
|
|
234
|
+
"""Show generic success message."""
|
|
235
|
+
click.secho(f"ā
{message}", fg="green", bold=True)
|
|
236
|
+
|
|
237
|
+
def present_initialization_complete(self, created_new: bool) -> None:
|
|
238
|
+
"""Show initialization completion with next steps.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
created_new: Whether a new roadmap was created or existing was joined
|
|
242
|
+
"""
|
|
243
|
+
click.echo()
|
|
244
|
+
if created_new:
|
|
245
|
+
click.secho("ā
Roadmap initialized successfully!", fg="green", bold=True)
|
|
246
|
+
click.echo(" š Data stored in: .roadmap/")
|
|
247
|
+
else:
|
|
248
|
+
click.secho("ā
Initialization complete!", fg="green", bold=True)
|
|
249
|
+
|
|
250
|
+
click.echo()
|
|
251
|
+
click.echo("Next steps:")
|
|
252
|
+
click.echo(' ⢠roadmap issue create "Your first task"')
|
|
253
|
+
click.echo(' ⢠roadmap milestone create "v1.0"')
|
|
254
|
+
click.echo(" ⢠roadmap list")
|
|
255
|
+
click.echo()
|
|
256
|
+
|
|
257
|
+
def present_existing_projects_info(self, project_names: list) -> None:
|
|
258
|
+
"""Show info about existing projects found.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
project_names: List of project names that were found
|
|
262
|
+
"""
|
|
263
|
+
click.echo()
|
|
264
|
+
if len(project_names) == 1:
|
|
265
|
+
click.secho(
|
|
266
|
+
f"ā¹ļø Found existing project: {project_names[0]}",
|
|
267
|
+
fg="cyan",
|
|
268
|
+
)
|
|
269
|
+
else:
|
|
270
|
+
projects_str = ", ".join(project_names[:3])
|
|
271
|
+
if len(project_names) > 3:
|
|
272
|
+
projects_str += f", +{len(project_names) - 3} more"
|
|
273
|
+
click.secho(
|
|
274
|
+
f"ā¹ļø Found {len(project_names)} projects: {projects_str}",
|
|
275
|
+
fg="cyan",
|
|
276
|
+
)
|
|
277
|
+
click.secho(" Run 'roadmap project list' to see all", fg="white")
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Presenter for CRUD operation results.
|
|
2
|
+
|
|
3
|
+
This presenter handles displaying results of create, update, delete, archive,
|
|
4
|
+
and restore operations. It shows a brief success message with key entity details.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from roadmap.adapters.cli.presentation.base_presenter import BasePresenter
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _infer_entity_type(entity: Any) -> str:
|
|
13
|
+
"""Infer entity type from class name or attributes.
|
|
14
|
+
|
|
15
|
+
Handles both real objects and mocks used in tests.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
entity: The entity object
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Entity type string (Issue, Project, Milestone, Entity)
|
|
22
|
+
"""
|
|
23
|
+
class_name = entity.__class__.__name__
|
|
24
|
+
|
|
25
|
+
# Handle mocks in tests
|
|
26
|
+
if class_name == "Mock":
|
|
27
|
+
# Try to infer from entity attributes
|
|
28
|
+
if hasattr(entity, "priority") and hasattr(entity, "issue_type"):
|
|
29
|
+
return "Issue"
|
|
30
|
+
elif hasattr(entity, "repository"):
|
|
31
|
+
return "Project"
|
|
32
|
+
elif hasattr(entity, "start_date") or hasattr(entity, "end_date"):
|
|
33
|
+
return "Milestone"
|
|
34
|
+
return "Entity"
|
|
35
|
+
|
|
36
|
+
return class_name
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _get_entity_id(entity: Any) -> str:
|
|
40
|
+
"""Extract entity ID."""
|
|
41
|
+
if hasattr(entity, "id"):
|
|
42
|
+
return str(entity.id)
|
|
43
|
+
if hasattr(entity, "entity_id"):
|
|
44
|
+
return str(entity.entity_id)
|
|
45
|
+
return "unknown"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _get_entity_title(entity: Any) -> str:
|
|
49
|
+
"""Extract entity title/name."""
|
|
50
|
+
if hasattr(entity, "name"):
|
|
51
|
+
return entity.name
|
|
52
|
+
if hasattr(entity, "title"):
|
|
53
|
+
return entity.title
|
|
54
|
+
if hasattr(entity, "summary"):
|
|
55
|
+
return entity.summary
|
|
56
|
+
return "Untitled"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class CreatePresenter(BasePresenter):
|
|
60
|
+
"""Presenter for create operation results."""
|
|
61
|
+
|
|
62
|
+
def render(self, entity: Any) -> None:
|
|
63
|
+
"""Render created entity summary.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
entity: The created entity object
|
|
67
|
+
"""
|
|
68
|
+
console = self._get_console()
|
|
69
|
+
entity_type = _infer_entity_type(entity)
|
|
70
|
+
entity_id = _get_entity_id(entity)
|
|
71
|
+
title = _get_entity_title(entity)
|
|
72
|
+
|
|
73
|
+
console.print(
|
|
74
|
+
f"ā
Created {entity_type.lower()}: {title} [{entity_id}]",
|
|
75
|
+
style="green",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class UpdatePresenter(BasePresenter):
|
|
80
|
+
"""Presenter for update operation results."""
|
|
81
|
+
|
|
82
|
+
def render(self, entity: Any, _updates: dict[str, Any] | None = None) -> None:
|
|
83
|
+
"""Render updated entity summary.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
entity: The updated entity object
|
|
87
|
+
_updates: Optional dict of fields that were updated
|
|
88
|
+
"""
|
|
89
|
+
console = self._get_console()
|
|
90
|
+
entity_type = _infer_entity_type(entity)
|
|
91
|
+
entity_id = _get_entity_id(entity)
|
|
92
|
+
title = _get_entity_title(entity)
|
|
93
|
+
|
|
94
|
+
console.print(
|
|
95
|
+
f"ā
Updated {entity_type.lower()}: {title} [{entity_id}]",
|
|
96
|
+
style="green",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Display updated fields if provided
|
|
100
|
+
if _updates:
|
|
101
|
+
for field, value in _updates.items():
|
|
102
|
+
if value is not None and field != "id": # Skip id field
|
|
103
|
+
if field == "estimated_hours" and value is not None:
|
|
104
|
+
# Format estimate as "Xh" or "Xd"
|
|
105
|
+
hours = float(value)
|
|
106
|
+
days = hours / 8.0
|
|
107
|
+
if days >= 1:
|
|
108
|
+
console.print(f" estimate: {days:.1f}d", style="cyan")
|
|
109
|
+
else:
|
|
110
|
+
console.print(f" estimate: {hours:.1f}h", style="cyan")
|
|
111
|
+
else:
|
|
112
|
+
# Format the field name nicely
|
|
113
|
+
display_field = field.replace("_", " ").title()
|
|
114
|
+
console.print(f" {display_field}: {value}", style="cyan")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class DeletePresenter(BasePresenter):
|
|
118
|
+
"""Presenter for delete operation results."""
|
|
119
|
+
|
|
120
|
+
def render(self, entity: Any) -> None:
|
|
121
|
+
"""Render deleted entity summary.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
entity: The deleted entity object
|
|
125
|
+
"""
|
|
126
|
+
console = self._get_console()
|
|
127
|
+
entity_type = _infer_entity_type(entity)
|
|
128
|
+
entity_id = _get_entity_id(entity)
|
|
129
|
+
title = _get_entity_title(entity)
|
|
130
|
+
|
|
131
|
+
console.print(
|
|
132
|
+
f"ā
Deleted {entity_type.lower()}: {title} [{entity_id}]",
|
|
133
|
+
style="green",
|
|
134
|
+
)
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Daily summary presenter for formatting and rendering output.
|
|
2
|
+
|
|
3
|
+
Handles all display formatting for the daily summary, including
|
|
4
|
+
table rendering and output styling.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import UTC, datetime
|
|
8
|
+
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
from roadmap.adapters.cli.presentation.base_presenter import BasePresenter
|
|
14
|
+
from roadmap.common.console import get_console
|
|
15
|
+
|
|
16
|
+
console = get_console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DailySummaryPresenter(BasePresenter):
|
|
20
|
+
"""Presenter for daily summary output.
|
|
21
|
+
|
|
22
|
+
Handles formatting and rendering of daily summary data
|
|
23
|
+
including tables, panels, and styling.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def render(self, data: dict) -> None:
|
|
27
|
+
"""Render complete daily summary to console.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
data: Dictionary from DailySummaryService.get_daily_summary_data()
|
|
31
|
+
"""
|
|
32
|
+
if not data["has_issues"]:
|
|
33
|
+
milestone = data["milestone"]
|
|
34
|
+
console.print(
|
|
35
|
+
f"ā
No issues assigned to you in [bold]{milestone.name}[/bold]",
|
|
36
|
+
style="green",
|
|
37
|
+
)
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
# Render header
|
|
41
|
+
self._render_header(data)
|
|
42
|
+
|
|
43
|
+
# Render sections
|
|
44
|
+
issues = data["issues"]
|
|
45
|
+
self._render_in_progress(issues["in_progress"])
|
|
46
|
+
self._render_overdue(issues["overdue"])
|
|
47
|
+
self._render_blocked(issues["blocked"])
|
|
48
|
+
self._render_up_next(issues["todo_high_priority"])
|
|
49
|
+
self._render_completed_today(issues["completed_today"])
|
|
50
|
+
|
|
51
|
+
# Render summary and tips
|
|
52
|
+
self._render_summary(issues)
|
|
53
|
+
self._render_tips(data)
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def _render_header(data: dict) -> None:
|
|
57
|
+
"""Render daily summary header with user and milestone info."""
|
|
58
|
+
current_user = data["current_user"]
|
|
59
|
+
milestone = data["milestone"]
|
|
60
|
+
|
|
61
|
+
header = Text()
|
|
62
|
+
header.append("Daily Summary - ", style="dim")
|
|
63
|
+
header.append(current_user, style="bold cyan")
|
|
64
|
+
header.append("\nMilestone: ", style="dim")
|
|
65
|
+
header.append(milestone.name, style="bold yellow")
|
|
66
|
+
|
|
67
|
+
if milestone.due_date:
|
|
68
|
+
header.append(
|
|
69
|
+
f" (due {milestone.due_date.strftime('%Y-%m-%d')})",
|
|
70
|
+
style="dim",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
header.append(f"\n{datetime.now(UTC).strftime('%A, %B %d, %Y')}", style="dim")
|
|
74
|
+
|
|
75
|
+
console.print(Panel(header, border_style="cyan"))
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def _render_in_progress(issues: list) -> None:
|
|
79
|
+
"""Render in-progress issues section."""
|
|
80
|
+
if issues:
|
|
81
|
+
progress_table = Table(
|
|
82
|
+
show_header=True,
|
|
83
|
+
header_style="bold yellow",
|
|
84
|
+
title="š In Progress",
|
|
85
|
+
title_style="bold yellow",
|
|
86
|
+
)
|
|
87
|
+
progress_table.add_column("ID", style="cyan", width=10)
|
|
88
|
+
progress_table.add_column("Title", style="white")
|
|
89
|
+
progress_table.add_column("Progress", width=12)
|
|
90
|
+
progress_table.add_column("Priority", width=10)
|
|
91
|
+
|
|
92
|
+
for issue in issues:
|
|
93
|
+
progress_table.add_row(
|
|
94
|
+
issue.id,
|
|
95
|
+
issue.title[:60] + "..." if len(issue.title) > 60 else issue.title,
|
|
96
|
+
issue.progress_display,
|
|
97
|
+
issue.priority.value,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
console.print(progress_table)
|
|
101
|
+
console.print()
|
|
102
|
+
else:
|
|
103
|
+
console.print(
|
|
104
|
+
Panel(
|
|
105
|
+
"[dim]No issues currently in progress[/dim]",
|
|
106
|
+
title="š In Progress",
|
|
107
|
+
title_align="left",
|
|
108
|
+
border_style="yellow",
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
console.print()
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def _render_overdue(issues: list) -> None:
|
|
115
|
+
"""Render overdue issues section."""
|
|
116
|
+
if issues:
|
|
117
|
+
overdue_table = Table(
|
|
118
|
+
show_header=True,
|
|
119
|
+
header_style="bold red",
|
|
120
|
+
title="ā ļø Overdue",
|
|
121
|
+
title_style="bold red",
|
|
122
|
+
)
|
|
123
|
+
overdue_table.add_column("ID", style="cyan", width=10)
|
|
124
|
+
overdue_table.add_column("Title", style="white")
|
|
125
|
+
overdue_table.add_column("Due Date", width=12, style="red")
|
|
126
|
+
overdue_table.add_column("Priority", width=10)
|
|
127
|
+
|
|
128
|
+
for issue in issues:
|
|
129
|
+
due_date_obj = issue.due_date
|
|
130
|
+
# Normalize issue due_date to UTC-aware datetime
|
|
131
|
+
if isinstance(due_date_obj, datetime):
|
|
132
|
+
if due_date_obj.tzinfo:
|
|
133
|
+
due_date_aware = due_date_obj.astimezone(UTC)
|
|
134
|
+
else:
|
|
135
|
+
due_date_aware = due_date_obj.replace(tzinfo=UTC)
|
|
136
|
+
else:
|
|
137
|
+
due_date_aware = datetime.fromisoformat(str(due_date_obj)).replace(
|
|
138
|
+
tzinfo=UTC
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
days_overdue = (datetime.now(UTC) - due_date_aware).days
|
|
142
|
+
overdue_table.add_row(
|
|
143
|
+
issue.id,
|
|
144
|
+
issue.title[:50] + "..." if len(issue.title) > 50 else issue.title,
|
|
145
|
+
f"{due_date_aware.strftime('%Y-%m-%d')} ({days_overdue}d)",
|
|
146
|
+
issue.priority.value,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
console.print(overdue_table)
|
|
150
|
+
console.print()
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def _render_blocked(issues: list) -> None:
|
|
154
|
+
"""Render blocked issues section."""
|
|
155
|
+
if issues:
|
|
156
|
+
blocked_table = Table(
|
|
157
|
+
show_header=True,
|
|
158
|
+
header_style="bold red",
|
|
159
|
+
title="š« Blocked",
|
|
160
|
+
title_style="bold red",
|
|
161
|
+
)
|
|
162
|
+
blocked_table.add_column("ID", style="cyan", width=10)
|
|
163
|
+
blocked_table.add_column("Title", style="white")
|
|
164
|
+
blocked_table.add_column("Priority", width=10)
|
|
165
|
+
|
|
166
|
+
for issue in issues:
|
|
167
|
+
blocked_table.add_row(
|
|
168
|
+
issue.id,
|
|
169
|
+
issue.title[:60] + "..." if len(issue.title) > 60 else issue.title,
|
|
170
|
+
issue.priority.value,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
console.print(blocked_table)
|
|
174
|
+
console.print()
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def _render_up_next(issues: list) -> None:
|
|
178
|
+
"""Render up next (high priority todos) section."""
|
|
179
|
+
if issues:
|
|
180
|
+
next_table = Table(
|
|
181
|
+
show_header=True,
|
|
182
|
+
header_style="bold blue",
|
|
183
|
+
title="š Up Next (High Priority)",
|
|
184
|
+
title_style="bold blue",
|
|
185
|
+
)
|
|
186
|
+
next_table.add_column("ID", style="cyan", width=10)
|
|
187
|
+
next_table.add_column("Title", style="white")
|
|
188
|
+
next_table.add_column("Priority", width=10)
|
|
189
|
+
next_table.add_column("Estimate", width=10)
|
|
190
|
+
|
|
191
|
+
for issue in issues:
|
|
192
|
+
priority_color = (
|
|
193
|
+
"bold red" if issue.priority.value == "critical" else "red"
|
|
194
|
+
)
|
|
195
|
+
next_table.add_row(
|
|
196
|
+
issue.id,
|
|
197
|
+
issue.title[:55] + "..." if len(issue.title) > 55 else issue.title,
|
|
198
|
+
f"[{priority_color}]{issue.priority.value}[/{priority_color}]",
|
|
199
|
+
issue.estimated_time_display,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
console.print(next_table)
|
|
203
|
+
console.print()
|
|
204
|
+
|
|
205
|
+
@staticmethod
|
|
206
|
+
def _render_completed_today(issues: list) -> None:
|
|
207
|
+
"""Render completed today section."""
|
|
208
|
+
if issues:
|
|
209
|
+
done_table = Table(
|
|
210
|
+
show_header=True,
|
|
211
|
+
header_style="bold green",
|
|
212
|
+
title="ā
Completed Today",
|
|
213
|
+
title_style="bold green",
|
|
214
|
+
)
|
|
215
|
+
done_table.add_column("ID", style="cyan", width=10)
|
|
216
|
+
done_table.add_column("Title", style="white")
|
|
217
|
+
done_table.add_column("Completed", width=12)
|
|
218
|
+
|
|
219
|
+
for issue in issues:
|
|
220
|
+
done_table.add_row(
|
|
221
|
+
issue.id,
|
|
222
|
+
issue.title[:60] + "..." if len(issue.title) > 60 else issue.title,
|
|
223
|
+
issue.actual_end_date.strftime("%H:%M"),
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
console.print(done_table)
|
|
227
|
+
console.print()
|
|
228
|
+
|
|
229
|
+
@staticmethod
|
|
230
|
+
def _render_summary(issues: dict) -> None:
|
|
231
|
+
"""Render summary footer with counters."""
|
|
232
|
+
summary = Text()
|
|
233
|
+
summary.append("\nš Summary: ", style="bold")
|
|
234
|
+
summary.append(f"{len(issues['in_progress'])} in progress", style="yellow")
|
|
235
|
+
summary.append(" ⢠", style="dim")
|
|
236
|
+
|
|
237
|
+
overdue_style = "red" if issues["overdue"] else "dim"
|
|
238
|
+
summary.append(f"{len(issues['overdue'])} overdue", style=overdue_style)
|
|
239
|
+
summary.append(" ⢠", style="dim")
|
|
240
|
+
|
|
241
|
+
blocked_style = "red" if issues["blocked"] else "dim"
|
|
242
|
+
summary.append(f"{len(issues['blocked'])} blocked", style=blocked_style)
|
|
243
|
+
summary.append(" ⢠", style="dim")
|
|
244
|
+
summary.append(
|
|
245
|
+
f"{len(issues['completed_today'])} completed today", style="green"
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
console.print(summary)
|
|
249
|
+
|
|
250
|
+
@staticmethod
|
|
251
|
+
def _render_tips(data: dict) -> None:
|
|
252
|
+
"""Render helpful tips based on data."""
|
|
253
|
+
issues = data["issues"]
|
|
254
|
+
|
|
255
|
+
if not issues["in_progress"] and issues["todo_high_priority"]:
|
|
256
|
+
issue = issues["todo_high_priority"][0]
|
|
257
|
+
console.print(
|
|
258
|
+
f"\nš” Tip: Start work on an issue with: [cyan]roadmap issue start {issue.id}[/cyan]",
|
|
259
|
+
style="dim",
|
|
260
|
+
)
|
|
261
|
+
elif issues["overdue"]:
|
|
262
|
+
issue = issues["overdue"][0]
|
|
263
|
+
console.print(
|
|
264
|
+
f"\nš” Tip: View overdue issue details with: [cyan]roadmap issue view {issue.id}[/cyan]",
|
|
265
|
+
style="dim",
|
|
266
|
+
)
|