Complete architectural separation of concerns implementing clean layered design: • Services Layer: Pure business logic isolated from presentation - WorkspaceService: TDD workspace operations - IssueService: Issue management and creation - ProjectService: Project management and milestones - ExportService: Unix-friendly data export • CLI Layer: Clean presentation with command/presenter separation - Commands delegate to services for all business operations - Presenters handle formatted output and error messaging - Framework provides unified interface • Benefits: - Eliminates mixed concerns in 943-line CLI monolith - Enables easier testing and maintenance - Preserves all existing functionality and Unix pipeline compatibility - Provides foundation for future CLI development Resolves issue #20: CLI separation from core logic 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
76 lines
2.9 KiB
Python
76 lines
2.9 KiB
Python
"""
|
|
Project service - business logic for project management operations.
|
|
"""
|
|
|
|
from typing import List, Dict, Any
|
|
|
|
from tddai.project_manager import ProjectManager, ProjectState, Priority, Milestone, Label
|
|
from tddai import TddaiError
|
|
|
|
|
|
class ProjectService:
|
|
"""Service for project management operations."""
|
|
|
|
def __init__(self):
|
|
self.project_manager = ProjectManager()
|
|
|
|
def setup_project_management(self) -> None:
|
|
"""Setup project management labels and structure."""
|
|
self.project_manager.ensure_project_labels()
|
|
|
|
def create_milestone(self, title: str, description: str = "", due_date: str = None) -> Milestone:
|
|
"""Create a new milestone (project)."""
|
|
return self.project_manager.create_milestone(title, description, due_date)
|
|
|
|
def list_milestones(self, state: str = "open") -> List[Milestone]:
|
|
"""List milestones."""
|
|
return self.project_manager.list_milestones(state)
|
|
|
|
def list_labels(self) -> List[Label]:
|
|
"""List repository labels."""
|
|
return self.project_manager.list_labels()
|
|
|
|
def set_issue_state(self, issue_number: int, state_name: str) -> Dict[str, Any]:
|
|
"""Set issue project state."""
|
|
# Convert string to ProjectState enum
|
|
state_map = {
|
|
'todo': ProjectState.TODO,
|
|
'active': ProjectState.ACTIVE,
|
|
'review': ProjectState.REVIEW,
|
|
'done': ProjectState.DONE,
|
|
'blocked': ProjectState.BLOCKED
|
|
}
|
|
|
|
if state_name not in state_map:
|
|
raise TddaiError(f"Invalid state '{state_name}'. Valid states: {list(state_map.keys())}")
|
|
|
|
project_state = state_map[state_name]
|
|
return self.project_manager.set_issue_state(issue_number, project_state)
|
|
|
|
def set_issue_priority(self, issue_number: int, priority_name: str) -> Dict[str, Any]:
|
|
"""Set issue priority."""
|
|
# Convert string to Priority enum
|
|
priority_map = {
|
|
'low': Priority.LOW,
|
|
'medium': Priority.MEDIUM,
|
|
'high': Priority.HIGH,
|
|
'critical': Priority.CRITICAL
|
|
}
|
|
|
|
if priority_name not in priority_map:
|
|
raise TddaiError(f"Invalid priority '{priority_name}'. Valid priorities: {list(priority_map.keys())}")
|
|
|
|
priority_level = priority_map[priority_name]
|
|
return self.project_manager.set_issue_priority(issue_number, priority_level)
|
|
|
|
def move_issue_to_done(self, issue_number: int) -> Dict[str, Any]:
|
|
"""Move issue to done state and close it."""
|
|
return self.project_manager.move_issue_to_done(issue_number)
|
|
|
|
def assign_issue_to_milestone(self, issue_number: int, milestone_id: int) -> Dict[str, Any]:
|
|
"""Assign issue to a milestone."""
|
|
return self.project_manager.assign_issue_to_milestone(issue_number, milestone_id)
|
|
|
|
def get_project_overview(self) -> Dict[str, Any]:
|
|
"""Get project management overview."""
|
|
return self.project_manager.get_project_overview() |