From 6d21fd5b61a478d6b4d2a7d0d1956fa818199b8e Mon Sep 17 00:00:00 2001 From: Ladebeze66 Date: Tue, 18 Mar 2025 11:24:17 +0100 Subject: [PATCH] first --- .gitignore | 1 + .requirements.txt | 2 + __pycache__/config.cpython-312.pyc | Bin 0 -> 868 bytes __pycache__/data_filter.cpython-312.pyc | Bin 0 -> 1469 bytes __pycache__/menu_handlers.cpython-312.pyc | Bin 0 -> 6211 bytes __pycache__/menu_principal.cpython-312.pyc | Bin 0 -> 1545 bytes __pycache__/odoo_connection.cpython-312.pyc | Bin 0 -> 1962 bytes __pycache__/ticket_manager.cpython-312.pyc | Bin 0 -> 7981 bytes __pycache__/utils.cpython-312.pyc | Bin 0 -> 2193 bytes menu_handlers.py | 1 + ticket_manager.py | 164 ++++++++++++-------- utils.py | 5 +- 12 files changed, 105 insertions(+), 68 deletions(-) create mode 100644 .gitignore create mode 100644 __pycache__/config.cpython-312.pyc create mode 100644 __pycache__/data_filter.cpython-312.pyc create mode 100644 __pycache__/menu_handlers.cpython-312.pyc create mode 100644 __pycache__/menu_principal.cpython-312.pyc create mode 100644 __pycache__/odoo_connection.cpython-312.pyc create mode 100644 __pycache__/ticket_manager.cpython-312.pyc create mode 100644 __pycache__/utils.cpython-312.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97f1089 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/odtkit diff --git a/.requirements.txt b/.requirements.txt index e69de29..fbcc3d6 100644 --- a/.requirements.txt +++ b/.requirements.txt @@ -0,0 +1,2 @@ +odoorpc +bs4 \ No newline at end of file diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..40c910c763ce854e30384dd5507fdeb8cb41c756 GIT binary patch literal 868 zcmZ{iJ#5oJ6vxj_8pn=Jf?6tIK?Fev5-}hM1E_+MB7zXElAx5TlI7+uiSyx$Y=hcN zVIr9lrnFN#HY!qAMs}7+mQXoHCbml5I`tglge7ly@7=xM|L(oh{g5O9P>UpgmA*3o z{BakaapUB)Oe=i=0c2`m9k2j_3hP1B2wCVm9Ph8QGbulun^3-)Q~+`l%0H6|LViNc z(XPa4H(pi=d)YM3ATLe{FPlNmd-Z8V!XbSn*SrXYNpGRi1egES|Mw^aLtjOYW56~O zm;SoITf!?TMNwZV*<4e^5aUE)Q^Sd(Nw`rVwM@bd6T=S$TgQg#7RQoz*_BK)Ttud! z8St5>l18f4=hxC+^tz4WTLA0e?wu-B9iLlrr zffBNj@zM0r-b}yi0+>77b4w!C*R%ZZ0tn$V!N?Dc~?G&9>oT+ qBY7ZyyY1Y0;ap7(U#>aQ`<6fjjM!p7hdzbculE;zf?MVS?d==|8oW;c literal 0 HcmV?d00001 diff --git a/__pycache__/data_filter.cpython-312.pyc b/__pycache__/data_filter.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d7596d8235d86f730fe2c922f064be884eacb0a GIT binary patch literal 1469 zcmZ8h&2Jk;6rc5e_+#U&lQwbUP<30Njay>#k%m@n5G5u8(FQ>x4iU)OdM3`M`(b8g z3yB;l2M$3(LOI2U9?UIO`6G}xa`7q>EYom6oVdA-xcJ1(+6lBH&3ilV{ocp^_Rap1 z&1L{!xw+r#6%ByD#NlKm1`o%t@rQi?0hAoD+Sk&m#Lo#Q;mWJB1fcv4SXH3HrMf8< z)xSmPQc+^r_mCA(do6I5{h(2k1}TM7_?G!TUgTmQGy#z|C3=cz^7M}fEo@6oX+vON zSWt$_kLn3(85FgxH|$P3I5R_@+0k!O;i(ZIHAd6avJdCzief=G#h zEq$H3PN`vGf^bjDtP~S0O%Oj@)W=LE6x)qr0*e+{s*Y%tqOU0jCHo~PQwV=OBHA_0 z-j(vY@1pWr9b06kW|e*D`&H`u&V8Gfp+&9gn(a`8ON~vIsX55*CEcO;1x=f z+$8wy>y1;6MQncwCQo?eNOcgQ4RliII)JH%$!!Uyn-YvY4*<^7)9-R(g=v9)JgD-Y z^%l4C+!^7mfG=hN*iLY7eBKc6{laP{pdQyxz#*akS1AFr&(%o4;Q`VR8s;9VC-o)p z-JAlN5}%Hd;1lj#{J>-yL66biIctnU%Alp%R98{h^foL2BIvv(7y^yyB$yzV5W$siYlHENd;xNAjFgqFL6hzT~a4v98A;1LtL}bK4buh!|+A!5Ki(d7dxPv z0+X+_m%@p+BBRi{78)0#+|<6YXSB5g^)Oe8@)z6lVg8M%@M`DMTv&KJI)9~e_1*CN z?I=I_)JWyEosYZYAT`!9I@8y~i5p?^#?j69fATw@t%ZyA&Uz3oJ~+Jj)vnQ*m=06L zgPAaOy{m!TRBJvoiqAk&)pzA6m2YLk)TQ52+&vQ&&b6Gda5Wm^cW>|Wqx^;T%WeCI z(jjkSe6lsY=SFAqEp6|kr@B_o`~$SK-j#Wt?kE_V>L>*~%wMgju>?j;5m%eZ_sDGg z`-mBhId}wOSWHweb<6c(;GlP~xclPj5b7*L{{ZKnDXMhscz8;hiX;C8 D0C#mi literal 0 HcmV?d00001 diff --git a/__pycache__/menu_handlers.cpython-312.pyc b/__pycache__/menu_handlers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4a3838cacc11d11a41c495aaaa84837cb1ab36c GIT binary patch literal 6211 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 qM@^rj#=kpIu;Hf-`<}* literal 0 HcmV?d00001 diff --git a/__pycache__/menu_principal.cpython-312.pyc b/__pycache__/menu_principal.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d9d55772444ffaca879a66743124fa39aa67a87 GIT binary patch literal 1545 zcmZux&1)M+6rYh+vMjCAD4|U$C7z_F&bF2mf2fDzq!?SYZi_@K(}SQao86H$@qRJ0 z8zRv{4n2g>gKxg{52(qX;bSkRks)1l2sD@8LUbtP)Hmyu5a~Xcw{PC>&D*!{{dWE+ zmx~DG@@Ib?94QF>EjLriB*x?x7{|y$7PgUtyV%KeGfuXfb(F4xk(K!cb#qo0bbg>{ zxqsv_O^L4b4cD@%ZZi?+j%QI@MDPF4r}r4OEfL*J5c<&fcoLsrdf@5zh37^~NsQ0E z`_v5dfSHFh5c<7=E&}6#nn}U&BMVxt$#0;JQGjDFC<{MLY&`GHqm z@3mX4?Y-}tWVcE7_jbN%?srIgx7{W(BD?`_TC0&HH7B+~8x66%t#JMC7u4hc5jN{|fj z;!V@3GK=^IC#NTY;nQKUQ6t}lEC?tcUN7$SJtmkCbVx#%63>?{A-C454efKHWulyH zToC1$>xaP!n!r2Jn`*u9Iket8;D)H0M%}YK4>;x7hb*YecI&ft*Ze_Lv6%2}W1u?* zb9IUHGNdWM5<8$Cp_j_yS#fiuG{(}Xj+9z#)JMwg*!XIs?8V02k@5u?k5@;^wQ=e4 zS!MlMY2!@U;3ddo!kUR>hFp_BK^>#TIc7dhf__Fn0e27KXXK?o#>O&5F>=2JJd@zL z6nqQtY=SSO;H!Wu3BH(ue+YOk#??T6KrGfAzXydm|BT@n{d%A+hRYD%e0X|dhCo5! z;yo`kf$moL7ULtl7UA^>Z$x-A!i`~ZJ0zSw@EE@_thX-)&fEuv%`93YKhTibHqgP0 znZbc>0jFe*(10{*EiE6~p&xZe<$jp2Ezg6}3VA0Gu7ZvVJWK}vE^M#D z5(cP8=tc3;xN`GZW%+UbMX7Ssd2(m0UO!jK^YYar@#M#`x_qvFJloXH)!Vbpr{`*Q zwy7Mgj?|^+YUQO`IlHnmQg>f1q0&1sEn3<_RE}ABaocf`TD_zsm*sJJfyXD~;@=du p;NK2ydY4}VKz^0@0Mx52#`xb%4re9^)ay$Hyg7NVf}1!#`ybYJenS8N literal 0 HcmV?d00001 diff --git a/__pycache__/odoo_connection.cpython-312.pyc b/__pycache__/odoo_connection.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77f5042b8d54489ca99a8b3d3220063c83d48c6a GIT binary patch literal 1962 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 literal 0 HcmV?d00001 diff --git a/__pycache__/utils.cpython-312.pyc b/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3cad0f851c9cc57f4f7e2f4893572f402ab7c8d GIT binary patch literal 2193 zcmZvdO>7fK6o6-T*K2QL#|d?QThb*-0SA#3S_q<{$PIy}pil^kN>pSm-ifn`z3a~I zCb8wTX@%4vMXl*2kpPK9524{uwU-fj|Xs&EMnc~lQbQ58XY(>UyV#0>R^u~X6e!TYDro{JvqI~&7A>#Tv35f63!3lMWC zi|A_>XoWIb@}o2W?WkuI(DX)KEti+iT9E|$lqu(f&WsIRoT+-9+gZ*ir)7-jJQ z3iEatRzytGrbPb8CUSzv^zO|Rp_ZY+kh+UZrVMJyWKuINat{8c&sC$wSlHumhUo|? z#Ts#hQA5)y`$)oEK=!t6+;oqD1(*P;b4qMKG|0IaZUGWUa+Mt^z(`B2^5i7O}OPF*?m zb<@J&O6bkY(%+%x87VJ_pP&5nWKOzqY*h%~uM8~

^1WVq5Rt3j=PHegS0;NvM3A zSztruZt;t39?AhU#aTriqQ&C-LdoJRrwXd)mbh4)bt&?y-tzsYeXVF8GVuh5(Rqa7 zm-bEa=g|aiMdhfrA^P$ZZ%L&CR4I$DBDEsRUq#o2#bU&n1=Tynf6S+aLOX#cP{CTb z(%uQX2NZjp#1*PSP>Ct4hOXbe35sIN!v;+%mi*qp>3#=KWIVPty!VhBX+37BnjSy? zi2Vx)r@t5u#f)j1BWk)zbgTbdW_K^8#HMn>pkNv(Qe<<>d2oal8_@`rGu&YrxDkvL z(SaG$0IAx^lq2;{#z@N2V8D~2nr?*!$2Y7ch^{0FtWhk*!NU$FCUZrGLj9mPlBtZ7 zC^JNxpUB)nE2q?#rq3k%iu+9jpHC zr#wtN48HW#Q)r*%D?^VF2fKZNx?9xERi)Kaf{L?3jGN@f znh>JFGI&{BTI0^}Sz!Q>EuXD!fl)%UBLK0ReAz(&an*BcJtbQV+}Zj~RK?7}(-$ob zpptD}j*S@Fq|9=GW-1orE2Yq+22(&7OGl8N43`=65xav7*CA&DgN6l(%iView%cI5 z$F-yB<4QtPNrya0Y%P%>8J6jYX)0+#J2E>qN8xk|sDcB!k-P}Diu=MMZ3JtMM07hz zD0t-X0IK8B^ptHmJh19i3@Qnmz-urlU@oSpkxW^Rx9FvV;n5UyM`Rwxh{?jja4zy3 zj1wzrn}kLS^K=i)H$XMN0x^vqR#X9%GsClid}GUvu7$`-W5;6vyX|t{Ok`#}@2|<7 z$@S#u{LcA?D%>#;&W0 zUxV%U>$m3uYdosn^F%~7b<1_#tJO!=YEfnI-zRmbx{00B1t%qjex4 z5QaXi#oce0`&n@liF@y^xtjnlTg=kUhOawmsCI(9P4_{Ud11DIfV;r>0g@h|%0JNd Xb-{~kA8+%42vy;(bs6Iwu8scy0SE^) literal 0 HcmV?d00001 diff --git a/menu_handlers.py b/menu_handlers.py index 747e676..2d9f4aa 100644 --- a/menu_handlers.py +++ b/menu_handlers.py @@ -1,4 +1,5 @@ from ticket_manager import TicketManager +from utils import get_user_choice # Initialisation de l'objet ticket_manager = TicketManager() diff --git a/ticket_manager.py b/ticket_manager.py index 7c468b8..00159e4 100644 --- a/ticket_manager.py +++ b/ticket_manager.py @@ -1,7 +1,7 @@ from odoo_connection import OdooConnection import os 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 data_filter import filter_ticket_data @@ -14,32 +14,57 @@ class TicketManager: self.odoo = self.conn.get_odoo_instance() self.model_name = "project.task" - def _check_connection(self): - """Vérifie la connexion Odoo""" - if self.odoo is None: - try: - self.conn = OdooConnection() - self.odoo = self.conn.get_odoo_instance() - except Exception as e: - print(f"Erreur de connexion: {e}") - self.odoo = None + def _ensure_connection(self): + """Vérifie et établit la onnexion si nécessaire""" + if not self.odoo: + self.odoo = self.conn.get_odoo_instance() 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(): + """ + 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 + try: - return self.odoo.execute(model, method, *args) - except Exception as e: - print(f"Erreur lors de {method} sur {model}: {e}") + # Exécuter la méthode sur le modèle via OdooRPC + return self.odoo.execute(model, method, *args) # type: ignore + + except odoorpc.error.RPCError as e: # type: ignore + print_error(f" Erreur RPC lors de '{method}' sur '{model}': {e}") 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): """Affiche la liste des modèles disponibles dans Odoo""" models = self._safe_execute('ir.model', 'search_read', [], ['model', 'name']) if not models: - print("Aucun modèle disponible.") + print_error("Aucun modèle disponible.") return [] print("\nListe des modèles disponibles:") @@ -51,7 +76,7 @@ class TicketManager: """Affiche les champs d'un modèle donné""" fields_info = self._safe_execute(model_name, 'fields_get') 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 [] print(f"\nChamps du modèle {model_name}:") @@ -59,73 +84,78 @@ class TicketManager: print(f"Champ: {field_name} - Type: {field_data['type']}") return fields_info - 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)] - ticket_ids = self._safe_execute(self.model_name, 'search', domain, 0, 200) - - 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") + def export_model_fields_to_json(self, model_name, filename): + """Exporte les champs d'un modèle dans un fichier JSON""" + fields_info = self._safe_execute(model_name, 'fields_get') + if not fields_info: + print_error(f"Aucun champ trouvé pour le modèle {model_name}.") 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'] + + # Construire le domaine de recherche des tickets domain = [('project_id', '=', project_id)] if 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) if not ticket_ids: - print("Aucun ticket trouvé") + print_error("Aucun ticket trouvé pour ce projet.") 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 = {} 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] + #Vérifier que ticket est bien un dictionnaire + 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}" - if key not in tickets_by_stage: tickets_by_stage[key] = [] 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(' ', '_')}") - + + # Sauvegarder les tickets par étape 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) - - print(f"Exportation terminée dans {project_dir}/") + + print(f"Exportation terminée. Les fichiers sont enregistrés dans : {project_dir}/") diff --git a/utils.py b/utils.py index fe484c9..54baa54 100644 --- a/utils.py +++ b/utils.py @@ -35,4 +35,7 @@ def get_user_choice(prompt, options): else: print("Choix invalide. Veuillez réessayer.") except ValueError: - print("Veuillez entrer un nombre valide.") \ No newline at end of file + print("Veuillez entrer un nombre valide.") + +def print_error(message): + print(f" Erreur: {message}") \ No newline at end of file