#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Script pour extraire les images manquantes des messages HTML dans un ticket Odoo et les ajouter aux pièces jointes. """ import os import json import re import requests import sys import shutil import argparse from datetime import datetime from typing import Dict, List, Any, Optional, Tuple def load_json_file(file_path: str) -> Any: """ Charge un fichier JSON. Args: file_path: Chemin du fichier JSON à charger Returns: Contenu du fichier JSON """ try: if os.path.exists(file_path): with open(file_path, 'r', encoding='utf-8') as f: return json.load(f) else: return None except Exception as e: print(f"Erreur lors du chargement du fichier {file_path}: {e}") return None def save_json_file(file_path: str, data: Any) -> bool: """ Sauvegarde des données dans un fichier JSON. Args: file_path: Chemin du fichier JSON à sauvegarder data: Données à sauvegarder Returns: True si la sauvegarde a réussi, False sinon """ try: with open(file_path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) return True except Exception as e: print(f"Erreur lors de la sauvegarde du fichier {file_path}: {e}") return False def download_image(url: str, save_path: str) -> bool: """ Télécharge une image depuis une URL. Args: url: URL de l'image à télécharger save_path: Chemin où sauvegarder l'image Returns: True si le téléchargement a réussi, False sinon """ try: # Créer le répertoire parent si nécessaire os.makedirs(os.path.dirname(save_path), exist_ok=True) # Télécharger l'image response = requests.get(url, stream=True) if response.status_code == 200: with open(save_path, 'wb') as f: response.raw.decode_content = True shutil.copyfileobj(response.raw, f) print(f"Image téléchargée et sauvegardée dans: {save_path}") return True else: print(f"Erreur lors du téléchargement de l'image: {response.status_code}") return False except Exception as e: print(f"Erreur lors du téléchargement de l'image: {e}") return False def extract_missing_attachments(ticket_dir: str) -> None: """ Extrait les images manquantes d'un ticket et les ajoute aux pièces jointes. Args: ticket_dir: Répertoire du ticket """ # Vérifier que le répertoire existe if not os.path.exists(ticket_dir): print(f"Répertoire introuvable: {ticket_dir}") return # Chemins des fichiers messages_file = os.path.join(ticket_dir, "all_messages.json") attachments_file = os.path.join(ticket_dir, "attachments_info.json") attachments_dir = os.path.join(ticket_dir, "attachments") # Vérifier que les fichiers nécessaires existent if not os.path.exists(messages_file): print(f"Fichier de messages introuvable: {messages_file}") return # Charger les messages messages_data = load_json_file(messages_file) if not messages_data: print("Impossible de charger les messages") return # Charger les pièces jointes existantes attachments_info = load_json_file(attachments_file) or [] # Vérifier si le dossier des attachements existe, sinon le créer if not os.path.exists(attachments_dir): os.makedirs(attachments_dir) # Extraire les IDs des pièces jointes existantes existing_attachment_ids = set() for attachment in attachments_info: if "id" in attachment: existing_attachment_ids.add(attachment["id"]) # Parcourir les messages pour trouver les images manquantes messages = messages_data.get("messages", []) newly_added_attachments = [] for message in messages: message_id = message.get("id") # Traiter uniquement les messages avec body_original contenant des images body_original = message.get("body_original", "") if not body_original: continue # Chercher toutes les références d'images image_matches = re.finditer(r']+src=["\']([^"\']+)["\'][^>]*>', body_original) for match in image_matches: img_url = match.group(1) # Extraire l'ID de l'image img_id = None access_token = None # Pattern 1: /web/image/ID?access_token=... id_match = re.search(r"/web/image/(\d+)", img_url) if id_match: img_id = int(id_match.group(1)) # Extraire le token d'accès token_match = re.search(r"access_token=([^&]+)", img_url) if token_match: access_token = token_match.group(1) # Vérifier si l'image existe déjà dans les pièces jointes if img_id and img_id not in existing_attachment_ids: print(f"Image manquante trouvée: ID {img_id} dans le message {message_id}") # Déterminer le nom du fichier file_name = f"image_{img_id}.png" # Nom par défaut # Chercher un attribut alt ou title qui pourrait contenir le nom alt_match = re.search(r']+alt=["\']([^"\']+)["\'][^>]*>', match.group(0)) if alt_match and alt_match.group(1).strip(): alt_text = alt_match.group(1).strip() # Nettoyer et limiter la longueur du nom alt_text = re.sub(r'[^\w\s.-]', '', alt_text) alt_text = alt_text[:50] # Limiter la longueur if alt_text: file_name = f"{alt_text}_{img_id}.png" # Chemin de destination pour l'image img_save_path = os.path.join(attachments_dir, file_name) # Télécharger l'image if download_image(img_url, img_save_path): # Taille du fichier file_size = os.path.getsize(img_save_path) # Ajouter l'information de la pièce jointe attachment_info = { "id": img_id, "name": file_name, "mimetype": "image/png", # Type par défaut "file_size": file_size, "create_date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "creator_name": message.get("author_details", {}).get("name", "Inconnu"), "download_status": "success", "local_path": img_save_path, "error": "", "was_missing": True, "message_id": message_id, "access_token": access_token } attachments_info.append(attachment_info) existing_attachment_ids.add(img_id) newly_added_attachments.append(attachment_info) # Sauvegarder immédiatement pour éviter la perte en cas d'erreur save_json_file(attachments_file, attachments_info) # Afficher un résumé if newly_added_attachments: print(f"Ajouté {len(newly_added_attachments)} nouvelles pièces jointes:") for att in newly_added_attachments: print(f" - {att['name']} (ID: {att['id']}, Taille: {att['file_size']} octets)") else: print("Aucune nouvelle pièce jointe ajoutée.") def main(): """ Point d'entrée principal du script. """ parser = argparse.ArgumentParser(description="Extrait les images manquantes des messages HTML dans un ticket Odoo.") parser.add_argument("ticket_dir", help="Répertoire du ticket contenant les messages et pièces jointes") args = parser.parse_args() extract_missing_attachments(args.ticket_dir) if __name__ == "__main__": main()