This commit is contained in:
Ladebeze66 2025-03-18 11:24:17 +01:00
parent ebf80209df
commit 6d21fd5b61
12 changed files with 105 additions and 68 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/odtkit

View File

@ -0,0 +1,2 @@
odoorpc
bs4

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,5 @@
from ticket_manager import TicketManager from ticket_manager import TicketManager
from utils import get_user_choice
# Initialisation de l'objet # Initialisation de l'objet
ticket_manager = TicketManager() ticket_manager = TicketManager()

View File

@ -1,7 +1,7 @@
from odoo_connection import OdooConnection from odoo_connection import OdooConnection
import os import os
import json import json
from utils import save_json, ensure_export_directory from utils import save_json, ensure_export_directory, print_error
from config import EXPORT_DIR from config import EXPORT_DIR
from data_filter import filter_ticket_data from data_filter import filter_ticket_data
@ -14,32 +14,57 @@ class TicketManager:
self.odoo = self.conn.get_odoo_instance() self.odoo = self.conn.get_odoo_instance()
self.model_name = "project.task" self.model_name = "project.task"
def _check_connection(self): def _ensure_connection(self):
"""Vérifie la connexion Odoo""" """Vérifie et établit la onnexion si nécessaire"""
if self.odoo is None: if not self.odoo:
try: self.odoo = self.conn.get_odoo_instance()
self.conn = OdooConnection()
self.odoo = self.conn.get_odoo_instance()
except Exception as e:
print(f"Erreur de connexion: {e}")
self.odoo = None
return self.odoo is not None return self.odoo is not None
def _safe_execute(self, model, method, *args): def _safe_execute(self, model, method, *args):
"""Exécute une méthode Odoo de manière sécurisée""" """
if not self._check_connection(): Exécute une méthode Odoo en toute sécurité via odoorpc.
Vérifie la connexion avant d'exécuter.
"""
# Vérifier que la connexion est bien établie
if not self._ensure_connection():
print_error("Connexion Odoo indisponible.")
return None return None
try: try:
return self.odoo.execute(model, method, *args) # Exécuter la méthode sur le modèle via OdooRPC
except Exception as e: return self.odoo.execute(model, method, *args) # type: ignore
print(f"Erreur lors de {method} sur {model}: {e}")
except odoorpc.error.RPCError as e: # type: ignore
print_error(f" Erreur RPC lors de '{method}' sur '{model}': {e}")
return None return None
except Exception as e:
print_error(f" Erreur inattendue lors de '{method}' sur '{model}': {e}")
return None
def get_ticket_by_id(self, ticket_id):
""" Récupère les détails d'un ticket par son ID et applique le filtre """
fields_to_read = ['id', 'name', 'code', 'stage_id', 'date_deadline', 'description', 'message_ids']
# Vérifier la connexion avant d'exécuter la requête
if not self._ensure_connection():
print_error("Connexion Odoo indisponible.")
return None
# Récupérer les données du ticket
ticket_data = self._safe_execute(self.model_name, 'read', [ticket_id], fields_to_read)
if not ticket_data:
print_error(f"Aucun ticket trouvé avec l'ID {ticket_id}")
return None
# Nettoyer et filtrer les données du ticket
return filter_ticket_data(ticket_data[0]) # Utilisation de data_filter.py
def list_models(self): def list_models(self):
"""Affiche la liste des modèles disponibles dans Odoo""" """Affiche la liste des modèles disponibles dans Odoo"""
models = self._safe_execute('ir.model', 'search_read', [], ['model', 'name']) models = self._safe_execute('ir.model', 'search_read', [], ['model', 'name'])
if not models: if not models:
print("Aucun modèle disponible.") print_error("Aucun modèle disponible.")
return [] return []
print("\nListe des modèles disponibles:") print("\nListe des modèles disponibles:")
@ -51,7 +76,7 @@ class TicketManager:
"""Affiche les champs d'un modèle donné""" """Affiche les champs d'un modèle donné"""
fields_info = self._safe_execute(model_name, 'fields_get') fields_info = self._safe_execute(model_name, 'fields_get')
if not fields_info: if not fields_info:
print(f"Aucun champ trouvé pour le modèle {model_name}.") print_error(f"Aucun champ trouvé pour le modèle {model_name}.")
return [] return []
print(f"\nChamps du modèle {model_name}:") print(f"\nChamps du modèle {model_name}:")
@ -59,73 +84,78 @@ class TicketManager:
print(f"Champ: {field_name} - Type: {field_data['type']}") print(f"Champ: {field_name} - Type: {field_data['type']}")
return fields_info return fields_info
def get_project_tickets_summary(self, project_id): def export_model_fields_to_json(self, model_name, filename):
"""Récupère un résumé des tickets d'un projet pour permettre la sélection""" """Exporte les champs d'un modèle dans un fichier JSON"""
domain = [('project_id', '=', project_id)] fields_info = self._safe_execute(model_name, 'fields_get')
ticket_ids = self._safe_execute(self.model_name, 'search', domain, 0, 200) if not fields_info:
print_error(f"Aucun champ trouvé pour le modèle {model_name}.")
if not ticket_ids:
print(f"Aucun ticket trouvé pour le projet ID: {project_id}")
return []
fields_to_read = ['id', 'name', 'code', 'stage_id', 'date_deadline']
tickets_data = self._safe_execute(self.model_name, 'read', ticket_ids, fields_to_read)
if not tickets_data:
print("Erreur lors de la récupération des données des tickets.")
return []
summary_tickets = []
for ticket in tickets_data:
stage_name = "Non défini"
if ticket.get('stage_id'):
stage_name = ticket['stage_id'][1] if isinstance(ticket['stage_id'], list) and len(ticket['stage_id']) > 1 else str(ticket['stage_id'])
summary_tickets.append({
'id': ticket['id'],
'name': ticket['name'],
'code': ticket.get('code', 'N/A'),
'stage_id': ticket.get('stage_id', [0, "Non défini"]),
'stage_name': stage_name,
'date_deadline': ticket.get('date_deadline', 'Non défini')
})
return summary_tickets
def export_tickets_by_project_and_stage(self, project_id, selected_stage_ids=None):
"""Exporte les tickets d'un projet classés par étape"""
project_data = self._safe_execute('project.project', 'search_read', [('id', '=', project_id)], ['id', 'name'])
if not project_data:
print(f"Projet ID {project_id} introuvable")
return return
data = {field_name: field_data['type'] for field_name, field_data in fields_info.items()}
filepath = os.path.join(EXPORT_DIR, filename)
if save_json(filepath, data):
print(f"Champs du modèle {model_name} exportés dans {filepath}")
else:
print_error(f"Erreur lors de l'exportation des champs du modèle {model_name} dans {filepath}")
def export_tickets_by_project_and_stage(self, project_id, selected_stage_ids=None):
""" Exporte les tickets d'un projet classés par étape """
# Vérifier la connexion Odoo
if not self._ensure_connection():
print_error("Connexion Odoo indisponible.")
return
# Récupérer le projet depuis Odoo
project_data = self._safe_execute('project.project', 'search_read', [('id', '=', project_id)], ['id', 'name'])
if not project_data:
print_error(f"Projet ID {project_id} introuvable.")
return
project_name = project_data[0]['name'] project_name = project_data[0]['name']
# Construire le domaine de recherche des tickets
domain = [('project_id', '=', project_id)] domain = [('project_id', '=', project_id)]
if selected_stage_ids: if selected_stage_ids:
domain.append(('stage_id', 'in', selected_stage_ids)) domain.append(('stage_id', 'in', selected_stage_ids))
# Récupérer les tickets du projet
ticket_ids = self._safe_execute(self.model_name, 'search', domain, 0, 1000) ticket_ids = self._safe_execute(self.model_name, 'search', domain, 0, 1000)
if not ticket_ids: if not ticket_ids:
print("Aucun ticket trouvé") print_error("Aucun ticket trouvé pour ce projet.")
return return
tickets = [self.get_ticket_by_id(ticket_id) for ticket_id in ticket_ids] # Lire les détails des tickets
tickets = [self.get_ticket_by_id(ticket_id) for ticket_id in ticket_ids if self.get_ticket_by_id(ticket_id)]
# Trier les tickets par étape
tickets_by_stage = {} tickets_by_stage = {}
for ticket in tickets: for ticket in tickets:
stage_id = ticket["Champs Relationnels"].get("stage_id", [0, "Non classé"])[0] #Vérifier que ticket est bien un dictionnaire
stage_name = ticket["Champs Relationnels"].get("stage_id", [0, "Non classé"])[1] if not isinstance(ticket, dict):
print_error("Erreur: le format du ticket est invalide.")
return None
# Vérifier que "Champs Relationnels" existe
champs_relationnels = ticket.get("Champs Relationnels", {})
# Vérifier que "stage_id" existe et est une liste avec au moins 2 éléments
stage_data = champs_relationnels.get("stage_id", [0, "Non classé"])
stage_id = stage_data[0] if isinstance(stage_data, list) and len(stage_data) > 0 else 0
stage_name = stage_data[1] if isinstance(stage_data, list) and len(stage_data) > 1 else "Non classé"
key = f"{stage_id}_{stage_name}" key = f"{stage_id}_{stage_name}"
if key not in tickets_by_stage: if key not in tickets_by_stage:
tickets_by_stage[key] = [] tickets_by_stage[key] = []
tickets_by_stage[key].append(ticket) tickets_by_stage[key].append(ticket)
# Créer le répertoire du projet
project_dir = ensure_export_directory(f"project_{project_id}_{project_name.replace(' ', '_')}") project_dir = ensure_export_directory(f"project_{project_id}_{project_name.replace(' ', '_')}")
# Sauvegarder les tickets par étape
for stage_key, stage_tickets in tickets_by_stage.items(): for stage_key, stage_tickets in tickets_by_stage.items():
stage_dir = os.path.join(project_dir, stage_key) stage_dir = os.path.join(project_dir, stage_key)
os.makedirs(stage_dir, exist_ok=True) os.makedirs(stage_dir, exist_ok=True)
save_json(os.path.join(stage_dir, "all_tickets.json"), stage_tickets) save_json(os.path.join(stage_dir, "all_tickets.json"), stage_tickets)
print(f"Exportation terminée dans {project_dir}/") print(f"Exportation terminée. Les fichiers sont enregistrés dans : {project_dir}/")

View File

@ -35,4 +35,7 @@ def get_user_choice(prompt, options):
else: else:
print("Choix invalide. Veuillez réessayer.") print("Choix invalide. Veuillez réessayer.")
except ValueError: except ValueError:
print("Veuillez entrer un nombre valide.") print("Veuillez entrer un nombre valide.")
def print_error(message):
print(f" Erreur: {message}")