"""Formatage et affichage Rich du dashboard Gitea.""" from __future__ import annotations from datetime import datetime, timezone from rich.console import Console from rich.table import Table from gitea_dashboard.collector import RepoData def _format_repo_name(repo: RepoData) -> str: """Formate le nom du repo avec les indicateurs visuels.""" indicators = [] if repo.is_fork: indicators.append("[F]") if repo.is_archived: indicators.append("[A]") if repo.is_mirror: indicators.append("[M]") if indicators: return f"{repo.name} {' '.join(indicators)}" return repo.name def _format_relative_date(iso_date: str) -> str: """Convertit une date ISO en date relative lisible.""" try: dt = datetime.fromisoformat(iso_date.replace("Z", "+00:00")) except (ValueError, AttributeError): return "" now = datetime.now(timezone.utc) delta = now - dt days = delta.days if days < 0: return "dans le futur" if days == 0: return "aujourd'hui" if days == 1: return "il y a 1j" if days < 30: return f"il y a {days}j" months = days // 30 if months < 12: return f"il y a {months}m" years = days // 365 return f"il y a {years}a" def _format_release(release: dict | None) -> str: """Formate la release pour l'affichage.""" if release is None: return "\u2014" tag = release.get("tag_name", "") published = release.get("published_at", "") if published: relative = _format_relative_date(published) if relative: return f"{tag} ({relative})" return tag def render_dashboard(repos: list[RepoData], console: Console | None = None) -> None: """Affiche le dashboard complet dans le terminal. - Tableau principal : nom repo, indicateurs (fork/archive/mirror), issues ouvertes, derniere release (tag + date relative) - Section milestones : par repo ayant des milestones, nom, progression (closed/total), date echeance Le parametre console permet l'injection pour les tests. """ if console is None: console = Console() if not repos: console.print("Aucun repo trouve.") return # Tableau principal table = Table(title="Gitea Dashboard") table.add_column("Repo", style="bold") table.add_column("Issues", justify="right") table.add_column("Release") for repo in repos: name = _format_repo_name(repo) issues_str = str(repo.open_issues) issues_style = "red" if repo.open_issues > 0 else "green" release_str = _format_release(repo.latest_release) table.add_row(name, f"[{issues_style}]{issues_str}[/{issues_style}]", release_str) console.print(table) # Section milestones — uniquement si au moins un repo en a repos_with_milestones = [r for r in repos if r.milestones] if repos_with_milestones: console.print() console.print("[bold]Milestones[/bold]") for repo in repos_with_milestones: for ms in repo.milestones: title = ms["title"] closed = ms["closed_issues"] total = ms["open_issues"] + ms["closed_issues"] pct = round(closed / total * 100) if total > 0 else 0 line = f" {repo.name} / {title} : {closed}/{total} ({pct}%)" due_on = ms.get("due_on") if due_on: # Extraire juste la date (YYYY-MM-DD) try: dt = datetime.fromisoformat(due_on.replace("Z", "+00:00")) line += f" \u2014 echeance {dt.strftime('%Y-%m-%d')}" except (ValueError, AttributeError): pass console.print(line)