V0.9 de l'amélioration du suivi des issues #262
1 changed files with 80 additions and 17 deletions
|
|
@ -6,6 +6,7 @@ import logging
|
|||
import json
|
||||
import os
|
||||
import asyncio
|
||||
import re
|
||||
from typing import Dict, Any, Optional
|
||||
from utils.premium import check_premium_tier
|
||||
|
||||
|
|
@ -47,14 +48,37 @@ def extract_issue_iid(payload: Dict[str, Any]) -> Optional[str]:
|
|||
return None
|
||||
|
||||
|
||||
def extract_issue_identifier(issue_payload: Optional[Dict[str, Any]]) -> Optional[str]:
|
||||
"""Récupère un identifiant stable à stocker dans le tracking."""
|
||||
if not isinstance(issue_payload, dict):
|
||||
def extract_issue_identifier(issue_payload: Optional[Any]) -> Optional[str]:
|
||||
"""Récupère un identifiant stable à stocker dans le tracking, même si le payload est imbriqué."""
|
||||
if issue_payload is None:
|
||||
return None
|
||||
for key in ("iid", "number", "id"):
|
||||
|
||||
if isinstance(issue_payload, dict):
|
||||
for key in ("iid", "number", "id", "issue_id"):
|
||||
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
|
||||
|
||||
|
||||
|
|
@ -187,6 +211,26 @@ def color_from_progress(progress: float) -> disnake.Color:
|
|||
return disnake.Color.from_rgb(r, g, b)
|
||||
|
||||
|
||||
def normalize_tracking_issue_id(issue_iid: Any) -> Optional[str]:
|
||||
"""Normalise un identifiant d’issue 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():
|
||||
"""Charge le tracking depuis gitlab_issues.json (avec migration automatique)"""
|
||||
tracking = {}
|
||||
|
|
@ -202,14 +246,23 @@ def load_tracking():
|
|||
try:
|
||||
with open(LEGACY_REPORTS_FILE, "r", encoding="utf-8") as f:
|
||||
legacy_data = json.load(f)
|
||||
for issue_iid, user_id in legacy_data.items():
|
||||
if issue_iid not in tracking:
|
||||
|
||||
migrated_count = 0
|
||||
for raw_issue_iid, user_id in legacy_data.items():
|
||||
issue_iid = normalize_tracking_issue_id(raw_issue_iid)
|
||||
if not issue_iid:
|
||||
kuby_logger.debug(f"Ignoré pendant la migration de tracking: issue_id={raw_issue_iid}")
|
||||
continue
|
||||
if issue_iid in tracking:
|
||||
continue
|
||||
tracking[issue_iid] = {
|
||||
"requester_id": user_id,
|
||||
"last_note_id": 0,
|
||||
"type": "legacy_report"
|
||||
}
|
||||
kuby_logger.info(f"Migration automatique: {len(legacy_data)} issues fusionnées")
|
||||
migrated_count += 1
|
||||
|
||||
kuby_logger.info(f"Migration automatique: {migrated_count} issue(s) fusionnée(s) depuis l'historique")
|
||||
except Exception as e:
|
||||
kuby_logger.error(f"Erreur migration gitlab_reports.json: {e}")
|
||||
|
||||
|
|
@ -277,7 +330,12 @@ class BugReportModal(disnake.ui.Modal):
|
|||
if result:
|
||||
issue_iid = extract_issue_identifier(result)
|
||||
if not issue_iid:
|
||||
kuby_logger.warning("Impossible de déterminer l’ID de l’issue créée depuis le retour Forgejo/GitLab")
|
||||
kuby_logger.warning("Impossible de déterminer l’ID de l’issue 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 l’ID de l’issue créée après fallback. Payload reçu: %s", result)
|
||||
await interaction.edit_original_response(content="✅ Votre rapport a été envoyé, mais l’identifiant de l’issue n’a pas pu être récupéré automatiquement.")
|
||||
return
|
||||
|
||||
|
|
@ -346,7 +404,12 @@ class FeatureSuggestionModal(disnake.ui.Modal):
|
|||
if result:
|
||||
issue_iid = extract_issue_identifier(result)
|
||||
if not issue_iid:
|
||||
kuby_logger.warning("Impossible de déterminer l’ID de l’issue créée depuis le retour Forgejo/GitLab")
|
||||
kuby_logger.warning("Impossible de déterminer l’ID de l’issue 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 l’ID de l’issue créée après fallback. Payload reçu: %s", result)
|
||||
await interaction.edit_original_response(content="✅ Votre suggestion a été envoyée, mais l’identifiant de l’issue n’a pas pu être récupéré automatiquement.")
|
||||
return
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue