llm_ticket3/fix_reports.py
2025-04-11 08:45:52 +02:00

328 lines
13 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Script de correction des rapports existants
Permet de:
1. Extraire correctement le JSON
2. Générer les fichiers CSV manquants
3. Analyser l'état des rapports générés
"""
import os
import sys
import json
import glob
import logging
import argparse
from datetime import datetime
import traceback
# Import des utilitaires
try:
from agents.utils.report_utils_bis import extraire_et_traiter_json
except ImportError:
print("ERREUR: Impossible d'importer les modules requis.")
print("Assurez-vous d'exécuter ce script depuis le répertoire racine du projet.")
sys.exit(1)
# Configuration du logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger("fix_reports")
def generate_csv_from_json(json_path, model_name=None):
"""
Génère un fichier CSV à partir du rapport JSON
Args:
json_path: Chemin vers le fichier JSON
model_name: Nom du modèle pour le nom du fichier CSV (facultatif)
"""
try:
# Charger le rapport JSON
with open(json_path, 'r', encoding='utf-8') as f:
rapport_data = json.load(f)
# Extraire l'ID du ticket
ticket_id = rapport_data.get("ticket_id", os.path.basename(json_path).split("_")[0])
# Extraire les échanges
chronologie_echanges = rapport_data.get("chronologie_echanges", [])
if not chronologie_echanges:
logger.error(f"Aucun échange trouvé dans le fichier JSON: {json_path}")
return False
# Déterminer le nom du modèle
if not model_name:
# Essayer de l'extraire des métadonnées
metadata = rapport_data.get("metadata", {})
model_name = metadata.get("model", "default").split(":")[0].lower()
# Créer le nom du fichier CSV
csv_path = os.path.join(os.path.dirname(json_path), f"{ticket_id}_{model_name}.csv")
# Générer le CSV complet avec toutes les colonnes
with open(csv_path, 'w', encoding='utf-8', newline='') as csvfile:
# Écrire l'en-tête
csvfile.write("Date,Émetteur,Type,Contenu\n")
# Écrire les échanges
for echange in chronologie_echanges:
date = echange.get('date', '').replace(',', ' ')
emetteur = echange.get('emetteur', '').replace(',', ' ')
type_msg = echange.get('type', '').replace(',', ' ')
contenu = echange.get('contenu', '').replace(',', ' ').replace('\n', ' ')
csvfile.write(f'"{date}","{emetteur}","{type_msg}","{contenu}"\n')
logger.info(f"Fichier CSV complet généré: {csv_path}")
# Générer également une version simplifiée Q&R pour la compatibilité
qr_csv_path = os.path.join(os.path.dirname(json_path), f"{ticket_id}_{model_name}_qr.csv")
with open(qr_csv_path, 'w', encoding='utf-8', newline='') as csvfile:
# Écrire l'en-tête
csvfile.write("Question,Réponse\n")
# Variables pour suivre les questions et réponses
current_question = None
# Parcourir les échanges et extraire les paires Q&R
for echange in chronologie_echanges:
emetteur = echange.get('emetteur', '').upper()
type_msg = echange.get('type', '').lower()
contenu = echange.get('contenu', '').replace(',', ' ').replace('\n', ' ')
# Si c'est une question du client
if emetteur == 'CLIENT' and (type_msg == 'question' or '?' in contenu):
# Si une question précédente existe sans réponse, l'écrire avec une réponse vide
if current_question:
csvfile.write(f'"{current_question}",""\n')
# Enregistrer la nouvelle question
current_question = contenu
# Si c'est une réponse ou un complément et qu'il y a une question en attente
elif emetteur == 'SUPPORT' and current_question:
# Préfixer la réponse pour la clarté
formatted_response = f"[{type_msg.upper()}] {contenu}"
# Écrire la paire question/réponse
csvfile.write(f'"{current_question}","{formatted_response}"\n')
current_question = None
# Si une question reste sans réponse à la fin
if current_question:
csvfile.write(f'"{current_question}",""\n')
logger.info(f"Fichier CSV Q&R simplifié généré: {qr_csv_path}")
return csv_path
except Exception as e:
logger.error(f"Erreur lors de la génération du CSV pour {json_path}: {e}")
traceback.print_exc()
return False
def fix_json_and_generate_csv(json_path):
"""
Corrige le JSON et génère le CSV manquant
Args:
json_path: Chemin vers le fichier JSON
"""
try:
# Charger le JSON
with open(json_path, 'r', encoding='utf-8') as f:
rapport_data = json.load(f)
# Extraire le rapport complet
rapport_complet = rapport_data.get("rapport_complet", "")
if not rapport_complet:
logger.error(f"Aucun rapport complet trouvé dans {json_path}")
return False
# Extraire et traiter le JSON
_, echanges_json, _ = extraire_et_traiter_json(rapport_complet)
# Vérifier si des échanges ont été extraits
if not echanges_json or "chronologie_echanges" not in echanges_json:
logger.error(f"Aucun échange extrait du rapport {json_path}")
return False
# Mettre à jour le JSON avec les échanges extraits
rapport_data["chronologie_echanges"] = echanges_json.get("chronologie_echanges", [])
# Sauvegarder le JSON corrigé
corrected_json_path = json_path.replace(".json", "_corrige.json")
with open(corrected_json_path, 'w', encoding='utf-8') as f:
json.dump(rapport_data, f, ensure_ascii=False, indent=2)
logger.info(f"Rapport JSON corrigé sauvegardé: {corrected_json_path}")
# Extraire le nom du modèle
metadata = rapport_data.get("metadata", {})
model_name = metadata.get("model", "unknown").split(":")[0].lower()
# Générer le CSV
csv_path = generate_csv_from_json(corrected_json_path, model_name)
if csv_path:
logger.info(f"CSV généré avec succès: {csv_path}")
return True
else:
logger.error("Échec de la génération du CSV")
return False
except Exception as e:
logger.error(f"Erreur lors de la correction du JSON {json_path}: {e}")
traceback.print_exc()
return False
def analyze_report(md_path):
"""
Analyse un rapport Markdown pour vérifier la présence des sections importantes
Args:
md_path: Chemin vers le fichier Markdown
"""
try:
# Lire le fichier
with open(md_path, 'r', encoding='utf-8') as f:
content = f.read()
# Vérifier les sections
has_table = "| Date | " in content or "| Question | Réponse |" in content
has_details = "## Détails des analyses" in content
# Vérifier la présence de la section synthèse globale (avec variations possibles)
has_synthese = any([
"## Synthèse globale des analyses d'images" in content,
"## 3.1 Synthèse globale des analyses d'images" in content,
"##3.1 Synthèse globale des analyses d'images" in content,
"## Synthèse globale" in content
])
# Vérifier si le fichier CSV existe
report_dir = os.path.dirname(md_path)
ticket_basename = os.path.basename(md_path).split('_')[0]
csv_files = [f for f in os.listdir(report_dir) if f.endswith('.csv') and ticket_basename in f]
has_csv = len(csv_files) > 0
# Afficher les résultats
print(f"\nAnalyse du rapport: {md_path}")
print(f"- Tableau des échanges: {'Présent ✅' if has_table else 'MANQUANT ❌'}")
print(f"- Détails des analyses: {'Présent ✅' if has_details else 'MANQUANT ❌'}")
print(f"- Synthèse globale: {'Présent ✅' if has_synthese else 'MANQUANT ❌'}")
if has_csv:
print(f"- Fichier CSV: Présent ✅ ({', '.join(csv_files)})")
else:
print(f"- Fichier CSV: MANQUANT ❌")
return {
"md_path": md_path,
"has_table": has_table,
"has_details": has_details,
"has_synthese": has_synthese,
"has_csv": has_csv,
"csv_files": csv_files
}
except Exception as e:
logger.error(f"Erreur lors de l'analyse du rapport {md_path}: {e}")
traceback.print_exc()
return None
def find_reports(base_dir=".", ticket_id=None):
"""
Recherche tous les rapports générés dans le répertoire de base
Args:
base_dir: Répertoire de base
ticket_id: Identifiant du ticket (optionnel)
Returns:
Liste de tuples (json_path, md_path)
"""
reports = []
# Motif de recherche des rapports
if ticket_id:
pattern = f"**/{ticket_id}/**/{ticket_id}_rapport_final.json"
else:
pattern = "**/T*/T*_rapport_final.json"
# Rechercher les fichiers JSON
json_files = glob.glob(os.path.join(base_dir, pattern), recursive=True)
for json_path in json_files:
# Trouver le fichier Markdown correspondant
md_path = json_path.replace(".json", ".md")
if os.path.exists(md_path):
reports.append((json_path, md_path))
return reports
def main():
parser = argparse.ArgumentParser(description="Outil de correction des rapports existants")
parser.add_argument("--dir", "-d", default=".", help="Répertoire de base contenant les rapports")
parser.add_argument("--ticket", "-t", help="ID du ticket à corriger (ex: T0101)")
parser.add_argument("--analyze", "-a", action="store_true", help="Analyser les rapports sans les corriger")
parser.add_argument("--fix", "-f", action="store_true", help="Corriger les rapports")
args = parser.parse_args()
# Rechercher les rapports
reports = find_reports(args.dir, args.ticket)
if not reports:
print(f"Aucun rapport trouvé dans {args.dir}" + (f" pour le ticket {args.ticket}" if args.ticket else ""))
return
print(f"Trouvé {len(reports)} rapport(s)")
# Analyser les rapports
if args.analyze or not args.fix:
analyze_results = []
for json_path, md_path in reports:
result = analyze_report(md_path)
if result:
analyze_results.append(result)
# Afficher un résumé
if analyze_results:
print("\nRésumé de l'analyse:")
print(f"- Total des rapports: {len(analyze_results)}")
print(f"- Rapports avec tableau: {sum(1 for r in analyze_results if r['has_table'])}")
print(f"- Rapports avec détails: {sum(1 for r in analyze_results if r['has_details'])}")
print(f"- Rapports avec synthèse: {sum(1 for r in analyze_results if r['has_synthese'])}")
print(f"- Rapports avec CSV: {sum(1 for r in analyze_results if r['has_csv'])}")
# Lister les rapports à corriger
reports_to_fix = [r["md_path"] for r in analyze_results if not (r["has_table"] and r["has_csv"])]
if reports_to_fix:
print("\nRapports à corriger (manque tableau ou CSV):")
for report in reports_to_fix:
print(f"- {report}")
# Corriger les rapports
if args.fix:
for json_path, md_path in reports:
# Analyser pour voir s'il a besoin d'être corrigé
result = analyze_report(md_path)
if result and not (result["has_table"] and result["has_csv"]):
print(f"\nCorrection du rapport {json_path}...")
success = fix_json_and_generate_csv(json_path)
if success:
print(f"✅ Rapport corrigé avec succès")
else:
print(f"❌ Échec de la correction du rapport")
else:
print(f"\nLe rapport {json_path} n'a pas besoin d'être corrigé")
if __name__ == "__main__":
main()