#!/usr/bin/env python3
"""
Module pour formater les rapports à partir des fichiers JSON générés par l'AgentReportGenerator.
Ce module prend en entrée un fichier JSON contenant les analyses et génère différents
formats de sortie (Markdown, HTML, etc.) sans utiliser de LLM.
"""
import os
import json
import argparse
import sys
import re
from datetime import datetime
from typing import Dict, List, Any, Optional, Tuple
def generate_markdown_report(json_path: str, output_path: Optional[str] = None) -> Tuple[bool, str]:
"""
Génère un rapport au format Markdown à partir d'un fichier JSON.
Args:
json_path: Chemin vers le fichier JSON contenant les données du rapport
output_path: Chemin de sortie pour le fichier Markdown (facultatif)
Returns:
Tuple (succès, chemin du fichier généré ou message d'erreur)
"""
try:
# Lire le fichier JSON
with open(json_path, "r", encoding="utf-8") as f:
rapport_data = json.load(f)
# Si le chemin de sortie n'est pas spécifié, le créer à partir du chemin d'entrée
if not output_path:
# Remplacer l'extension JSON par MD
output_path = os.path.splitext(json_path)[0] + ".md"
# Générer le contenu Markdown
markdown_content = _generate_markdown_content(rapport_data)
# Écrire le contenu dans le fichier de sortie
with open(output_path, "w", encoding="utf-8") as f:
f.write(markdown_content)
print(f"Rapport Markdown généré avec succès: {output_path}")
return True, output_path
except Exception as e:
error_message = f"Erreur lors de la génération du rapport Markdown: {str(e)}"
print(error_message)
return False, error_message
def _generate_markdown_content(rapport_data: Dict) -> str:
"""
Génère le contenu Markdown à partir des données du rapport.
Args:
rapport_data: Dictionnaire contenant les données du rapport
Returns:
Contenu Markdown
"""
ticket_id = rapport_data.get("ticket_id", "")
timestamp = rapport_data.get("metadata", {}).get("timestamp", "")
generation_date = rapport_data.get("metadata", {}).get("generation_date", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
# Récupérer les infos sur le workflow et les agents
workflow = rapport_data.get("workflow", {}).get("etapes", [])
agents_info = rapport_data.get("metadata", {}).get("agents", {})
# Entête du document
markdown = f"# Rapport d'analyse du ticket #{ticket_id}\n\n"
markdown += f"*Généré le: {generation_date}*\n\n"
# 1. Résumé exécutif
if "resume" in rapport_data and rapport_data["resume"]:
markdown += "## Résumé du problème\n\n"
markdown += rapport_data["resume"] + "\n\n"
# 2. Chronologie des échanges (tableau)
markdown += "## Chronologie des échanges client/support\n\n"
markdown += "_Agent utilisé: AgentTicketAnalyser_\n\n"
if "chronologie_echanges" in rapport_data and rapport_data["chronologie_echanges"]:
# Créer un tableau pour les échanges
markdown += "| Date | Émetteur | Type | Contenu | Statut |\n"
markdown += "|------|---------|------|---------|--------|\n"
# Prétraitement pour détecter les questions sans réponse
questions_sans_reponse = {}
echanges = rapport_data["chronologie_echanges"]
for i, echange in enumerate(echanges):
if echange.get("type", "").lower() == "question" and echange.get("emetteur", "").lower() == "client":
has_response = False
# Vérifier si la question a une réponse
for j in range(i+1, len(echanges)):
next_echange = echanges[j]
if next_echange.get("type", "").lower() == "réponse" and next_echange.get("emetteur", "").lower() == "support":
has_response = True
break
questions_sans_reponse[i] = not has_response
# Générer les lignes du tableau
for i, echange in enumerate(echanges):
date = echange.get("date", "-")
emetteur = echange.get("emetteur", "-")
type_msg = echange.get("type", "-")
contenu = echange.get("contenu", "-")
# Ajouter un statut pour les questions sans réponse
statut = ""
if emetteur.lower() == "client" and type_msg.lower() == "question" and questions_sans_reponse.get(i, False):
statut = "**Sans réponse**"
markdown += f"| {date} | {emetteur} | {type_msg} | {contenu} | {statut} |\n"
# Ajouter une note si aucune réponse du support n'a été trouvée
if not any(echange.get("emetteur", "").lower() == "support" for echange in echanges):
markdown += "\n**Note: Aucune réponse du support n'a été trouvée dans ce ticket.**\n\n"
else:
markdown += "*Aucun échange détecté dans le ticket.*\n\n"
# 3. Récapitulatif des questions et réponses
if "tableau_questions_reponses" in rapport_data and rapport_data["tableau_questions_reponses"]:
markdown += "## Résumé des questions et réponses\n\n"
markdown += rapport_data["tableau_questions_reponses"] + "\n\n"
# 4. Analyse des images
markdown += "## Analyse des images\n\n"
markdown += "_Agent utilisé: AgentImageAnalyser_\n\n"
if "images_analyses" in rapport_data and rapport_data["images_analyses"]:
images_list = rapport_data["images_analyses"]
if not images_list:
markdown += "*Aucune image pertinente n'a été identifiée.*\n\n"
else:
for i, img_data in enumerate(images_list, 1):
image_name = img_data.get("image_name", f"Image {i}")
sorting_info = img_data.get("sorting_info", {})
reason = sorting_info.get("reason", "Non spécifiée")
markdown += f"### Image {i}: {image_name}\n\n"
# Raison de la pertinence
if reason:
markdown += f"**Raison de la pertinence**: {reason}\n\n"
# Ajouter l'analyse détaillée dans une section dépliable
analyse_detail = img_data.get("analyse", "Aucune analyse disponible")
if analyse_detail:
markdown += "Analyse détaillée de l'image
\n\n"
markdown += "```\n" + analyse_detail + "\n```\n\n"
markdown += "Voir le résultat de l'analyse de ticket
\n\n"
markdown += "```\n" + str(rapport_data["ticket_analyse"]) + "\n```\n\n"
markdown += "Prompt de {agent_name}
\n\n"
# Si le prompt est trop long, le tronquer pour éviter des rapports trop volumineux
if len(prompt) > 2000:
debut = prompt[:1000].strip()
fin = prompt[-1000:].strip()
prompt_tronque = f"{debut}\n\n[...]\n\n{fin}"
markdown += f"```\n{prompt_tronque}\n```\n\n"
else:
markdown += f"```\n{prompt}\n```\n\n"
markdown += "
', html) # Tables simplifiées (sans analyser la structure) html = re.sub(r'\| (.*?) \|', r'
\1', html, flags=re.DOTALL)
# Details/Summary
html = re.sub(r'{html}
" return html def process_report(json_path: str, output_format: str = "markdown") -> None: """ Traite un rapport dans le format spécifié. Args: json_path: Chemin vers le fichier JSON contenant les données du rapport output_format: Format de sortie (markdown ou html) """ if output_format.lower() == "markdown": generate_markdown_report(json_path) elif output_format.lower() == "html": generate_html_report(json_path) else: print(f"Format non supporté: {output_format}") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Formateur de rapports à partir de fichiers JSON") parser.add_argument("json_path", help="Chemin vers le fichier JSON contenant les données du rapport") parser.add_argument("--format", "-f", choices=["markdown", "html"], default="markdown", help="Format de sortie (markdown par défaut)") parser.add_argument("--output", "-o", help="Chemin de sortie pour le rapport (facultatif)") args = parser.parse_args() if args.format == "markdown": generate_markdown_report(args.json_path, args.output) elif args.format == "html": generate_html_report(args.json_path, args.output) else: print(f"Format non supporté: {args.format}")