1504-16:12

This commit is contained in:
Ladebeze66 2025-04-15 16:12:53 +02:00
parent 4cd697e11a
commit 43da046555
7 changed files with 254 additions and 366 deletions

View File

@ -1152,11 +1152,221 @@ Read file: extracted_message/message_228942_clean.html
---
Cancelled
---
```bash
python extract_text_from_html.py
```
```
fgras-ca@PC-DEV:~/llm-ticket3$ python extract_text_from_html.py
✅ Texte extrait et sauvegardé dans : extracted_message/message_text.txt
fgras-ca@PC-DEV:~/llm-ticket3$
```
---
Read file: extracted_message/message_text.txt
---
Parfait ! J'ai créé un script `extract_text_from_html.py` qui extrait uniquement le texte de votre message HTML, en supprimant toutes les balises et le formatage, tout en conservant la structure du message.
Voici le texte extrait de votre message :
```
Bonjour,
Je ne parviens pas à accéder au l'essai au bleu :
Merci par avance pour votre.
Cordialement
```
Le script a correctement extrait le texte du message HTML tout en préservant sa structure avec les sauts de ligne entre les paragraphes. Notez que par défaut, les références aux images ne sont pas incluses. Si vous souhaitez les inclure, vous pouvez utiliser l'option `--preserve-images` ou `-p` :
```bash
python extract_text_from_html.py --preserve-images
```
Cela ajoutera des marqueurs pour les images dans le texte, comme :
```
[IMAGE: https://odoo.cbao.fr/web/image/145435?access_token=608ac9e7-3627-4a13-a8ec-06ff5046ebf3]
```
Vous pouvez également spécifier des fichiers d'entrée et de sortie différents :
```bash
python extract_text_from_html.py --input mon_fichier.html --output resultat.txt
```
Ce script fonctionne bien pour filtrer complètement le code HTML et produire un texte propre que vous pouvez facilement intégrer dans votre fil de discussion.
---
_**User**_
je voudrai fusionner ces deux scprits en un seul afin d'effectuer toutes les actions en un seul script
---
_**Assistant**_
Pour fusionner les deux scripts (`extract_single_message.py` et `extract_text_from_html.py`) en un seul, nous allons combiner les fonctionnalités d'extraction de message et de nettoyage du HTML en un seul processus. Voici comment cela peut être fait :
### Script fusionné
```python
import json
import os
import argparse
import requests
from bs4 import BeautifulSoup, Tag
def parse_args():
parser = argparse.ArgumentParser(description="Extrait un message et son texte pur d'un fichier JSON et HTML.")
parser.add_argument("--input", "-i", default="messages_raw.json", help="Fichier JSON des messages")
parser.add_argument("--message-id", "-m", type=int, default=228942, help="ID du message à traiter")
parser.add_argument("--output-html", "-o", default="extracted_message", help="Répertoire de sortie pour le HTML")
parser.add_argument("--output-text", "-t", default="extracted_message/message_text.txt", help="Fichier texte de sortie")
parser.add_argument("--preserve-images", action="store_true", help="Préserver les références aux images")
return parser.parse_args()
def clean_message_html(html, keep_img=True):
soup = BeautifulSoup(html, "html.parser")
# Supprimer les balises inutiles
for tag in soup.select(".o_mail_info, .recipients_info, .o_mail_tracking, .o_thread_icons, .o_thread_tooltip_container, .o_thread_full_spand_icon"):
tag.decompose()
# Supprimer les images inutiles
for img in soup.find_all("img"):
if not isinstance(img, Tag):
continue
if not img.has_attr("src"):
continue
src = img["src"]
if not isinstance(src, str):
continue
if "font_to_img" in src:
img.decompose()
elif keep_img and not src.startswith("http") and "/web/image/" not in src:
img.decompose()
return str(soup)
def download_image(url, output_dir, filename="image.png"):
try:
r = requests.get(url, stream=True, timeout=10)
if r.status_code == 200:
path = os.path.join(output_dir, filename)
with open(path, "wb") as f:
for chunk in r.iter_content(1024):
f.write(chunk)
return path
else:
print(f"⚠️ Échec du téléchargement de l'image ({r.status_code}): {url}")
except Exception as e:
print(f"⚠️ Erreur lors du téléchargement de l'image: {e}")
return None
def extract_and_clean(input_file, message_id, output_html_dir, output_text_file, preserve_images=False):
if not os.path.exists(input_file):
print(f"❌ Fichier non trouvé : {input_file}")
return
os.makedirs(output_html_dir, exist_ok=True)
with open(input_file, "r", encoding="utf-8") as f:
data = json.load(f)
messages = data.get("messages", [])
msg = next((m for m in messages if m["id"] == message_id), None)
if not msg:
print(f"❌ Message ID {message_id} non trouvé.")
return
print(f"✅ Message trouvé : {msg.get('subject')}")
# Nettoyer le HTML
original_html = msg.get("body", "")
clean_html = clean_message_html(original_html)
# Sauvegarder le HTML nettoyé
html_path = os.path.join(output_html_dir, f"message_{message_id}_clean.html")
with open(html_path, "w", encoding="utf-8") as f:
f.write(clean_html)
print(f"📄 HTML nettoyé sauvegardé : {html_path}")
# Extraire les images utiles
soup = BeautifulSoup(clean_html, "html.parser")
image_urls = []
for img in soup.find_all("img"):
if isinstance(img, Tag) and img.has_attr("src"):
src = img["src"]
if isinstance(src, str) and (src.startswith("http") or "/web/image/" in src):
image_urls.append(src)
# Télécharger la 1ère image
downloaded = None
for url in image_urls:
if isinstance(url, str) and url.startswith("http"):
downloaded = download_image(url, output_html_dir, "image.png")
break
# Extraire le texte
paragraphs = []
for p in soup.find_all("p"):
if isinstance(p, Tag):
text = p.get_text(strip=True)
if text: # Ignorer les paragraphes vides
paragraphs.append(text)
# Traiter les images si demandé
if preserve_images:
for img in soup.find_all("img"):
if isinstance(img, Tag) and img.has_attr("src"):
paragraphs.append(f"[IMAGE: {img['src']}]")
# Créer le texte final avec une structure claire
text_content = "\n\n".join(paragraphs)
# Sauvegarder le texte
with open(output_text_file, "w", encoding="utf-8") as f:
f.write(text_content)
print(f"✅ Texte extrait et sauvegardé dans : {output_text_file}")
if downloaded:
print(f"🖼️ Image téléchargée dans : {downloaded}")
else:
print(f"❗Aucune image HTTP directe n'a été téléchargée (images internes Odoo seulement ?)")
if __name__ == "__main__":
args = parse_args()
extract_and_clean(args.input, args.message_id, args.output_html, args.output_text, args.preserve_images)
```
### Fonctionnalités du script fusionné :
1. **Extraction du message** : Le script extrait le message spécifié par son ID à partir d'un fichier JSON.
2. **Nettoyage du HTML** : Il nettoie le contenu HTML pour supprimer les balises inutiles et les images non pertinentes.
3. **Téléchargement d'images** : Il télécharge la première image externe trouvée dans le message.
4. **Extraction de texte** : Il extrait le texte pur du message et le sauvegarde dans un fichier texte.
5. **Options de ligne de commande** : Vous pouvez spécifier le fichier d'entrée, l'ID du message, le répertoire de sortie pour le HTML et le fichier texte, ainsi qu'une option pour préserver les références aux images.
### Exemples d'utilisation :
- Pour exécuter le script avec les paramètres par défaut :
```bash
python extract_single_message.py
```
- Pour spécifier un fichier d'entrée et filtrer les images externes :
```bash
python extract_single_message.py --input /chemin/vers/messages_raw.json --message-id 228942 --preserve-images
```
Ce script fusionné vous permettra d'effectuer toutes les actions nécessaires en une seule exécution. N'hésitez pas à l'adapter selon vos besoins spécifiques !
---

View File

@ -1,142 +0,0 @@
# extract_single_message.py
import json
import os
import sys
import argparse
from bs4 import BeautifulSoup, Tag
def parse_args():
"""Parse les arguments de ligne de commande."""
parser = argparse.ArgumentParser(description="Extrait un message spécifique et ses images depuis un fichier JSON.")
parser.add_argument("--input", "-i",
help="Chemin vers le fichier JSON contenant les messages (par défaut: messages_raw.json)",
default="messages_raw.json")
parser.add_argument("--message-id", "-m",
help="ID du message à extraire (par défaut: 228942)",
type=int, default=228942)
parser.add_argument("--output", "-o",
help="Répertoire de sortie (par défaut: extracted_message)",
default="extracted_message")
parser.add_argument("--create-test", "-t",
help="Créer un fichier de test si le fichier d'entrée n'existe pas",
action="store_true")
return parser.parse_args()
def create_test_data(input_file, message_id):
"""Crée un fichier de test contenant un message avec une image."""
print(f"⚠️ Le fichier {input_file} n'existe pas. Création d'un exemple de test...")
test_data = {
"messages": [
{
"id": message_id,
"body": "<p>Voici une image de test:</p><img src='https://example.com/image.jpg' alt='Test'>",
"author_id": [1, "Utilisateur Test"],
"date": "2023-01-01 12:00:00",
"subject": "Message de test"
}
]
}
try:
# Créer le répertoire parent si nécessaire
parent_dir = os.path.dirname(input_file)
if parent_dir and not os.path.exists(parent_dir):
os.makedirs(parent_dir, exist_ok=True)
with open(input_file, "w", encoding="utf-8") as f:
json.dump(test_data, f, indent=2, ensure_ascii=False)
print(f"✅ Fichier de test créé : {input_file}")
return True
except Exception as e:
print(f"❌ Erreur lors de la création du fichier de test : {e}")
return False
def extract_message(input_file, message_id, output_dir, create_test=False):
"""Extrait un message spécifique et ses images depuis un fichier JSON."""
# Vérifier si le fichier d'entrée existe
if not os.path.exists(input_file):
if create_test:
created = create_test_data(input_file, message_id)
if not created:
print(f"❌ Impossible de créer le fichier de test {input_file}")
return False
else:
print(f"❌ Le fichier {input_file} n'existe pas. Utilisez --create-test pour créer un exemple.")
return False
try:
# Créer le répertoire de sortie
os.makedirs(output_dir, exist_ok=True)
# Charger les données
with open(input_file, "r", encoding="utf-8") as f:
data = json.load(f)
messages = data.get("messages", [])
found = next((m for m in messages if m["id"] == message_id), None)
if not found:
print(f"❌ Message ID {message_id} non trouvé.")
return False
print(f"✅ Message ID {message_id} trouvé.")
# 1. Sauvegarder le HTML brut
body_html = found.get("body", "")
html_path = os.path.join(output_dir, f"message_{message_id}.html")
with open(html_path, "w", encoding="utf-8") as f:
f.write(body_html)
print(f"📄 HTML sauvegardé dans : {html_path}")
# 2. Extraire les images depuis le HTML
soup = BeautifulSoup(body_html, "html.parser")
image_urls = []
# Extraction sécurisée des URLs d'images
for img in soup.find_all("img"):
if isinstance(img, Tag) and img.has_attr("src"):
image_urls.append(img["src"])
image_data = {
"message_id": message_id,
"author": found.get("author_id", [None, None])[1],
"date": found.get("date"),
"subject": found.get("subject"),
"image_urls": image_urls
}
# 3. Sauvegarder sous forme JSON
json_path = os.path.join(output_dir, f"message_{message_id}_meta.json")
with open(json_path, "w", encoding="utf-8") as f:
json.dump(image_data, f, indent=2, ensure_ascii=False)
print(f"🧾 Métadonnées sauvegardées dans : {json_path}")
print("\n✅ Extraction terminée.")
print("🖼️ Images détectées :")
for url in image_urls:
print(f" - {url}")
return True
except Exception as e:
print(f"❌ Erreur lors de l'extraction : {e}")
return False
if __name__ == "__main__":
args = parse_args()
success = extract_message(
input_file=args.input,
message_id=args.message_id,
output_dir=args.output,
create_test=args.create_test
)
if not success:
sys.exit(1)

View File

@ -1,5 +1,3 @@
# extract_clean_message_with_image.py
import json
import os
import argparse
@ -7,31 +5,30 @@ import requests
from bs4 import BeautifulSoup, Tag
def parse_args():
parser = argparse.ArgumentParser(description="Extrait un message filtré et nettoyé avec son image utile.")
parser = argparse.ArgumentParser(description="Extrait un message et son texte pur d'un fichier JSON et HTML.")
parser.add_argument("--input", "-i", default="messages_raw.json", help="Fichier JSON des messages")
parser.add_argument("--message-id", "-m", type=int, default=228942, help="ID du message à traiter")
parser.add_argument("--output", "-o", default="extracted_message", help="Répertoire de sortie")
parser.add_argument("--output-html", "-o", default="extracted_message", help="Répertoire de sortie pour le HTML")
parser.add_argument("--output-text", "-t", default="extracted_message/message_text.txt", help="Fichier texte de sortie")
parser.add_argument("--preserve-images", action="store_true", help="Préserver les références aux images")
return parser.parse_args()
def clean_message_html(html, keep_img=True):
soup = BeautifulSoup(html, "html.parser")
# Supprimer les balises inutiles (tooltips, tracking, etc.)
# Supprimer les balises inutiles
for tag in soup.select(".o_mail_info, .recipients_info, .o_mail_tracking, .o_thread_icons, .o_thread_tooltip_container, .o_thread_full_spand_icon"):
tag.decompose()
# Supprimer les images inutiles (font_to_img, sauf si /web/image/)
# Supprimer les images inutiles
for img in soup.find_all("img"):
if not isinstance(img, Tag):
continue
if not img.has_attr("src"):
continue
src = img["src"]
if not isinstance(src, str):
continue
if "font_to_img" in src:
img.decompose()
elif keep_img and not src.startswith("http") and "/web/image/" not in src:
@ -54,12 +51,12 @@ def download_image(url, output_dir, filename="image.png"):
print(f"⚠️ Erreur lors du téléchargement de l'image: {e}")
return None
def extract_and_clean(input_file, message_id, output_dir):
def extract_and_clean(input_file, message_id, output_html_dir, output_text_file, preserve_images=False):
if not os.path.exists(input_file):
print(f"❌ Fichier non trouvé : {input_file}")
return
os.makedirs(output_dir, exist_ok=True)
os.makedirs(output_html_dir, exist_ok=True)
with open(input_file, "r", encoding="utf-8") as f:
data = json.load(f)
@ -77,10 +74,17 @@ def extract_and_clean(input_file, message_id, output_dir):
original_html = msg.get("body", "")
clean_html = clean_message_html(original_html)
# Sauvegarder le HTML nettoyé
html_path = os.path.join(output_html_dir, f"message_{message_id}_clean.html")
with open(html_path, "w", encoding="utf-8") as f:
f.write(clean_html)
print(f"📄 HTML nettoyé sauvegardé : {html_path}")
# Extraire les images utiles
soup = BeautifulSoup(clean_html, "html.parser")
image_urls = []
for img in soup.find_all("img"):
if isinstance(img, Tag) and img.has_attr("src"):
src = img["src"]
@ -91,15 +95,31 @@ def extract_and_clean(input_file, message_id, output_dir):
downloaded = None
for url in image_urls:
if isinstance(url, str) and url.startswith("http"):
downloaded = download_image(url, output_dir, "image.png")
downloaded = download_image(url, output_html_dir, "image.png")
break
# Sauvegarde HTML nettoyé
html_path = os.path.join(output_dir, f"message_{message_id}_clean.html")
with open(html_path, "w", encoding="utf-8") as f:
f.write(clean_html)
# Extraire le texte
paragraphs = []
for p in soup.find_all("p"):
if isinstance(p, Tag):
text = p.get_text(strip=True)
if text: # Ignorer les paragraphes vides
paragraphs.append(text)
print(f"📄 HTML nettoyé sauvegardé : {html_path}")
# Traiter les images si demandé
if preserve_images:
for img in soup.find_all("img"):
if isinstance(img, Tag) and img.has_attr("src"):
paragraphs.append(f"[IMAGE: {img['src']}]")
# Créer le texte final avec une structure claire
text_content = "\n\n".join(paragraphs)
# Sauvegarder le texte
with open(output_text_file, "w", encoding="utf-8") as f:
f.write(text_content)
print(f"✅ Texte extrait et sauvegardé dans : {output_text_file}")
if downloaded:
print(f"🖼️ Image téléchargée dans : {downloaded}")
else:
@ -107,4 +127,4 @@ def extract_and_clean(input_file, message_id, output_dir):
if __name__ == "__main__":
args = parse_args()
extract_and_clean(args.input, args.message_id, args.output)
extract_and_clean(args.input, args.message_id, args.output_html, args.output_text, args.preserve_images)

View File

@ -1,68 +0,0 @@
# extract_text_from_html.py
import os
import argparse
from bs4 import BeautifulSoup, Tag
def parse_args():
parser = argparse.ArgumentParser(description="Extrait le texte pur d'un fichier HTML.")
parser.add_argument("--input", "-i", default="extracted_message/message_228942_clean.html",
help="Fichier HTML à traiter")
parser.add_argument("--output", "-o", default="extracted_message/message_text.txt",
help="Fichier texte de sortie")
parser.add_argument("--preserve-images", "-p", action="store_true",
help="Préserver les références aux images")
return parser.parse_args()
def extract_text(html_file, output_file, preserve_images=False):
"""Extrait le texte d'un fichier HTML et le sauvegarde dans un fichier texte."""
if not os.path.exists(html_file):
print(f"❌ Fichier non trouvé : {html_file}")
return False
# Créer le répertoire de sortie si nécessaire
output_dir = os.path.dirname(output_file)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
# Charger et parser le HTML
with open(html_file, "r", encoding="utf-8") as f:
html_content = f.read()
soup = BeautifulSoup(html_content, "html.parser")
# Supprimer les balises vides et inutiles
for tag in soup.find_all():
if isinstance(tag, Tag):
if not tag.get_text(strip=True):
tag.decompose()
# Extraire le texte avec une meilleure mise en forme
paragraphs = []
# Traiter les paragraphes
for p in soup.find_all("p"):
if isinstance(p, Tag):
text = p.get_text(strip=True)
if text: # Ignorer les paragraphes vides
paragraphs.append(text)
# Traiter les images si demandé
if preserve_images:
for img in soup.find_all("img"):
if isinstance(img, Tag) and img.has_attr("src"):
paragraphs.append(f"[IMAGE: {img['src']}]")
# Créer le texte final avec une structure claire
text_content = "\n\n".join(paragraphs)
# Sauvegarder le texte
with open(output_file, "w", encoding="utf-8") as f:
f.write(text_content)
print(f"✅ Texte extrait et sauvegardé dans : {output_file}")
return True
if __name__ == "__main__":
args = parse_args()
extract_text(args.input, args.output, args.preserve_images)

View File

@ -1,52 +0,0 @@
# extract_tracking_values_by_id.py
import json
import os
from odoo.auth_manager import AuthManager
CONFIG_FILE = "config.json"
def load_config(path=CONFIG_FILE):
with open(path, "r") as f:
return json.load(f)
def fetch_tracking_values(ids, config_file=CONFIG_FILE):
config = load_config(config_file)
odoo_cfg = config["odoo"]
auth = AuthManager(
url=odoo_cfg["url"],
db=odoo_cfg["db"],
username=odoo_cfg["username"],
api_key=odoo_cfg["api_key"]
)
if not auth.login():
print("❌ Échec de l'authentification.")
return
result = auth.read(
model="mail.tracking.value",
ids=ids,
fields=["field", "field_desc", "old_value_char", "new_value_char"]
)
# Sauvegarde JSON
out_path = "tracking_value_result.json"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(result, f, indent=2, ensure_ascii=False)
print(f"✅ Résultat enregistré dans {out_path}")
for entry in result:
print(f"- Champ : {entry['field_desc']}")
print(f" Ancienne valeur : {entry['old_value_char']}")
print(f" Nouvelle valeur : {entry['new_value_char']}")
print()
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Lire les valeurs de suivi (mail.tracking.value)")
parser.add_argument("ids", nargs="+", type=int, help="IDs à interroger (ex: 179095)")
parser.add_argument("--config", "-c", default=CONFIG_FILE, help="Chemin vers le fichier config")
args = parser.parse_args()
fetch_tracking_values(args.ids, args.config)

View File

@ -1,71 +0,0 @@
# extract_tracking_values.py
import json
import os
from odoo.auth_manager import AuthManager
from odoo.ticket_manager import TicketManager
from odoo.message_manager import MessageManager
CONFIG_FILE = "config.json"
def load_config(path=CONFIG_FILE):
with open(path, "r") as f:
return json.load(f)
def extract_tracking_values(ticket_code: str, config_file: str = CONFIG_FILE):
config = load_config(config_file)
odoo = config["odoo"]
# Auth
auth = AuthManager(
url=odoo["url"],
db=odoo["db"],
username=odoo["username"],
api_key=odoo["api_key"]
)
if not auth.login():
print("❌ Authentification échouée.")
return
# Get ticket
ticket_manager = TicketManager(auth)
ticket = ticket_manager.get_ticket_by_code(ticket_code)
if not ticket or "id" not in ticket:
print(f"❌ Ticket non trouvé : {ticket_code}")
return
ticket_id = ticket["id"]
# Get messages
msg_manager = MessageManager(auth)
messages = msg_manager.get_ticket_messages(ticket_id)
# Collect tracking IDs
tracking_ids = []
for msg in messages:
tracking_ids.extend(msg.get("tracking_value_ids", []))
if not tracking_ids:
print(f" Aucun changement détecté pour le ticket {ticket_code}")
return
# Fetch tracking values
tracking_data = auth.read(
model="mail.tracking.value",
ids=tracking_ids,
fields=["field", "field_desc", "old_value_char", "new_value_char"]
)
# Export JSON
output_path = os.path.join("output", f"tracking_values_{ticket_code}.json")
os.makedirs("output", exist_ok=True)
with open(output_path, "w", encoding="utf-8") as f:
json.dump(tracking_data, f, indent=2, ensure_ascii=False)
print(f"✅ Modifications sauvegardées dans : {output_path}")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Extract tracking changes for an Odoo ticket.")
parser.add_argument("ticket_code", help="Code du ticket Odoo (ex: T12345)")
parser.add_argument("--config", "-c", help="Fichier config", default=CONFIG_FILE)
args = parser.parse_args()
extract_tracking_values(args.ticket_code, args.config)

View File

@ -1,9 +0,0 @@
[
{
"id": 179095,
"field": "user_id",
"field_desc": "Assigné à",
"old_value_char": "",
"new_value_char": "Fabien LAFAY"
}
]