From a23772179f78e507b00014362572c5786e50f356 Mon Sep 17 00:00:00 2001 From: bielvigna Date: Mon, 21 Sep 2026 10:54:38 -0300 Subject: [PATCH 1/2] fix --- .env.example | 2 + python/__pycache__/rpa.cpython-314.pyc | Bin 0 -> 24688 bytes python/__pycache__/test_rpa.cpython-314.pyc | Bin 0 -> 1015 bytes python/rpa.py | 192 ++++++++------------ 4 files changed, 81 insertions(+), 113 deletions(-) create mode 100644 .env.example create mode 100644 python/__pycache__/rpa.cpython-314.pyc create mode 100644 python/__pycache__/test_rpa.cpython-314.pyc diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..06def96 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +URL_PRIMEIRO_BANCO=seubanco_legado +URL_SEGUNDO_BANCO=seunovobanco diff --git a/python/__pycache__/rpa.cpython-314.pyc b/python/__pycache__/rpa.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3763e2a236a743cfa864b52ab56a14927642df90 GIT binary patch literal 24688 zcmeHvdvF^^n%@8fK)eV7AOOBekP<0^BB>WiiF$(%Nt8%}8d9VrQ-VPdA|ZnSJOCsa z_Ii2FRZJ^!iC$YVeY=je*-h4)auV&Xlhq}Ab-ML!>Z+2e3_Qevo@Huv6<4M5pH5cF ziBpy2`{soKIC^=#TX$7E5*sr;-Cw_^`}=*5?(LQ`BLUZITYhr+t098;Z}=cyTH)r4 zh>9SVh>HZN8YYtbuPUkHp4CY;_gj+Gz^{5(dqS7gHDa2Q;nEZOr2d2WdZ6j4%ppUl@o~r+W4H@&OSW_ex znDfuYlA-7okD4tDQlUUHLKq8n7FNQn`cc;6hEfTsCibMm_ z^I|BG2+YG)= z_zc8!&^hYV33xw1g#+%Gdod6XxaUId;lYGE5n6Ny5{cAem~y|E3b|tmE_x2qQQ>)b znfT-fF*g-TCBu<$`jhWp3563~>0U1+4n&jTxd6}+NWqVg2cr_na5M(LS3*z*2J8ukjK2z_nDf8D~=Y$RrC#hhog zUBp?P_+|^?Cbkn&pTYM~M}*alL_N{S{Tr!2P*P8XRUT9N_;3`a3(O?_lR%6JK_$+<6XfNML{-y3aG=> za36AS+qvxks}9eldZ5x=sh|7^=Xx*(lRgA%K{NG6r7 zk)eS}cNA9CD}hMcfOlfDZNHS+I{_7+4@O{p@p#lyHL~tRr7+?+IOZ*CvV71}sTxS&s}0o(b!_hpA5`~ATbUYOaJZ=sh_8dc#Fr2(5%)?BuU~up+S0Z4a@(@ub3=M0T&hQ?dn>w5DI^Bd-OCh5APjI)1DKX9+T>vr?c+keu&)_!n>T-Dq>`>y{j z|C+P)Zu7G7H>Qf;RMc;{+;r1f#&wRio?EYJ_@Lvxjt#5hKB00Q{DM%~4&AfWuAaU1 z%EuFT>_3~Pt%trS)zn(P)Dz{dH|GCQ_=A%7O1`VTZy?I5?i&faQN8YJShrW+HxUN=LqcaTez}D(x*zT)%r&d+ zw`M=w&X{^>P4Aa`AQoEoRRZVWPrT;AO6^}9E`{)yI`2XF`O8Yv;5I{0VxN!-yOdj% zm*56iD}?+tF0H#_3_&!>7_K-U`IQ{D#VHm|Eq?N2DjbY;u|!%uGw9K>>i8V12`8cfR!xQ2 zl0<42f+PiKMhi15MkTPxt^7A*iG&y9z~<({^WkJHLE!{G41WnM8o8X|^{cO4T~59J z>T9olR&LucmEGvQ-n-JWYX9EEE$4U7Gp06L(?&TUbwN*J>$#q!IXb^pPfDCF-%Coo zOkAbMC7uRF3zzsVt|HZO^^C;R#&MDsT}{MvlL&hX$8`$EmBWW6DKBI?4kH0@g2y`U zQ0HPG9B~6!30)4j0qq0>;cEf+EEghhS%i1-z$ZTfSOjYv6-acArFUL@F3_3wcAnqa z+1KT7+1`0!`%zg8JkE3p&lg_a-SMrho>B_&2elJ}JrJ;37}{_gaAoXjh@y6JQGhIi z3xI)O{en8NCho_NLmgwau#SeV@u){!Im#1(XgC>8!-v2nr49j^LHJ9Yf?(5UP_bcm zu7uuh&)J(Z_GZSuHD_Yjhea-+Ww^NR!^>`_tTo*C@X0`5gDg#(RUZ0Y#W=`%xPURFCgG&t;K zb;n8X*x(R_nhF%_V2$Ex0LUmBzszb7HnHW&z+xb}5R+b(3K48s0uiqiujL*A_ay_# z6o5VN(Y72j(6H^#Sq$RuB3J zDD}k%TPA>19>R0Llfn;_HAv{6)dKj9yu>kifqA4ag(DGaFQzL2Q5WlO{{=)fLC!mi5qWf4w8()UxI?Ux6 zuh-Jfj*k`@!@;Hg4OjJQ^V{=FJ_+=0=*w@IubXKO*>VW@UPaw%_`}NE+wSbUdzQA3 zEuH$@R`G7#cj^EMFVC*jWi1WsddrG>rT3;WXLV<+?jJTV)}1+PcgEVCv+mDW_utvg zSPx5y-nG1C$yv8%tXmmt>$<*rb@yuc{X@Chj!bRGhtDy!eYx7BncAawE1BBib$#Wk zef9YJwI4LT*O;sB%2anT)jQYq)|KX!vo||<=6dV+hts#icdO|wrx^Q4&OVW`PtcRk zG4`2teZ@QEyEAXiDK6DU{%9ZrnN0vr5%H8XF`^xa@ zHG22(!xFVsw{+s6p3s`P&a2pc`v|>z;O-&%^aXnSB5m`pX`cVw;d=MfcTUmnBY)Yz zIL4Po*G&~mzRf5PmvD}Gj{xTSJuq*TY}u4lS8?@}>YHcY(>AiCfO$xI323u$0ccQQ z$KrA?vJ5#D02;r8x=yJ`jl2{k{l<*MKE-+|X?~R${6&+P1Ip2&b_0HO&t4wnDiv}U zLK+2a%#5T{l9g@1+%ooCCyqn@7eJH|$|-;$WC_F+jwLQ+MOPjSl4eM6E@+>Ej{I?q z9)OFCI#4TwA!7xE^4coS#K!e$YzLXE{JhJj6)uyG!o<%ax2+Fw#q;>st<}Ta;x}-kuKRc^sL{h zS)>AhI4{a+rC6Qp>(!Fm;OlLZeZ8f!l9j(+6SVlr-<|~JK}0K_ksh-Bw`!sEU5_Jj zF1kp4mx|oJR}fa>`YHY6c;7q-njpC5=d+&dAge?jQ-Nsk&Gbem^hTiw`;GNR+%Wwf z*;(){Dy^i_H<9l+ag7`A%*Tm$7mXLoMWIJdJ8qaVJho>64;1vQf$V~BZO2~3LvTUg z@;4AZLS3+PN)6)Lt|?uixHhE$!E1LqEec-yQC}XIjiI%Ql1W{uL@GdqL5qxLt;Ilx zD2>TdOL4pcNa;;76bW65MMHks%XD?2ASZj3>Vo$0!dyYM@{+(Nd5-FbETC+UUjkt& zeo0V6>BWLsi+IPsq4q;^>Hr3uzz4QFdEKZ%q^5GShD2-@%n&I1iTbS=YURzB!c=HB zkO=ux;khx^oD9cfV5;EyiZu=RM!fw#(mUuQsbQp~A0GnQa%^&lLdk+HFX~ka?Q-dQ zK^7}|DF}SjY0Qa&saT8_M3?y(s|kQ~#+IWx-X8@MF^E*rWGrpm)z`PPbLTGj&sw>c zyeymy1OqYFGM9w*qHN4RTjL>WF$@Oe7+5S5AjwiEp*ZRk1|t}ZV(BK?7^SO)>dNAT zsgK3e9HiOO*--LoC=~S=St}=h3X(kZN`lpc9Lm+3x`=sD zbQQ;uPb`i)CU%L5`RNEkZZZ&A2(fk)aycp>6bqtm6<3t3OC*sPi{!c-YIq5|IDr~z zwB+y?F90`50dn!o8_X)y2rhZ5eTAzBO+;-#T>o3!_ zy}u}9%8xCbK)JZ>?M0M}%U^%>^;ef(mE_`~HT^MOE-tUURhO;m%2n;jRPCX=2Xo!S zneJhx`xH|(k}V%yI+4$8uDCIBeT1$aUNfIuuW&98->cht>&*MUT-~lr-L8+jm^yE+ zZYWbXl&d?HsXO&o{Y;&2>BRDL8BO(iljp{r zPG@b?>w4$PwOeJ`%C2?2YgL0m8A|2t3KF-?fD*TXlek?(l?McE+R<|B8omDF zwqF9&|kN8JTz$C(0O$at%TKnOCzDT{Lz(_`IU1^SD4a9 zupes6?pp}6t$XiOt?BzgKVq@on7Iyv_K0$;SNG9dyR)@Dk1n_t z3$~Ol9fMLV4!X7@>*&lmc4Zv9a*m#iqla(fd%@4H)-)t~%aP<2)G-u^b-d;IRV=ri;5 zu&cg>kB@lAhE94%0fGBiy%et>Iy*FQX29!X zjmN+wGctT0QyT_+V*?{+h6jDTXrM>Y0ILDsG~r|lTpKvyzyKaHV6_1-{XcP8m;lQ= zAH&&~c-U{^aCbNw4u)f_0U)<)@}hx;TEgIuFvvsNJ^0AsS`NsXz*`7v7D6Xe-ZnEA ziX;OZ)b(hoKZew-V-ws&w?)oaW8{rp=*&nx4`~zVQX%skDwmKZ*Q^BfBY6LxFyRki zsgqe+_O5C6NtTxOk4728{-yrS29_gh`lCFMG?VtEQX&oO64|xt;Df#5lq3Q)sN&&)6YHPsZMJ zJIdIH5J^(2+i!k*U0=IO-qOE6ldIpEso(jr{pSroZ2+!+;8*3$uJN^rGr0->rxSiU zG`}{nklnSw)Q1sPaOK^vFe1h@au~CUFxO*ykKZ~*_nZKTY1Z8*)LPwU=I?~jzn=q^Dkolq=__>WuyiD761&$mGR1802!rS&MKZ63{(`ev8VMe zk(m1?W=gt%rb}KcC6m=uUJejwJq_k4je+GV%PI)NWWG1WwMyqAM=% zfZ=`|PyZ*7jZ<~;ZV?oLx~O`@O?wef*E$ z+~K2+;v0>31lT`@UY@g;0cX!wmS^kw*z9FsfLCJFD&{eQCJTHWaMu9O(shEfzj1hU z+(%A&IrJcSZ^S`u#_?^VjOzD+Oxnfq?tHAM)B9)e@*CjXMQSOJj}=BODUH<|>}x#-IB4km_&YFY&!XrtMwR^h->8$H(=I z;fNAn9bVI)l$dt<&9R)hF=KALWy!hsWZZigcMoIky<@*KlISW0lV!F9#%~TSw zpH@^aFy|_5D*;D}rx5yof1*0}fQ$<$b7LLwl|u8_(}~&=Is#ryGXsuQ}qfdBVQ?>_N3);~u51hTLtR~KSDkG2=^Xg^YAF%A+G zNc@JY3xOoZp;!&xvbL)WFZnO0K*qiVLOO8t08)J^U4xQ(K|FwxOCXrf1z6M71=#Z7 zkHlan4XfoIQZ{S??tuXHLMX@)n~QNNqoO#khm=uHiie_Mx1enycGbTaLw`=(yMy+& z5{OFM2V&?_2^*x4p@IWH5VCM-Q^5s)5b7FZ_2Tn?7r)d3NP+FAHbDxEEr1l|Uqcyy zDu6tYKs5yH|3JkMY+~Z!1dm?|;l%ThAOY-zLk_$}siF`!aAQW%kfaU}Lt?oQHVmz4 zj!Cd#&&L-S!+;WO7+lj2eWthE14;keS#x8~yftIqdh24YWnZRcAJej*F(0@yd)N7A z;XjZ5X_OuffvVgcleV>FE#Op%UX#9Tbw{pxXQq1R z?FObA_;B6o>@DZ};SUzyTckUVG7Z4BQGV9lJkQJC+V{`p>N_*_opjeprv4N!nQy!K za<0;osr1n8hnPx`=AA1^9$#F!c{1l{$v9emIQyf>_anL1-b`yR)4DI$dMML+h-p2- zIG|7z>Scucnfd`vr?7hdeb6ZE$kgn(tzl}=LQw3fS!qU@-ptALb%fRhr2@iOFL$on zJYO2LN&>y)YgzRNhyY*gCN`)9HvSGm1VNWyN*Z`={x=}dKP!aLZt6QF-FY5DpaP)K z2TX~PIwi*FlIyG?Z-HiAk*P{~udsgIE2zrx^OV{jCMe}lno z3=kl)rGPH5YSd5h$s(!w`N%GaOqaj`46Kpc=IBQ!AnHG3QXwJ`xe{YbQwS*LLaa7U z1=2B2cR>9NQc)>PY5+tMng>-vfamTSu0W2jTRBw$J{NFw%h%=Td$3SW$B&=AM@QYk zi2s5CYVA3F0QIMM_dEoNVkLk@V&+(-1oaz8bQJmcuL}8i&zh!J;^Ur=4l#y4B|eTi zdx4KXTKM;6`$lqor!#%0Y4Z6T8Oo3$hMZ^m7P97rtRbw#$pyM7`q!_5Cf>FJjJ9_5 zyr8UqO3}ai%|&I&Ul9%1S4g_5E$j5;oI5klowx0bv-@@`>pXaey4(I|ujYjMbnZ86AEPPY_5FoPg3XEs z%L@3D@QwoCwLqwuV|R*hmU0Dx=}2OI#1n};Ap0Ay>rbrS#qjvcy` zhami8+EEBX_}ElCl)&pH0&XGSV}S@NC8*07tYGjW255w!7!1((kart+Dv)6Skii#B z-9QrWVt^J0>OBm8h5_d}%)9RLegmNJ2t=sw!aJ7tAOME}KWtjO0>0S+a4AiKS16pb z0-G8CDdy))5jvq14qMncY{ADApbPugKo@(k3=UXunu8zU;}0ogbXX`t2_1&5J?vKfzU;OFCyZv`x{*{mEnOvJb zmzxfLIvu3LFRV>RvON)|9-!1FjfB&LIK;%^kQyF`wB7Ed2hZj-=QiUIT-!Jt@+ySC zhmD`#xOc%H>=oDpfI~#rLc>$J78-%?6>N@rX53*0As5{Rp9OoM1>&1_FML+?!krQU z^(gSA_!<}@?{p|^+2ox03UH3hI-rq>{2JVbi57w)j$5yAMnjy6%|>8{fh6{e5ouQ; z*A?SshcvjwxV#1+wrNw3paDR}%NjXpADr|ihB(Qe9GdWraL#)=Z~qVpmVE;kH6=Oj z!TEc@9r-G<<+2BVJP@Y-9kMEk?XsG9C=r{7IJoW|;pO#@;hy@p5U?iL9>eW`f|GcT z@r)HtLJ$s@;Erw3izV^ec?WOs7fLVSwO>b)FXFYFB(LQq`69mhJXE*X@y;&E@M0ws z)EAKMUF4~$LY`W1%0cHx-(n1hlz1v0+w~+lo<0ax{Lx$=ndu|xiTT__Br_3VCZbGV zENhNs4RIxwDiB7N>RgH7wg^2kw&#?+?&`CaMrDb;dKCm};C*zpC+lj@xw`TgKUzb9QB%U5pcU3~=)FBl%>6v4cXMv)(&bQal$f&pO-gE2!UBPTsmg z?;HEFMDnJCVUR98+;RNQ&W*{lc++*kQC~h4oI3;(JAXay@G6C)8{upsD{&0e|J*?b zaL_{WCEEroonj+m_D13qxC0xA2H3Q(WJT{5h=HY1h5zzfL-C-i#~(F_DTKoYOSqH& zyuxW7cv=h|zW2$$gsoX=QWE*=~yeLL7XnUePD=wb$u;-pe<6o_U|N0ZMYQa!6gRb2I-`t!raiJ1h-a3aqH z%wm2^CHns3QIyeWD43+0FkXvMIyj1RF`R@m;Y#roK{zjyLP?udy~v#e2ipz>3$b`2 z4~3s8cW`Hd`rnZ9&tc(9pg1Y>20yW;8GfKPmg|;|KXehsDmZO1V`$77n$`?WwR&I%D*-@{cQ{o}GPO9^8$q=)~X8k)XJ;11BA zcYAxZf6-&=*BOHFC|#v+$1TC%;g;aqC-1M8lwGhF8#{r!#03u)WTePl`JFFu*_n*f zDsf*p9NmUBq zuM$;I#n<}F`fbE7ZKeTT$uH|#22>@VsI+j;>iyt_8jkw?9@Ut~&Gjv-hI2x9K_*rg zhlAn3^0b3{;15ZsaE`J@IFT$6!IO1gq7e7+i;xK{a^WDl+JxZ{v*EF0K2|p$!lTt$ zBc6HgBZrR|R;@YZ*;M3M6CYVGZj-U zs0HLDRWUjY%+S9G=H}28QQSE5_LuT<%|7xU8jqzh+yfxZZ+PB*~Z)aLBPK-hq zYv3JnG!Pnu__Xd25)Kkl5A3EV?Gz!Bp8igTkZqT7;kmx!2ZXRRmWNqFxS+zd2o-|4 zwWgesgd>Dl9#U+(marVpBLrXC&|g6;lNYqahEOv3PeejKcAi1;>j3BZscMPh8K7Xp zafa-*PF)`nvG1nu3dvWfA^*LbH8#*+#_Q>!UdBRsv98ZXBt`@z4-JjGy-Dw}33O#Q z{0@pUO;ME3kogW1=Sf{DzTQ2}%^&CH&jFpYH0Y_r=O48zpY+th%FC5kYddApigX literal 0 HcmV?d00001 diff --git a/python/rpa.py b/python/rpa.py index b3ad52b..3082292 100644 --- a/python/rpa.py +++ b/python/rpa.py @@ -17,16 +17,35 @@ engine_destino = create_engine(URL_DESTINO) +def criar_mapa_ids(ids_origem, ids_existentes, ids_gerados): + """Cria o mapa de IDs sem assumir que os IDs da origem são reutilizáveis.""" + if len(ids_origem) != len(ids_gerados): + raise ValueError("A quantidade de IDs de origem e destino deve ser igual") + return dict(zip(ids_origem, ids_gerados)) + # inserir_df -def inserir_dataframe(df, nome_tabela, schema): - """Insere o DataFrame no banco de destino definindo o usuário de auditoria na sessão.""" +def inserir_dataframe(df, nome_tabela, schema, sequence=None): + """Insere o DataFrame e retorna o mapa entre o ID de origem e o ID gerado.""" if df.empty: - return + return {} with engine_destino.begin() as conn: - conn.execute(text(f"SET LOCAL app.current_user_id = '0';")) - + + ids_origem = df["id"].tolist() if "id" in df.columns else [] + if sequence: + if not ids_origem: + raise ValueError("Inserção com sequence exige uma coluna id de origem") + ids_destino = conn.execute( + text("SELECT nextval(CAST(:sequence AS regclass)) " + "FROM generate_series(1, :quantidade)"), + {"sequence": sequence, "quantidade": len(df)}, + ).scalars().all() + df = df.copy() + df["id"] = ids_destino + else: + ids_destino = ids_origem + df.to_sql( nome_tabela, con=conn, @@ -35,6 +54,8 @@ def inserir_dataframe(df, nome_tabela, schema): index=False ) + return criar_mapa_ids(ids_origem, [], ids_destino) + # Tratamento de dados def limpar_digitos(valor): @@ -74,18 +95,15 @@ def carregar_empresa(): "criado_em": pd.Timestamp.now() }) - ids_existentes = pd.read_sql("SELECT id FROM public.empresa", engine_destino)["id"].tolist() - - df_destino = df_destino[~df_destino["id"].isin(ids_existentes)] qtd_enviados = len(df_destino) - - if not df_destino.empty: - inserir_dataframe(df_destino, "empresa", "public") + mapa_ids = inserir_dataframe( + df_destino, "empresa", "public", sequence="public.empresa_id_seq" + ) logging.info(f"Tabela empresa -> Encontrados: {qtd_encontrados} | Enviados: {qtd_enviados}") df_atualizado = pd.read_sql("SELECT id FROM public.empresa", engine_destino) - return {i: i for i in df_atualizado["id"]} + return mapa_ids # Tabelas usuario_sistema, colaborador, email_colaborador, telefone_colaborador @@ -112,7 +130,7 @@ def carregar_usuarios_e_colaboradores(mapa_empresa_ids): # Tabela public.usuario_sistema df_usuario = pd.DataFrame({ "id": df_colab["id_colaborador"], - "id_empresa": df_colab["id_empresa"], + "id_empresa": df_colab["id_empresa"].map(mapa_empresa_ids), "nome": (df_colab["nome"].str.strip() + " " + df_colab["sobrenome"].str.strip()), "email_login": df_colab["email_limpo"], "firebase_uid": None, @@ -121,18 +139,15 @@ def carregar_usuarios_e_colaboradores(mapa_empresa_ids): "criado_em": pd.Timestamp.now() }) - ids_existentes = pd.read_sql("SELECT id FROM public.usuario_sistema", engine_destino)["id"].tolist() - - df_usuario = df_usuario[~df_usuario["id"].isin(ids_existentes)] - - if not df_usuario.empty: - inserir_dataframe(df_usuario, "usuario_sistema", "public") + mapa_usuario_ids = inserir_dataframe( + df_usuario, "usuario_sistema", "public", sequence="public.usuario_sistema_id_seq" + ) # Tabela public.colaborador df_colaborador_detalhe = pd.DataFrame({ "id": df_colab["id_colaborador"], - "id_empresa": df_colab["id_empresa"], - "id_usuario": df_colab["id_colaborador"], + "id_empresa": df_colab["id_empresa"].map(mapa_empresa_ids), + "id_usuario": df_colab["id_colaborador"].map(mapa_usuario_ids), "cpf": df_colab["cpf_limpo"], "nome": (df_colab["nome"].str.strip() + " " + df_colab["sobrenome"].str.strip()), "cargo": df_colab["cargo"].str.strip(), @@ -144,39 +159,29 @@ def carregar_usuarios_e_colaboradores(mapa_empresa_ids): "criado_em": pd.Timestamp.now() }) - ids_existentes = pd.read_sql("SELECT id FROM public.colaborador", engine_destino)["id"].tolist() - - df_colaborador_detalhe = df_colaborador_detalhe[~df_colaborador_detalhe["id"].isin(ids_existentes)] qtd_colab_enviados = len(df_colaborador_detalhe) - - if not df_colaborador_detalhe.empty: - inserir_dataframe(df_colaborador_detalhe, "colaborador", "public") + mapa_colaborador_destino = inserir_dataframe( + df_colaborador_detalhe, + "colaborador", + "public", + sequence="public.colaborador_id_seq", + ) # Tabela public.email_colaborador df_email = pd.DataFrame({ - "id_colaborador": df_colab["id_colaborador"], + "id_colaborador": df_colab["id_colaborador"].map(mapa_colaborador_destino), "email": df_colab["email_limpo"], "principal": True, "criado_em": pd.Timestamp.now() }).dropna(subset=["email"]) if not df_email.empty: - dados_existentes = pd.read_sql("SELECT id_colaborador, email FROM public.email_colaborador", engine_destino) - emails_existentes = dados_existentes["email"].tolist() - colabs_existentes = dados_existentes["id_colaborador"].tolist() - - df_email_filtrado = df_email[ - (~df_email["email"].isin(emails_existentes)) & - (~df_email["id_colaborador"].isin(colabs_existentes)) - ] - - if not df_email_filtrado.empty: - inserir_dataframe(df_email_filtrado, "email_colaborador", "public") + inserir_dataframe(df_email, "email_colaborador", "public") # Tabela public.telefone_colaborador df_tel = pd.DataFrame({ - "id_colaborador": df_colab["id_colaborador"], + "id_colaborador": df_colab["id_colaborador"].map(mapa_colaborador_destino), "numero_telefone": df_colab["telefone_limpo"], "principal": True, "criado_em": pd.Timestamp.now() @@ -185,22 +190,11 @@ def carregar_usuarios_e_colaboradores(mapa_empresa_ids): df_tel = df_tel[df_tel["numero_telefone"].str.len().between(10, 15)] if not df_tel.empty: - dados_tel_existentes = pd.read_sql("SELECT id_colaborador, numero_telefone FROM public.telefone_colaborador", engine_destino) - tels_existentes = dados_tel_existentes["numero_telefone"].tolist() - colabs_tel_existentes = dados_tel_existentes["id_colaborador"].tolist() - - df_tel_filtrado = df_tel[ - (~df_tel["numero_telefone"].isin(tels_existentes)) & - (~df_tel["id_colaborador"].isin(colabs_tel_existentes)) - ] - - if not df_tel_filtrado.empty: - inserir_dataframe(df_tel_filtrado, "telefone_colaborador", schema="public") + inserir_dataframe(df_tel, "telefone_colaborador", schema="public") - logging.info(f"Tabela colaborador -> Encontrados: {qtd_encontrados} | Enviados: {qtd_colab_enviados}") + logging.info(f"Tabela colaborador -> Encontrados: {qtd_encontrados} | Enviados: {qtd_colab_enviados}") - df_atualizado = pd.read_sql("SELECT id FROM public.colaborador", engine_destino) - return {i: i for i in df_atualizado["id"]} + return mapa_usuario_ids # Tabela pdca.ciclo @@ -227,8 +221,8 @@ def carregar_ciclos(mapa_empresa_ids, mapa_colaborador_ids): df_destino = pd.DataFrame({ "id": df["id_ciclo"], - "id_empresa": df["id_empresa"], - "id_responsavel": df["id_responsavel"], + "id_empresa": df["id_empresa"].map(mapa_empresa_ids), + "id_responsavel": df["id_responsavel"].map(mapa_colaborador_ids), "titulo": df["nome"].str.strip(), "descricao": df["descricao"].fillna("Sem descrição"), "status": df["status"].map(status_map).fillna("PLANEJAMENTO"), @@ -237,15 +231,9 @@ def carregar_ciclos(mapa_empresa_ids, mapa_colaborador_ids): "criado_em": df["criado_em"] }) - ids_existentes = pd.read_sql("SELECT id FROM pdca.ciclo", engine_destino)["id"].tolist() - - df_destino = df_destino[~df_destino["id"].isin(ids_existentes)] - - if not df_destino.empty: - inserir_dataframe(df_destino, "ciclo", schema="pdca") - - df_atualizado = pd.read_sql("SELECT id FROM pdca.ciclo", engine_destino) - return {i: i for i in df_atualizado["id"]} + return inserir_dataframe( + df_destino, "ciclo", schema="pdca", sequence="pdca.ciclo_id_seq" + ) # Tabela pdca.plano_acao @@ -276,25 +264,19 @@ def carregar_planos_acao(mapa_ciclo_ids, mapa_colaborador_ids): df_destino = pd.DataFrame({ "id": df["id_plano_acao"], - "id_ciclo": df["id_ciclo"], + "id_ciclo": df["id_ciclo"].map(mapa_ciclo_ids), "nome": df["nome"].str.strip(), "objetivo": df["descricao"], "prioridade": df["prioridade"].map(prioridade_map).fillna("MEDIA"), "status": df["status"].map(status_map).fillna("RASCUNHO"), "origem": "IMPORTACAO", - "criado_por": df["id_criador"], + "criado_por": df["id_criador"].map(mapa_colaborador_ids), "criado_em": pd.Timestamp.now() }) - ids_existentes = pd.read_sql("SELECT id FROM pdca.plano_acao", engine_destino)["id"].tolist() - - df_destino = df_destino[~df_destino["id"].isin(ids_existentes)] - - if not df_destino.empty: - inserir_dataframe(df_destino, "plano_acao", schema="pdca") - - df_atualizado = pd.read_sql("SELECT id FROM pdca.plano_acao", engine_destino) - return {i: i for i in df_atualizado["id"]} + return inserir_dataframe( + df_destino, "plano_acao", schema="pdca", sequence="pdca.plano_acao_id_seq" + ) # Tabela pdca.plano_5w2h @@ -313,8 +295,8 @@ def carregar_plano_5w2h(mapa_plano_ids, mapa_colaborador_ids): df_destino = pd.DataFrame({ "id": df["id_plano_acao_5w2h"], - "id_plano_acao": df["id_plano_acao"], - "id_who_responsavel": df["who"], + "id_plano_acao": df["id_plano_acao"].map(mapa_plano_ids), + "id_who_responsavel": df["who"].map(mapa_colaborador_ids), "what_acao": df["what"], "why_justificativa": df["why"].fillna("Sem justificativa definida"), "where_local": df["where"].fillna("Não especificado"), @@ -325,12 +307,12 @@ def carregar_plano_5w2h(mapa_plano_ids, mapa_colaborador_ids): "criado_em": pd.Timestamp.now() }) - ids_existentes = pd.read_sql("SELECT id FROM pdca.plano_5w2h", engine_destino)["id"].tolist() - - df_destino = df_destino[~df_destino["id"].isin(ids_existentes)] - - if not df_destino.empty: - inserir_dataframe(df_destino, "plano_5w2h", schema="pdca") + inserir_dataframe( + df_destino, + "plano_5w2h", + schema="pdca", + sequence="pdca.plano_5w2h_id_seq", + ) # Tabela pdca.meta @@ -361,8 +343,8 @@ def carregar_metas(mapa_ciclo_ids, mapa_plano_ids): df_destino = pd.DataFrame({ "id": df["id_meta"], - "id_ciclo": df["id_ciclo"], - "id_plano_acao": df["id_plano_acao"], + "id_ciclo": df["id_ciclo"].map(mapa_ciclo_ids), + "id_plano_acao": df["id_plano_acao"].map(mapa_plano_ids), "objetivo": (df["descricao_meta"].fillna("") + " - " + df["objetivo"].fillna("")).str.strip(" - "), "valor_base": 0.00, "valor_alvo": 0.00, @@ -375,12 +357,7 @@ def carregar_metas(mapa_ciclo_ids, mapa_plano_ids): "criado_em": df["criado_em"] }) - ids_existentes = pd.read_sql("SELECT id FROM pdca.meta", engine_destino)["id"].tolist() - - df_destino = df_destino[~df_destino["id"].isin(ids_existentes)] - - if not df_destino.empty: - inserir_dataframe(df_destino, "meta", schema="pdca") + inserir_dataframe(df_destino, "meta", schema="pdca", sequence="pdca.meta_id_seq") # Tabela pdca.tarefa @@ -411,8 +388,8 @@ def carregar_tarefas(mapa_plano_ids, mapa_colaborador_ids): df_destino = pd.DataFrame({ "id": df["id_tarefa"], - "id_plano_acao": df["id_plano_acao"], - "id_responsavel": df["id_colaborador"], + "id_plano_acao": df["id_plano_acao"].map(mapa_plano_ids), + "id_responsavel": df["id_colaborador"].map(mapa_colaborador_ids), "titulo": df["titulo"].str.strip(), "descricao": df["descricao"].fillna("Sem descrição"), "prioridade": df["prioridade"].map(prioridade_map).fillna("MEDIA"), @@ -422,16 +399,7 @@ def carregar_tarefas(mapa_plano_ids, mapa_colaborador_ids): "criado_em": pd.Timestamp.now() }) - ids_existentes = pd.read_sql("SELECT id FROM pdca.tarefa", engine_destino)["id"].tolist() - - planos_validos = pd.read_sql("SELECT id FROM pdca.plano_acao", engine_destino)["id"].tolist() - - df_destino = df_destino[df_destino["id_plano_acao"].isin(planos_validos)] - - df_destino = df_destino[~df_destino["id"].isin(ids_existentes)] - - if not df_destino.empty: - inserir_dataframe(df_destino, "tarefa", schema="pdca") + inserir_dataframe(df_destino, "tarefa", schema="pdca", sequence="pdca.tarefa_id_seq") # Tabela pdca.problema @@ -456,9 +424,9 @@ def carregar_problemas(mapa_ciclo_ids, mapa_colaborador_ids): df_destino = pd.DataFrame({ "id": df["id_problema"], - "id_ciclo": df["id_ciclo"], + "id_ciclo": df["id_ciclo"].map(mapa_ciclo_ids), "id_problema_pai": None, - "criado_por": df["id_colaborador"], + "criado_por": df["id_colaborador"].map(mapa_colaborador_ids), "titulo": df["titulo"].str.strip(), "descricao": df["descricao"].str.strip(), "peso": 0.50, @@ -468,12 +436,9 @@ def carregar_problemas(mapa_ciclo_ids, mapa_colaborador_ids): "criado_em": df["encontrado_em"] }) - ids_existentes = pd.read_sql("SELECT id FROM pdca.problema", engine_destino)["id"].tolist() - - df_destino = df_destino[~df_destino["id"].isin(ids_existentes)] - - if not df_destino.empty: - inserir_dataframe(df_destino, "problema", schema="pdca") + inserir_dataframe( + df_destino, "problema", schema="pdca", sequence="pdca.problema_id_seq" + ) # ATUALIZAÇÃO DE SEQUÊNCIAS DO POSTGRES @@ -517,7 +482,8 @@ def executar_rpa(): logging.info("--- PROCESSO CONCLUÍDO COM SUCESSO ---") except Exception as e: logging.error(f"Falha na execução da migração: {str(e)}", exc_info=True) + raise if __name__ == "__main__": - executar_rpa() \ No newline at end of file + executar_rpa() From 5b6cbee0a5959ec3a77d1ca2e88eaad545c47c2c Mon Sep 17 00:00:00 2001 From: bielvigna Date: Mon, 21 Sep 2026 11:13:54 -0300 Subject: [PATCH 2/2] fix: ajustando ci/cd --- .github/workflows/deploy-producao.yml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/deploy-producao.yml b/.github/workflows/deploy-producao.yml index 61fd378..15c16ef 100644 --- a/.github/workflows/deploy-producao.yml +++ b/.github/workflows/deploy-producao.yml @@ -24,17 +24,6 @@ jobs: sudo apt-get update -y sudo apt-get install -y postgresql-client - - name: Exclui banco atual - env: - DATABASE_URL: ${{ secrets.NEON_PROD_DATABASE_URL }} - run: | - psql "$DATABASE_URL" -v ON_ERROR_STOP=1 <<'SQL' - DROP SCHEMA IF EXISTS public CASCADE; - DROP SCHEMA IF EXISTS pdca CASCADE; - DROP SCHEMA IF EXISTS auditoria CASCADE; - SQL - - - name: Aplica todos os scripts no banco em produção env: DATABASE_URL: ${{ secrets.NEON_PROD_DATABASE_URL }} @@ -60,3 +49,9 @@ jobs: env: FULL_URL: ${{ secrets.NEON_PROD_DATABASE_URL }} run: papermill python/dataload.ipynb /tmp/dataload-output.ipynb + + - name: Executa RPA em produção + env: + FULL_URL: ${{ secrets.URL_SEGUNDO_BANCO }} + URL_PRIMEIRO_BANCO: ${{ secrets.PRIMEIRO_DATABASE_URL }} + run: papermill python/rpa.ipynb /tmp/rpa-output.ipynb \ No newline at end of file