""" View models for displaying complex data structures. """ from typing import Dict, Any, List from .formatters import OutputFormatter class WorkspaceView: """View for workspace information display.""" @staticmethod def show_status(summary: Dict[str, Any]) -> None: """Display workspace status.""" if summary['clean']: OutputFormatter.info("No active issue workspace") print(" Use 'make tdd-start NUM=X' to begin working on an issue") return if summary['dirty']: OutputFormatter.warning("Workspace directory exists but no current issue file") print(" Run 'make tdd-finish' to clean up or 'make tdd-start' to create new workspace") return if summary['active']: WorkspaceView._show_active_workspace(summary) else: OutputFormatter.error("Failed to load workspace") @staticmethod def _show_active_workspace(summary: Dict[str, Any]) -> None: """Display active workspace details.""" OutputFormatter.header("Active Issue Workspace", "=") issue_num = summary['issue_number'] issue_title = summary['issue_title'] print(f"๐ŸŽฏ Issue #{issue_num}: {issue_title}") OutputFormatter.key_value("Status", summary['issue_state']) OutputFormatter.key_value("Workspace", f"{summary['workspace_dir']}/issue_{issue_num}/") OutputFormatter.empty_line() # Test files test_count = summary['test_count'] test_files = summary.get('test_files', []) print(f"๐Ÿงช Generated Tests ({test_count}):") if test_files: for test_file in test_files: print(f" - {test_file}") else: print(" - No tests generated yet") OutputFormatter.empty_line() # Workspace files print("๐Ÿ“‹ Workspace Files:") print(" - requirements.md (review and break down issue)") print(" - test_plan.md (plan test scenarios)") print(" - tests/ (generated test files)") OutputFormatter.empty_line() # Commands commands = [ "make tdd-add-test (generate another test)", "make tdd-finish (complete and move tests to main)" ] OutputFormatter.format_command_list(commands) @staticmethod def show_start_success(summary: Dict[str, Any]) -> None: """Display successful workspace start.""" issue_num = summary['issue_number'] OutputFormatter.success(f"Workspace created for issue #{issue_num}") OutputFormatter.key_value("Workspace", f"{summary['workspace_dir']}/issue_{issue_num}/") OutputFormatter.key_value("Requirements", summary['requirements_file']) OutputFormatter.key_value("Test plan", summary['test_plan_file']) OutputFormatter.empty_line() print("๐Ÿ’ก Next steps:") print(" 1. Review requirements.md and break down the issue") print(" 2. Plan test scenarios in test_plan.md") print(" 3. Use 'make tdd-add-test' to generate tests") print(" 4. Use 'make tdd-finish' when complete") @staticmethod def show_finish_success(issue_number: int, test_count: int) -> None: """Display successful workspace finish.""" OutputFormatter.info(f"Finishing work on issue #{issue_number}") OutputFormatter.empty_line() if test_count > 0: print(f"๐Ÿ“ฆ Moving {test_count} test(s) to tests/ directory...") OutputFormatter.success("Tests moved to main tests/ directory") else: OutputFormatter.warning("No tests found in workspace") print("๐Ÿงน Cleaning up workspace...") OutputFormatter.success(f"Issue #{issue_number} workspace cleaned up") OutputFormatter.empty_line() print("๐Ÿ’ก Next steps:") print(" - Run 'make test' to verify tests fail (red state)") print(" - Implement code to make tests pass (green state)") print(" - Start next issue with 'make tdd-start NUM=X'") class IssueView: """View for issue information display.""" @staticmethod def show_list(issues: List[Any], title: str = "Project Issues") -> None: """Display issue list.""" OutputFormatter.header(title, "=") if not issues: print("No issues found") return for issue in issues: status_icon = "๐ŸŸข" if issue.state == "open" else "๐Ÿ”ด" print(f"{status_icon} #{issue.number}: {issue.title}") print(f" Status: {issue.state.upper()} | Created: {issue.created_at.strftime('%Y-%m-%d')}") # Truncate body for list view body = getattr(issue, 'body', '') body_preview = body[:80] + "..." if len(body) > 80 else body if body_preview: print(f" {body_preview}") OutputFormatter.empty_line() print("๐Ÿ’ก Tip: Use 'make show-issue NUM=X' for full details") @staticmethod def show_open_issues(issues: List[Any]) -> None: """Display open issues list.""" OutputFormatter.header("Open Project Issues (Active Backlog)", "=") if not issues: print("No open issues found") return for issue in issues: print(f"[OPEN] #{issue.number}: {issue.title}") created = issue.created_at.strftime('%Y-%m-%d') updated = issue.updated_at.strftime('%Y-%m-%d') print(f" Created: {created} | Updated: {updated}") # Truncate body for list view body = getattr(issue, 'body', '') body_preview = body[:80] + "..." if len(body) > 80 else body if body_preview: print(f" {body_preview}") OutputFormatter.empty_line() print("๐Ÿ’ก Tip: Use 'make show-issue NUM=X' for full details or 'make list-issues' for all issues") @staticmethod def show_creation_success(result: Dict[str, Any], issue_type: str = "issue") -> None: """Display successful issue creation.""" OutputFormatter.success(f"{issue_type.title()} created successfully!") OutputFormatter.key_value("Number", f"#{result['number']}") OutputFormatter.key_value("Title", result['title']) OutputFormatter.key_value("Status", result['state']) if 'html_url' in result: OutputFormatter.key_value("URL", result['html_url']) OutputFormatter.empty_line() print("๐Ÿ’ก Next steps:") print(f" - Use 'make tdd-start NUM={result['number']}' to begin work") print(f" - Use 'make show-issue NUM={result['number']}' to view details") @staticmethod def show_issue_details(issue_data: Dict[str, Any]) -> None: """Display comprehensive issue details.""" OutputFormatter.header(f"Issue #{issue_data['number']} Details", "=") print(f"**Title:** {issue_data['title']}") print(f"**Status:** {issue_data['state'].upper()}") print(f"**Number:** #{issue_data['number']}") print(f"**Created:** {issue_data['created_at'].strftime('%Y-%m-%d %H:%M')}") print(f"**Updated:** {issue_data['updated_at'].strftime('%Y-%m-%d %H:%M')}") print(f"**URL:** {issue_data['html_url']}") if issue_data['assignee']: print(f"**Assignee:** {issue_data['assignee']}") OutputFormatter.empty_line() print("**Project Management:**") # Milestone information if issue_data['milestone']: milestone = issue_data['milestone'] print(f" ๐Ÿ“‹ Milestone: #{milestone['id']} - {milestone['title']} ({milestone['state']})") else: print(" ๐Ÿ“‹ Milestone: None") # Project/Board information print(" ๐ŸŽฏ Project: Getting Started (assumed - requires board API)") # Labels and state information print(f" ๐Ÿ“Š State: {issue_data['state_label']}") print(f" ๐Ÿšจ Priority: {issue_data['priority_label']}") if issue_data['type_labels']: type_display = ', '.join(issue_data['type_labels']) print(f" ๐Ÿท๏ธ Type: {type_display}") if issue_data['other_labels']: print(f" ๐Ÿท๏ธ Other Labels: {', '.join(issue_data['other_labels'])}") print(f" ๐Ÿ“ Kanban Column: {issue_data['kanban_column']}") OutputFormatter.empty_line() print("**Description:**") print(issue_data['body']) OutputFormatter.empty_line() print("๐Ÿ’ก Tip: Use 'make list-issues' to see all issues") @staticmethod def show_coverage_analysis(coverage_data: Dict[str, Any]) -> None: """Display test coverage analysis results.""" OutputFormatter.header(f"Test Coverage Analysis for Issue #{coverage_data['issue_number']}", "=") print(f"๐Ÿ“‹ Issue: #{coverage_data['issue_number']} - {coverage_data['issue_title']}") print(f"๐Ÿ“Š Coverage: {coverage_data['coverage_percentage']:.1f}%") OutputFormatter.empty_line() # Show requirements analysis print("๐ŸŽฏ Identified Requirements:") if coverage_data['requirements']: for req in coverage_data['requirements']: priority_icon = {"critical": "๐Ÿšจ", "important": "โš ๏ธ", "nice-to-have": "๐Ÿ’ก"} icon = priority_icon.get(req.priority, "๐Ÿ“") print(f" {icon} [{req.priority.upper()}] {req.category}: {req.description}") else: print(" No specific requirements detected") OutputFormatter.empty_line() # Show existing tests print("๐Ÿงช Existing Test Coverage:") issue_number = coverage_data['issue_number'] issue_related_tests = [t for t in coverage_data['existing_tests'] if t.related_issue == issue_number] if issue_related_tests: for test in issue_related_tests: test_count = len(test.test_methods) print(f" โœ… {test.file_path.name} ({test_count} test methods)") if test.test_methods: for method in test.test_methods[:3]: # Show first 3 print(f" - {method}") if len(test.test_methods) > 3: print(f" - ... and {len(test.test_methods) - 3} more") else: print(" ๐Ÿ“ No tests specifically for this issue found") # Show general tests that might be relevant relevant_tests = [t for t in coverage_data['existing_tests'] if any(keyword in ' '.join(t.coverage_keywords) for req in coverage_data['requirements'] for keyword in req.keywords)] if relevant_tests: print(" ๐Ÿ“‹ Potentially relevant tests:") for test in relevant_tests[:3]: print(f" ๐Ÿ“„ {test.file_path.name}") OutputFormatter.empty_line() # Show coverage gaps if coverage_data['coverage_gaps']: print("โŒ Coverage Gaps Found:") for gap in coverage_data['coverage_gaps']: priority_icon = {"critical": "๐Ÿšจ", "important": "โš ๏ธ", "nice-to-have": "๐Ÿ’ก"} icon = priority_icon.get(gap.requirement.priority, "๐Ÿ“") print(f" {icon} Missing: {gap.requirement.description}") print(f" ๐Ÿ’ก Suggested test: {gap.suggested_test_name}") print(f" ๐Ÿ“„ Suggested file: {gap.suggested_test_file}") OutputFormatter.empty_line() else: print("โœ… No significant coverage gaps detected!") OutputFormatter.empty_line() # Show recommendations print("๐Ÿ“ Recommendations:") for recommendation in coverage_data['recommendations']: print(f" {recommendation}") class ProjectView: """View for project management information display.""" @staticmethod def show_setup_success() -> None: """Display successful project setup.""" OutputFormatter.success("Project management setup complete!") print("๐Ÿ“‹ Available states: todo, active, review, done, blocked") print("๐Ÿ“Š Available priorities: low, medium, high, critical") @staticmethod def show_milestone_list(milestones: List[Any]) -> None: """Display milestone list.""" OutputFormatter.header("Project Milestones", "=") if not milestones: print("No milestones found") return for milestone in milestones: status_icon = "๐ŸŸข" if milestone.state == "open" else "๐Ÿ”ด" print(f"{status_icon} Milestone #{milestone.id}: {milestone.title}") print(f" State: {milestone.state.upper()}") print(f" Issues: {milestone.open_issues} open, {milestone.closed_issues} closed") if milestone.description: print(f" Description: {milestone.description}") if milestone.due_on: print(f" Due: {milestone.due_on}") OutputFormatter.empty_line() @staticmethod def show_overview(overview: Dict[str, Any]) -> None: """Display project overview.""" OutputFormatter.header("Project Management Overview", "=") OutputFormatter.key_value("Milestones", f"{overview['milestones']} total") OutputFormatter.key_value("Active Projects", overview['active_projects'], 1) OutputFormatter.key_value("Completed Projects", overview['completed_projects'], 1) OutputFormatter.key_value("Total Labels", overview['total_labels']) ready_status = "โœ… Yes" if overview['project_management_ready'] else "โŒ No - run setup-project-mgmt" OutputFormatter.key_value("Project Management Ready", ready_status)