Amélioration du suivi des issues

This commit is contained in:
Mathis 2026-07-12 12:26:01 +02:00
parent 8736d52bf8
commit b23c0dde53

View file

@ -58,6 +58,93 @@ def extract_issue_identifier(issue_payload: Optional[Dict[str, Any]]) -> Optiona
return None
def normalize_labels(labels: Any) -> list[str]:
"""Normalise une liste de labels provenant de Forgejo/GitLab."""
if not labels:
return []
if isinstance(labels, str):
return [labels]
if isinstance(labels, list):
normalized = []
for item in labels:
if isinstance(item, dict):
name = item.get("name") or item.get("title") or item.get("label") or item.get("id")
else:
name = item
if name is None:
continue
name = str(name).strip()
if name and name not in normalized:
normalized.append(name)
return normalized
return []
def extract_label_change(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Extrait le changement de labels dun payload dissue."""
changes = payload.get("changes") or {}
labels_change = changes.get("labels") or changes.get("label")
if not isinstance(labels_change, dict):
return None
previous = normalize_labels(labels_change.get("previous"))
current = normalize_labels(labels_change.get("current"))
if not previous and not current:
return None
added = [label for label in current if label not in previous]
removed = [label for label in previous if label not in current]
if not added and not removed:
return None
return {
"previous": previous,
"current": current,
"added": added,
"removed": removed,
}
def calculate_progress_from_labels(labels: list[str]) -> float:
"""Estime une progression visuelle à partir des labels présents."""
if not labels:
return 0.1
normalized = [label.lower() for label in labels]
if any(token in normalized for token in ["done", "resolved", "fixed", "closed", "terminé", "résolu", "ok"]):
return 1.0
if any(token in normalized for token in ["review", "ready", "qa", "testing", "test", "validated", "validation"]):
return 0.8
if any(token in normalized for token in ["in progress", "working", "wip", "develop", "dev", "investigating", "progress"]):
return 0.6
if any(token in normalized for token in ["confirmed", "accepted", "triaged", "priority", "urgent"]):
return 0.4
if any(token in normalized for token in ["bug", "enhancement", "feature", "manual", "new"]):
return 0.2
return 0.3
def build_progress_bar(progress: float, width: int = 18) -> str:
"""Construit une barre de progression stylée en Unicode."""
progress = max(0.0, min(1.0, progress))
filled = int(round(progress * width))
filled = max(0, min(width, filled))
empty = width - filled
percent = int(progress * 100)
return f"{'' * filled}{'' * empty} {percent}%"
def color_from_progress(progress: float) -> disnake.Color:
"""Retourne une couleur violet→bleu selon la progression."""
progress = max(0.0, min(1.0, progress))
purple = (117, 82, 255)
blue = (74, 144, 255)
r = int(purple[0] + (blue[0] - purple[0]) * progress)
g = int(purple[1] + (blue[1] - purple[1]) * progress)
b = int(purple[2] + (blue[2] - purple[2]) * progress)
return disnake.Color.from_rgb(r, g, b)
def load_tracking():
"""Charge le tracking depuis gitlab_issues.json (avec migration automatique)"""
tracking = {}
@ -397,12 +484,32 @@ class BugReport(commands.Cog):
pass
elif event_type == "issue":
# 🕵️ 5. GESTION DES CLÔTURES (Optionnel : si tu fermes le ticket sur Forgejo)
action = payload.get("object_attributes", {}).get("action") or payload.get("action")
state = payload.get("object_attributes", {}).get("state") or payload.get("issue", {}).get("state")
label_change = extract_label_change(payload)
if label_change:
current_labels = label_change["current"]
progress = calculate_progress_from_labels(current_labels)
bar = build_progress_bar(progress)
added = ", ".join(label_change["added"]) if label_change["added"] else "aucun"
removed = ", ".join(label_change["removed"]) if label_change["removed"] else "aucun"
try:
embed = disnake.Embed(
title="📈 Évolution du ticket",
description=f"Le suivi de **{issue_title}** a été mis à jour après un changement de labels.",
color=color_from_progress(progress),
)
embed.add_field(name="Progression", value=bar, inline=False)
embed.add_field(name="Labels ajoutés", value=added if added != "aucun" else "Aucun", inline=True)
embed.add_field(name="Labels retirés", value=removed if removed != "aucun" else "Aucun", inline=True)
embed.set_thumbnail(url=self.bot.user.display_avatar.url)
await user.send(embed=embed)
kuby_logger.info(f"Notification de progression envoyée à {user} pour l'issue #{issue_iid}")
except Exception as e:
kuby_logger.error(f"Erreur notification progression: {e}")
is_closing_now = action in ["close", "closed"] or state == "closed"
if is_closing_now:
try:
embed = disnake.Embed(