from core.models import ProgressReport, Task class MarkdownBuilder: @staticmethod def _parse_progress(p_val: str) -> int: if p_val == "1": return 100 try: return int(p_val.replace("%", "")) except: return 0 @staticmethod def _get_status_badge(status: str) -> str: s = status.upper().strip() if "COMPLETED" in s: return "" if "DURING" in s or "PROCESS" in s: return "" if "NOT STARTED" in s: return "" return f"`{status}`" @staticmethod def build_table(report: ProgressReport) -> str: if not report.tasks: return "_No tasks found._" header = "| Phase | ID | Task | Assignee | Timeline | Progress | Status |\n" separator = "| :--- | :--- | :--- | :--- | :--- | :--- | :--- |\n" rows = [] current_phase = "" for task in report.tasks: phase_display = f"**{task.phase}**" if task.phase != current_phase else "" current_phase = task.phase badge = MarkdownBuilder._get_status_badge(task.status) prog_val = MarkdownBuilder._parse_progress(task.progress_val) filled = prog_val // 10 bar = "█" * filled + "░" * (10 - filled) rows.append( f"| {phase_display} | `{task.task_id}` | {task.task_name} | `{task.assignee}` | {task.start_date} - {task.end_date} | `{bar} {prog_val}%` | {badge} |" ) return header + separator + "\n".join(rows) @staticmethod def build_team_progress(report: ProgressReport) -> str: if not report.tasks: return "" phases = {} for t in report.tasks: if t.phase not in phases: phases[t.phase] = [] phases[t.phase].append(t) total_tasks = len(report.tasks) completed = len([t for t in report.tasks if "COMPLETED" in t.status.upper()]) in_progress = len([t for t in report.tasks if "DURING" in t.status.upper()]) avg_progress = sum(MarkdownBuilder._parse_progress(t.progress_val) for t in report.tasks) / total_tasks # Build phase rows — NO leading spaces (prevents GitHub treating them as code blocks) phase_rows_html = "" colors = ["#B026FF", "#33CCFF", "#FF3333", "#FFD700"] for i, (name, t_list) in enumerate(phases.items()): p_total = len(t_list) p_done = len([t for t in t_list if "COMPLETED" in t.status.upper()]) p_avg = sum(MarkdownBuilder._parse_progress(t.progress_val) for t in t_list) / p_total color = colors[i % len(colors)] # Key fix: use textwrap.dedent-style — keep lines flush left, no indentation phase_rows_html += ( f'
| {name} | {p_avg:.0f}% ({p_done}/{p_total}) |
| '
' SYSTEM READINESS '
f'{avg_progress:.1f}% '
'
| '
''
' Phase Breakdown '
f' | '
'