diff --git a/gui_ollama.py b/gui_ollama.py index 6ef6d348..aed62aa8 100644 --- a/gui_ollama.py +++ b/gui_ollama.py @@ -71,10 +71,10 @@ def chat_interface(user, user_input): # Affichage des derniers messages sous forme de bulle de chat history_display = "" for c in user_history[-5:]: - history_display += f"👤 **{user}** : {c['user']}\n" - history_display += f"🤖 **Bot** : {c['bot']}\n\n" + history_display += f"Utilisateur : {c['user']}\n" + history_display += f"Bot : {c['bot']}\n\n" - return f"📌 **Utilisateur :** {user}\n\n{history_display}👤 **{user}** : {user_input}\n🤖 **Bot** : {response}" + return f"Utilisateur : {user}\n\n{history_display}Utilisateur : {user_input}\nBot : {response}" # Interface Gradio stylisée with gr.Blocks(css=""" @@ -85,7 +85,7 @@ with gr.Blocks(css=""" .gr-button {background-color: #007bff; color: white; border-radius: 5px;} """) as iface: with gr.Column(): - gr.Markdown("

đź’¬ Chat avec Ollama OVH

") + gr.Markdown("

Chat avec Ollama OVH

") with gr.Row(): user_input = gr.Textbox(label="Nom d'utilisateur", placeholder="Entrez votre nom...", interactive=True) diff --git a/odoo_data/odoo_toolkit_new/main.py b/odoo_data/odoo_toolkit_new/main.py index 49599d07..3d894a29 100755 --- a/odoo_data/odoo_toolkit_new/main.py +++ b/odoo_data/odoo_toolkit_new/main.py @@ -1,16 +1,6 @@ from menu_principal import run_menu def main(): - """Point d'entrée principal de l'application""" - print("=================================================") - print(" GESTIONNAIRE DE TICKETS ODOO SIMPLIFIÉ ") - print("=================================================") - print("Fonctionnalités:") - print("1. Afficher la liste des modèles disponibles") - print("2. Afficher les champs d'un modèle donné") - print("3. Exporter les informations des champs en JSON") - print("4. Exporter les tickets d'un projet par étape") - print("=================================================") run_menu() diff --git a/odoo_data/odoo_toolkit_new/ticket_manager.py b/odoo_data/odoo_toolkit_new/ticket_manager.py index 7c102d6c..7c468b84 100644 --- a/odoo_data/odoo_toolkit_new/ticket_manager.py +++ b/odoo_data/odoo_toolkit_new/ticket_manager.py @@ -15,32 +15,24 @@ class TicketManager: self.model_name = "project.task" def _check_connection(self): - """Vérifie que la connexion Odoo est active""" + """Vérifie la connexion Odoo""" if self.odoo is None: - print("Erreur: La connexion à Odoo n'est pas établie.") - print("Tentative de reconnexion...") try: self.conn = OdooConnection() self.odoo = self.conn.get_odoo_instance() except Exception as e: - print(f"Erreur lors de la tentative de reconnexion: {e}") + print(f"Erreur de connexion: {e}") self.odoo = None - return self.odoo is not None def _safe_execute(self, model, method, *args): """Exécute une méthode Odoo de manière sécurisée""" if not self._check_connection(): return None - try: - if self.odoo is None: - print("Erreur: Impossible d'exécuter la commande, la connexion à Odoo est inactive.") - return None - return self.odoo.execute(model, method, *args) except Exception as e: - print(f"Erreur lors de l'exécution de {method} sur {model}: {e}") + print(f"Erreur lors de {method} sur {model}: {e}") return None def list_models(self): @@ -54,7 +46,7 @@ class TicketManager: for model in models: print(f"Modèle: {model['name']} (ID: {model['model']})") return models - + def list_model_fields(self, model_name): """Affiche les champs d'un modèle donné""" fields_info = self._safe_execute(model_name, 'fields_get') @@ -66,285 +58,7 @@ class TicketManager: for field_name, field_data in fields_info.items(): print(f"Champ: {field_name} - Type: {field_data['type']}") return fields_info - - def export_model_fields_to_json(self, model_name, filename): - """Exporte les informations des champs d'un modèle en JSON""" - fields_info = self.list_model_fields(model_name) - if not fields_info: - return False - - file_path = os.path.join(EXPORT_DIR, f"{filename}.json") - try: - with open(file_path, 'w', encoding='utf-8') as f: - json.dump(fields_info, f, indent=4, ensure_ascii=False) - print(f"Informations des champs exportées dans {file_path}") - return True - except Exception as e: - print(f"Erreur lors de l'exportation des champs: {str(e)}") - return False - def get_ticket_by_id(self, ticket_id): - """Récupère un ticket par son ID""" - all_fields = self._safe_execute(self.model_name, 'fields_get') - if not all_fields: - return None - - field_names = list(all_fields.keys()) - - ticket_data = self._safe_execute(self.model_name, 'read', [ticket_id], field_names) - if not ticket_data: - return None - - ticket = ticket_data[0] - formatted_ticket = { - "ID du Ticket": ticket["id"], - "Nom": ticket["name"], - "Code": ticket.get("code", "N/A"), - "Date Limite": ticket.get("date_deadline", "Non défini"), - "Champs Simples": {}, - "Champs Relationnels": {}, - "Discussions": [] - } - - # Classer les champs par type - for field_name, field_info in all_fields.items(): - field_type = field_info.get('type') - if field_type in ["many2one", "one2many", "many2many"]: - if field_name in ticket: - formatted_ticket["Champs Relationnels"][field_name] = ticket[field_name] - else: - if field_name in ticket: - formatted_ticket["Champs Simples"][field_name] = ticket[field_name] - - # Récupérer les discussions - if "message_ids" in ticket and ticket["message_ids"]: - formatted_ticket["Discussions"] = self.get_ticket_discussions(ticket["message_ids"]) - - return formatted_ticket - - def get_ticket_discussions(self, message_ids): - """Récupère et nettoie les discussions associées à un ticket""" - model_name = "mail.message" - - if not message_ids: - return [] - - messages = self._safe_execute(model_name, 'read', message_ids, ["id", "subject", "body", "author_id", "date"]) - if messages is None: - return [] - - formatted_messages = [] - for msg in messages: - # Utiliser la fonction clean_html de data_filter - from data_filter import clean_html - cleaned_content = clean_html(msg["body"]) - - if cleaned_content: - formatted_messages.append({ - "ID Message": msg["id"], - "Sujet": msg["subject"] or "Sans sujet", - "Contenu": cleaned_content, - "Auteur": msg["author_id"][1] if msg["author_id"] else "Inconnu", - "Date": msg["date"] - }) - - return formatted_messages - - def search_tickets_by_fields(self, field_criteria, limit=50): - """Recherche des tickets selon des critères de champs""" - # Construire le domaine de recherche - domain = [] - for field, value in field_criteria.items(): - domain.append((field, '=', value)) - - print(f"\nRecherche de tickets avec les critères: {field_criteria}") - - # Rechercher les tickets correspondants - ticket_ids = self._safe_execute(self.model_name, 'search', domain, 0, limit) - if not ticket_ids: - print("Aucun ticket ne correspond aux critères de recherche.") - return [] - - print(f"Nombre de tickets trouvés: {len(ticket_ids)}") - - # Récupérer tous les tickets trouvés - tickets = [] - for ticket_id in ticket_ids: - ticket = self.get_ticket_by_id(ticket_id) - if ticket: - tickets.append(ticket) - - return 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 - - Args: - project_id (int): L'ID du projet - selected_stage_ids (list, optional): Liste des IDs d'étapes à inclure. Si None, toutes les étapes sont incluses. - """ - try: - # Convertir project_id en entier - project_id = int(project_id) - - # Vérifier si le projet existe - projects = self._safe_execute('project.project', 'search_read', [('id', '=', project_id)], ['id', 'name']) - if not projects: - print(f"Aucun projet trouvé avec l'ID: {project_id}") - return - - project_name = projects[0]['name'] - print(f"\nRécupération des tickets du projet: {project_name} (ID: {project_id})") - - # Construire le domaine de recherche - domain = [('project_id', '=', project_id)] - - # Si des étapes spécifiques sont sélectionnées, ajouter à la condition - if selected_stage_ids and len(selected_stage_ids) > 0: - domain.append(('stage_id', 'in', selected_stage_ids)) - print(f"Filtrage sur {len(selected_stage_ids)} étapes sélectionnées") - - ticket_ids = self._safe_execute(self.model_name, 'search', domain, 0, 1000) - - if not ticket_ids: - print(f"Aucun ticket trouvé avec les critères spécifiés.") - return - - # Récupérer les tickets avec les champs essentiels - fields_to_read = ['id', 'name', 'code', 'stage_id', 'date_deadline', - 'description', 'priority', 'sequence', 'message_ids'] - - 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 - - print(f"Nombre de tickets trouvés: {len(tickets_data)}") - - # Convertir les tickets au format approprié et les filtrer - formatted_tickets = [] - for ticket in tickets_data: - formatted_ticket = { - "ID du Ticket": ticket["id"], - "Nom": ticket["name"], - "Code": ticket.get("code", "N/A"), - "Date Limite": ticket.get("date_deadline", "Non défini"), - "Champs Simples": { - "description": ticket.get("description", ""), - "priority": ticket.get("priority", ""), - "sequence": ticket.get("sequence", "") - }, - "Champs Relationnels": { - "project_id": [project_id, project_name], - "stage_id": ticket.get("stage_id", [0, "Inconnu"]) - }, - "Discussions": [] - } - - # Récupérer les discussions si nécessaire - if ticket.get("message_ids"): - formatted_ticket["Discussions"] = self.get_ticket_discussions(ticket["message_ids"]) - - # Filtrer le ticket - filtered_ticket = filter_ticket_data(formatted_ticket) - formatted_tickets.append(filtered_ticket) - - if not formatted_tickets: - print("Aucun ticket n'a pu être formaté correctement.") - return - - # Créer un répertoire pour le projet avec son nom pour plus de clarté - safe_project_name = project_name.replace('/', '_').replace(' ', '_').replace('\\', '_') - project_dir = ensure_export_directory(f"project_{project_id}_{safe_project_name}") - - print(f"\nExportation des tickets pour le projet {project_name} (ID: {project_id}):") - - # Classer les tickets par stage_id - tickets_by_stage = {} - for ticket in formatted_tickets: - # Vérifier que stage_id existe dans les champs relationnels - if 'Champs Relationnels' in ticket and 'stage_id' in ticket['Champs Relationnels']: - stage_data = ticket['Champs Relationnels']['stage_id'] - - if isinstance(stage_data, list) and len(stage_data) > 0: - stage_id = stage_data[0] - stage_name = stage_data[1] if len(stage_data) > 1 else "Inconnu" - else: - # Si stage_id n'est pas une liste, utiliser une valeur par défaut - stage_id = 0 - stage_name = "Inconnu" - - key = f"{stage_id}_{stage_name}" - if key not in tickets_by_stage: - tickets_by_stage[key] = [] - tickets_by_stage[key].append(ticket) - else: - # Si le ticket n'a pas de stage_id, le mettre dans "Non classé" - key = "0_Non_classé" - if key not in tickets_by_stage: - tickets_by_stage[key] = [] - tickets_by_stage[key].append(ticket) - - # Vérifier si des tickets ont été classés - if not tickets_by_stage: - print("Aucun ticket n'a pu être classé par stage_id.") - # Sauvegarde tous les tickets dans un seul fichier - all_tickets_file = os.path.join(project_dir, "all_tickets.json") - save_json(all_tickets_file, formatted_tickets) - print(f"Tous les tickets ont été sauvegardés dans {all_tickets_file}") - return - - # Créer des répertoires pour chaque stage_id et sauvegarder les tickets - for stage_key, stage_tickets in tickets_by_stage.items(): - stage_parts = stage_key.split('_', 1) - stage_id = stage_parts[0] - stage_name = stage_parts[1] if len(stage_parts) > 1 else "Inconnu" - safe_stage_name = stage_name.replace('/', '_').replace('\\', '_') - stage_dir = os.path.join(project_dir, f"{stage_id}_{safe_stage_name}") - os.makedirs(stage_dir, exist_ok=True) - - print(f"- Stage '{stage_name}' (ID: {stage_id}): {len(stage_tickets)} tickets") - - # Sauvegarder tous les tickets du stage dans un fichier unique - all_tickets_file = os.path.join(stage_dir, f"all_tickets.json") - save_json(all_tickets_file, stage_tickets) - - # Sauvegarder chaque ticket individuellement - for ticket in stage_tickets: - ticket_code = ticket.get('Code', 'N/A') - ticket_id = ticket.get('ID du Ticket', 'N/A') - ticket_file = os.path.join(stage_dir, f"ticket_{ticket_code}_{ticket_id}.json") - save_json(ticket_file, ticket) - - print(f" Tickets sauvegardés dans {stage_dir}/") - - print(f"\nExportation terminée dans {project_dir}/") - - except ValueError: - print(f"Erreur: Le project_id '{project_id}' n'est pas un nombre valide.") - except Exception as e: - print(f"Erreur lors de l'exportation des tickets: {e}") - import traceback - traceback.print_exc() - - def get_available_projects(self): - """Récupère la liste des projets disponibles""" - projects = self._safe_execute('project.project', 'search_read', [], ['id', 'name']) - if not projects: - print("Aucun projet disponible.") - return {} - - projects_dict = {} - print("\nProjets disponibles:") - for project in projects: - project_id = project['id'] - project_name = project['name'] - projects_dict[project_id] = project_name - print(f"ID: {project_id} - {project_name}") - - return projects_dict - def get_project_tickets_summary(self, project_id): """Récupère un résumé des tickets d'un projet pour permettre la sélection""" domain = [('project_id', '=', project_id)] @@ -354,7 +68,6 @@ class TicketManager: print(f"Aucun ticket trouvé pour le projet ID: {project_id}") return [] - # Récupérer des informations résumées sur les tickets fields_to_read = ['id', 'name', 'code', 'stage_id', 'date_deadline'] tickets_data = self._safe_execute(self.model_name, 'read', ticket_ids, fields_to_read) @@ -362,7 +75,6 @@ class TicketManager: print("Erreur lors de la récupération des données des tickets.") return [] - # Formater les données pour l'affichage summary_tickets = [] for ticket in tickets_data: stage_name = "Non défini" @@ -379,51 +91,41 @@ class TicketManager: }) return summary_tickets - - def get_project_stages(self, project_id): - """Récupère les étapes (stage_id) disponibles pour un projet donné + + 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 - Args: - project_id (int): L'ID du projet - - Returns: - dict: Un dictionnaire des étapes {stage_id: stage_name} - """ - # Récupérer les tickets du projet + project_name = project_data[0]['name'] domain = [('project_id', '=', project_id)] + if selected_stage_ids: + domain.append(('stage_id', 'in', selected_stage_ids)) + ticket_ids = self._safe_execute(self.model_name, 'search', domain, 0, 1000) - if not ticket_ids: - print(f"Aucun ticket trouvé pour le projet ID: {project_id}") - return {} + print("Aucun ticket trouvé") + return - # Récupérer uniquement les stage_id des tickets - tickets_data = self._safe_execute(self.model_name, 'read', ticket_ids, ['stage_id']) + tickets = [self.get_ticket_by_id(ticket_id) for ticket_id in ticket_ids] - if not tickets_data: - print("Erreur lors de la récupération des étapes.") - return {} + tickets_by_stage = {} + for ticket in tickets: + stage_id = ticket["Champs Relationnels"].get("stage_id", [0, "Non classé"])[0] + stage_name = ticket["Champs Relationnels"].get("stage_id", [0, "Non classé"])[1] + key = f"{stage_id}_{stage_name}" + + if key not in tickets_by_stage: + tickets_by_stage[key] = [] + tickets_by_stage[key].append(ticket) - # Extraire les stage_id uniques - stage_ids = set() - for ticket in tickets_data: - if ticket.get('stage_id'): - stage_ids.add(ticket['stage_id'][0]) + project_dir = ensure_export_directory(f"project_{project_id}_{project_name.replace(' ', '_')}") - if not stage_ids: - print("Aucune étape trouvée pour ce projet.") - return {} + for stage_key, stage_tickets in tickets_by_stage.items(): + stage_dir = os.path.join(project_dir, stage_key) + os.makedirs(stage_dir, exist_ok=True) + save_json(os.path.join(stage_dir, "all_tickets.json"), stage_tickets) - # Récupérer les noms des étapes - stages_data = self._safe_execute('project.task.type', 'read', list(stage_ids), ['id', 'name']) - - if not stages_data: - print("Erreur lors de la récupération des noms des étapes.") - return {} - - # Créer un dictionnaire des étapes - stages_dict = {} - for stage in stages_data: - stages_dict[stage['id']] = stage['name'] - - return stages_dict \ No newline at end of file + print(f"Exportation terminée dans {project_dir}/")