llm_ticket3/extract_missing_attachment.py
2025-04-15 17:40:48 +02:00

230 lines
8.3 KiB
Python

#!/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'<img[^>]+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'<img[^>]+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()