# Récupération de tickets projet 3 étape 8 (2025-04-15 08:48:01)
_**User**_
comment récupérer 10 tickets du project 3 stage 8
---
_**Assistant**_
Pour récupérer 10 tickets du projet avec l'ID 3 et à l'étape 8, vous pouvez utiliser la commande suivante :
```bash
python -m odoo.retrieve_tickets_batch batch --project 3 --stage 8 --limit 10
```
Cette commande spécifie le projet et l'étape, tout en limitant le nombre de tickets extraits à 10. Assurez-vous que votre fichier de configuration `config.json` est correctement configuré pour que la commande fonctionne.
---
_**User**_
J'utilise deux fichiers avec le même nom pour l'extraction de mes fichiers la commande de base retrieve_ticket.py à la racine suivi du code d'un ticket déclenche l'extraction du ticket et la génération d'un rapport final hors la commande retrieve_ticket_batch.py dans odoo extrait un groupe de ticket mais il me manque la génération des rapports pour chacun de ces ticket as tu compris ma demande? Si oui comment procèderais tu pour le régler
---
_**Assistant**_
Oui, j'ai compris votre demande. Vous souhaitez que le script `retrieve_tickets_batch.py` génère un rapport pour chaque ticket extrait, similaire à ce que fait le script `retrieve_ticket.py` pour un ticket unique.
Pour résoudre ce problème, voici les étapes que je suivrais :
1. **Modifier la fonction d'extraction des tickets dans `retrieve_tickets_batch.py`** : Après avoir extrait les données de chaque ticket, vous devez appeler une fonction pour générer un rapport pour ce ticket.
2. **Réutiliser la logique de génération de rapport** : Vous pouvez extraire la logique de génération de rapport de `retrieve_ticket.py` dans une fonction séparée, que vous pourrez appeler à partir de `retrieve_tickets_batch.py`.
3. **Créer un répertoire de sortie pour chaque ticket** : Assurez-vous que chaque ticket a son propre répertoire de sortie, comme cela est fait dans `retrieve_ticket.py`.
Voici un exemple de ce à quoi pourrait ressembler la modification dans `retrieve_tickets_batch.py` :
### Étape 1 : Créer une fonction pour générer le rapport
Ajoutez une fonction dans `retrieve_tickets_batch.py` pour générer le rapport :
```python
def generate_ticket_report(ticket_code, ticket_dir, result):
"""
Génère un rapport pour un ticket extrait.
Args:
ticket_code: Code du ticket
ticket_dir: Répertoire où les données du ticket sont stockées
result: Résultat de l'extraction du ticket
"""
# Logique pour générer le rapport
summary = {
"ticket_code": ticket_code,
"output_directory": ticket_dir,
"message_count": result.get("messages_count", 0),
"attachment_count": result.get("attachments_count", 0),
# Ajoutez d'autres informations pertinentes ici
}
summary_path = os.path.join(ticket_dir, "extraction_summary.json")
with open(summary_path, 'w', encoding='utf-8') as f:
json.dump(summary, f, indent=2, ensure_ascii=False)
```
### Étape 2 : Appeler cette fonction après l'extraction de chaque ticket
Modifiez la section où vous extrayez les tickets dans `retrieve_tickets_batch.py` pour appeler `generate_ticket_report` après chaque extraction :
```python
# Extraire les tickets par lot
result = batch_manager.extract_tickets_batch(
domain=domain,
limit=limit,
offset=offset,
base_output_dir=base_output_dir,
skip_existing=skip_existing
)
# Pour chaque ticket extrait, générer un rapport
for ticket in result.get('tickets', []):
ticket_code = ticket['code']
ticket_dir = os.path.join(base_output_dir, f"{ticket_code}_{timestamp}")
os.makedirs(ticket_dir, exist_ok=True)
# Appeler la fonction d'extraction pour chaque ticket
ticket_result = ticket_manager.extract_ticket_data(ticket_code, ticket_dir)
# Générer le rapport pour ce ticket
generate_ticket_report(ticket_code, ticket_dir, ticket_result)
```
### Étape 3 : Tester les modifications
Après avoir effectué ces modifications, testez le script `retrieve_tickets_batch.py` pour vous assurer que les rapports sont générés correctement pour chaque ticket extrait.
Ces étapes devraient vous permettre d'ajouter la fonctionnalité de génération de rapports pour chaque ticket extrait par le script de traitement par lots. Si vous avez besoin d'aide supplémentaire pour l'implémentation, n'hésitez pas à demander !
---
_**User**_
il me manque également l'extraction des attachments (pièces-jointes) et la génération de rapports au niveau de mon retrieve_ticket_batch.py je pense qu'une partie de ces fonctionalités sont dans le fichier racine qui porte le même non que celui de odoo il serait judicieux de changer le nom du fichier retrieve_ticket.py à la racine et lui incoporer comme pour retrieve_ticket dans odoo pour harmoniser avec retrieve_ticket_batch et pouvoir récupérer pour chaques tickets les pièces jointes et générer les rapport as tu compris ma demande? Si oui guide moi étape par étape pour que je règle le problème
---
_**Assistant**_
Oui, j'ai bien compris votre demande. Vous souhaitez harmoniser les fonctionnalités de génération de rapports et d'extraction des pièces jointes entre les scripts `retrieve_ticket.py` et `retrieve_tickets_batch.py`. Voici comment procéder étape par étape :
### Étape 1 : Renommer le fichier `retrieve_ticket.py`
1. **Renommer le fichier** : Changez le nom du fichier `retrieve_ticket.py` à la racine en quelque chose comme `retrieve_single_ticket.py` pour éviter toute confusion avec le fichier `retrieve_ticket.py` dans le dossier `odoo`.
### Étape 2 : Modifier le fichier renommé pour inclure l'extraction des pièces jointes
1. **Ouvrir le fichier `retrieve_single_ticket.py`** (anciennement `retrieve_ticket.py`).
2. **Ajouter la logique pour extraire les pièces jointes**. Vous pouvez utiliser la logique existante dans `retrieve_ticket.py` pour gérer les pièces jointes. Assurez-vous que la fonction `extract_ticket_data` dans `TicketManager` gère également l'extraction des pièces jointes.
Voici un exemple de ce que vous pourriez ajouter :
```python
# Dans retrieve_single_ticket.py
def extract_ticket_data(ticket_code, ticket_dir):
# Logique existante pour extraire les données du ticket
# ...
# Logique pour extraire les pièces jointes
attachments = self.get_attachments(ticket_code)
for attachment in attachments:
# Code pour sauvegarder chaque pièce jointe dans ticket_dir
with open(os.path.join(ticket_dir, attachment['name']), 'wb') as f:
f.write(attachment['data'])
```
### Étape 3 : Modifier `retrieve_tickets_batch.py` pour utiliser le nouveau fichier
1. **Ouvrir `retrieve_tickets_batch.py`**.
2. **Modifier l'importation** pour utiliser le nouveau fichier :
```python
from .retrieve_single_ticket import extract_ticket_data
```
3. **Appeler la fonction d'extraction des pièces jointes** dans la boucle où vous traitez chaque ticket extrait :
```python
# Pour chaque ticket extrait, générer un rapport et extraire les pièces jointes
for ticket in result.get('tickets', []):
ticket_code = ticket['code']
ticket_dir = os.path.join(base_output_dir, f"{ticket_code}_{timestamp}")
os.makedirs(ticket_dir, exist_ok=True)
# Appeler la fonction d'extraction pour chaque ticket
ticket_result = extract_ticket_data(ticket_code, ticket_dir)
# Générer le rapport pour ce ticket
generate_ticket_report(ticket_code, ticket_dir, ticket_result)
```
### Étape 4 : Tester les modifications
1. **Exécuter le script `retrieve_tickets_batch.py`** pour vous assurer que les pièces jointes sont correctement extraites et que les rapports sont générés pour chaque ticket.
2. **Vérifier les répertoires de sortie** pour vous assurer que les pièces jointes sont présentes et que les rapports contiennent les informations correctes.
### Étape 5 : Harmoniser les fonctions
1. **Vérifiez que toutes les fonctions nécessaires** pour l'extraction des données et la génération des rapports sont présentes dans le nouveau fichier `retrieve_single_ticket.py`.
2. **Assurez-vous que la logique est cohérente** entre les deux fichiers pour éviter toute duplication de code.
En suivant ces étapes, vous devriez être en mesure d'harmoniser les fonctionnalités d'extraction des pièces jointes et de génération de rapports entre vos scripts. Si vous avez besoin d'aide supplémentaire à n'importe quelle étape, n'hésitez pas à demander !
---
_**User**_
ce n'est pas tout à fait je que je voudrai le fichier retrieve_ticket.py à la racine je voudri qu'il soit une entrée à mes fonctionalités odoo. changeons son nom et qu'il soit capable d'exécuté l'extraction d'un ticket ou l'extraction d'un groupe de ticket. Regagerde bien ce fichier à la racine, les fichiers dans le répertoire odoo retrieve_ticket.py (la fonctionalité d'esxtraction d'un ticket est fonctionelle j'extrait les infos, les pièces_jointes et je génére des rapports) regarde bience fonctionnement et reproduit le pour l'extraction en groupe de ticket avec retrieve_ticket_batch.py.
---
_**Assistant**_
Je comprends maintenant. Vous souhaitez que le fichier `retrieve_ticket.py` à la racine serve de point d'entrée unifié pour toutes vos fonctionnalités d'extraction Odoo, qu'il s'agisse d'extraire un seul ticket ou un groupe de tickets.
Voici comment procéder étape par étape :
### Étape 1 : Renommer et adapter le fichier `retrieve_ticket.py` à la racine
Renommez le fichier en quelque chose comme `odoo_extractor.py` pour indiquer clairement qu'il s'agit du point d'entrée principal.
### Étape 2 : Modifier le fichier pour qu'il puisse gérer les deux types d'extraction
Modifiez le fichier pour qu'il puisse traiter à la fois l'extraction d'un ticket unique et l'extraction par lots :
```python
#!/usr/bin/env python3
"""
Point d'entrée unifié pour l'extraction de données depuis Odoo.
Ce script permet d'extraire un ticket unique ou un groupe de tickets selon les paramètres fournis.
"""
import os
import sys
import json
import argparse
from datetime import datetime
from odoo.auth_manager import AuthManager
from odoo.ticket_manager import TicketManager
from odoo.retrieve_ticket import retrieve_ticket
from odoo.retrieve_tickets_batch import retrieve_tickets_batch, parse_domain
from core.utils import setup_logging, log_separator
from formatters.json_to_markdown import create_markdown_from_json
# Importation de l'extracteur d'images HTML (optionnel)
try:
from utils.image_extractor import extract_images_from_ticket
from utils.image_extractor.extract_all_images import enhance_ticket_data_with_images
HTML_IMAGE_EXTRACTOR_AVAILABLE = True
except ImportError:
HTML_IMAGE_EXTRACTOR_AVAILABLE = False
def parse_arguments():
"""Parse les arguments de ligne de commande."""
parser = argparse.ArgumentParser(description="Extrait des données depuis Odoo (ticket unique ou par lots).")
# Paramètres communs
parser.add_argument("--config", "-c", help="Fichier de configuration", default="config.json")
parser.add_argument("--verbose", "-v", action="store_true", help="Mode verbeux")
parser.add_argument("--output", "-o", help="Répertoire de sortie", default=None)
parser.add_argument("--no-md", action="store_true", help="Désactiver la génération automatique du fichier Markdown")
parser.add_argument("--extract-html-images", action="store_true", help="Activer l'extraction des images intégrées dans le HTML")
# Sous-parseurs pour les différentes commandes
subparsers = parser.add_subparsers(dest="command", help="Commande à exécuter")
# Commande pour extraire un ticket unique
single_parser = subparsers.add_parser("ticket", help="Extrait un ticket unique par son code")
single_parser.add_argument("ticket_code", help="Code du ticket à extraire")
# Commande pour extraire un lot de tickets
batch_parser = subparsers.add_parser("batch", help="Extrait un lot de tickets")
batch_parser.add_argument("--domain", "-d", help="Critères de recherche (format: field1:op1:value1;field2:op2:value2)")
batch_parser.add_argument("--project", "-p", type=int, help="ID du projet")
batch_parser.add_argument("--stage", "-s", type=int, help="ID de l'étape/statut")
batch_parser.add_argument("--limit", "-l", type=int, default=50, help="Nombre maximum de tickets à extraire")
batch_parser.add_argument("--offset", type=int, default=0, help="Index de départ pour la pagination")
batch_parser.add_argument("--include-existing", action="store_true", help="Inclure les tickets déjà extraits")
# Commande pour lister les projets
projects_parser = subparsers.add_parser("projects", help="Liste les projets disponibles")
# Commande pour lister les étapes
stages_parser = subparsers.add_parser("stages", help="Liste les étapes/statuts disponibles")
return parser.parse_args()
def load_config(config_file):
"""Charge le fichier de configuration."""
try:
with open(config_file, 'r') as f:
return json.load(f)
except Exception as e:
print(f"Erreur lors du chargement du fichier de configuration: {e}")
sys.exit(1)
def main():
"""Fonction principale du script."""
args = parse_arguments()
config = load_config(args.config)
# Configurer la journalisation
log_level = "DEBUG" if args.verbose else "INFO"
setup_logging(log_level)
# Extraire les informations de connexion
odoo_config = config.get("odoo", {})
url = odoo_config.get("url")
db = odoo_config.get("db")
username = odoo_config.get("username")
api_key = odoo_config.get("api_key")
if not all([url, db, username, api_key]):
print("Informations de connexion Odoo manquantes dans le fichier de configuration")
sys.exit(1)
# Traiter la commande appropriée
if args.command == "ticket":
# Extraction d'un ticket unique
result = retrieve_ticket(
ticket_code=args.ticket_code,
output=args.output,
config_file=args.config,
verbose=args.verbose
)
if not result:
print(f"Échec de l'extraction du ticket {args.ticket_code}")
sys.exit(1)
if not args.no_md:
# Génération du rapport Markdown
json_file = os.path.join(result, "all_messages.json")
if os.path.exists(json_file):
md_file_path = os.path.join(result, "rapport.md")
print(f"Génération du rapport Markdown...")
if create_markdown_from_json(json_file, md_file_path):
print(f"Rapport Markdown créé: {md_file_path}")
else:
print("Échec de la génération du rapport Markdown")
elif args.command == "batch":
# Extraction par lots
domain = []
if args.domain:
domain = parse_domain(args.domain)
# Ajouter project_id et stage_id si spécifiés
if args.project:
domain.append(["project_id", "=", args.project])
if args.stage:
domain.append(["stage_id", "=", args.stage])
result = retrieve_tickets_batch(
domain=domain,
limit=args.limit,
offset=args.offset,
output=args.output,
config_file=args.config,
verbose=args.verbose,
skip_existing=not args.include_existing
)
if not result or result.get("status") == "error":
print("Échec de l'extraction par lots")
sys.exit(1)
# Générer des rapports Markdown pour chaque ticket extrait
if not args.no_md:
processed_tickets = result.get("processed_tickets", [])
for ticket_info in processed_tickets:
ticket_dir = ticket_info.get("output_dir")
if ticket_dir and os.path.exists(ticket_dir):
json_file = os.path.join(ticket_dir, "all_messages.json")
if os.path.exists(json_file):
md_file_path = os.path.join(ticket_dir, "rapport.md")
print(f"Génération du rapport Markdown pour {ticket_info.get('code')}...")
if create_markdown_from_json(json_file, md_file_path):
print(f"Rapport Markdown créé: {md_file_path}")
else:
print(f"Échec de la génération du rapport Markdown pour {ticket_info.get('code')}")
elif args.command == "projects":
# Lister les projets
from odoo.retrieve_tickets_batch import list_projects
list_projects(args.config)
elif args.command == "stages":
# Lister les étapes
from odoo.retrieve_tickets_batch import list_stages
list_stages(args.config)
else:
parser = argparse.ArgumentParser(description="Extrait des données depuis Odoo (ticket unique ou par lots).")
parser.print_help()
sys.exit(1)
if __name__ == "__main__":
main()
```
### Étape 3 : Modifier `retrieve_tickets_batch.py` pour inclure l'extraction des pièces jointes et la génération de rapports
1. **Ouvrir le fichier `odoo/retrieve_tickets_batch.py`**
2. **Modifiez la fonction `retrieve_tickets_batch` pour inclure l'extraction des pièces jointes**. Assurez-vous qu'elle utilise les mêmes mécanismes que `retrieve_ticket.py` dans le dossier `odoo`.
3. **Ajoutez la génération de rapports** pour chaque ticket traité.
Voici un exemple de modification :
```python
def retrieve_tickets_batch(domain=None, limit=50, offset=0, output=None, config_file="config.json",
verbose=False, skip_existing=True):
"""
Extrait un lot de tickets répondant à des critères spécifiques.
"""
# ... code existant ...
# Après avoir trouvé et extrait les tickets
processed_tickets = []
for ticket in found_tickets:
ticket_code = ticket.get('code')
if not ticket_code:
continue
# Créer un dossier spécifique pour ce ticket
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
ticket_dir = os.path.join(base_output_dir, f"{ticket_code}_{timestamp}")
os.makedirs(ticket_dir, exist_ok=True)
# Extraire les données du ticket, y compris les pièces jointes
ticket_result = ticket_manager.extract_ticket_data(ticket_code, ticket_dir)
if ticket_result:
# Ajouter le ticket à la liste des tickets traités
processed_tickets.append({
"code": ticket_code,
"name": ticket.get('name'),
"output_dir": ticket_dir,
"messages_count": ticket_result.get("messages_count", 0),
"attachments_count": ticket_result.get("attachments_count", 0)
})
# Créer un résumé d'extraction pour ce ticket
summary = {
"timestamp": timestamp,
"ticket_code": ticket_code,
"output_directory": ticket_dir,
"message_count": ticket_result.get("messages_count", 0),
"attachment_count": ticket_result.get("attachments_count", 0),
"files_created": [
os.path.basename(ticket_result.get("ticket_info", "")),
os.path.basename(ticket_result.get("ticket_summary", "")),
os.path.basename(ticket_result.get("messages_file", "")),
os.path.basename(ticket_result.get("ticket_data_file", ""))
]
}
summary_path = os.path.join(ticket_dir, "extraction_summary.json")
with open(summary_path, 'w', encoding='utf-8') as f:
json.dump(summary, f, indent=2, ensure_ascii=False)
# Ajouter les tickets traités au résultat
result["processed_tickets"] = processed_tickets
# ... reste du code existant ...
return result
```
### Étape 4 : Tester les modifications
1. **Testez l'extraction d'un ticket unique** avec la nouvelle commande :
```bash
python odoo_extractor.py ticket TS-1234
```
2. **Testez l'extraction par lots** avec la nouvelle commande :
```bash
python odoo_extractor.py batch --project 3 --stage 8 --limit 10
```
3. **Vérifiez les dossiers de sortie** pour vous assurer que toutes les données sont correctement extraites, y compris les pièces jointes, et que les rapports sont générés.
### Étape 5 : Documentation
Mettez à jour votre documentation pour refléter les changements apportés et les nouvelles commandes disponibles.
Avec ces modifications, vous aurez un point d'entrée unifié pour toutes vos fonctionnalités d'extraction Odoo, avec une cohérence dans le traitement des tickets, qu'ils soient extraits individuellement ou par lots.
---
_**User**_
peux tu me dire précisément où je dois ajouter ce code dans mon fichier
---
_**Assistant**_
Bien sûr ! Voici les instructions précises sur où ajouter le code dans votre fichier `odoo/retrieve_tickets_batch.py`.
### Étape 1 : Ajouter la logique d'extraction des pièces jointes et de génération de rapports
1. **Ouvrez le fichier `odoo/retrieve_tickets_batch.py`.**
2. **Localisez la fonction `retrieve_tickets_batch`.** Cette fonction commence par la définition suivante :
```python
def retrieve_tickets_batch(domain=None, limit=50, offset=0, output=None, config_file="config.json",
verbose=False, skip_existing=True):
```
3. **Ajoutez le code pour traiter chaque ticket extrait.** Vous devez insérer le code après la section où vous avez extrait les tickets, généralement après l'appel à `batch_manager.extract_tickets_batch`. Voici un exemple de la structure de la fonction avec les ajouts :
### Exemple de code à insérer
Voici un exemple de ce que vous devez ajouter, avec des commentaires pour indiquer où insérer le code :
```python
def retrieve_tickets_batch(domain=None, limit=50, offset=0, output=None, config_file="config.json",
verbose=False, skip_existing=True):
# ... code existant pour charger la configuration et l'authentification ...
# Extraire les tickets par lot
result = batch_manager.extract_tickets_batch(
domain=domain,
limit=limit,
offset=offset,
base_output_dir=base_output_dir,
skip_existing=skip_existing
)
# Ajoutez ce code après l'extraction des tickets
processed_tickets = []
for ticket in result.get('tickets', []):
ticket_code = ticket['code']
if not ticket_code:
continue
# Créer un dossier spécifique pour ce ticket
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
ticket_dir = os.path.join(base_output_dir, f"{ticket_code}_{timestamp}")
os.makedirs(ticket_dir, exist_ok=True)
# Extraire les données du ticket, y compris les pièces jointes
ticket_result = ticket_manager.extract_ticket_data(ticket_code, ticket_dir)
if ticket_result:
# Ajouter le ticket à la liste des tickets traités
processed_tickets.append({
"code": ticket_code,
"name": ticket.get('name'),
"output_dir": ticket_dir,
"messages_count": ticket_result.get("messages_count", 0),
"attachments_count": ticket_result.get("attachments_count", 0)
})
# Créer un résumé d'extraction pour ce ticket
summary = {
"timestamp": timestamp,
"ticket_code": ticket_code,
"output_directory": ticket_dir,
"message_count": ticket_result.get("messages_count", 0),
"attachment_count": ticket_result.get("attachments_count", 0),
"files_created": [
os.path.basename(ticket_result.get("ticket_info", "")),
os.path.basename(ticket_result.get("ticket_summary", "")),
os.path.basename(ticket_result.get("messages_file", "")),
os.path.basename(ticket_result.get("ticket_data_file", ""))
]
}
summary_path = os.path.join(ticket_dir, "extraction_summary.json")
with open(summary_path, 'w', encoding='utf-8') as f:
json.dump(summary, f, indent=2, ensure_ascii=False)
# Ajouter les tickets traités au résultat
result["processed_tickets"] = processed_tickets
# ... reste du code existant ...
return result
```
### Étape 2 : Vérifiez les dépendances
Assurez-vous que toutes les dépendances nécessaires (comme `datetime`, `os`, `json`, etc.) sont importées en haut de votre fichier si elles ne le sont pas déjà.
### Étape 3 : Tester les modifications
Après avoir ajouté ce code, testez le script pour vous assurer que les pièces jointes sont extraites et que les rapports sont générés correctement pour chaque ticket.
Si vous avez besoin de plus d'aide ou de précisions, n'hésitez pas à demander !
---
_**User**_
For the code present, we get this error:
```
"{" was not closed
```
How can I resolve this? If you propose a fix, please make it concise.
---
_**Assistant**_
The error message indicates that there is an issue with the JSON-like structure in your code, specifically that a curly brace `{` was not closed properly.
In the snippet you provided, it looks like the `summary` dictionary is being created, but it may be missing some closing braces or commas for the key-value pairs.
Here’s a concise fix for the `summary` dictionary. Ensure that you include all necessary key-value pairs and close the dictionary properly:
```python
# Créer un résumé d'extraction pour ce ticket
summary = {
"timestamp": timestamp,
"ticket_code": ticket_code,
"output_directory": ticket_dir,
"message_count": ticket_result.get("messages_count", 0),
"attachment_count": ticket_result.get("attachments_count", 0),
"files_created": [
os.path.basename(ticket_result.get("ticket_info", "")),
os.path.basename(ticket_result.get("ticket_summary", "")),
os.path.basename(ticket_result.get("messages_file", "")),
os.path.basename(ticket_result.get("ticket_data_file", ""))
]
} # Ensure this closing brace is present
```
Make sure that the entire structure is correctly formatted and that there are no missing commas or braces in the surrounding code as well. After making this change, the error should be resolved.
---
_**User**_
l'extration unique fonctionne mais par lot les commandes ne fonctionnent pas correctement
---
_**Assistant**_
Pour résoudre le problème d'extraction par lots dans votre script `odoo_extractor.py`, il semble que la commande pour extraire des tickets par lots ne soit pas correctement configurée. Voici quelques étapes pour diagnostiquer et corriger le problème :
### Étape 1 : Vérifiez la commande de traitement des lots
Assurez-vous que la section de votre code qui traite la commande `batch` est correctement configurée. Voici un extrait de code que vous devriez avoir dans la fonction `main()` :
```python
elif args.command == "batch":
# Extraction par lots
domain = []
if args.domain:
domain = parse_domain(args.domain)
# Ajouter project_id et stage_id si spécifiés
if args.project:
domain.append(["project_id", "=", args.project])
if args.stage:
domain.append(["stage_id", "=", args.stage])
result = retrieve_tickets_batch(
domain=domain,
limit=args.limit,
offset=args.offset,
output=args.output,
config_file=args.config,
verbose=args.verbose,
skip_existing=not args.include_existing
)
if not result or result.get("status") == "error":
print("Échec de l'extraction par lots")
sys.exit(1)
```
### Étape 2 : Vérifiez les arguments de la ligne de commande
Assurez-vous que vous passez tous les arguments nécessaires lorsque vous exécutez la commande. Par exemple, la commande que vous avez utilisée :
```bash
python odoo_extractor.py batch --project 3 --stage 8 --limit 10
```
Vérifiez que vous avez également défini les arguments `--domain` si nécessaire, ou que vous avez bien configuré les autres options.
### Étape 3 : Vérifiez la fonction `retrieve_tickets_batch`
Assurez-vous que la fonction `retrieve_tickets_batch` dans `odoo/retrieve_tickets_batch.py` est correctement définie et qu'elle gère les paramètres que vous lui passez. Voici un exemple de ce que vous devriez avoir :
```python
def retrieve_tickets_batch(domain=None, limit=50, offset=0, output=None, config_file="config.json",
verbose=False, skip_existing=True):
# ... code pour charger la configuration et l'authentification ...
# Extraire les tickets par lot
result = batch_manager.extract_tickets_batch(
domain=domain,
limit=limit,
offset=offset,
base_output_dir=base_output_dir,
skip_existing=skip_existing
)
# ... code pour traiter les résultats ...
```
### Étape 4 : Ajoutez des messages de débogage
Pour mieux comprendre où le problème se situe, vous pouvez ajouter des messages de débogage dans votre code. Par exemple, avant d'appeler `retrieve_tickets_batch`, vous pouvez imprimer les valeurs des paramètres :
```python
print(f"Domain: {domain}, Limit: {args.limit}, Offset: {args.offset}, Output: {args.output}")
```
### Étape 5 : Testez à nouveau
Après avoir vérifié et corrigé les points ci-dessus, testez à nouveau la commande d'extraction par lots :
```bash
python odoo_extractor.py batch --project 3 --stage 8 --limit 10
```
Si vous continuez à rencontrer des problèmes, veuillez fournir le message d'erreur exact ou le comportement observé, afin que je puisse vous aider davantage.
---
_**User**_
peux tu me guider précisément pour corriger le problème
---
_**Assistant**_
Voici les étapes précises pour corriger le problème dans votre fichier `odoo_extractor.py` :
### Étape 1 : Ajouter la gestion des commandes "batch"
Vous devez ajouter le code qui manque dans votre fonction `main()` pour traiter la commande "batch". Ouvrez `odoo_extractor.py` et localisez la section suivante :
```python
elif args.command == "projects":
# Lister les projets
from odoo.retrieve_tickets_batch import list_projects
list_projects(args.config)
elif args.command == "stages":
# Lister les étapes/statuts
from odoo.retrieve_tickets_batch import list_stages
list_stages(args.config)
```
### Étape 2 : Ajouter la section de code pour la commande "batch"
Juste avant cette section (ou après la section traitant la commande "ticket"), ajoutez le code suivant :
```python
elif args.command == "batch":
# Extraction par lots
domain = []
if args.domain:
domain = parse_domain(args.domain)
# Ajouter project_id et stage_id si spécifiés
if args.project:
domain.append(["project_id", "=", args.project])
if args.stage:
domain.append(["stage_id", "=", args.stage])
result = retrieve_tickets_batch(
domain=domain,
limit=args.limit,
offset=args.offset,
output=args.output,
config_file=args.config,
verbose=args.verbose,
skip_existing=not args.include_existing
)
if not result or result.get("status") == "error":
print("Échec de l'extraction par lots")
sys.exit(1)
# Générer des rapports Markdown pour chaque ticket extrait
if not args.no_md:
processed_tickets = result.get("processed_tickets", [])
for ticket_info in processed_tickets:
ticket_dir = ticket_info.get("output_dir")
if ticket_dir and os.path.exists(ticket_dir):
json_file = os.path.join(ticket_dir, "all_messages.json")
if os.path.exists(json_file):
md_file_path = os.path.join(ticket_dir, "rapport.md")
print(f"Génération du rapport Markdown pour {ticket_info.get('code')}...")
if create_markdown_from_json(json_file, md_file_path):
print(f"Rapport Markdown créé: {md_file_path}")
else:
print(f"Échec de la génération du rapport Markdown pour {ticket_info.get('code')}")
```
### Étape 3 : Corriger la description de l'argument include-existing
Dans votre définition des arguments, l'option `--include-existing` a une description incorrecte. Trouvez cette ligne :
```python
batch_parser.add_argument("--include-existing", action="store_true", help="Index de départ pour la pagination")
```
Et remplacez-la par :
```python
batch_parser.add_argument("--include-existing", action="store_true", help="Inclure les tickets déjà extraits")
```
### Étape 4 : Importer toutes les dépendances nécessaires
Assurez-vous que toutes les dépendances nécessaires sont importées en haut du fichier. Vérifiez particulièrement que vous avez ces importations :
```python
from odoo.retrieve_tickets_batch import retrieve_tickets_batch, parse_domain, list_projects, list_stages
```
### Étape 5 : Sauvegarder et tester
Sauvegardez le fichier et testez la commande d'extraction par lots :
```bash
python odoo_extractor.py batch --project 3 --stage 8 --limit 10
```
Avec ces modifications, la commande d'extraction par lots devrait fonctionner correctement. Si vous rencontrez encore des problèmes, n'hésitez pas à me le faire savoir.
---
_**User**_
peux tu corriger mon fichier odoo_extractor.py
---
_**Assistant**_
Read file: odoo_extractor.py
---
Read file: odoo/retrieve_tickets_batch.py
---
Je dois mettre à jour le fichier `odoo_extractor.py` pour corriger le problème avec les commandes d'extraction par lots. Voici les corrections que je vais apporter :
---
```diff
from formatters.json_to_markdown import create_markdown_from_json
from odoo.retrieve_ticket import retrieve_ticket
- from odoo.retrieve_tickets_batch import retrieve_tickets_batch, parse_domain
+ from odoo.retrieve_tickets_batch import retrieve_tickets_batch, parse_domain, list_projects, list_stages
#importation de l'extracteur d'images HTML (optionnel)
batch_parser.add_argument("--limit", "-l", type=int, default=50, help="Nombre maximum de tickets à extraire")
batch_parser.add_argument("--offset", type=int, default=0, help="Index de départ pour la pagination")
- batch_parser.add_argument("--include-existing", action="store_true", help="Index de départ pour la pagination")
+ batch_parser.add_argument("--include-existing", action="store_true", help="Inclure les tickets déjà extraits")
# Commande pour lister les projets
domain.append(["stage_id", "=", args.stage])
+ print(f"Démarrage de l'extraction par lot avec les critères: {domain}")
+ print(f"Limite: {args.limit} tickets, offset: {args.offset}")
+
result = retrieve_tickets_batch(
domain=domain,
sys.exit(1)
+ # Afficher un résumé des résultats
+ print(f"Extraction par lot terminée")
+ print(f"Tickets traités: {result.get('processed', 0)}")
+ print(f"Tickets ignorés: {result.get('skipped', 0)}")
+
# Générer des rapports Markdown pour chaque ticket extrait
if not args.no_md:
+ print("Génération des rapports Markdown pour les tickets extraits...")
processed_tickets = result.get("processed_tickets", [])
for ticket_info in processed_tickets:
print(f"Rapport Markdown créé: {md_file_path}")
else:
- print("Echec de la génération du rapport Markdown.")
+ print(f"Echec de la génération du rapport Markdown pour {ticket_info.get('code')}")
+ else:
+ print(f"Fichier de messages introuvable pour {ticket_info.get('code')}")
+ else:
+ print(f"Répertoire introuvable pour {ticket_info.get('code')}")
elif args.command == "projects":
# Lister les projets
- from odoo.retrieve_tickets_batch import list_projects
list_projects(args.config)
elif args.command == "stages":
# Lister les étapes/statuts
- from odoo.retrieve_tickets_batch import list_stages
list_stages(args.config)
```
---
Voilà ! J'ai corrigé votre fichier `odoo_extractor.py` pour résoudre les problèmes avec les commandes d'extraction par lots.
Les modifications principales sont :
1. Correction de la description de l'option `--include-existing` (remplacé "Index de départ pour la pagination" par "Inclure les tickets déjà extraits")
2. Importation des fonctions `list_projects` et `list_stages` directement au début du fichier pour éviter les importations multiples
3. Ajout d'affichages supplémentaires pour mieux suivre le processus d'extraction par lots
4. Amélioration de la gestion des erreurs et des messages pour la génération des rapports Markdown
5. Ajout de messages plus détaillés pour chaque ticket traité
Vous pouvez maintenant utiliser la commande suivante pour extraire 10 tickets du projet 3 à l'étape 8 :
```bash
python odoo_extractor.py batch --project 3 --stage 8 --limit 10
```
Cette commande devrait fonctionner correctement et extraire les tickets avec leurs pièces jointes et générer les rapports appropriés.
---
_**User**_
l'extraction des attachments semble se faire correctement mais le rapport json et md du ticket ne se fait pas correctement comme avec l'appel de retrieve_ticket.py. L'extraction se fait dans output dans un répertoire batch hors je voudrai que les tickets se charge directement dans output comme avec retrieve_ticket.py normalement j'ai implémenté une méthode pour ne pas recharger un ticket déjà présent avec retrieve_ticket_batch.py. Je voudrai que tu analyses la sources de ces problèmes et que tu me guides précisement pas à pas pour les régler. Avant de commencer résume ma demande nous ajusterons si c'est nécessaire
---
_**Assistant**_
Votre demande concerne deux problèmes dans l'extraction par lots avec `retrieve_tickets_batch.py` :
1. Les rapports JSON et Markdown ne sont pas générés correctement, contrairement à l'extraction avec `retrieve_ticket.py`
2. Les tickets extraits sont placés dans un sous-répertoire `batch` dans `output`, alors que vous préférez qu'ils soient directement dans `output` comme avec `retrieve_ticket.py`
Vous souhaitez conserver la fonctionnalité qui évite de recharger les tickets déjà extraits, mais vous voulez harmoniser le comportement des deux méthodes d'extraction.
Est-ce que cette compréhension de votre demande est correcte ?
---
_**User**_
oui
---
_**Assistant**_
Je vais vous guider pas à pas pour résoudre ces deux problèmes.
### Étape 1 : Analyser le problème du répertoire de sortie
Le premier problème est que les tickets extraits par lots sont placés dans un sous-répertoire `batch` au lieu d'être directement dans le répertoire `output`. Cela est probablement géré dans la fonction `extract_tickets_batch` du fichier `odoo/batch_ticket_manager.py`.
---
Searched codebase "batch_ticket_manager.py" • **2** results
| File |
|------|
| `undefined` |
| `undefined` |
---
Read file: odoo/batch_ticket_manager.py
---
Searched codebase "def extract_tickets_batch in batch_ticket_manager.py" • **25** results
| File | Lines |
|------|-------|
| `odoo/batch_ticket_manager.py` | L183-226 |
| `odoo/batch_ticket_manager.py` | L249-287 |
| `odoo/batch_ticket_manager.py` | L226-249 |
| `odoo/batch_ticket_manager.py` | L331-345 |
| `odoo/retrieve_tickets_batch.py` | L53-101 |
| `odoo/batch_ticket_manager.py` | L110-161 |
| `odoo/batch_ticket_manager.py` | L287-331 |
| `odoo/retrieve_tickets_batch.py` | L101-140 |
| `odoo/batch_ticket_manager.py` | L28-66 |
| `odoo/retrieve_tickets_batch.py` | L140-168 |
| `odoo/batch_ticket_manager.py` | L161-183 |
| `odoo/retrieve_tickets_batch.py` | L1-53 |
| `odoo/batch_ticket_manager.py` | L1-28 |
| `odoo/retrieve_tickets_batch.py` | L168-225 |
| `odoo/retrieve_tickets_batch.py` | L359-415 |
| `odoo/batch_ticket_manager.py` | L66-110 |
| `odoo/__init__.py` | L1-12 |
| `odoo/retrieve_tickets_batch.py` | L415-418 |
| `odoo/retrieve_tickets_batch.py` | L326-353 |
| `odoo/retrieve_tickets_batch.py` | L225-282 |
| `odoo/retrieve_tickets_batch.py` | L353-359 |
| `odoo/ticket_manager.py` | L206-220 |
| `odoo/retrieve_tickets_batch.py` | L282-326 |
| `docs/odoo/exemples.py` | L50-80 |
| `odoo/ticket_manager.py` | L1-45 |
---
Voici les étapes pour résoudre les deux problèmes identifiés :
### Étape 1 : Comprendre la source des problèmes
Après analyse du code, j'ai identifié les sources des problèmes :
1. **Problème de répertoire** : Dans `batch_ticket_manager.py`, à la ligne 221, un sous-répertoire `batch_{timestamp}` est créé pour stocker tous les tickets extraits par lots :
```python
batch_dir = os.path.join(base_output_dir, f"batch_{timestamp}")
```
2. **Problème de rapports** : Dans `retrieve_tickets_batch.py`, il y a déjà une logique pour extraire les données des tickets et générer les rapports, mais elle n'est peut-être pas utilisée correctement.
### Étape 2 : Modifier batch_ticket_manager.py
Nous devons modifier la fonction `extract_tickets_batch` pour ne pas créer un sous-répertoire `batch_{timestamp}` et traiter chaque ticket directement dans le répertoire principal.
---
Read file: odoo/batch_ticket_manager.py
---
Maintenant que j'ai bien identifié les problèmes, voici les étapes précises pour les résoudre :
### Étape 1 : Modifier le fichier batch_ticket_manager.py
Vous devez modifier la fonction `extract_tickets_batch` dans le fichier `odoo/batch_ticket_manager.py` pour ne pas créer un sous-répertoire batch. Voici comment faire :
1. **Ouvrez le fichier** `odoo/batch_ticket_manager.py`
2. **Localisez ces lignes** (autour de la ligne 220-221) :
```python
# Créer le répertoire principal de sortie
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
batch_dir = os.path.join(base_output_dir, f"batch_{timestamp}")
ensure_dir(batch_dir)
```
3. **Remplacez-les par** :
```python
# Utiliser directement le répertoire principal de sortie
ensure_dir(base_output_dir)
batch_dir = base_output_dir # Pour compatibilité avec le reste du code
```
4. **Modifiez la création des répertoires de tickets** (autour de la ligne 252) :
```python
# Créer un sous-répertoire pour ce ticket
ticket_dir = os.path.join(batch_dir, f"ticket_{ticket_code}")
```
par :
```python
# Créer un répertoire spécifique pour ce ticket avec timestamp
timestamp_ticket = datetime.now().strftime("%Y%m%d_%H%M%S")
ticket_dir = os.path.join(base_output_dir, f"{ticket_code}_{timestamp_ticket}")
```
5. **Modifiez également le nom du fichier de résumé global** (autour de la ligne 310) :
```python
batch_summary_path = os.path.join(batch_dir, "batch_summary.json")
```
par :
```python
batch_summary_path = os.path.join(base_output_dir, f"batch_summary_{timestamp}.json")
```
### Étape 2 : Vérifier le problème des rapports JSON et MD
Le problème des rapports est probablement lié au fait que les rapports sont générés dans le fichier `retrieve_tickets_batch.py`, mais ils ne sont pas correctement intégrés dans le processus de traitement par lots. Vérifions cela.
---
Read file: odoo/retrieve_tickets_batch.py
---
Je comprends maintenant le problème. Le fichier `retrieve_tickets_batch.py` extrait déjà les données des tickets, mais il y a un conflit entre ce code et celui dans `batch_ticket_manager.py`. Nous avons un double traitement qui crée de la confusion. Voici comment procéder pour résoudre tous les problèmes :
### Étape 3 : Modifier retrieve_tickets_batch.py
Le fichier `retrieve_tickets_batch.py` contient son propre code pour extraire les tickets et créer des répertoires, ce qui crée un conflit avec la logique dans `batch_ticket_manager.py`. Nous devons modifier ce fichier pour utiliser uniquement la fonctionnalité de `batch_ticket_manager.py`.
1. **Ouvrez le fichier** `odoo/retrieve_tickets_batch.py`
2. **Localisez la section** qui commence par (autour de la ligne 100) :
```python
processed_tickets =[]
for ticket in result.get('tickets', []):
# ... code qui traite chaque ticket ...
```
3. **Remplacez toute cette section** (jusqu'à la fin de la boucle) par :
```python
# Les tickets traités sont déjà dans result["processed_tickets"]
# Vous n'avez pas besoin de les traiter à nouveau ici
```
### Étape 4 : Configurer la génération des rapports Markdown
Assurez-vous que la fonction de génération des rapports Markdown est correctement appelée depuis `odoo_extractor.py`.
1. **Vérifiez que le code suivant est présent** dans `odoo_extractor.py` dans la section de la commande "batch" :
```python
# Générer des rapports Markdown pour chaque ticket extrait
if not args.no_md:
print("Génération des rapports Markdown pour les tickets extraits...")
processed_tickets = result.get("processed_tickets", [])
for ticket_info in processed_tickets:
ticket_dir = ticket_info.get("output_dir")
if ticket_dir and os.path.exists(ticket_dir):
json_file = os.path.join(ticket_dir, "all_messages.json")
if os.path.exists(json_file):
md_file_path = os.path.join(ticket_dir, "rapport.md")
print(f"Génération du rapport Markdown pour {ticket_info.get('code')}...")
if create_markdown_from_json(json_file, md_file_path):
print(f"Rapport Markdown créé: {md_file_path}")
else:
print(f"Échec de la génération du rapport Markdown pour {ticket_info.get('code')}")
```
### Récapitulatif des modifications à apporter
#### 1. Dans `odoo/batch_ticket_manager.py` :
```python
# Avant
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
batch_dir = os.path.join(base_output_dir, f"batch_{timestamp}")
ensure_dir(batch_dir)
# Après
ensure_dir(base_output_dir)
batch_dir = base_output_dir # Pour compatibilité avec le reste du code
```
```python
# Avant
ticket_dir = os.path.join(batch_dir, f"ticket_{ticket_code}")
# Après
timestamp_ticket = datetime.now().strftime("%Y%m%d_%H%M%S")
ticket_dir = os.path.join(base_output_dir, f"{ticket_code}_{timestamp_ticket}")
ensure_dir(ticket_dir)
```
```python
# Avant
batch_summary_path = os.path.join(batch_dir, "batch_summary.json")
# Après
batch_summary_path = os.path.join(base_output_dir, f"batch_summary_{timestamp}.json")
```
#### 2. Dans `odoo/retrieve_tickets_batch.py` :
Supprimez ou commentez la section de code qui duplique le traitement des tickets (lignes 100-150 environ).
Après ces modifications, les deux fonctions d'extraction devraient avoir un comportement cohérent :
- Les tickets seront extraits directement dans le répertoire output
- Les rapports JSON et Markdown seront générés correctement
- La fonction qui évite de recharger les tickets déjà extraits sera préservée
Est-ce que ces instructions sont suffisamment claires ? Voulez-vous que je détaille davantage une partie spécifique ?
---
_**User**_
il faut peut-être revoir la gestion batch dans ces 3 fichiers il y a peut être des éléments inutiles que l'on pourrait simplifier
---
_**Assistant**_
Vous avez raison, il y a plusieurs éléments redondants et des optimisations possibles dans les trois fichiers. Voici une analyse de la situation et mes recommandations pour simplifier le système :
### Analyse des problèmes
1. **Duplication de code** : Il y a du code qui s'exécute en double entre `batch_ticket_manager.py` et `retrieve_tickets_batch.py`, notamment pour l'extraction des tickets.
2. **Structure de répertoires incohérente** : Les tickets extraits sont actuellement placés dans un sous-répertoire `batch_timestamp` plutôt que directement dans `output`.
3. **Flux de traitement complexe** : Le flux d'extraction des tickets est fragmenté entre plusieurs fichiers, ce qui rend difficile le suivi du processus.
### Plan de simplification
Voici comment nous pouvons simplifier :
#### 1. Modifications dans `batch_ticket_manager.py`
```python
# Remplacer
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
batch_dir = os.path.join(base_output_dir, f"batch_{timestamp}")
ensure_dir(batch_dir)
# Par
ensure_dir(base_output_dir)
batch_dir = base_output_dir # Pour compatibilité
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
```
```python
# Remplacer
ticket_dir = os.path.join(batch_dir, f"ticket_{ticket_code}")
# Par
timestamp_ticket = datetime.now().strftime("%Y%m%d_%H%M%S")
ticket_dir = os.path.join(base_output_dir, f"{ticket_code}_{timestamp_ticket}")
```
```python
# Remplacer
processed_tickets.append({
"code": ticket_code,
"id": ticket.get("id"),
"name": ticket.get("name"),
"dir": ticket_dir,
"messages_count": result.get("messages_count", 0),
"attachments_count": result.get("attachments_count", 0)
})
# Par
processed_tickets.append({
"code": ticket_code,
"id": ticket.get("id"),
"name": ticket.get("name"),
"output_dir": ticket_dir, # Modifier la clé pour harmoniser avec retrieve_tickets_batch.py
"messages_count": result.get("messages_count", 0),
"attachments_count": result.get("attachments_count", 0)
})
```
```python
# Remplacer
batch_summary_path = os.path.join(batch_dir, "batch_summary.json")
# Par
batch_summary_path = os.path.join(base_output_dir, f"batch_summary_{timestamp}.json")
```
#### 2. Simplifier `retrieve_tickets_batch.py`
Cette fonction contient une duplication : elle appelle `batch_manager.extract_tickets_batch()` puis refait une extraction de tickets. Nous devrions supprimer ce code dupliqué.
```python
# Supprimer ou commenter ce bloc
processed_tickets =[]
for ticket in result.get('tickets', []):
ticket_code = ticket['code']
if not ticket_code:
continue
# Créer un dossier spécifique pour ce ticket
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
ticket_dir = os.path.join(base_output_dir, f"{ticket_code}_{timestamp}")
os.makedirs(ticket_dir, exist_ok=True)
# Extraire les données du ticket, y compris les pièces jointes
ticket_result = ticket_manager.extract_ticket_data(ticket_code, ticket_dir)
# ... reste du code ...
```
Au lieu de cela, nous pouvons simplement récupérer les tickets déjà traités :
```python
# Ajouter ce bloc à la place
# Les tickets et leurs données sont déjà traités par batch_manager.extract_tickets_batch()
processed_tickets = result.get("processed_tickets", [])
```
#### 3. Assurer la cohérence dans `odoo_extractor.py`
Le fichier `odoo_extractor.py` semble déjà bien configuré pour générer des rapports Markdown. Nous devrions juste nous assurer que les noms de champs correspondent à ce qui est retourné par `batch_ticket_manager.py` (notamment vérifier si c'est "dir" ou "output_dir").
### Plan d'action
1. Modifier d'abord `batch_ticket_manager.py` pour changer la structure des répertoires et normaliser les noms de champs
2. Ensuite, simplifier `retrieve_tickets_batch.py` en supprimant le code redondant
3. Enfin, vérifier la cohérence dans `odoo_extractor.py`
Voulez-vous que je vous guide étape par étape pour effectuer ces modifications ?
---
_**User**_
je te laisse la main pour corriger ces problèmes de répertoires et de génération de rapport markdown et json comme pour retrieve_ticket.py
---
_**Assistant**_
Je vais maintenant corriger les problèmes identifiés dans les fichiers. Commençons par les modifications du gestionnaire de lots de tickets.
### Étape 1 : Modifier le fichier batch_ticket_manager.py
---
```diff
# Créer le répertoire principal de sortie
+ ensure_dir(base_output_dir)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
- batch_dir = os.path.join(base_output_dir, f"batch_{timestamp}")
- ensure_dir(batch_dir)
# Charger les tickets déjà extraits
logging.info(f"Traitement du ticket {ticket_code}...")
- # Créer un sous-répertoire pour ce ticket
- ticket_dir = os.path.join(batch_dir, f"ticket_{ticket_code}")
+ # Créer un répertoire spécifique pour ce ticket avec timestamp
+ timestamp_ticket = datetime.now().strftime("%Y%m%d_%H%M%S")
+ ticket_dir = os.path.join(base_output_dir, f"{ticket_code}_{timestamp_ticket}")
+ ensure_dir(ticket_dir)
try:
"id": ticket.get("id"),
"name": ticket.get("name"),
- "dir": ticket_dir,
+ "output_dir": ticket_dir, # Changé de "dir" à "output_dir" pour cohérence
"messages_count": result.get("messages_count", 0),
"attachments_count": result.get("attachments_count", 0)
batch_summary = {
"timestamp": timestamp,
- "batch_dir": batch_dir,
+ "batch_dir": base_output_dir,
"search_criteria": {
"domain": domain,
}
- batch_summary_path = os.path.join(batch_dir, "batch_summary.json")
+ batch_summary_path = os.path.join(base_output_dir, f"batch_summary_{timestamp}.json")
save_json(batch_summary, batch_summary_path)
logging.info(f"Tickets en échec: {len(failed_tickets)}")
logging.info(f"Tickets exclus par tag 'BRG-LAB WIN': {len(brg_lab_win_tickets)}") # Message pour les tickets exclus
- logging.info(f"Répertoire de sortie: {batch_dir}")
+ logging.info(f"Répertoire de sortie: {base_output_dir}")
return {
"status": "success",
- "batch_dir": batch_dir,
+ "batch_dir": base_output_dir,
"processed": len(processed_tickets),
"skipped": len(skipped_tickets),
"skipped_tickets": skipped_tickets,
"failed_tickets": failed_tickets,
- "excluded_tickets": brg_lab_win_tickets # Liste des tickets exclus
+ "excluded_tickets": brg_lab_win_tickets, # Liste des tickets exclus
+ "tickets": tickets # Ajouter la liste complète des tickets pour compatibilité avec le code existant
}
```
---
### Étape 2 : Simplifier le fichier retrieve_tickets_batch.py en supprimant le code redondant
Maintenant, simplifions le fichier `retrieve_tickets_batch.py` en supprimant la partie qui duplique l'extraction des tickets.
---
```diff
)
- processed_tickets =[]
- for ticket in result.get('tickets', []):
- ticket_code = ticket['code']
- if not ticket_code:
- continue
-
- # Créer un dossier spécifique pour ce ticket
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
- ticket_dir = os.path.join(base_output_dir, f"{ticket_code}_{timestamp}")
- os.makedirs(ticket_dir, exist_ok=True)
-
- # Extraire les données du ticket, y compris les pièces jointes
- ticket_result = ticket_manager.extract_ticket_data(ticket_code, ticket_dir)
-
- if ticket_result:
- # Ajouter le ticket à la liste des tickets traités
- processed_tickets.append({
- "code": ticket_code,
- "name": ticket.get('name'),
- "output_dir": ticket_dir,
- "messages_count": ticket_result.get("messages_count", 0),
- "attachments_count": ticket_result.get("attachments_count",0)
- })
-
- # Créer un résumé d'extraction pour ce ticket
- summary = {
- "timestamp": timestamp,
- "ticket_code": ticket_code,
- "output_directory": ticket_dir,
- "message_count": ticket_result.get("messages_count", 0),
- "attachment_count": ticket_result.get("attachments_count", 0),
- "files_created": [
- os.path.basename(ticket_result.get("ticket_info", "")),
- os.path.basename(ticket_result.get("ticket_summary", "")),
- os.path.basename(ticket_result.get("messages_file", "")),
- os.path.basename(ticket_result.get("ticket_data_file", ""))
- ]
- }
-
- summary_path = os.path.join(ticket_dir, "extraction_summary.json")
- with open(summary_path, 'w', encoding='utf-8') as f:
- json.dump(summary, f, indent=2, ensure_ascii=False)
-
- # Ajouter les tickets traités au résultat
- result["processed_tickets"] = processed_tickets
# Afficher le résumé
log_separator()
```
---
### Étape 3 : Mettre à jour le fichier odoo_extractor.py pour assurer la cohérence
Maintenant, vérifions et ajustons le fichier `odoo_extractor.py` pour s'assurer que les noms de champs et la génération des rapports Markdown sont cohérents.
---
Read file: odoo_extractor.py
---
Le fichier `odoo_extractor.py` semble déjà bien configuré pour gérer la génération des rapports Markdown. Il utilise le bon nom de champ `output_dir` pour localiser les répertoires de tickets et possède déjà la logique pour générer les rapports.
Maintenant, testons les modifications apportées pour vérifier que tout fonctionne correctement.
---
```bash
cd /home/fgras-ca/llm-ticket3 && python odoo_extractor.py batch --project 3 --stage 8 --limit 5 --verbose
```
```
Dict[str, Any]:
"""
Charge le fichier de configuration.
Args:
config_file: Chemin vers le fichier de configuration
Returns:
Dictionnaire contenant la configuration
"""
try:
with open(config_file, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
logging.error(f"Erreur lors du chargement du fichier de configuration: {e}")
return {}
def get_auth_manager(config_file: str = "config.json", force_new: bool = False) -> Optional[AuthManager]:
"""
Obtient une instance unique du gestionnaire d'authentification Odoo.
Args:
config_file: Chemin vers le fichier de configuration
force_new: Force la création d'une nouvelle instance
Returns:
Instance du AuthManager ou None en cas d'erreur
"""
global _auth_manager_instance
# Si une instance existe déjà et qu'on ne force pas la création, la retourner
if _auth_manager_instance is not None and not force_new:
return _auth_manager_instance
# Charger la configuration
config = load_config(config_file)
# Extraire les informations de connexion
odoo_config = config.get("odoo", {})
url = odoo_config.get("url")
db = odoo_config.get("db")
username = odoo_config.get("username")
api_key = odoo_config.get("api_key")
if not all([url, db, username, api_key]):
logging.error("Informations de connexion Odoo manquantes dans le fichier de configuration")
return None
# Créer une nouvelle instance
try:
auth_manager = AuthManager(
url=url,
db=db,
username=username,
api_key=api_key
)
# Se connecter à Odoo
if not auth_manager.login():
logging.error("Échec de l'authentification à Odoo")
return None
# Stocker l'instance pour les appels futurs
_auth_manager_instance = auth_manager
return auth_manager
except Exception as e:
logging.exception(f"Erreur lors de l'initialisation du gestionnaire d'authentification: {e}")
return None
def get_output_dir(config_file: str = "config.json", subdir: Optional[str] = None) -> str:
"""
Obtient le répertoire de sortie à partir de la configuration.
Args:
config_file: Chemin vers le fichier de configuration
subdir: Sous-répertoire à ajouter au chemin (optionnel)
Returns:
Chemin du répertoire de sortie
"""
config = load_config(config_file)
output_dir = config.get("output_dir", "output")
if subdir:
return os.path.join(output_dir, subdir)
return output_dir
```
## Étape 2: Modifier les fichiers utilisant l'authentification
### A. D'abord, examinons `retrieve_ticket.py`:
---
Read file: odoo/retrieve_ticket.py
---
### B. Puis `retrieve_tickets_batch.py`:
---
Read file: odoo/retrieve_tickets_batch.py
---
## Étape 3: Modification des fichiers pour utiliser le connecteur centralisé
### A. Mettons à jour `retrieve_ticket.py`:
```python:odoo/retrieve_ticket.py
#!/usr/bin/env python3
import os
import sys
import json
import logging
import argparse
from datetime import datetime
from .ticket_manager import TicketManager
from .odoo_connector import get_auth_manager, get_output_dir
from core.utils import setup_logging, log_separator
def retrieve_ticket(ticket_code, output=None, config_file="config.json", verbose=False):
# Configurer la journalisation
log_level = logging.DEBUG if verbose else logging.INFO
setup_logging(log_level, "retrieve_ticket.log")
# Obtenir le gestionnaire d'authentification
auth_manager = get_auth_manager(config_file)
if not auth_manager:
logging.error("Impossible d'obtenir le gestionnaire d'authentification Odoo")
return None
# Définir le répertoire de sortie
output_base = output or get_output_dir(config_file)
output_dir = os.path.join(output_base, f"ticket_{ticket_code}")
# Créer le répertoire de sortie spécifique au ticket
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
ticket_dir = os.path.join(output_dir, f"{ticket_code}_{timestamp}")
os.makedirs(ticket_dir, exist_ok=True)
logging.info(f"Extraction du ticket {ticket_code}")
log_separator()
try:
# Extraire les données du ticket
ticket_manager = TicketManager(auth_manager)
result = ticket_manager.extract_ticket_data(ticket_code, ticket_dir)
if not result:
logging.error(f"Échec de l'extraction du ticket {ticket_code}")
return None
# Afficher le résumé
log_separator()
logging.info(f"Extraction terminée avec succès")
logging.info(f"Ticket: {ticket_code}")
logging.info(f"Répertoire: {ticket_dir}")
logging.info(f"Messages traités: {result.get('messages_count', 0)}")
logging.info(f"Pièces jointes: {result.get('attachments_count', 0)}")
log_separator()
# Générer un rapport de fin
summary = {
"timestamp": timestamp,
"ticket_code": ticket_code,
"output_directory": ticket_dir,
"message_count": result.get("messages_count", 0),
"attachment_count": result.get("attachments_count", 0),
"files_created": [
os.path.basename(result.get("ticket_info", "")),
os.path.basename(result.get("ticket_summary", "")),
os.path.basename(result.get("messages_file", "")),
os.path.basename(result.get("ticket_data_file", ""))
]
}
summary_path = os.path.join(ticket_dir, "extraction_summary.json")
with open(summary_path, 'w', encoding='utf-8') as f:
json.dump(summary, f, indent=2, ensure_ascii=False)
print(f"\nExtraction du ticket {ticket_code} terminée avec succès.")
print(f"Les données ont été sauvegardées dans: {ticket_dir}")
return ticket_dir
except Exception as e:
logging.exception(f"Une erreur est survenue: {e}")
return None
def parse_arguments():
parser = argparse.ArgumentParser(description="Récupère un ticket Odoo par son code et extrait ses données.")
parser.add_argument("ticket_code", help="Code du ticket à extraire")
parser.add_argument("--output", "-o", help="Répertoire de sortie", default=None)
parser.add_argument("--config", "-c", help="Fichier de configuration", default="config.json")
parser.add_argument("--verbose", "-v", action="store_true", help="Mode verbeux")
return parser.parse_args()
def main():
args = parse_arguments()
result = retrieve_ticket(
ticket_code=args.ticket_code,
output=args.output,
config_file=args.config,
verbose=args.verbose
)
if not result:
sys.exit(1)
if __name__ == "__main__":
main()
```
### B. Mettons à jour `retrieve_tickets_batch.py`:
```python:odoo/retrieve_tickets_batch.py
#!/usr/bin/env python3
"""
Script pour extraire des lots de tickets Odoo par critères (projet, statut, etc.).
Ce script permet d'automatiser la récupération de nombreux tickets en évitant
de recharger des tickets déjà traités.
"""
import os
import sys
import json
import logging
import argparse
from datetime import datetime
from .ticket_manager import TicketManager
from .batch_ticket_manager import BatchTicketManager
from .odoo_connector import get_auth_manager, get_output_dir, load_config
from core.utils import setup_logging, log_separator
def retrieve_tickets_batch(domain=None, limit=50, offset=0, output=None, config_file="config.json",
verbose=False, skip_existing=True):
"""
Extrait un lot de tickets répondant à des critères spécifiques.
Args:
domain: Liste de critères de recherche au format Odoo
ex: [["project_id", "=", 1], ["stage_id", "=", 5]]
limit: Nombre maximal de tickets à extraire
offset: Index de départ pour la pagination
output: Répertoire de sortie
config_file: Chemin vers le fichier de configuration
verbose: Mode verbeux pour les logs
skip_existing: Ignorer les tickets déjà extraits
Returns:
Dictionnaire avec le résultat de l'opération ou None en cas d'erreur
"""
# Configurer la journalisation
log_level = logging.DEBUG if verbose else logging.INFO
setup_logging(log_level, "retrieve_tickets_batch.log")
# Obtenir le gestionnaire d'authentification
auth_manager = get_auth_manager(config_file)
if not auth_manager:
logging.error("Impossible d'obtenir le gestionnaire d'authentification Odoo")
return None
# Définir le répertoire de sortie
base_output_dir = output or get_output_dir(config_file)
logging.info(f"Démarrage de l'extraction par lot avec les critères: {domain}")
logging.info(f"Limite: {limit} tickets, offset: {offset}")
log_separator()
try:
# Initialiser les gestionnaires
ticket_manager = TicketManager(auth_manager)
batch_manager = BatchTicketManager(ticket_manager)
# Vérifier combien de tickets correspondent aux critères
total_count = batch_manager.get_ticket_count(domain)
logging.info(f"Nombre total de tickets correspondant aux critères: {total_count}")
if total_count == 0:
logging.warning("Aucun ticket ne correspond aux critères spécifiés")
return {"status": "warning", "message": "Aucun ticket ne correspond aux critères"}
# Extraire les tickets par lot
result = batch_manager.extract_tickets_batch(
domain=domain,
limit=limit,
offset=offset,
base_output_dir=base_output_dir,
skip_existing=skip_existing
)
# Afficher le résumé
log_separator()
logging.info(f"Extraction par lot terminée")
logging.info(f"Tickets traités: {result.get('processed', 0)}")
logging.info(f"Tickets ignorés: {result.get('skipped', 0)}")
logging.info(f"Tickets exclus par tag 'BRG-LAB WIN': {result.get('excluded_by_tag', 0)}")
logging.info(f"Répertoire: {result.get('batch_dir', '')}")
log_separator()
# Afficher les tickets exclus à cause du tag "BRG-LAB WIN"
excluded_tickets = result.get('excluded_tickets', [])
if excluded_tickets:
print("\nLes tickets suivants ont été exclus car ils possèdent le tag 'BRG-LAB WIN':")
for ticket in excluded_tickets:
print(f"- {ticket.get('code', 'Code inconnu')}: {ticket.get('name', 'Nom inconnu')}")
print(f"Total: {len(excluded_tickets)} ticket(s) exclu(s)")
print("Ces tickets sont exclus de l'extraction conformément à la politique.")
return result
except Exception as e:
logging.exception(f"Une erreur est survenue: {e}")
return None
def list_projects(config_file="config.json"):
"""
Liste tous les projets disponibles dans Odoo.
Args:
config_file: Chemin vers le fichier de configuration
Returns:
Liste des projets ou None en cas d'erreur
"""
# Obtenir le gestionnaire d'authentification
auth_manager = get_auth_manager(config_file)
if not auth_manager:
print("Impossible d'obtenir le gestionnaire d'authentification Odoo")
return None
try:
# Récupérer la liste des projets
ticket_manager = TicketManager(auth_manager)
batch_manager = BatchTicketManager(ticket_manager)
projects = batch_manager.get_projects()
if not projects:
print("Aucun projet trouvé")
return None
print("\nListe des projets disponibles:")
print("-" * 50)
for project in projects:
print(f"ID: {project['id']}, Nom: {project['name']}")
print("-" * 50)
return projects
except Exception as e:
print(f"Une erreur est survenue: {e}")
return None
def list_stages(config_file="config.json"):
"""
Liste tous les statuts (étapes) disponibles dans Odoo.
Args:
config_file: Chemin vers le fichier de configuration
Returns:
Liste des étapes ou None en cas d'erreur
"""
# Obtenir le gestionnaire d'authentification
auth_manager = get_auth_manager(config_file)
if not auth_manager:
print("Impossible d'obtenir le gestionnaire d'authentification Odoo")
return None
try:
# Récupérer la liste des étapes
ticket_manager = TicketManager(auth_manager)
batch_manager = BatchTicketManager(ticket_manager)
stages = batch_manager.get_stages()
if not stages:
print("Aucune étape trouvée")
return None
print("\nListe des étapes disponibles:")
print("-" * 50)
for stage in stages:
print(f"ID: {stage['id']}, Nom: {stage['name']}")
print("-" * 50)
return stages
except Exception as e:
print(f"Une erreur est survenue: {e}")
return None
# [Le reste du fichier reste inchangé]
```
### C. Mettons à jour `odoo_extractor.py`:
---
Read file: odoo_extractor.py
---
Voici la version mise à jour de `odoo_extractor.py` :
```python:odoo_extractor.py
"""
Script pour récupérer les tickets depuis odoo.
Extraction d'un ticket unique ou d'un groupe de tickets.
"""
import os
import sys
import json
import argparse
from datetime import datetime
from core.utils import setup_logging, log_separator
from formatters.json_to_markdown import create_markdown_from_json
from odoo.retrieve_ticket import retrieve_ticket
from odoo.retrieve_tickets_batch import retrieve_tickets_batch, parse_domain, list_projects, list_stages
from odoo.odoo_connector import get_auth_manager, load_config
#importation de l'extracteur d'images HTML (optionnel)
try:
from utils.image_extractor import extract_images_from_ticket
from utils.image_extractor.extract_all_images import enhance_ticket_data_with_images
HTML_IMAGE_EXTRACTOR_AVAILABLE = True
except ImportError:
HTML_IMAGE_EXTRACTOR_AVAILABLE = False
def parse_arguments():
"""Parse les arguments de ligne de commande."""
parser = argparse.ArgumentParser(description="Extrait des données depuis Odoo (ticket unique ou par lots).")
# Paramètres communs
parser.add_argument("--config", "-c", help="Fichier de configuration", default="config.json")
parser.add_argument("--verbose", "-v", action="store_true", help="Mode verbeux")
parser.add_argument("--output", "-o", help="Répertoire de sortie", default=None)
parser.add_argument("--no-md", action="store_true", help="Désactiver la génération automatique du fichier Markdown")
parser.add_argument("--extract-html-images", action="store_true", help="Activer l'extraction des images intégrées dans le HTML")
# Sous-parseurs pour les différentes commandes
subparsers = parser.add_subparsers(dest="command", help="Commande à exécuter")
# Commande pour extraire un ticket unique
single_parser = subparsers.add_parser("ticket", help="Extrait un ticket unique par son code")
single_parser.add_argument("ticket_code", help="Code du ticket à extraire")
# Commande pour extraire un lot de tickets
batch_parser = subparsers.add_parser("batch", help="Extrait un lot de tickets")
batch_parser.add_argument("--domain", "-d", help="Critères de recherche (format: field1:op1:value1;field2:op2:value2)")
batch_parser.add_argument("--project", "-p", type=int, help="Id du projet")
batch_parser.add_argument("--stage", "-s", type=int, help="Id de l'étape/statut")
batch_parser.add_argument("--limit", "-l", type=int, default=50, help="Nombre maximum de tickets à extraire")
batch_parser.add_argument("--offset", type=int, default=0, help="Index de départ pour la pagination")
batch_parser.add_argument("--include-existing", action="store_true", help="Inclure les tickets déjà extraits")
# Commande pour lister les projets
projects_parser = subparsers.add_parser("projects", help="Liste les projets disponibles")
# Commande pour lister les étapes/statuts
stages_parser = subparsers.add_parser("stages", help="Liste les étapes/statuts disponibles")
return parser.parse_args()
def main():
"""Point d'entrée principal du script."""
args = parse_arguments()
# Vérifier si le fichier de configuration existe
if not os.path.exists(args.config):
print(f"Erreur: Fichier de configuration '{args.config}' introuvable.")
sys.exit(1)
# Configurer la journalisation
log_level = "DEBUG" if args.verbose else "INFO"
setup_logging(log_level)
# Vérifier la connexion Odoo une seule fois
if not get_auth_manager(args.config):
print("Impossible de se connecter à Odoo. Vérifiez vos informations de connexion.")
sys.exit(1)
# Traiter la commande appropriée
if args.command == "ticket":
# Extraction d'un ticket unique
result = retrieve_ticket(
ticket_code=args.ticket_code,
output=args.output,
config_file=args.config,
verbose=args.verbose
)
if not result:
print(f"Echec de l'extraction du ticket {args.ticket_code}")
sys.exit(1)
if not args.no_md:
# Génération du rapport Markdown
json_file = os.path.join(result, "all_messages.json")
if os.path.exists(json_file):
md_file_path = os.path.join(result, "rapport.md")
print(f"Génération du rapport Markdown...")
if create_markdown_from_json(json_file, md_file_path):
print(f"Rapport Markdown créé: {md_file_path}")
else:
print("Echec de la génération du rapport Markdown.")
elif args.command == "batch":
# Extraction par lot
domain = []
if args.domain:
domain = parse_domain(args.domain)
# Ajouter project_id et stage_id si spécifiés
if args.project:
domain.append(["project_id", "=", args.project])
if args.stage:
domain.append(["stage_id", "=", args.stage])
print(f"Démarrage de l'extraction par lot avec les critères: {domain}")
print(f"Limite: {args.limit} tickets, offset: {args.offset}")
result = retrieve_tickets_batch(
domain=domain,
limit=args.limit,
offset=args.offset,
output=args.output,
config_file=args.config,
verbose=args.verbose,
skip_existing=not args.include_existing
)
if not result or result.get("status") == "error":
print("Echec de l'extraction par lots")
sys.exit(1)
# Afficher un résumé des résultats
print(f"Extraction par lot terminée")
print(f"Tickets traités: {result.get('processed', 0)}")
print(f"Tickets ignorés: {result.get('skipped', 0)}")
# Générer des rapports Markdown pour chaque ticket extrait
if not args.no_md:
print("Génération des rapports Markdown pour les tickets extraits...")
processed_tickets = result.get("processed_tickets", [])
for ticket_info in processed_tickets:
ticket_dir = ticket_info.get("output_dir")
if ticket_dir and os.path.exists(ticket_dir):
json_file = os.path.join(ticket_dir, "all_messages.json")
if os.path.exists(json_file):
md_file_path = os.path.join(ticket_dir, "rapport.md")
print(f"Génération du rapport Markdown pour {ticket_info.get('code')}...")
if create_markdown_from_json(json_file, md_file_path):
print(f"Rapport Markdown créé: {md_file_path}")
else:
print(f"Echec de la génération du rapport Markdown pour {ticket_info.get('code')}")
else:
print(f"Fichier de messages introuvable pour {ticket_info.get('code')}")
else:
print(f"Répertoire introuvable pour {ticket_info.get('code')}")
elif args.command == "projects":
# Lister les projets
list_projects(args.config)
elif args.command == "stages":
# Lister les étapes/statuts
list_stages(args.config)
else:
parser = argparse.ArgumentParser(description="Extrait des données depuis Odoo (ticket unique ou par lots).")
parser.print_help()
sys.exit(1)
if __name__ == "__main__":
main()
```
## Étape 4: Vérification des modifications
Voici un résumé des modifications effectuées:
1. **Nouveau fichier `odoo/odoo_connector.py`**:
- Contient les fonctions pour obtenir une instance unique du gestionnaire d'authentification
- Encapsule la logique de lecture de configuration et d'initialisation
- Fournit des fonctions utilitaires comme `get_output_dir` pour la cohérence
2. **Modifications dans `retrieve_ticket.py`**:
- Suppression de la logique de connexion Odoo
- Utilisation des fonctions du nouveau module `odoo_connector`
3. **Modifications dans `retrieve_tickets_batch.py`**:
- Suppression des initialisations redondantes du gestionnaire d'authentification
- Utilisation des fonctions du nouveau module `odoo_connector`
4. **Modifications dans `odoo_extractor.py`**:
- Simplification des imports
- Utilisation des fonctions centralisées pour l'authentification
Ces modifications garantissent que:
- L'authentification est centralisée dans un seul endroit
- Une seule instance d'AuthManager est utilisée par le programme
- Le code est plus maintenu, évitant les duplications
La prochaine fois que vous devrez modifier la logique d'authentification, vous n'aurez qu'à le faire dans `odoo_connector.py` et tous les fichiers qui l'utilisent bénéficieront automatiquement des changements.
---
_**User**_
j'ai déjà il me semble un fichier auth_manager.py qui s'occupe de l'authentification il faudrait se baser sur lui pour réaliser les modifications voulus
---
_**Assistant**_
Vous avez raison, votre fichier `auth_manager.py` gère déjà parfaitement l'authentification à Odoo. Au lieu de créer un nouveau fichier, nous allons plutôt étendre ce fichier existant pour centraliser la création des instances d'authentification.
Voici comment procéder:
## Étape 1: Modifier `auth_manager.py` pour ajouter la centralisation
Plutôt que de créer un nouveau fichier, nous allons ajouter des fonctions au fichier `auth_manager.py` existant:
```python:odoo/auth_manager.py
import json
import logging
import os
import requests
from typing import Dict, Any, Optional
# Variable globale pour stocker l'instance du gestionnaire d'authentification
_auth_manager_instance = None
class AuthManager:
"""
Gestionnaire d'authentification pour l'API Odoo.
Gère la connexion et les appels RPC à l'API Odoo.
"""
def __init__(self, url: str, db: str, username: str, api_key: str):
"""
Initialise le gestionnaire d'authentification.
Args:
url: URL de l'instance Odoo
db: Nom de la base de données Odoo
username: Nom d'utilisateur pour la connexion
api_key: Clé API ou mot de passe pour l'authentification
"""
self.url = url.rstrip('/')
self.db = db
self.username = username
self.api_key = api_key
self.uid = None
self.session = requests.Session()
self.session.headers.update({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
self.max_retries = 3
self.timeout = 30 # secondes
def login(self) -> bool:
"""
Se connecte à l'API Odoo en utilisant les identifiants fournis.
Returns:
True si l'authentification réussie, False sinon
"""
try:
logging.info(f"Tentative de connexion à {self.url} avec l'utilisateur {self.username}")
endpoint = '/web/session/authenticate'
payload = {
"jsonrpc": "2.0",
"params": {
"db": self.db,
"login": self.username,
"password": self.api_key
}
}
response = self.session.post(
f"{self.url}{endpoint}",
data=json.dumps(payload),
timeout=self.timeout
)
response.raise_for_status()
result = response.json()
if 'error' in result:
error = result['error']
logging.error(f"Erreur d'authentification: {error.get('message', 'Erreur inconnue')}")
return False
self.uid = result.get('result', {}).get('uid')
if not self.uid:
logging.error("Erreur: UID non trouvé dans la réponse d'authentification")
return False
logging.info(f"Authentification réussie. UID: {self.uid}")
return True
except requests.RequestException as e:
logging.error(f"Erreur de connexion à l'API Odoo: {e}")
return False
except json.JSONDecodeError as e:
logging.error(f"Erreur de décodage JSON: {e}")
return False
except Exception as e:
logging.error(f"Erreur inattendue lors de l'authentification: {e}")
return False
def _rpc_call(self, endpoint: str, params: Dict[str, Any], retry_count: int = 0) -> Any:
"""
Effectue un appel RPC à l'API Odoo.
Args:
endpoint: Point de terminaison de l'API
params: Paramètres de l'appel
retry_count: Nombre de tentatives actuelles (pour les nouvelles tentatives)
Returns:
Résultat de l'appel RPC ou None en cas d'erreur
"""
if not self.uid and endpoint != '/web/session/authenticate':
logging.warning("Tentative d'appel RPC sans être authentifié. Reconnexion...")
if not self.login():
logging.error("Échec de la reconnexion")
return None
try:
payload = {
"jsonrpc": "2.0",
"params": params
}
response = self.session.post(
f"{self.url}{endpoint}",
data=json.dumps(payload),
timeout=self.timeout
)
response.raise_for_status()
result = response.json()
if 'error' in result:
error = result['error']
error_msg = error.get('message', 'Erreur inconnue')
error_data = error.get('data', {})
error_name = error_data.get('name', 'UnknownError')
logging.error(f"Erreur RPC: {error_name} - {error_msg}")
# Gérer les erreurs d'authentification
if "session expired" in error_msg or "Access denied" in error_msg:
if retry_count < self.max_retries:
logging.info("Session expirée, nouvelle tentative d'authentification...")
if self.login():
return self._rpc_call(endpoint, params, retry_count + 1)
return None
return result.get('result')
except requests.RequestException as e:
logging.error(f"Erreur de requête RPC: {e}")
if retry_count < self.max_retries:
logging.info(f"Nouvelle tentative ({retry_count + 1}/{self.max_retries})...")
return self._rpc_call(endpoint, params, retry_count + 1)
return None
except json.JSONDecodeError as e:
logging.error(f"Erreur de décodage JSON dans la réponse RPC: {e}")
return None
except Exception as e:
logging.error(f"Erreur inattendue lors de l'appel RPC: {e}")
return None
def search_read(self, model: str, domain: list, fields: list, **kwargs) -> list:
"""
Effectue une recherche et lecture sur le modèle spécifié.
Args:
model: Nom du modèle Odoo
domain: Domaine de recherche (filtres)
fields: Liste des champs à récupérer
**kwargs: Arguments supplémentaires (limit, offset, etc.)
Returns:
Liste des enregistrements trouvés
"""
params = {
"model": model,
"method": "search_read",
"args": [domain, fields],
"kwargs": kwargs
}
return self._rpc_call("/web/dataset/call_kw", params) or []
def read(self, model: str, ids: list, fields: list) -> list:
"""
Lit les enregistrements spécifiés par leurs IDs.
Args:
model: Nom du modèle Odoo
ids: Liste des IDs des enregistrements à lire
fields: Liste des champs à récupérer
Returns:
Liste des enregistrements lus
"""
if not ids:
return []
params = {
"model": model,
"method": "read",
"args": [ids, fields],
"kwargs": {}
}
return self._rpc_call("/web/dataset/call_kw", params) or []
def get_fields(self, model: str) -> Dict[str, Any]:
"""
Récupère les informations sur les champs d'un modèle.
Args:
model: Nom du modèle Odoo
Returns:
Dictionnaire avec les informations sur les champs
"""
params = {
"model": model,
"method": "fields_get",
"args": [],
"kwargs": {}
}
return self._rpc_call("/web/dataset/call_kw", params) or {}
# Fonctions d'aide pour centraliser l'authentification
def load_config(config_file: str = "config.json") -> Dict[str, Any]:
"""
Charge le fichier de configuration.
Args:
config_file: Chemin vers le fichier de configuration
Returns:
Dictionnaire contenant la configuration
"""
try:
with open(config_file, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
logging.error(f"Erreur lors du chargement du fichier de configuration: {e}")
return {}
def get_auth_manager(config_file: str = "config.json", force_new: bool = False) -> Optional[AuthManager]:
"""
Obtient une instance unique du gestionnaire d'authentification Odoo.
Args:
config_file: Chemin vers le fichier de configuration
force_new: Force la création d'une nouvelle instance
Returns:
Instance du AuthManager ou None en cas d'erreur
"""
global _auth_manager_instance
# Si une instance existe déjà et qu'on ne force pas la création, la retourner
if _auth_manager_instance is not None and not force_new:
return _auth_manager_instance
# Charger la configuration
config = load_config(config_file)
# Extraire les informations de connexion
odoo_config = config.get("odoo", {})
url = odoo_config.get("url")
db = odoo_config.get("db")
username = odoo_config.get("username")
api_key = odoo_config.get("api_key")
if not all([url, db, username, api_key]):
logging.error("Informations de connexion Odoo manquantes dans le fichier de configuration")
return None
# Créer une nouvelle instance
try:
auth_manager = AuthManager(
url=url,
db=db,
username=username,
api_key=api_key
)
# Se connecter à Odoo
if not auth_manager.login():
logging.error("Échec de l'authentification à Odoo")
return None
# Stocker l'instance pour les appels futurs
_auth_manager_instance = auth_manager
return auth_manager
except Exception as e:
logging.exception(f"Erreur lors de l'initialisation du gestionnaire d'authentification: {e}")
return None
def get_output_dir(config_file: str = "config.json", subdir: Optional[str] = None) -> str:
"""
Obtient le répertoire de sortie à partir de la configuration.
Args:
config_file: Chemin vers le fichier de configuration
subdir: Sous-répertoire à ajouter au chemin (optionnel)
Returns:
Chemin du répertoire de sortie
"""
config = load_config(config_file)
output_dir = config.get("output_dir", "output")
if subdir:
return os.path.join(output_dir, subdir)
return output_dir
```
## Étape 2: Modifier `retrieve_ticket.py`
```python:odoo/retrieve_ticket.py
#!/usr/bin/env python3
import os
import sys
import json
import logging
import argparse
from datetime import datetime
from .ticket_manager import TicketManager
from .auth_manager import get_auth_manager, get_output_dir, load_config
from core.utils import setup_logging, log_separator
def retrieve_ticket(ticket_code, output=None, config_file="config.json", verbose=False):
# Configurer la journalisation
log_level = logging.DEBUG if verbose else logging.INFO
setup_logging(log_level, "retrieve_ticket.log")
# Obtenir le gestionnaire d'authentification
auth_manager = get_auth_manager(config_file)
if not auth_manager:
logging.error("Impossible d'obtenir le gestionnaire d'authentification Odoo")
return None
# Définir le répertoire de sortie
output_base = output or get_output_dir(config_file)
output_dir = os.path.join(output_base, f"ticket_{ticket_code}")
# Créer le répertoire de sortie spécifique au ticket
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
ticket_dir = os.path.join(output_dir, f"{ticket_code}_{timestamp}")
os.makedirs(ticket_dir, exist_ok=True)
logging.info(f"Extraction du ticket {ticket_code}")
log_separator()
try:
# Extraire les données du ticket
ticket_manager = TicketManager(auth_manager)
result = ticket_manager.extract_ticket_data(ticket_code, ticket_dir)
if not result:
logging.error(f"Échec de l'extraction du ticket {ticket_code}")
return None
# Afficher le résumé
log_separator()
logging.info(f"Extraction terminée avec succès")
logging.info(f"Ticket: {ticket_code}")
logging.info(f"Répertoire: {ticket_dir}")
logging.info(f"Messages traités: {result.get('messages_count', 0)}")
logging.info(f"Pièces jointes: {result.get('attachments_count', 0)}")
log_separator()
# Générer un rapport de fin
summary = {
"timestamp": timestamp,
"ticket_code": ticket_code,
"output_directory": ticket_dir,
"message_count": result.get("messages_count", 0),
"attachment_count": result.get("attachments_count", 0),
"files_created": [
os.path.basename(result.get("ticket_info", "")),
os.path.basename(result.get("ticket_summary", "")),
os.path.basename(result.get("messages_file", "")),
os.path.basename(result.get("ticket_data_file", ""))
]
}
summary_path = os.path.join(ticket_dir, "extraction_summary.json")
with open(summary_path, 'w', encoding='utf-8') as f:
json.dump(summary, f, indent=2, ensure_ascii=False)
print(f"\nExtraction du ticket {ticket_code} terminée avec succès.")
print(f"Les données ont été sauvegardées dans: {ticket_dir}")
return ticket_dir
except Exception as e:
logging.exception(f"Une erreur est survenue: {e}")
return None
def parse_arguments():
parser = argparse.ArgumentParser(description="Récupère un ticket Odoo par son code et extrait ses données.")
parser.add_argument("ticket_code", help="Code du ticket à extraire")
parser.add_argument("--output", "-o", help="Répertoire de sortie", default=None)
parser.add_argument("--config", "-c", help="Fichier de configuration", default="config.json")
parser.add_argument("--verbose", "-v", action="store_true", help="Mode verbeux")
return parser.parse_args()
def main():
args = parse_arguments()
result = retrieve_ticket(
ticket_code=args.ticket_code,
output=args.output,
config_file=args.config,
verbose=args.verbose
)
if not result:
sys.exit(1)
if __name__ == "__main__":
main()
```
## Étape 3: Modifier `retrieve_tickets_batch.py`
```python:odoo/retrieve_tickets_batch.py
#!/usr/bin/env python3
"""
Script pour extraire des lots de tickets Odoo par critères (projet, statut, etc.).
Ce script permet d'automatiser la récupération de nombreux tickets en évitant
de recharger des tickets déjà traités.
"""
import os
import sys
import json
import logging
import argparse
from datetime import datetime
from .ticket_manager import TicketManager
from .batch_ticket_manager import BatchTicketManager
from .auth_manager import get_auth_manager, get_output_dir, load_config
from core.utils import setup_logging, log_separator
def retrieve_tickets_batch(domain=None, limit=50, offset=0, output=None, config_file="config.json",
verbose=False, skip_existing=True):
"""
Extrait un lot de tickets répondant à des critères spécifiques.
Args:
domain: Liste de critères de recherche au format Odoo
ex: [["project_id", "=", 1], ["stage_id", "=", 5]]
limit: Nombre maximal de tickets à extraire
offset: Index de départ pour la pagination
output: Répertoire de sortie
config_file: Chemin vers le fichier de configuration
verbose: Mode verbeux pour les logs
skip_existing: Ignorer les tickets déjà extraits
Returns:
Dictionnaire avec le résultat de l'opération ou None en cas d'erreur
"""
# Configurer la journalisation
log_level = logging.DEBUG if verbose else logging.INFO
setup_logging(log_level, "retrieve_tickets_batch.log")
# Obtenir le gestionnaire d'authentification
auth_manager = get_auth_manager(config_file)
if not auth_manager:
logging.error("Impossible d'obtenir le gestionnaire d'authentification Odoo")
return None
# Définir le répertoire de sortie
base_output_dir = output or get_output_dir(config_file)
logging.info(f"Démarrage de l'extraction par lot avec les critères: {domain}")
logging.info(f"Limite: {limit} tickets, offset: {offset}")
log_separator()
try:
# Initialiser les gestionnaires
ticket_manager = TicketManager(auth_manager)
batch_manager = BatchTicketManager(ticket_manager)
# Vérifier combien de tickets correspondent aux critères
total_count = batch_manager.get_ticket_count(domain)
logging.info(f"Nombre total de tickets correspondant aux critères: {total_count}")
if total_count == 0:
logging.warning("Aucun ticket ne correspond aux critères spécifiés")
return {"status": "warning", "message": "Aucun ticket ne correspond aux critères"}
# Extraire les tickets par lot
result = batch_manager.extract_tickets_batch(
domain=domain,
limit=limit,
offset=offset,
base_output_dir=base_output_dir,
skip_existing=skip_existing
)
# Afficher le résumé
log_separator()
logging.info(f"Extraction par lot terminée")
logging.info(f"Tickets traités: {result.get('processed', 0)}")
logging.info(f"Tickets ignorés: {result.get('skipped', 0)}")
logging.info(f"Tickets exclus par tag 'BRG-LAB WIN': {result.get('excluded_by_tag', 0)}")
logging.info(f"Répertoire: {result.get('batch_dir', '')}")
log_separator()
# Afficher les tickets exclus à cause du tag "BRG-LAB WIN"
excluded_tickets = result.get('excluded_tickets', [])
if excluded_tickets:
print("\nLes tickets suivants ont été exclus car ils possèdent le tag 'BRG-LAB WIN':")
for ticket in excluded_tickets:
print(f"- {ticket.get('code', 'Code inconnu')}: {ticket.get('name', 'Nom inconnu')}")
print(f"Total: {len(excluded_tickets)} ticket(s) exclu(s)")
print("Ces tickets sont exclus de l'extraction conformément à la politique.")
return result
except Exception as e:
logging.exception(f"Une erreur est survenue: {e}")
return None
def list_projects(config_file="config.json"):
"""
Liste tous les projets disponibles dans Odoo.
Args:
config_file: Chemin vers le fichier de configuration
Returns:
Liste des projets ou None en cas d'erreur
"""
# Obtenir le gestionnaire d'authentification
auth_manager = get_auth_manager(config_file)
if not auth_manager:
print("Impossible d'obtenir le gestionnaire d'authentification Odoo")
return None
try:
# Récupérer la liste des projets
ticket_manager = TicketManager(auth_manager)
batch_manager = BatchTicketManager(ticket_manager)
projects = batch_manager.get_projects()
if not projects:
print("Aucun projet trouvé")
return None
print("\nListe des projets disponibles:")
print("-" * 50)
for project in projects:
print(f"ID: {project['id']}, Nom: {project['name']}")
print("-" * 50)
return projects
except Exception as e:
print(f"Une erreur est survenue: {e}")
return None
def list_stages(config_file="config.json"):
"""
Liste tous les statuts (étapes) disponibles dans Odoo.
Args:
config_file: Chemin vers le fichier de configuration
Returns:
Liste des étapes ou None en cas d'erreur
"""
# Obtenir le gestionnaire d'authentification
auth_manager = get_auth_manager(config_file)
if not auth_manager:
print("Impossible d'obtenir le gestionnaire d'authentification Odoo")
return None
try:
# Récupérer la liste des étapes
ticket_manager = TicketManager(auth_manager)
batch_manager = BatchTicketManager(ticket_manager)
stages = batch_manager.get_stages()
if not stages:
print("Aucune étape trouvée")
return None
print("\nListe des étapes disponibles:")
print("-" * 50)
for stage in stages:
print(f"ID: {stage['id']}, Nom: {stage['name']}")
print("-" * 50)
return stages
except Exception as e:
print(f"Une erreur est survenue: {e}")
return None
# [Le reste du fichier reste inchangé]
```
## Étape 4: Modifier `odoo_extractor.py`
```python:odoo_extractor.py
"""
Script pour récupérer les tickets depuis odoo.
Extraction d'un ticket unique ou d'un groupe de tickets.
"""
import os
import sys
import json
import argparse
from datetime import datetime
from core.utils import setup_logging, log_separator
from formatters.json_to_markdown import create_markdown_from_json
from odoo.retrieve_ticket import retrieve_ticket
from odoo.retrieve_tickets_batch import retrieve_tickets_batch, parse_domain, list_projects, list_stages
from odoo.auth_manager import get_auth_manager, load_config
#importation de l'extracteur d'images HTML (optionnel)
try:
from utils.image_extractor import extract_images_from_ticket
from utils.image_extractor.extract_all_images import enhance_ticket_data_with_images
HTML_IMAGE_EXTRACTOR_AVAILABLE = True
except ImportError:
HTML_IMAGE_EXTRACTOR_AVAILABLE = False
def parse_arguments():
"""Parse les arguments de ligne de commande."""
parser = argparse.ArgumentParser(description="Extrait des données depuis Odoo (ticket unique ou par lots).")
# Paramètres communs
parser.add_argument("--config", "-c", help="Fichier de configuration", default="config.json")
parser.add_argument("--verbose", "-v", action="store_true", help="Mode verbeux")
parser.add_argument("--output", "-o", help="Répertoire de sortie", default=None)
parser.add_argument("--no-md", action="store_true", help="Désactiver la génération automatique du fichier Markdown")
parser.add_argument("--extract-html-images", action="store_true", help="Activer l'extraction des images intégrées dans le HTML")
# Sous-parseurs pour les différentes commandes
subparsers = parser.add_subparsers(dest="command", help="Commande à exécuter")
# Commande pour extraire un ticket unique
single_parser = subparsers.add_parser("ticket", help="Extrait un ticket unique par son code")
single_parser.add_argument("ticket_code", help="Code du ticket à extraire")
# Commande pour extraire un lot de tickets
batch_parser = subparsers.add_parser("batch", help="Extrait un lot de tickets")
batch_parser.add_argument("--domain", "-d", help="Critères de recherche (format: field1:op1:value1;field2:op2:value2)")
batch_parser.add_argument("--project", "-p", type=int, help="Id du projet")
batch_parser.add_argument("--stage", "-s", type=int, help="Id de l'étape/statut")
batch_parser.add_argument("--limit", "-l", type=int, default=50, help="Nombre maximum de tickets à extraire")
batch_parser.add_argument("--offset", type=int, default=0, help="Index de départ pour la pagination")
batch_parser.add_argument("--include-existing", action="store_true", help="Inclure les tickets déjà extraits")
# Commande pour lister les projets
projects_parser = subparsers.add_parser("projects", help="Liste les projets disponibles")
# Commande pour lister les étapes/statuts
stages_parser = subparsers.add_parser("stages", help="Liste les étapes/statuts disponibles")
return parser.parse_args()
def main():
"""Point d'entrée principal du script."""
args = parse_arguments()
# Vérifier si le fichier de configuration existe
if not os.path.exists(args.config):
print(f"Erreur: Fichier de configuration '{args.config}' introuvable.")
sys.exit(1)
# Configurer la journalisation
log_level = "DEBUG" if args.verbose else "INFO"
setup_logging(log_level)
# Vérifier la connexion Odoo une seule fois
if not get_auth_manager(args.config):
print("Impossible de se connecter à Odoo. Vérifiez vos informations de connexion.")
sys.exit(1)
# Traiter la commande appropriée
if args.command == "ticket":
# Extraction d'un ticket unique
result = retrieve_ticket(
ticket_code=args.ticket_code,
output=args.output,
config_file=args.config,
verbose=args.verbose
)
if not result:
print(f"Echec de l'extraction du ticket {args.ticket_code}")
sys.exit(1)
if not args.no_md:
# Génération du rapport Markdown
json_file = os.path.join(result, "all_messages.json")
if os.path.exists(json_file):
md_file_path = os.path.join(result, "rapport.md")
print(f"Génération du rapport Markdown...")
if create_markdown_from_json(json_file, md_file_path):
print(f"Rapport Markdown créé: {md_file_path}")
else:
print("Echec de la génération du rapport Markdown.")
elif args.command == "batch":
# [LE RESTE DU CODE RESTE INCHANGÉ]
```
## Résumé des modifications
### 1. Modification de `auth_manager.py`:
- Ajout d'une variable globale `_auth_manager_instance` pour stocker une instance unique
- Ajout de fonctions utilitaires : `load_config`, `get_auth_manager`, `get_output_dir`
### 2. Modification de `retrieve_ticket.py`:
- Suppression de la création directe d'une instance `AuthManager`
- Utilisation de `get_auth_manager` pour obtenir une instance partagée
- Utilisation de `get_output_dir` pour obtenir le répertoire de sortie
### 3. Modification de `retrieve_tickets_batch.py`:
- Idem, utilisation des fonctions centralisées au lieu de créer une instance à chaque fois
### 4. Modification de `odoo_extractor.py`:
- Simplification des imports
- Utilisation des fonctions centralisées
## Avantages de cette approche:
1. Une seule instance d'`AuthManager` est utilisée par toute l'application
2. Le code de connexion est centralisé et facile à modifier
3. Le pattern Singleton évite les connexions multiples et améliore les performances
4. Le fichier `auth_manager.py` reste le point central pour tout ce qui est lié à l'authentification
Ce modèle respecte votre structure de code existante tout en centralisant l'authentification.
---
_**User**_
peux tu effectuer les modifcations
---
_**Assistant**_
Je vais vous aider à effectuer les modifications nécessaires pour centraliser l'authentification Odoo. D'après les erreurs du linter, il y a quelques problèmes dans le fichier `auth_manager.py` que vous avez déjà commencé à modifier. Je vais corriger ces problèmes et effectuer les modifications dans tous les fichiers concernés.
Commençons par corriger le fichier `auth_manager.py` :
---
Cancelled
---
---
---