V0.9 de l'amélioration du suivi des issues #262

Merged
Gameur merged 1 commit from dev into main 2026-07-12 16:12:39 +00:00

View file

@ -6,6 +6,7 @@ import logging
import json import json
import os import os
import asyncio import asyncio
import re
from typing import Dict, Any, Optional from typing import Dict, Any, Optional
from utils.premium import check_premium_tier from utils.premium import check_premium_tier
@ -47,14 +48,37 @@ def extract_issue_iid(payload: Dict[str, Any]) -> Optional[str]:
return None return None
def extract_issue_identifier(issue_payload: Optional[Dict[str, Any]]) -> Optional[str]: def extract_issue_identifier(issue_payload: Optional[Any]) -> Optional[str]:
"""Récupère un identifiant stable à stocker dans le tracking.""" """Récupère un identifiant stable à stocker dans le tracking, même si le payload est imbriqué."""
if not isinstance(issue_payload, dict): if issue_payload is None:
return None return None
for key in ("iid", "number", "id"):
value = issue_payload.get(key) if isinstance(issue_payload, dict):
if value not in (None, ""): for key in ("iid", "number", "id", "issue_id"):
return str(value) value = issue_payload.get(key)
if value not in (None, ""):
return str(value)
for key in ("html_url", "web_url", "url"):
value = issue_payload.get(key)
if isinstance(value, str):
match = re.search(r"/(?:issues|tickets)/([0-9]+)", value)
if match:
return match.group(1)
for value in issue_payload.values():
found = extract_issue_identifier(value)
if found:
return found
return None
if isinstance(issue_payload, list):
for item in issue_payload:
found = extract_issue_identifier(item)
if found:
return found
return None return None
@ -187,6 +211,26 @@ def color_from_progress(progress: float) -> disnake.Color:
return disnake.Color.from_rgb(r, g, b) return disnake.Color.from_rgb(r, g, b)
def normalize_tracking_issue_id(issue_iid: Any) -> Optional[str]:
"""Normalise un identifiant dissue pour le tracking, en ne conservant que les valeurs numériques valides."""
if issue_iid is None:
return None
if isinstance(issue_iid, int):
return str(issue_iid)
if isinstance(issue_iid, str):
normalized = issue_iid.strip()
if not normalized:
return None
if normalized.lower() in {"none", "null", "nan", "undefined"}:
return None
if re.fullmatch(r"\d+", normalized):
return normalized
return None
def load_tracking(): def load_tracking():
"""Charge le tracking depuis gitlab_issues.json (avec migration automatique)""" """Charge le tracking depuis gitlab_issues.json (avec migration automatique)"""
tracking = {} tracking = {}
@ -202,14 +246,23 @@ def load_tracking():
try: try:
with open(LEGACY_REPORTS_FILE, "r", encoding="utf-8") as f: with open(LEGACY_REPORTS_FILE, "r", encoding="utf-8") as f:
legacy_data = json.load(f) legacy_data = json.load(f)
for issue_iid, user_id in legacy_data.items():
if issue_iid not in tracking: migrated_count = 0
tracking[issue_iid] = { for raw_issue_iid, user_id in legacy_data.items():
"requester_id": user_id, issue_iid = normalize_tracking_issue_id(raw_issue_iid)
"last_note_id": 0, if not issue_iid:
"type": "legacy_report" kuby_logger.debug(f"Ignoré pendant la migration de tracking: issue_id={raw_issue_iid}")
} continue
kuby_logger.info(f"Migration automatique: {len(legacy_data)} issues fusionnées") if issue_iid in tracking:
continue
tracking[issue_iid] = {
"requester_id": user_id,
"last_note_id": 0,
"type": "legacy_report"
}
migrated_count += 1
kuby_logger.info(f"Migration automatique: {migrated_count} issue(s) fusionnée(s) depuis l'historique")
except Exception as e: except Exception as e:
kuby_logger.error(f"Erreur migration gitlab_reports.json: {e}") kuby_logger.error(f"Erreur migration gitlab_reports.json: {e}")
@ -277,7 +330,12 @@ class BugReportModal(disnake.ui.Modal):
if result: if result:
issue_iid = extract_issue_identifier(result) issue_iid = extract_issue_identifier(result)
if not issue_iid: if not issue_iid:
kuby_logger.warning("Impossible de déterminer lID de lissue créée depuis le retour Forgejo/GitLab") kuby_logger.warning("Impossible de déterminer lID de lissue créée depuis le retour Forgejo/GitLab. Tentative de fallback par titre.")
lookup = await gitlab_client.find_issue_by_title(f"[MANUAL] {bug_title}", state="open")
issue_iid = extract_issue_identifier(lookup)
if not issue_iid:
kuby_logger.warning("Impossible de déterminer lID de lissue créée après fallback. Payload reçu: %s", result)
await interaction.edit_original_response(content="✅ Votre rapport a été envoyé, mais lidentifiant de lissue na pas pu être récupéré automatiquement.") await interaction.edit_original_response(content="✅ Votre rapport a été envoyé, mais lidentifiant de lissue na pas pu être récupéré automatiquement.")
return return
@ -346,7 +404,12 @@ class FeatureSuggestionModal(disnake.ui.Modal):
if result: if result:
issue_iid = extract_issue_identifier(result) issue_iid = extract_issue_identifier(result)
if not issue_iid: if not issue_iid:
kuby_logger.warning("Impossible de déterminer lID de lissue créée depuis le retour Forgejo/GitLab") kuby_logger.warning("Impossible de déterminer lID de lissue créée depuis le retour Forgejo/GitLab. Tentative de fallback par titre.")
lookup = await gitlab_client.find_issue_by_title(f"[SUGGESTION] {suggestion_title}", state="open")
issue_iid = extract_issue_identifier(lookup)
if not issue_iid:
kuby_logger.warning("Impossible de déterminer lID de lissue créée après fallback. Payload reçu: %s", result)
await interaction.edit_original_response(content="✅ Votre suggestion a été envoyée, mais lidentifiant de lissue na pas pu être récupéré automatiquement.") await interaction.edit_original_response(content="✅ Votre suggestion a été envoyée, mais lidentifiant de lissue na pas pu être récupéré automatiquement.")
return return