mirror of
https://github.com/Ladebeze66/odoo_toolkit.git
synced 2025-12-13 10:46:52 +01:00
J6-8
This commit is contained in:
parent
2e6d83df8a
commit
f339433b0f
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -18,12 +18,34 @@ def handle_list_model_fields():
|
||||
if model_name.lower() == 'q':
|
||||
return
|
||||
|
||||
# Assurons-nous que model_name est bien une chaîne de caractères
|
||||
model_name = str(model_name)
|
||||
|
||||
fields = ticket_manager.get_model_fields_with_types(model_name)
|
||||
if fields:
|
||||
print(f"\nChamps du modèle '{model_name}':")
|
||||
for field, info in fields.items():
|
||||
relation_info = f" (relation avec {info['relation']})" if info["relation"] else ""
|
||||
print(f"- {field} : {info['type']}{relation_info}")
|
||||
required_info = " [Obligatoire]" if info.get("required") else ""
|
||||
readonly_info = " [Lecture seule]" if info.get("readonly") else ""
|
||||
|
||||
print(f"- {field} : {info['type']}{relation_info}{required_info}{readonly_info}")
|
||||
|
||||
# Afficher la description du champ si disponible
|
||||
if info.get("string") and info.get("string") != field:
|
||||
print(f" Label: {info['string']}")
|
||||
|
||||
# Afficher l'aide si disponible
|
||||
if info.get("help"):
|
||||
print(f" Description: {info['help']}")
|
||||
|
||||
# Afficher les options si c'est un champ de sélection
|
||||
if info.get("selection"):
|
||||
print(" Options:")
|
||||
for key, value in info["selection"]:
|
||||
print(f" - {key}: {value}")
|
||||
|
||||
print("") # Ligne vide pour séparer les champs
|
||||
|
||||
def handle_search_ticket_by_id():
|
||||
"""Gère la recherche d'un ticket par ID"""
|
||||
@ -114,4 +136,25 @@ def handle_project_tickets_by_stage():
|
||||
return
|
||||
|
||||
# Exporter les tickets selon les filtres choisis
|
||||
ticket_manager.export_tickets_by_project_and_stage(project_id, stage_id)
|
||||
ticket_manager.export_tickets_by_project_and_stage(project_id, stage_id)
|
||||
|
||||
def handle_extract_ticket_attachments():
|
||||
"""Gère l'extraction des pièces jointes et messages d'un ticket"""
|
||||
# Demander à l'utilisateur d'entrer l'ID du ticket
|
||||
ticket_id_input = input("\nEntrez l'ID du ticket à extraire (ou 'q' pour quitter): ")
|
||||
if ticket_id_input.lower() == 'q':
|
||||
return
|
||||
|
||||
try:
|
||||
ticket_id = int(ticket_id_input)
|
||||
except ValueError:
|
||||
print_error("L'ID du ticket doit être un nombre entier.")
|
||||
return
|
||||
|
||||
# Extraire les pièces jointes et les messages
|
||||
output_dir = ticket_manager.extract_ticket_attachments_and_messages(ticket_id)
|
||||
|
||||
if output_dir:
|
||||
print(f"\nExtraction terminée avec succès. Les fichiers ont été enregistrés dans: {output_dir}")
|
||||
else:
|
||||
print_error(f"L'extraction a échoué pour le ticket avec l'ID {ticket_id}")
|
||||
@ -3,7 +3,8 @@ from menu_handlers import (
|
||||
handle_search_ticket_by_id,
|
||||
handle_search_ticket_by_code,
|
||||
handle_list_models,
|
||||
handle_list_model_fields
|
||||
handle_list_model_fields,
|
||||
handle_extract_ticket_attachments
|
||||
)
|
||||
|
||||
def display_main_menu():
|
||||
@ -14,8 +15,9 @@ def display_main_menu():
|
||||
print("3. Rechercher un ticket par Code")
|
||||
print("4. Afficher la liste des modèles disponibles")
|
||||
print("5. Afficher les champs d'un modèle donné")
|
||||
print("6. Quitter")
|
||||
return input("\nChoisissez une option (1-6): ")
|
||||
print("6. Extraire les pièces jointes, messages et informations détaillées d'un ticket")
|
||||
print("7. Quitter")
|
||||
return input("\nChoisissez une option (1-7): ")
|
||||
|
||||
|
||||
def run_menu():
|
||||
@ -33,7 +35,9 @@ def run_menu():
|
||||
elif choice == '5':
|
||||
handle_list_model_fields()
|
||||
elif choice == '6':
|
||||
handle_extract_ticket_attachments()
|
||||
elif choice == '7':
|
||||
print("Au revoir!")
|
||||
break
|
||||
else:
|
||||
print("Option invalide. Veuillez choisir entre 1 et 6.")
|
||||
print("Option invalide. Veuillez choisir entre 1 et 7.")
|
||||
@ -1,6 +1,7 @@
|
||||
from odoo_connection import OdooConnection
|
||||
import os
|
||||
import json
|
||||
import base64
|
||||
from utils import print_error
|
||||
from config import EXPORT_DIR
|
||||
|
||||
@ -75,17 +76,27 @@ class TicketManager:
|
||||
return models_dict
|
||||
|
||||
def get_model_fields_with_types(self, model_name):
|
||||
"""Récupère et sauvegarde les champs d'un modèle avec leurs types"""
|
||||
fields_info = self._safe_execute(model_name, 'fields_get', [], ['name', 'type', 'relation'])
|
||||
"""Récupère et sauvegarde les champs d'un modèle avec tous leurs attributs"""
|
||||
# Récupérer tous les attributs disponibles pour chaque champ
|
||||
fields_info = self._safe_execute(model_name, 'fields_get', [])
|
||||
if not fields_info:
|
||||
print_error(f"Impossible de récupérer les champs pour {model_name}")
|
||||
return {}
|
||||
|
||||
# Construire un dictionnaire {champ: {type, relation (si relationnel)}}
|
||||
# Sauvegarde en JSON (tous les attributs sont conservés)
|
||||
self.save_raw_ticket_data(fields_info, f"fields_{model_name}_complete.json")
|
||||
print(f"Liste complète des champs du modèle '{model_name}' sauvegardée dans 'fields_{model_name}_complete.json'")
|
||||
|
||||
# Pour la compatibilité, construire aussi le dictionnaire simplifié
|
||||
fields_dict = {
|
||||
field: {
|
||||
"type": info["type"],
|
||||
"relation": info.get("relation", None)
|
||||
"type": info.get("type", "unknown"),
|
||||
"relation": info.get("relation", None),
|
||||
"string": info.get("string", field),
|
||||
"help": info.get("help", ""),
|
||||
"required": info.get("required", False),
|
||||
"readonly": info.get("readonly", False),
|
||||
"selection": info.get("selection", []) if info.get("type") == "selection" else None
|
||||
}
|
||||
for field, info in fields_info.items()
|
||||
}
|
||||
@ -255,3 +266,190 @@ class TicketManager:
|
||||
|
||||
print(f"Exportation terminée. Les fichiers sont organisés dans : {export_base_dir}/")
|
||||
|
||||
def extract_ticket_attachments_and_messages(self, ticket_id):
|
||||
"""
|
||||
Extrait toutes les pièces jointes et messages d'un ticket
|
||||
et les sauvegarde dans un répertoire dédié.
|
||||
|
||||
Args:
|
||||
ticket_id: ID du ticket à extraire
|
||||
"""
|
||||
# Récupérer les informations du ticket
|
||||
ticket = self.get_ticket_by_id(ticket_id)
|
||||
if not ticket:
|
||||
print_error(f"Impossible de trouver le ticket avec l'ID {ticket_id}")
|
||||
return
|
||||
|
||||
# Créer un répertoire pour ce ticket
|
||||
ticket_name = ticket.get('name', 'Sans nom').replace('/', '_').replace('\\', '_')
|
||||
ticket_dir = os.path.join(EXPORT_DIR, f"ticket_{ticket_id}_{ticket_name}")
|
||||
os.makedirs(ticket_dir, exist_ok=True)
|
||||
|
||||
# Sauvegarder les données du ticket
|
||||
self.save_raw_ticket_data(ticket, os.path.join(ticket_dir, "ticket_info.json"))
|
||||
|
||||
# Récupérer les informations de contact supplémentaires si disponibles
|
||||
contact_info = {}
|
||||
if ticket.get('partner_id'):
|
||||
partner_id = ticket.get('partner_id')[0] if isinstance(ticket.get('partner_id'), list) else ticket.get('partner_id')
|
||||
partner_details = self._safe_execute('res.partner', 'read', [partner_id],
|
||||
['name', 'email', 'phone', 'mobile', 'street', 'city', 'zip', 'country_id', 'comment'])
|
||||
if partner_details:
|
||||
contact_info = partner_details[0]
|
||||
self.save_raw_ticket_data(contact_info, os.path.join(ticket_dir, "contact_info.json"))
|
||||
print(f"Informations de contact extraites pour le partenaire {partner_id}")
|
||||
|
||||
# Récupérer les activités liées au ticket
|
||||
activity_ids = ticket.get('activity_ids', [])
|
||||
if activity_ids:
|
||||
activities = self._safe_execute('mail.activity', 'read', activity_ids,
|
||||
['id', 'res_id', 'activity_type_id', 'summary', 'note', 'date_deadline', 'user_id', 'create_date'])
|
||||
if activities:
|
||||
self.save_raw_ticket_data(activities, os.path.join(ticket_dir, "activities.json"))
|
||||
print(f"{len(activities)} activités extraites pour le ticket {ticket_id}")
|
||||
|
||||
# Récupérer les messages (discussions)
|
||||
message_ids = ticket.get('message_ids', [])
|
||||
if message_ids:
|
||||
# Récupérer tous les messages avec leurs détails
|
||||
messages = self._safe_execute('mail.message', 'read', message_ids,
|
||||
['id', 'body', 'date', 'author_id', 'email_from', 'subject', 'parent_id', 'message_type', 'subtype_id', 'attachment_ids'])
|
||||
|
||||
if messages:
|
||||
# Sauvegarder tous les messages dans un fichier
|
||||
self.save_raw_ticket_data(messages, os.path.join(ticket_dir, "messages.json"))
|
||||
|
||||
# Organiser les messages par fil de discussion
|
||||
messages_by_thread = {}
|
||||
for message in messages:
|
||||
parent_id = message.get('parent_id', False)
|
||||
parent_id = parent_id[0] if isinstance(parent_id, list) and len(parent_id) > 0 else False
|
||||
|
||||
if not parent_id: # Message principal
|
||||
thread_id = message['id']
|
||||
if thread_id not in messages_by_thread:
|
||||
messages_by_thread[thread_id] = {
|
||||
'main_message': message,
|
||||
'replies': []
|
||||
}
|
||||
else: # Réponse à un message
|
||||
if parent_id not in messages_by_thread:
|
||||
messages_by_thread[parent_id] = {
|
||||
'main_message': None,
|
||||
'replies': []
|
||||
}
|
||||
messages_by_thread[parent_id]['replies'].append(message)
|
||||
|
||||
# Sauvegarder les fils de discussion
|
||||
self.save_raw_ticket_data(messages_by_thread, os.path.join(ticket_dir, "message_threads.json"))
|
||||
|
||||
# Sauvegarder chaque message dans un fichier séparé pour une meilleure lisibilité
|
||||
messages_dir = os.path.join(ticket_dir, "messages")
|
||||
os.makedirs(messages_dir, exist_ok=True)
|
||||
|
||||
for message in messages:
|
||||
message_id = message.get('id', 0)
|
||||
message_date = message.get('date', '').replace(':', '-').replace(' ', '_')
|
||||
message_path = os.path.join(messages_dir, f"message_{message_id}_{message_date}.json")
|
||||
|
||||
# Récupérer les détails de l'auteur si disponible
|
||||
if message.get('author_id') and isinstance(message.get('author_id'), list) and len(message.get('author_id')) > 0:
|
||||
author_id = message.get('author_id')[0]
|
||||
author_details = self._safe_execute('res.partner', 'read', [author_id], ['name', 'email', 'phone', 'function', 'company_id'])
|
||||
if author_details:
|
||||
message['author_details'] = author_details[0]
|
||||
|
||||
# Récupérer les détails du sous-type si disponible
|
||||
if message.get('subtype_id') and isinstance(message.get('subtype_id'), list) and len(message.get('subtype_id')) > 0:
|
||||
subtype_id = message.get('subtype_id')[0]
|
||||
subtype_details = self._safe_execute('mail.message.subtype', 'read', [subtype_id], ['name', 'description', 'default'])
|
||||
if subtype_details:
|
||||
message['subtype_details'] = subtype_details
|
||||
|
||||
with open(message_path, "w", encoding="utf-8") as f:
|
||||
json.dump(message, f, indent=4, ensure_ascii=False)
|
||||
|
||||
print(f"{len(messages)} messages extraits pour le ticket {ticket_id}")
|
||||
|
||||
# Récupérer les suiveurs du ticket
|
||||
follower_ids = ticket.get('message_follower_ids', [])
|
||||
if follower_ids:
|
||||
followers = self._safe_execute('mail.followers', 'read', follower_ids, ['id', 'partner_id', 'name', 'email', 'subtype_ids'])
|
||||
if followers:
|
||||
# Enrichir les informations des suiveurs
|
||||
for follower in followers:
|
||||
if follower.get('partner_id') and isinstance(follower.get('partner_id'), list) and len(follower.get('partner_id')) > 0:
|
||||
partner_id = follower.get('partner_id')[0]
|
||||
partner_details = self._safe_execute('res.partner', 'read', [partner_id], ['name', 'email', 'phone', 'function', 'company_id'])
|
||||
if partner_details:
|
||||
follower['partner_details'] = partner_details[0]
|
||||
|
||||
# Récupérer les détails des sous-types
|
||||
if follower.get('subtype_ids'):
|
||||
subtype_details = self._safe_execute('mail.message.subtype', 'read', follower.get('subtype_ids'), ['name', 'description', 'default'])
|
||||
if subtype_details:
|
||||
follower['subtype_details'] = subtype_details
|
||||
|
||||
self.save_raw_ticket_data(followers, os.path.join(ticket_dir, "followers.json"))
|
||||
print(f"{len(followers)} suiveurs extraits pour le ticket {ticket_id}")
|
||||
|
||||
# Récupérer les pièces jointes
|
||||
attachment_ids = self._safe_execute('ir.attachment', 'search',
|
||||
[('res_model', '=', self.model_name), ('res_id', '=', ticket_id)])
|
||||
|
||||
if attachment_ids:
|
||||
attachments = self._safe_execute('ir.attachment', 'read', attachment_ids,
|
||||
['id', 'name', 'datas', 'mimetype', 'create_date', 'create_uid', 'description', 'res_name', 'public', 'type'])
|
||||
|
||||
if attachments:
|
||||
# Sauvegarder les métadonnées des pièces jointes
|
||||
attachments_info = [{
|
||||
'id': att.get('id'),
|
||||
'name': att.get('name'),
|
||||
'mimetype': att.get('mimetype'),
|
||||
'create_date': att.get('create_date'),
|
||||
'description': att.get('description'),
|
||||
'res_name': att.get('res_name'),
|
||||
'type': att.get('type'),
|
||||
'file_path': f"attachments/{att.get('id')}_{att.get('name', '').replace('/', '_')}"
|
||||
} for att in attachments]
|
||||
|
||||
self.save_raw_ticket_data(attachments_info, os.path.join(ticket_dir, "attachments_info.json"))
|
||||
|
||||
# Créer un répertoire pour les pièces jointes
|
||||
attachments_dir = os.path.join(ticket_dir, "attachments")
|
||||
os.makedirs(attachments_dir, exist_ok=True)
|
||||
|
||||
# Sauvegarder chaque pièce jointe
|
||||
for attachment in attachments:
|
||||
attachment_id = attachment.get('id', 0)
|
||||
attachment_name = attachment.get('name', f"attachment_{attachment_id}").replace('/', '_')
|
||||
attachment_data = attachment.get('datas')
|
||||
|
||||
if attachment_data:
|
||||
try:
|
||||
# Décoder les données base64
|
||||
binary_data = base64.b64decode(attachment_data)
|
||||
file_path = os.path.join(attachments_dir, f"{attachment_id}_{attachment_name}")
|
||||
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(binary_data)
|
||||
|
||||
print(f"Pièce jointe {attachment_name} sauvegardée dans {file_path}")
|
||||
except Exception as e:
|
||||
print_error(f"Erreur lors de la sauvegarde de la pièce jointe {attachment_name}: {e}")
|
||||
|
||||
print(f"{len(attachments)} pièces jointes extraites pour le ticket {ticket_id}")
|
||||
|
||||
# Extraire les historiques de timesheet si disponibles
|
||||
timesheet_ids = ticket.get('timesheet_ids', [])
|
||||
if timesheet_ids:
|
||||
timesheets = self._safe_execute('account.analytic.line', 'read', timesheet_ids,
|
||||
['id', 'date', 'user_id', 'name', 'unit_amount', 'employee_id', 'department_id'])
|
||||
if timesheets:
|
||||
self.save_raw_ticket_data(timesheets, os.path.join(ticket_dir, "timesheets.json"))
|
||||
print(f"{len(timesheets)} feuilles de temps extraites pour le ticket {ticket_id}")
|
||||
|
||||
print(f"Extraction terminée. Les fichiers sont disponibles dans: {ticket_dir}")
|
||||
return ticket_dir
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user