refactor: Separate CLI presentation from core business logic
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>
This commit is contained in:
69
services/issue_service.py
Normal file
69
services/issue_service.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
Issue service - business logic for issue operations.
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Any, Optional
|
||||
from datetime import datetime
|
||||
|
||||
from tddai import IssueFetcher, TddaiError
|
||||
from tddai.issue_creator import IssueCreator
|
||||
from gitea.models import Issue, Priority
|
||||
|
||||
|
||||
class IssueService:
|
||||
"""Service for issue operations."""
|
||||
|
||||
def __init__(self):
|
||||
self.issue_fetcher = IssueFetcher()
|
||||
self.issue_creator = IssueCreator()
|
||||
|
||||
def get_issue(self, issue_number: int) -> Issue:
|
||||
"""Get a specific issue by number."""
|
||||
return self.issue_fetcher.fetch_issue(issue_number)
|
||||
|
||||
def list_issues(self, state: str = "all") -> List[Issue]:
|
||||
"""List issues with optional state filter."""
|
||||
return self.issue_fetcher.fetch_issues(state)
|
||||
|
||||
def list_open_issues(self) -> List[Issue]:
|
||||
"""List only open issues."""
|
||||
return self.issue_fetcher.fetch_open_issues()
|
||||
|
||||
def create_issue(self, title: str, body: str, **kwargs) -> Dict[str, Any]:
|
||||
"""Create a new issue."""
|
||||
return self.issue_creator.create_issue(title, body, **kwargs)
|
||||
|
||||
def create_enhancement_issue(self, title: str, use_case: str,
|
||||
technical_requirements: str = "",
|
||||
acceptance_criteria: List[str] = None,
|
||||
dependencies: List[str] = None,
|
||||
priority: str = "Medium") -> Dict[str, Any]:
|
||||
"""Create a structured enhancement issue."""
|
||||
return self.issue_creator.create_enhancement_issue(
|
||||
title, use_case, technical_requirements,
|
||||
acceptance_criteria, dependencies, priority
|
||||
)
|
||||
|
||||
def create_from_template(self, template_file: str, **kwargs) -> Dict[str, Any]:
|
||||
"""Create issue from template file."""
|
||||
return self.issue_creator.create_from_template(template_file, **kwargs)
|
||||
|
||||
def get_issue_summary(self, issue_number: int) -> Dict[str, Any]:
|
||||
"""Get issue summary for display purposes."""
|
||||
issue = self.get_issue(issue_number)
|
||||
|
||||
return {
|
||||
'number': issue.number,
|
||||
'title': issue.title,
|
||||
'body': issue.body,
|
||||
'state': issue.state,
|
||||
'priority': issue.priority,
|
||||
'status': issue.status,
|
||||
'created_at': issue.created_at,
|
||||
'updated_at': issue.updated_at,
|
||||
'html_url': issue.html_url,
|
||||
'assignee': issue.assignee.login if issue.assignee else None,
|
||||
'labels': [label.name for label in issue.labels],
|
||||
'has_milestone': issue.milestone is not None,
|
||||
'milestone_title': issue.milestone.title if issue.milestone else None
|
||||
}
|
||||
Reference in New Issue
Block a user