From 591fd85ffb54616ed6c2568ff78e9d0a44a2bb88 Mon Sep 17 00:00:00 2001 From: Ladebeze66 Date: Tue, 18 Mar 2025 15:00:32 +0100 Subject: [PATCH] modif --- __pycache__/menu_handlers.cpython-312.pyc | Bin 6211 -> 2735 bytes __pycache__/menu_principal.cpython-312.pyc | Bin 1545 -> 1036 bytes __pycache__/ticket_manager.cpython-312.pyc | Bin 7981 -> 6106 bytes menu_handlers.py | 116 +++++++-------------- ticket_manager.py | 84 +++++++-------- 5 files changed, 76 insertions(+), 124 deletions(-) diff --git a/__pycache__/menu_handlers.cpython-312.pyc b/__pycache__/menu_handlers.cpython-312.pyc index c4a3838cacc11d11a41c495aaaa84837cb1ab36c..dad4e16e64aa336ceb1bb8cf0d3e42f1b0fe0026 100644 GIT binary patch literal 2735 zcmaJ@OKcNI7@pm=y^bBnuH!sNlV)gu#3eXFMX4H43n3Iz+El74R8k>p<6V+XY_Bsr z8?ft0QPl&No_Z)1Mdi{8%7tSNJrr{6#l}HNR;hX@m)@$Iek#=@G|6}Lz zec$ijW3dQ=R+-$p!MS@;@F2IT5g z1rlNSKoM(&DHtk6swyP&FFTG1wyzvhq8SS-cSZ~hv!{%lzaYngr3EEtEoTI$o6MkE zEE&{{+pEs1a@L**=CvydF$F-08Mz?k4do2O3Sswcwm9Ae8(?OF!ONMODnk-8XyFTx z*W@KOL-tT-;KBI$;Lwa#P=+o~6G=aklZG@|)3Q|4@^dO3Dkw!GJ0lh4yh8L$scd%H zKeDV(%<{up!1a!?9Y+o2%hI7+kyjP3&>Fwbnj- zuY7N=mKuE&KK*RF6*ad%MLc8zQ)+ZIe0l>?2Vvs5&MxGq_^G4l{?XKESa=|E(C=ay zJ4K6SzrtWImQ~R#^Le4Y%r43}YqeRAW$p|@FR`b|waGj1O}*JY*%M^!kwZ5HpA}^x zEmrWS9{zo>EvORKwIa@GvSPdsUyQgsP^>wh3V7v~&me;LU^k&NTY&BopK{9z?tYz=A7eRVwJ&?F@ z2zhV}IAYI(*n1M{gK3ek@&M?VAL)+V3=15IQ&0ZoV3mJCtXXC^2+gwlc%fByN;?nI z&Mww!jGR0Slj@C4F2*aT^31d$dbH7~((vsPq|>NZJyGO>zn2IiEqZZU_ji#7Y~19Q|_ z&q6J5;Kh^?DuZxpt?Ruy0s-$$s!Y1dskR zbW3PEgc5J8#gEnF#~ShBrE?q6R!h}St%gT|ePHhFsr5`WIzFidQtM*Jn%Gkpdm3Wz zt+yYFhgh8mh@kFmowa@GM%!E0BJ0t%>!1I4>HAAJvi0cTW^+faYh-z3d9c=bcD4E3 zdUNtg5H%m!PM~1xT41;y7`|6|6c~NB9rY^(6qVzv;S+wPU;=YrI6N9c4??NY9^pYZ z2mM4kY~2N62Ve#bs^)dVcy1pxD|duxjkV=Fh%n8u4(WOLL_w2{yz(A70CVjBn$C{o qiNJB(A86oDl=&xwlD*&dF10)jB)Bo|>47MBp4;l`;7)T^p8o*I6Ox<& literal 6211 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 qM@^rj#=kpIu;Hf-`<}* diff --git a/__pycache__/menu_principal.cpython-312.pyc b/__pycache__/menu_principal.cpython-312.pyc index 3d9d55772444ffaca879a66743124fa39aa67a87..9e0330bbab95442b55d35af4b53bc2dad041aeb1 100644 GIT binary patch delta 413 zcmeC=>EYlz&CAQh00ip3H`6yUP2`i%lmYUlGo&!2Fy=7iGDa~nGNdrIFhnt>Fat^E zN)}Dlmmm$Aj1$+IGe%8(>LJby;)6g6%W6glE0{r(b#fbH7^@MGZ#cPt$(Ye-@)D+I zE|6ucKwPXb*^pUyay+vn3kQ%p@thcA(d5a@R<<%VK>M;a7#NCpNNX&L<7SEF1|a0N|yyyI@oS-3r+~`vYnj4s+29rz|CI-a;~2y z%Pn4z#`uiHyp){OqGE_uU`s(}6|sW|kW#Qszc_4i^HWN5QtgTafnp#h6pI3h56p~= Yj9;0U7@0nCuriu_7T{uZWdv&j09V6KcK`qY delta 915 zcmZuw&ubGw6rS0gB%5Rt8%u5VP}f@21x;#`DoRv}6k5^t5D*N=60(~nxBnv;*_)y!U0*M>;ldc=B8RJpYp`kwHB(3FW513Pp8{Kg&29p0qAw)=K2hkAg#kZY=#gx2$o0rva9Hk<6>m(K z#z`+H9LFEL72)cg+mB&v4`Hq}*@{4#VC{rYjO-%tV#vorHyYv315ZLe?lpzUE0MsG zTgjnXA9>vy(un`yg=4h6uExDLLN4y(ypIb$bogDk(T-ByBjq!qgaa>;Mmg}tRqRKr zb*E7^7$3PpWk6q|uB};y?a~P_$6>Pyz$V&Fqz9RqkD1x$(cNUGQ`)*Vkf-~y@+mdm zVO#eH@@!u|b6{lqvNRW3UR#`LpG3(cly|kN#{3k&K->RRDLb^npmbgW<$qBS85H?S%z3J_Y*>L{fL{i52nHC~L*==h6>iyr0t2>}!Ego?&C|}6 zL^~-0^a48fT%L2!z31<{`t7(o+V$sPP(m>3KfFJaj@<0($$Sr1A0Q2B*g!>`!I<)l z!DJYiS%WQd8LsHd_={B!5uvB8$?w)RWhvN3!sHvohs|FR>ni=~#i-zm41m7Y(I zu0FS#U5`!HrTa?q?{cEiJGOFlWomU#qv!R{8g=cjl5;+s-|0NtJG{u)$-PCxs-p$(5-o;Q-33!*$qJa zYmk6xQ_pJVARJ@v15$|rIbF#?_S$At+bmb+?%f_qGwo94K_ShyGaz)VSuRI8ifdd?F!onveEHH1?#88?(&Cw=_|ML-g@*p3 zj5mbwuV89b$*)6WT#ku@d2eiYmCcgJQZK0qG4fF)C~Hvc+wFBrI4JrcEj)mZk35VV zI6i}_oWq_$Qz^)t5Bf#)4DQU1&+Dlw@9-ABo^UvFBNVMUyeGiudYVIdRP{R?82dRG z1Jy!s$B4&iQXYALR}~!LnK}Wup*+KE>xni6nAUJaJJexo+lI`D9qD{Tj_6%Vg#?v}1VU4d!&ouQ-mV(=sf*nh8H8AQ(06ASw08o{!%8tK4i|=d0k!L*P`kb!*ewV29snV(wl*ZUPkCG0MTk!Rk3@$ErmXh+nEcycX*F;f=<_V zEJOA#?|VTdp+xk!7Wz%MC&tL$>xsYVAiDWQE$nS|6pw>yxB{-xerydb;sha5(Wk(f zNyo?+iBr4GAq*36gebhmkuqJP`%qm(;FrT#Y3TjoK!;@c}X)cHCM4**0OZhqw9!-}75h-uKd@(2(zK+}dx_KWk~3{AeJ-(eRL84V;|( zJ}_-5eOVt1FBZ#M#n8u19hUTgvuLeuvKZqBX!vhP{3klShE6{~{jmI!GcbM|Z=u%I GGV^~tctj)s literal 7981 zcmbVRU2qgvcJ7|(`EO|?%}5&kK?6e4$jitAY!(@V1xO%3m__W`$iTRnZb>8NNAB(c zG}W`qgCE8z5+F&HSKNr|-R|Z{Pc!@BH*v6%}3zN^s!OsJDrt{vH2Vg)tk?K7_^;B~SvL zppx`3O=H`ZunpVD)jn*8t3AOa9m9^KbJ&@54ZFw~CgDzchCQV1NO+UJVINJ|sAH7i z{FoA4_ie@#hS^D5*!_F_6Q=dbcZGENNII1gV@f=oIvuv_9+|%?M#trJO0O4Faz+xP z;`NENq(p_d1jEwOr0$!L;wdF6N>W+^Dc-*K&%Ar~T=eL`S@>5o5>F_i6jkD}D+o;WjP|ps-|0znOhG~I<*Q4RpY#&p@j9`b_Auv!o1qakFe1&1R;DXvCxS{q6 zKEeAjJ?xwGgjv1f9C_3rpW;VFDKi9ekBKtAP>P2&bAreji;+1wo}5U;N8%6eaqyZk zP8Kr>F)5}LE+NX?NIFIKA;l-+%7c3{C(1JP#l-~l#^|!Qz?ZPY?^z0}DM~5!-8ToR zRJ3?crN*t7Hhb|h?kM|XHp^zCbW#KwWLfy2;PmX-odc=35{E^~BCMMm-TY;xRQm~ucYwXA~f9!t-L#fSAL4lqr3icU? zY$)>9@3sF$DVs|b=nOqXg>9KG82FP1_ap$7$cYN~;GV)?2KaCZoAVZKF2T{tNnVoPR4-n9N1*Kypt4W*Tmw&a+y_1IP0&nbaXX{a44^KtToSM{2HM6U3v*(MQ_|Qa z;}8i6h`SmG5J9Ij5sMUM6ww9CQJdsYt zFDJyv>1-|6CrM&P;?ABq!X?s@jEvcyb+vOq|Io0v_i))}YfwDJD~gyBGQd67*sxow zgZGs1UJGv^x-%)_zUT}u zjmkPL%E**t0}bVmB|E&2#rEzc0U7Z_)G2+5| zi$}CGKU$Qv(9kk_4hKGUfVAiGHdy6fwj7{-)pEGm{_BQd?{53AceB_&7(85W|FYfz z?O3@X!QDpXd-e}dO;MxZH2i|j*+A*OtHRn!>M&`Cd7GFdxVwqEqEd~5%HB8CJ7-tz zvcF9A9H?}6Uy!dLv5=zL1!n`atfvE!x-$K6gFB1ZpLj3}RvQhnfNUC1K%+ea>Ossr zH^ECBxF_7eQRG{G0u1^`2u6`Z&jV(LUAip}@If1f=1vfGH`usQF$#Apz#$Q%g2)Sr zcuLfL0$6q_PGG3}lE~0xoGgW4J*Ra>f{~f6(C*E|N^4N0bmpq%L?qf_DH5Ix0J)?G z+u?@f#|oJ&%orH=7;k|~sRWae5ag(mj^a~vpV@^jlZ+dSm^UC_h34AqzB~z&vs=!U zJ3Wz&{b&Z{5vXoZ|JE>`uYBr!CHkFIebY)^N1?7`Hu%e}_qNWrE$m--ZSki|b(iwK zfAu#Kpzog#&o^nIzGb!_H0B$SwteQcS^o3LlD}JXcNvZfM)hw&$*+ zB=cr7L#b%Xy_r2Qw0C4A9vcHV@f`S9K*svaPr}+MUC!k!;45N>pBE;|;0=!T0z5Hi2d|?Hm8u=^ z=298KZQ$)LQ+n#nCP14F(B{dsf#4$A0A#T-J~?4hT@fbW{yn&-drhVQE&tC}oJP2a zOyDdknn;7O1`@QUKBps@0F3n>G392;x_YuMavPxJ;dXK7CMU!mPG=Oj0#OwKC80*> zj<_NwWz$F#M$>)fg5s$W2pPy95;9nALSeWi06B}ps!Pa<8q>2|Uj;pLz)6rG-+&4L zSl4KhRO8%*rMkm;-y@Tn4$f!i6N}pn%Uc9sRqM>~%&9qAtK7Zp-m}`)z0$U?(6(=> zZ9hWt7+qku=TA&~5tqlNPfQ<}nVf5!?=N&8(Oy5Q)$~1N`w^1I0FsTGyYXc&8IeKw z?F6vicfur^$f|G0&yhNjOomHI-ZgcG!laqDi(OnxPyj9xxsl6(PNR`#(M{`LjKhm)FqvC zqa+!Rl4w;?v6{>T4dHWBbI_^4Bu+N`qKbXX(Dy*Fj7apK6~3cePZTnp*d2hifDd5&lLRpTA+4n-<^F^2kspB!aI9>Dey+#{lpi% z{r}v%*)Pij@*_i|yg&!_-YBhro+0#$Z$sSxBU%a55k7^%|Y02 zu1rFlD|k^`1sB#i2GUukG|*+66jU9XSssBEDpcD@nGM@~eQr6s*vBl)B}Lfp-Q>13pa71yVO*4Ao`LlIfeZ|JRs1wwvw1PVSs5PUSqR z8*J#`fo!jWNf1(0Zz&45nC1mhs^cHV(Af_=7?#Ns&h3;vo&rU9pMdS9K+oWMXge+t>@+fN{iJ! zj{eAoq6UP}UpH8zX-WZ%8s&RDHy|`xlGGZj1$#CId)98r6`Is4IE&^NHJR8hTh0ZR zt0nUWjOF^uOqW7pG}EUe*;s{(C3qQ9JTi%6F}|G;IV0H2`mKbmd6l|BpwtT0bNX!( zNo2ieFsG|v@SCmRAPbLyC?p)k2nj%SuQ?S$L7|SnghR?1o_-Z_(1IAr222ZkR!k5>nGzFn2HD~?7)|qq ze~;0jPDgdOwZ!K%cMd)nxFJUh(MbAAwu(( zGw8DFb}-BmvZn5m#EAqS6D3qV=`dDDumTF8ba#@!BEm-!xy-@`4GVt~`)eSV4yjyG zh+4@AS+`R3AIV%eg>;gK)AJfAf)kY`4C{552`jzIY(ya?KxFDpwzypBxi#6D~zV~T(bw?6|={dH&T8z(iS^R zccW~b*5UIhb04zZ=U&KUe1|E(GL$R=Z$nnky?I`Pt$~b3sClJ6T&NFgoo~%wTokl( z?`h{_+MuAF61A~}mQu9Q%u@Z;Jp0Js1R?BnCkYuUw9vt2_RuOgH}yMadhh%UJem4P zo&|wHZYD4@uGMsF)w?0^^|ipDTZ5krPWQ|x+O9)d<2%c2FN}j&rMA6L+dj+u(tpoC zA6j^Gv3pTisvXLEANd>GAfC zzv}$FQ`>cT;qcP-V;Z{^W;X6xX?VTR@H#{~{6dTN_Pa|BXYwc3I(Er9=7+mh`|IMDo>{o}hiLYXR`Nny6p?4v%(m!12AJ#s&qKC*=QHzW$)s5zTu>SD= zm5zgjj)O}b@2pdH&shlKTJr<9b8Df-&s?9nK4U*+XFiw!@1~c^ysbp$bQW{$|Ttj!U#53&n&hbU)_dqovPDX-bP=d*C}!#lKvMlblxNY diff --git a/menu_handlers.py b/menu_handlers.py index 99ce47d..cc712c3 100644 --- a/menu_handlers.py +++ b/menu_handlers.py @@ -1,9 +1,40 @@ from ticket_manager import TicketManager -from utils import get_user_choice +from utils import get_user_choice, print_error # Initialisation de l'objet ticket_manager = TicketManager() +def handle_search_ticket_by_id(): + """Gère la recherche d'un ticket par ID""" + ticket_id = input("Entrez l'ID du ticket(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 n ombre entier.") + return + + ticket = ticket_manager.get_ticket_by_id(ticket_id) + if ticket: + print("\n Ticket trouvé:") + print(ticket) + else: + print_error(f"Aucun ticket trouvé avec l'ID: {ticket_id}") + +def handle_search_ticket_by_code(): + """Recherche un ticket via son code""" + ticket_code = input("\nEntrez le code du ticket à rechercher (ou 'q' pour quitter): ") + if ticket_code.lower() == 'q': + return + + ticket = ticket_manager.get_ticket_by_code(ticket_code) + if ticket: + print("\n Ticket trouvé:") + print(ticket) + else: + print_error(f"Aucun ticket trouvé avec le code '{ticket_code}'.") + def handle_project_tickets_by_stage(): """Gère l'exportation des tickets d'un projet par étape""" # Récupérer la liste des projets disponibles @@ -11,7 +42,7 @@ def handle_project_tickets_by_stage(): if not projects: print("Aucun projet disponible. Impossible de continuer.") return - + # Demander à l'utilisateur de choisir un projet project_id_input = input("\nEntrez l'ID du projet (ou 'q' pour quitter): ") if project_id_input.lower() == 'q': @@ -19,85 +50,12 @@ def handle_project_tickets_by_stage(): try: project_id = int(project_id_input) - if project_id not in projects: - print(f"Aucun projet trouvé avec l'ID: {project_id}") + if project_id not in projects.keys(): + print_error(f"Aucun projet trouvé avec l'ID: {project_id}") return except ValueError: print("L'ID du projet doit être un nombre entier.") return - # Récupérer les étapes (stage_id) du projet - print(f"\nRécupération des étapes du projet: {projects[project_id]} (ID: {project_id})") - stages = ticket_manager.get_project_stages(project_id) - - if not stages: - print("Aucune étape trouvée pour ce projet. Impossible de continuer.") - return - - # Afficher les étapes disponibles - print("\nÉtapes disponibles:") - for stage_id, stage_name in stages.items(): - print(f"ID: {stage_id} - {stage_name}") - - # Demander à l'utilisateur s'il veut sélectionner ou exclure des étapes - selection_mode = input("\nSouhaitez-vous:\n1. Exporter toutes les étapes\n2. Sélectionner des étapes spécifiques par ID\n3. Exclure certaines étapes par ID\nVotre choix (1/2/3): ") - - selected_stage_ids = None - - if selection_mode == '2': - # Sélectionner des étapes spécifiques par ID - stage_id_input = input("Entrez les IDs des étapes à inclure (séparés par des virgules): ") - try: - selected_stage_ids = [int(x.strip()) for x in stage_id_input.split(',') if x.strip()] - # Vérifier que les IDs existent dans les étapes disponibles - valid_ids = [stage_id for stage_id in selected_stage_ids if stage_id in stages] - invalid_ids = [stage_id for stage_id in selected_stage_ids if stage_id not in stages] - - selected_stage_ids = valid_ids - - if invalid_ids: - print(f"Attention: Les IDs suivants ne sont pas valides et seront ignorés: {', '.join(map(str, invalid_ids))}") - - if not selected_stage_ids: - print("Aucun ID d'étape valide sélectionné. Exportation annulée.") - return - - print(f"Étapes sélectionnées: {', '.join([f'{stages[stage_id]} (ID: {stage_id})' for stage_id in selected_stage_ids])}") - except ValueError: - print("Erreur dans la sélection des étapes. Format attendu: 1,2,3,...") - return - - elif selection_mode == '3': - # Exclure certaines étapes par ID - stage_id_input = input("Entrez les IDs des étapes à exclure (séparés par des virgules): ") - try: - excluded_stage_ids = [int(x.strip()) for x in stage_id_input.split(',') if x.strip()] - # Vérifier que les IDs existent dans les étapes disponibles - valid_excluded_ids = [stage_id for stage_id in excluded_stage_ids if stage_id in stages] - invalid_ids = [stage_id for stage_id in excluded_stage_ids if stage_id not in stages] - - excluded_stage_ids = valid_excluded_ids - - if invalid_ids: - print(f"Attention: Les IDs suivants ne sont pas valides et seront ignorés: {', '.join(map(str, invalid_ids))}") - - # Sélectionner toutes les étapes sauf les exclues - selected_stage_ids = [stage_id for stage_id in stages.keys() if stage_id not in excluded_stage_ids] - - if not selected_stage_ids: - print("Toutes les étapes ont été exclues. Exportation annulée.") - return - - print(f"Étapes sélectionnées: {', '.join([f'{stages[stage_id]} (ID: {stage_id})' for stage_id in selected_stage_ids])}") - except ValueError: - print("Erreur dans la sélection des étapes. Format attendu: 1,2,3,...") - return - - # Confirmer l'action - confirmation = input(f"\nVoulez-vous exporter les tickets du projet {projects[project_id]}? (o/n): ") - if confirmation.lower() != 'o': - print("Exportation annulée.") - return - - # Exporter les tickets - ticket_manager.export_tickets_by_project_and_stage(project_id, selected_stage_ids) \ No newline at end of file + #Exportet les tickets du projet + ticket_manager.export_tickets_by_project_and_stage(project_id) \ No newline at end of file diff --git a/ticket_manager.py b/ticket_manager.py index e264c5c..ef11efc 100644 --- a/ticket_manager.py +++ b/ticket_manager.py @@ -1,6 +1,5 @@ from odoo_connection import OdooConnection import os -import json from utils import save_json, ensure_export_directory, print_error from config import EXPORT_DIR from data_filter import filter_ticket_data @@ -43,13 +42,8 @@ class TicketManager: 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 - + fields_to_read = ['id', 'name', 'code', 'stage_id', 'project_id', 'date_deadline', 'description', 'message_ids'] + # Récupérer les données du ticket ticket_data = self._safe_execute(self.model_name, 'read', [ticket_id], fields_to_read) @@ -59,64 +53,64 @@ class TicketManager: # Nettoyer et filtrer les données du ticket return filter_ticket_data(ticket_data[0]) # Utilisation de data_filter.py + + def get_ticket_by_code(self, ticket_code): + """Récupérer un ticket via son code""" + domain = [('code', '=', ticket_code)] + ticket_ids = self._safe_execute(self.model_name, 'search', domain, 0, 1) + if not ticket_ids: + print_error(f"Aucun ticket trouvé avec le code '{ticket_code}'.") + return None + + return self.get_ticket_by_id(ticket_ids[0]) + + def get_available_projects(self): + """Retourne la liste des projets disponibles""" + projects = self._safe_execute('project.project', 'search_read', [], ['id', 'name']) + if not projects: + print_error("Aucun projet trouvé.") + return {} + return {proj['id']: proj['name'] for proj in projects} - def export_tickets_by_project_and_stage(self, project_id, selected_stage_ids=None): + def export_tickets_by_project_and_stage(self, project_id): """ 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 + domain = [('project_id', '=', project_id)] ticket_ids = self._safe_execute(self.model_name, 'search', domain, 0, 1000) if not ticket_ids: - print_error("Aucun ticket trouvé pour ce projet.") + print_error(f"Aucun ticket trouvé pour le projet {project_id}.") return - + # 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 = [] + for ticket_id in ticket_ids: + ticket = self.get_ticket_by_id(ticket_id) + if ticket: + tickets.append(ticket) + + #trier les tickets par étape tickets_by_stage = {} for ticket in tickets: - #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 + tickets_by_stage.setdefault(key, []).append(ticket) + + # Sauvegarde des fichiers + project_dir = ensure_export_directory(f"project_{project_id}") 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. Les fichiers sont enregistrés dans : {project_dir}/") +