#!/usr/bin/env python3 """ Script pour convertir les fichiers JSON de tickets en Markdown formaté. Ce script prend les données JSON des tickets extraits et crée un fichier Markdown structuré. """ import os import sys import json import argparse from datetime import datetime def clean_html(html_content): """ Nettoie le contenu HTML pour le Markdown. Supprime les balises, les bas de page, les messages automatiques et les sections vides. """ if not html_content: return "" import re # Supprimer les balises simples html_content = re.sub(r'|

|

|
|
', '\n', html_content) # Supprimer les bas de page et messages automatiques du support html_content = re.sub(r'Droit à la déconnexion :.*?(?=\n\n|\Z)', '', html_content, flags=re.DOTALL) html_content = re.sub(r'\*\s*\*\s*\*.*?(?=\n\n|\Z)', '', html_content, flags=re.DOTALL) html_content = re.sub(r'Ce message électronique et tous les fichiers.*?(?=\n\n|\Z)', '', html_content, flags=re.DOTALL) html_content = re.sub(r'Afin d\'assurer une meilleure traçabilité.*?(?=\n\n|\Z)', '', html_content, flags=re.DOTALL) html_content = re.sub(r'_Confidentialité :.*?(?=\n\n|\Z)', '', html_content, flags=re.DOTALL) html_content = re.sub(r'Support technique.*?(?=\n\n|\Z)', '', html_content, flags=re.DOTALL) # Suppression de l'image signature CBAO et autres images html_content = re.sub(r'!\[CBAO - développeur de rentabilité.*?(?=\n\n|\Z)', '', html_content, flags=re.DOTALL) html_content = re.sub(r'!\[.*?\]\(/web/image/.*?\)', '', html_content) html_content = re.sub(r'!\[cid:.*?\]\(/web/image/.*?\)', '', html_content) # Supprimer les balises HTML restantes html_content = re.sub(r'<.*?>', '', html_content) # Remplacer les entités HTML courantes html_content = html_content.replace(' ', ' ') html_content = html_content.replace('<', '<') html_content = html_content.replace('>', '>') html_content = html_content.replace('&', '&') html_content = html_content.replace('"', '"') # Supprimer les lignes avec uniquement des ** html_content = re.sub(r'^\s*\*\*\s*\*\*\s*$', '', html_content, flags=re.MULTILINE) html_content = re.sub(r'^\s*\*\*\s*$', '', html_content, flags=re.MULTILINE) # Supprimer le \--- à la fin des messages html_content = re.sub(r'\\---\s*$', '', html_content) # Supprimer les crochets isolés html_content = re.sub(r'\[\s*$', '', html_content) # Supprimer les lignes vides multiples html_content = re.sub(r'\n\s*\n', '\n\n', html_content) # Nettoyer au début et à la fin html_content = html_content.strip() # Supprimer les sections vides (comme "*Contenu vide*") if not html_content or html_content.lower() == "*contenu vide*": return "*Contenu vide*" return html_content def format_date(date_str): """ Formate une date ISO en format lisible. """ if not date_str: return "" try: dt = datetime.fromisoformat(date_str.replace('Z', '+00:00')) return dt.strftime("%d/%m/%Y %H:%M:%S") except (ValueError, TypeError): return date_str def create_markdown_from_json(json_file, output_file): """ Crée un fichier Markdown à partir d'un fichier JSON de messages. Args: json_file: Chemin vers le fichier JSON contenant les messages output_file: Chemin du fichier Markdown à créer """ try: with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) except Exception as e: print(f"Erreur : {e}") return False # Obtenir le répertoire du ticket pour accéder aux autres fichiers ticket_dir = os.path.dirname(json_file) # Essayer de lire le fichier ticket_info.json si disponible ticket_info = {} ticket_info_path = os.path.join(ticket_dir, "ticket_info.json") if os.path.exists(ticket_info_path): try: with open(ticket_info_path, 'r', encoding='utf-8') as f: ticket_info = json.load(f) except Exception as e: print(f"Avertissement: Impossible de lire ticket_info.json: {e}") # Récupérer les informations du sommaire du ticket ticket_summary = {} if "ticket_summary" in data: ticket_summary = data.get("ticket_summary", {}) else: summary_path = os.path.join(ticket_dir, "ticket_summary.json") if os.path.exists(summary_path): try: with open(summary_path, 'r', encoding='utf-8') as f: ticket_summary = json.load(f) except Exception as e: print(f"Avertissement: Impossible de lire ticket_summary.json: {e}") # Tenter de lire le fichier structure.json structure = {} structure_path = os.path.join(ticket_dir, "structure.json") if os.path.exists(structure_path): try: with open(structure_path, 'r', encoding='utf-8') as f: structure = json.load(f) except Exception as e: print(f"Avertissement: Impossible de lire structure.json: {e}") # Commencer à construire le contenu Markdown md_content = [] # Ajouter l'en-tête du document avec les informations du ticket ticket_code = ticket_summary.get("code", os.path.basename(ticket_dir).split('_')[0]) ticket_name = ticket_summary.get("name", "") md_content.append(f"# Ticket {ticket_code}: {ticket_name}") md_content.append("") # Ajouter des métadonnées du ticket md_content.append("## Informations du ticket") md_content.append("") md_content.append(f"- **Code**: {ticket_code}") md_content.append(f"- **Nom**: {ticket_name}") md_content.append(f"- **Projet**: {ticket_summary.get('project_name', '')}") md_content.append(f"- **État**: {ticket_summary.get('stage_name', '')}") # Chercher l'utilisateur assigné dans les métadonnées assigned_to = "" if "user_id" in structure and structure["user_id"]: user_id = structure["user_id"] if isinstance(user_id, list) and len(user_id) > 1: assigned_to = user_id[1] md_content.append(f"- **Assigné à**: {assigned_to}") # Ajouter le client si disponible partner = "" if "partner_id" in ticket_info: partner_id = ticket_info.get("partner_id", []) if isinstance(partner_id, list) and len(partner_id) > 1: partner = partner_id[1] # Ajouter l'email du client si disponible partner_email = "" if "email_from" in ticket_info and ticket_info["email_from"]: partner_email = ticket_info["email_from"] if partner: partner += f", {partner_email}" else: partner = partner_email md_content.append(f"- **Client**: {partner}") # Ajouter les tags s'ils sont disponibles tags = [] if "tag_ids" in ticket_info: tag_ids = ticket_info.get("tag_ids", []) or [] for tag in tag_ids: if isinstance(tag, list) and len(tag) > 1: tags.append(tag[1]) if tags: md_content.append(f"- **Tags**: {', '.join(tags)}") # Ajouter les dates md_content.append(f"- **Créé le**: {format_date(ticket_info.get('create_date', ''))}") md_content.append(f"- **Dernière modification**: {format_date(ticket_info.get('write_date', ''))}") if "date_deadline" in ticket_info and ticket_info.get("date_deadline"): md_content.append(f"- **Date limite**: {format_date(ticket_info.get('date_deadline', ''))}") md_content.append("") # Ajouter la description du ticket description = ticket_info.get("description", "") if description: cleaned_description = clean_html(description) if cleaned_description and cleaned_description != "*Contenu vide*": md_content.append("## Description") md_content.append("") md_content.append(cleaned_description) md_content.append("") # Ajouter les messages messages = [] if "messages" in data: messages = data.get("messages", []) if not messages: md_content.append("## Messages") md_content.append("") md_content.append("*Aucun message disponible*") else: # Filtrer les messages système non pertinents filtered_messages = [] for msg in messages: # Ignorer les messages système vides if msg.get("is_system", False) and not msg.get("body", "").strip(): continue # Ignorer les changements d'état sans contenu if msg.get("is_stage_change", False) and not msg.get("body", "").strip(): # Sauf si on veut les garder pour la traçabilité filtered_messages.append(msg) continue filtered_messages.append(msg) # Si nous avons au moins un message significatif if filtered_messages: md_content.append("## Messages") md_content.append("") # Trier les messages par date filtered_messages.sort(key=lambda x: x.get("date", "")) for i, message in enumerate(filtered_messages): if not isinstance(message, dict): continue # Déterminer l'auteur du message author = "Système" author_details = message.get("author_details", {}) if author_details and author_details.get("name"): author = author_details.get("name") else: author_id = message.get("author_id", []) if isinstance(author_id, list) and len(author_id) > 1: author = author_id[1] # Formater la date date = format_date(message.get("date", "")) # Déterminer le type de message message_type = "" if message.get("is_stage_change", False): message_type = "Changement d'état" elif message.get("is_system", False): message_type = "Système" elif message.get("is_note", False): message_type = "Commentaire" elif message.get("email_from", False): message_type = "E-mail" # Récupérer le sujet du message subject = message.get("subject", "") # Créer l'en-tête du message md_content.append(f"### Message {i+1}") md_content.append(f"**De**: {author}") md_content.append(f"**Date**: {date}") md_content.append(f"**Type**: {message_type}") if subject: md_content.append(f"**Sujet**: {subject}") # Ajouter le corps du message body = message.get("body", "") cleaned_body = clean_html(body) md_content.append("") if not cleaned_body or cleaned_body == "*Contenu vide*": md_content.append("*Contenu vide*") else: md_content.append(cleaned_body) # Ajouter les pièces jointes si elles existent attachment_ids = message.get("attachment_ids", []) has_attachments = False # Vérifier si les pièces jointes existent et ne sont pas vides if attachment_ids: valid_attachments = [] for att in attachment_ids: if isinstance(att, list) and len(att) > 1: valid_attachments.append(att) if valid_attachments: has_attachments = True md_content.append("") md_content.append("**Pièces jointes:**") for att in valid_attachments: md_content.append(f"- {att[1]}") md_content.append("") md_content.append("---") md_content.append("") # Ajouter une section pour les pièces jointes du ticket si elles existent attachment_data = {} attachment_path = os.path.join(ticket_dir, "attachments.json") if os.path.exists(attachment_path): try: with open(attachment_path, 'r', encoding='utf-8') as f: attachment_data = json.load(f) except Exception as e: print(f"Avertissement: Impossible de lire attachments.json: {e}") if attachment_data and "attachments" in attachment_data: attachments = attachment_data.get("attachments", []) if attachments: md_content.append("## Pièces jointes") md_content.append("") md_content.append("| Nom | Type | Taille | Date |") md_content.append("|-----|------|--------|------|") for att in attachments: name = att.get("name", "") mimetype = att.get("mimetype", "") file_size = att.get("file_size", 0) size_str = f"{file_size / 1024:.1f} KB" if file_size else "" create_date = format_date(att.get("create_date", "")) md_content.append(f"| {name} | {mimetype} | {size_str} | {create_date} |") md_content.append("") # Ajouter des informations sur l'extraction extract_time = datetime.now().strftime("%d/%m/%Y %H:%M:%S") md_content.append("## Informations sur l'extraction") md_content.append("") md_content.append(f"- **Date d'extraction**: {extract_time}") md_content.append(f"- **Répertoire**: {ticket_dir}") # Écrire le contenu dans le fichier de sortie try: with open(output_file, 'w', encoding='utf-8') as f: f.write("\n".join(md_content)) print(f"Rapport Markdown créé : {output_file}") return True except Exception as e: print(f"Erreur lors de l'écriture du fichier Markdown: {e}") return False if __name__ == "__main__": parser = argparse.ArgumentParser(description="Convertir les fichiers JSON de tickets en Markdown") parser.add_argument("--ticket_code", "-t", help="Code du ticket à convertir (ex: T11067)") parser.add_argument("--date_dir", "-d", help="Dossier spécifique par date, optionnel (ex: 20250403_155134)") parser.add_argument("--input_dir", "-i", default="output", help="Dossier racine contenant les tickets") parser.add_argument("--output_name", "-o", default="rapport.md", help="Nom du fichier Markdown à générer") args = parser.parse_args() if not args.ticket_code: print("Erreur : Vous devez spécifier un code de ticket. Exemple : -t T11067") sys.exit(1) # Construire le chemin d'entrée ticket_dir = f"{args.input_dir}/ticket_{args.ticket_code}" if args.date_dir: ticket_dir = f"{ticket_dir}/{args.ticket_code}_{args.date_dir}" else: # Trouver le dossier le plus récent import glob date_dirs = glob.glob(f"{ticket_dir}/{args.ticket_code}_*") if date_dirs: ticket_dir = max(date_dirs) # Prend le plus récent par ordre alphabétique json_file = f"{ticket_dir}/all_messages.json" output_file = f"{ticket_dir}/{args.output_name}" if not os.path.exists(json_file): print(f"Erreur : Le fichier {json_file} n'existe pas.") sys.exit(1) if create_markdown_from_json(json_file, output_file): print(f"Rapport Markdown créé : {output_file}") else: print("Échec de la création du rapport Markdown") sys.exit(1)