From b467452fedd46cbe8eb89423634cb998ca9c5c72 Mon Sep 17 00:00:00 2001 From: MrWaradana Date: Mon, 17 Nov 2025 13:15:30 +0700 Subject: [PATCH] feat: add collector db connection and maximo route for each equipment --- src/__pycache__/config.cpython-311.pyc | Bin 3434 -> 4148 bytes src/__pycache__/main.cpython-311.pyc | Bin 4858 -> 5301 bytes src/database/__pycache__/core.cpython-311.pyc | Bin 7427 -> 8628 bytes src/database/core.py | 50 ++++++++++-------- .../__pycache__/model.cpython-311.pyc | Bin 5113 -> 5325 bytes .../__pycache__/router.cpython-311.pyc | Bin 7450 -> 11472 bytes .../__pycache__/schema.cpython-311.pyc | Bin 11067 -> 11518 bytes .../__pycache__/service.cpython-311.pyc | Bin 14452 -> 17065 bytes src/equipment/router.py | 13 ++++- src/equipment/service.py | 47 ++++++++++++++++ src/main.py | 5 +- src/modules/equipment/Eac.py | 4 +- .../equipment/__pycache__/Eac.cpython-311.pyc | Bin 15130 -> 15130 bytes .../equipment/__pycache__/run.cpython-311.pyc | Bin 2793 -> 2817 bytes 14 files changed, 94 insertions(+), 25 deletions(-) diff --git a/src/__pycache__/config.cpython-311.pyc b/src/__pycache__/config.cpython-311.pyc index 1c86c540a64a326586a062264b829acaa488a744..953269a3a35863538a009578c449dde0b95eb310 100644 GIT binary patch delta 895 zcmaDQwMBt%IWI340}wpDTjHZMif&$6GN(Kidu^L8op&r3=FG*7y_cy z!7>`Dk|~;NgqJY`<$xF}rvaAJO3}v9r3seN!6Kstlwrw|hnWH738rc-o4l7%rd}JY zNjF6b!#JH3rBuz?3@OT~+CWkzRUORM0FtVyI{DVXM9DQA@gfmJ?3as??4K0oI z49zU`4D~F`Zt)=$2KWbsWLk(aFx-+v2s#J3y14p+0Ut|e%X^|C>xFr>j)fli{a@gg-R)B1f ziVqC+4{>!tQU;SNvI7~;i!eJh$nzFQh^Mb>l)s;WYmq(30S+L74@B^T2mv7Ri^C>2 zKczG$)vhQ4$YliL;swl`*K(CJ)qh}P5R;h@c0tDAf|dUT(SVDh0aru=E-(n)5EP#v zc0od8Mez=o3xbXp1s$&lIyUg#kX4=$c0tAdgwzGukc+Y*S7bvjFi71{R9_%=LBst* zVDtsWn2U-rR}^C|Fv#7I)tC`FCwhhO3gHX7-WN4}u4wvPl=ZzL>kHBYvY;Ml!v;Mh z^FOdK2u3np&~W;|0AfJM3$F1f+@um@E|6Wp=mu2^m+)i?M-gh^eZVc*zyb8Hh|~nL R3sO24%t9{+PqyGS0svG4^B@2K delta 175 zcmdm@@JfnrIWI340}vd{<;i@=F_BM#X$sRujVLC&6tyUICWche6!jF1HGIpM7#LOq zF$6?ufMqmOv@m2eQy7C8G_^O+V7|pVnT4yGQEPJpR~)0|EshXRU)LyqKi48lpdm$8 rAc7A>@Ph~eAn}XCCO1E&G$+-r$PdV61mfatOq+Xo%9$p=<97o9P!T6x diff --git a/src/__pycache__/main.cpython-311.pyc b/src/__pycache__/main.cpython-311.pyc index bd90e128cab4928df6d253e78726ce200c47edd5..5a613dff8cc061dae2dea91cc2bdb792a78218ce 100644 GIT binary patch delta 1300 zcmb7EO=uHA6rS1bW;fepYinzoHi~T;+f_+h`{Pe)ZKBm`{Xr~(5nR&U5mS@hFuTPf z^#}3bK~R~C-g>HndeETYwTR#?m560Q5HE^?)SLDo&LoDmpa_H2i@# z6%>FrfTc^~v}k~9oB=M0mql_&3PWEh`CFbEa$8awR~|G`8ZkRyW~EULJVIuq;Qu=+ zdT;6stuA)J4lrkzJ2iG#Og|R+{3AU4R zkjok4xRuGi-8ERRrVkZzlhZ{AK`7P`qsxwYecsOxR_$lI~;LP(zv>BW#< zkb(=5{z9aGKC+`2*>OX>=`TvHBq3kyTu>TrW{XP4J0{YznaihgIXpmKDO;{=W#BFb)@WMFG%Y$z=A&t)iI+z#XVZp}K^IaOVb*ZWg1buE z)H9O?(y%q@AdI&$4NC_1Qnn-wWG!Pdg9dSqitI0A-=ifg@({ugp!#a*$>(YoVCZlG n{Bz~c)NX>|dRYG;un6dDnSJL0*I0ysMUKJr&ek2PV&Q)S%$z)S delta 1020 zcmZuwO-vI(6rS1bcKeI`|H^NnPzh@JF`@jG0*W9S;bbpO=?tXU?l!aANK{0P2d`#s z#sjBn1P`Y1;1Qw+Z^U>od-CGNQ%y867!%*LX@tbLeXsBP=Dq#i%cC4D4bveS-k2`q*ibQ3A=2FjvjGSIsmZDY>=a>GaEf8Z zO=?MG)pE{lCTPY?>YAk_Qo2Dcg;8h;sO*9$btdAeR7|6rCOcEY=4Q;)Z&JE+ArF8<7Y+kWWq|9ZU63vRy* zDSzr|9S|~ar_po>?T_Nf2-*+1A|Cm`CP!5HKvhKUk%2!L@z1pIubx+pVo(+qVFrRB zLjZjcf9Y*j<^iqn58fI3AY_51e04>)RBcrlEOC~W_inH*zEs&(IRPcC8-gXrGZ{9< z?^O10_X~1Vkf&m?xTfiFc(7|D7Au}`=}2AaBq1q!E6ykr`(;Acsz_>yRF=>f)3YXJ zEGSmQ98A3KM~?BdPO>RF$^7bRS=>y=D`_e*xV(@QMHc diff --git a/src/database/__pycache__/core.cpython-311.pyc b/src/database/__pycache__/core.cpython-311.pyc index 0ed6dde33acef6d87d1811a4c530ab5555041ba3..30be8c6d5a95ea2cda17f0eb4fd9de5340fc6895 100644 GIT binary patch delta 1899 zcmaJ>ZERCj7{2G;wrlD7LD%nX-DMx`O4$e75Md74$~FnGAtOO6VRpM`TeWM)xg9d2 zVF^q02S0!laVjy8khuwof%?xF!!LgrlM<)a`$zqwn3z~2`a|Q#`?gyLM7{T(=brOE z&w1bT@t$*b_xn-HI~L0(0iL6Ox)UE?u=vD2@j~+#y6O5@{e{13&oqbv4bXbUu57;H zSds+cI+W5AYXr_n8)z-C@ZP9e(gP1=jc+RQjsM996(=PVqT*W6#X@U!H`JS{i`GKl zDjGVoM8GAK(la6Y)~iA+T$VNek7}zZ3<`>8K~TI)BKU$*dSVd>VWyEAz6X29wo$L* zUl5i+cKyk9wGe9obM=~e!`iCUEC{hSaMfPyu)%CsYH5cOpzaBsQU|l@RLmnnSk8V& ze1wP>C7)TdM28O^?2jHDKEhqnCEW^P0sfxUt#1M^?ffUHnomicPcs>sVUcZtpH?}Z zPA2JiCe22bG1dSk6<0@QjmX+3X=d~RMHtMTsImae3wg8ST+gbxDW_{&W(<*L@471>` z7L?mUFNo*jT6 zJAyER(1vgvV0}D>gfa@#*cj4~t)5KsleQBkobyn)lC|-?EpT8Mdk!JsSz{dtJph_< zhNWjH%gmKxp>q~Tz*S=Cq`15+f;EI)yw|>HC=!wE*Y*_>ULjhAN>w$HPTijhW=w@O zJDY}{r}$0Bow{HAP`y~!A>1UrZNjbEUYm5=CPDRfTTP!?%Kl*AMVj|T)2R$Sosp;G zsrV#iawaX)lse0(oSC9>Il=f|$Cfjma&t7U&ZWl7L8oxc354emo(H&pfUXB~b$zHO zL3S3Up8(v2cV76_AlQO;tf5tFXnwFDR_FtHtLs%u>E)VZS94e*UF1LO7RPt=+haVM!Ai)%5C2GLb3eg~%QOLer<>)3B`fcWy-w266xHk-TU4^JNenTAfOieUTS7DK8yhL*k>-f5-!f%eDFt6fHD(jTePXSA z$luU{M~mTL9?II^jgL*ImDwcS!(Im#ZwvMB?A!iM;_k@_|MKx~s~cozC|4EC>o(_2 z*1XwPsL(mZp9O$>2sy#dNlkOuE>wpWx8-%tyxEg?`SUhs-da^K8>&GIa1S9TcnWsD zwZ?zg0OxvfvLL|kVK4|5_xa?N!7GEy16PNy4d>jQtCd~zro1b#_|o#RJ|-e7%v-hf(wMJHKptCA9=^n AD*ylh delta 1254 zcmZ8gOK%%h6rMYN+jZjFdHR)!?Kl}bc8wbXa+?U(=>yVMf>a^|ig7wOslhMZv1!AC z78*9Rs#G1R>I$(yhPH-{Ss_)T{~(nrl+F?fBqSC{AQrHIa~!9Fj(tDtp6@&79)IV~ zw}opt_m^(BQ^MzA^+5T@wQEpbRXB=vlc#)cbf0K4XC^Q?hhY(nhu=ZL}?87Os9)991qJ z?~Jt>#&PxI(|k-D-aeX>juM*cYisgPF&JnH_sZ#Ne9s?}1rT zkcEnJRYc&1ZH~>sLtAz_hA~w(9K~X@v|i!GqG7vGue7Q)T_h=ojCcn#{B}Rf!t?ed zi*-x(LoA%3L}r-urnoDM!?-ipWu5#~6x4s?c!A~MXU8;)!GJTFTOheCs1L(b*7T=J z3fPKet%!v_@D^HnvtB(`(s{Zcn+M&=@z5vEf{YbIZjBY-V|Ol|rA%j8FW2;Dsdj-2 zI>NA)8Vz34#1d{)o0x?k-6zqd#6X5EcHbPBV%X0|gAbyoh_{Fn_#@5`oFzyQypBMv zLdoO9)Rrg0R^g84Z3lUERy`mR@Q)|F{0eDbCZHQEQUos|81{y!H@IkS?Nz68;w=KQ zOi&RKA&Boo0xllBW9<>~uGM>&sc5WC=el05*Lo8`M^S9JHhHtykBd!=Y{IVZ5!2z` z&_wsr=rB|IQ?XR)m$(p%DEY6^;w9*eCD~=z9ZMgf^Hh@|3vO8SMy1@`^Gp`c(NUIK zsfrEk&l5vJ-G>(B#}{xUSH^EMHV-3#NcJ`2mI>w&4Esi_QlSdsJ=~(1R8!0$Jgo@t z1{T;&_$r_e^*Uj_`l?yfN%}q<3eKSUtHGdU3j^0-E4bLRNKM0o;IFI#_d?5Q!^&%$ zi5sV4WE9N~mx?98J+J5mfS-ar|XJTCHoX3SSi0k)?aDpYMJh xKFmDn9qE5x_;Vr(N0lk&g-;bfv%~kwsQjB1{!mt#A1+P?nF60oCe8G}{sqSJAAtY> diff --git a/src/database/core.py b/src/database/core.py index 9c6361b..b029e0e 100644 --- a/src/database/core.py +++ b/src/database/core.py @@ -15,7 +15,7 @@ from typing import AsyncGenerator from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import DeclarativeBase, sessionmaker -from src.config import SQLALCHEMY_DATABASE_URI +from src.config import SQLALCHEMY_DATABASE_URI, COLLECTOR_URI engine = create_async_engine( SQLALCHEMY_DATABASE_URI, @@ -23,6 +23,12 @@ engine = create_async_engine( future=True ) +collector_engine = create_async_engine( + COLLECTOR_URI, + echo=False, + future=True +) + async_session = sessionmaker( engine, class_=AsyncSession, @@ -31,13 +37,22 @@ async_session = sessionmaker( autoflush=False, ) +collector_async_session = sessionmaker( + collector_engine, + class_=AsyncSession, + expire_on_commit=False, + autocommit=False, + autoflush=False, +) def get_db(request: Request): return request.state.db +def get_collector_db(request: Request): + return request.state.collector_db DbSession = Annotated[AsyncSession, Depends(get_db)] - +CollectorDbSession = Annotated[AsyncSession, Depends(get_collector_db)] class CustomBase: __repr_attrs__ = [] @@ -113,6 +128,18 @@ async def get_session(): finally: await session.close() +@contextmanager +async def get_collector_session(): + """Context manager to ensure the collector session is closed after use.""" + session = collector_async_session() + try: + yield session + await session.commit() + except: + await session.rollback() + raise + finally: + await session.close() def resolve_table_name(name): """Resolves table names to their mapped names.""" @@ -122,20 +149,6 @@ def resolve_table_name(name): raise_attribute_error = object() - -# def resolve_attr(obj, attr, default=None): -# """Attempts to access attr via dotted notation, returns none if attr does not exist.""" -# try: -# return functools.reduce(getattr, attr.split("."), obj) -# except AttributeError: -# return default - - -# def get_model_name_by_tablename(table_fullname: str) -> str: -# """Returns the model name of a given table.""" -# return get_class_by_tablename(table_fullname=table_fullname).__name__ - - def get_class_by_tablename(table_fullname: str) -> Any: """Return class reference mapped to table.""" @@ -149,8 +162,3 @@ def get_class_by_tablename(table_fullname: str) -> Any: mapped_class = _find_class(mapped_name) return mapped_class - - -# def get_table_name_by_class_instance(class_instance: Base) -> str: -# """Returns the name of the table for a given class instance.""" -# return class_instance._sa_instance_state.mapper.mapped_table.name diff --git a/src/equipment/__pycache__/model.cpython-311.pyc b/src/equipment/__pycache__/model.cpython-311.pyc index bf48a1730cbf6daeb71f28e4c6bec614849d3e8d..476bc8f06debaf17ab67f74696f43641e8c57ceb 100644 GIT binary patch delta 267 zcmeyVepZujIWI340|FxF#PaxpqSTbk@cDj7gIPh1XhJG3tC^z$6x^tl-&@`w=Ab1&ef%9?~{3^`= diff --git a/src/equipment/__pycache__/router.cpython-311.pyc b/src/equipment/__pycache__/router.cpython-311.pyc index 0b126e17445740eb797a11368d756cfa40de9c28..02dad94cd044579d0a4b78b001bbef85ffdfba71 100644 GIT binary patch literal 11472 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 delta 2366 zcmcgtOK%%h6rQm?e#Wo7Y{zfM&%7T^t0pKZNqLlpG;JzThDC6FZ`x2#V(!>TbU})g zRRyKd-4*dtrJ#sNMJhs(V95^%hy|J*yGktR4uOQ=oEbZ@6R8m!CZ13Co_o%BzH@x8 zv;X>oNza#Vw^M+p?U8@=Ue}fRJ!D?Poh_Jv#lIeVscE0%W-{gFDI0woYdEj za!ToydzG}D*7weGM#;)qQ7{SAvJI-fLTUOV=V^ZBfv9?!vLMfVep=yCqca=Nqco& zc9J^AHwJv;$OpBeF8>L9po=7^|A5m;dXI6Q0?r=fd_f>-;A}gAGo#mrpNMqfG6_(SnC_58 zgW<)0wad~7>{I-lL*?E87c=}}U?$U#{w5YcSB%{;8L>mKR`Hf45^R{;f@9JIY|ilM z;G#InKMH;zj`K+9bgUj9hC@A`ooWfJgbLCbu*~vLLm2pbXny7_`i>$D18D6xXmy#s zU0bavbgjBvWt6f>I9PyJbpt$szdOQHs~|b%UK!v$#?4zKdP@RAkt-gfct94TIi{Oo9yJH!8p;mia(5A`bBj(Mfk7X3;ZRSv!B?Ov%_ZhQAX9yODPW4KE^K57|oyFC$z)cm+VU z10?E?(FAu(6WBaByvhHLehV%8Cicjsm-{NlPw};QTBQX(KGpGTTmggJnM#VGCy8tOtmPR2L% zRybHEm^G=wR@bV`iY_*UiwKK&>sDwZYP) zT6zs!KIGG>pFp<1*D7}Dow^2A&8(<5>ILwDbn^Tl7})>-l~kCV^*8Jk#Z=dVQLet=FoQX=WHY%?#g}W>%|=jR-P?;5BD>W%WzQ@aE{_s97rO z;DvLdnJcv_!`3tG1#7*~f7zddWVJG#X1Cx}gjc<9;c{+{@8#awaEYd;!v7CFf_O>X f7Z%d+9{KztXw^3SZWv9X1f$tII+C{)KpO*omESS1EwDwE+XTb1g@NY~+?gMZ?U zT{N!;4<0a@z=?p3iP0z?O}wK2et2)AZF9V9)91`=jr!8U%tLL^&{Cd z&$8QU%;)Vlx4hzAZdz=#_#3oWjk6ZWu}(y9>^-9nQT~{%-0=tNb4HeSKAa?|?ZmOGauUJop_(zef=-r>7Mu zD`!-BMoP<5s-(<_xsUA?L0&WiS5L+) zaz@O_>Q#8|93(NQcdgrp&2nV~kwBb;4OhStsDtLZs3x?Q$x%MGP+Cg7j_bT&@WLuD z=nQv%bWVDZ))`X^1|xik9b7v6EcnKy50B}wlo3m<#!@=tp9?SSd(7*MsZ$1%g7-De$|#7su)KwL!7k(W^A5P8HD zqJWr2R5VEFA;z`kz(Zm+C9TMHekbws7+x@OJfYZ7yAfN(c6_D9|LTmmpf}KWY-`K^ zp0(nb3N|6~TDa>vAqvcguJn}9Wi}(lkE&v$iXP%742JuO3b(@(-qUE(zeO*Y-T`e; i;b+)Ku7NM|F4|3wkhPk>^A5L8lZ`*~Gg_eAOMU@LPVDml delta 690 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 RJL}1#3-1qKVl^b<{tx&7wTb`$ delta 2601 zcmbVNZ){Ul6o2=%eI4EUf9+bfwc9#iWo~RRP{0i$Q$b{ah<_TTl)l?hc&{z@y)uU# zg9MGD(Tod0BL+wWL;RvPQA7M71ofjBznGU8zW70jF(eX=Mx*E4vOgq{5MSEUcYf#G zbI<=?c!}IBdER%sT@pO!F8cHD^iO#r@^v!R_3(g0RkQ}(opS4*l!r*FQ?1jyx-aE} zcZWJp^Xq|BKo6#ZdSj{)FiCZ3Q)hc8iPoBG1PEi9#?5PEFPKBxaHhB=_3x&K#i&H!D&>L`L*T{KkZz_clIQf zPPk39dh0Rg9uD0R4Wr5WrWsk^v4&+N%D-)E;u{+(WIp%zg!m7R2EZ=GoA^m3ocLcx z7xF`HFKOnN+CzT%Joxy*jGq%8e#RLk9`18CJG>R|nHHYu=~y)3tN2)J#i!b4JL7Q3 zTs`foBncV+0^Hi-adi7Zy$+x`jaDEknFv(u6=%g&@mB&BcTR!y+xa2Sa2u-!OH2lm zj4>O&dx2O=F0YxCu_(XpO(+eByLr$z#6AA4KFg8QiWxH*WDyiZ`M-l9KH)#kuLKU- zu_k(>F$2w!8nt$I9of>eJBUnY*<0Fn5ct>J2>IYRiW(dB%yrDL7A zsRcv~!)2{VVo2gZlJ;8{!n@_zSEg*-QW!PLnrYYpYlDY9#ILo49FGC}1pl+;2pO&( zY;7Yu9syt^>8yn~bBiVC)w^7>5e~5RNHzeOV;?wc-Y7NGyT;RSdIb1q`QI@{hIug_ zBb%#l#J`pw-GXWky{J-clLADvylx$Jgi-kGPDF9>e7(Qw^(Y=hOu@rDStAWH~0(lthykuL49N{uY*h1Xgp7vkG+JpiRhiAY{|WSwOru{i}wFx>7e|n#8)g!3=N}d-JED==Sfrv7hbIi!>Fl< zI#~|0xR*kE@srD9eo+SuxmE`ludkNHT>Am|5WdDaPU4MR9ykHsy*2P2S>p!Oa*XY! zjMgl70U#Qq*0*_+iCe{mj#^`kp^AZIJ%4e<{C3g9jia%)kI=wU|(7w1tgfzP^@T3A&f~(kJ zVEXu%5A0Deb*#O*eB~hN#(!MPX;3p`noJnA?E5p!fYgf7M1!nmpTE6oWkC3^luI}} z>?jCV@*h{7wWCR~@lLOn$p*25#_{APB%(6HUt7)S%>FTOD?|2cqpiR~pK=r8mNh!W-USw7l?+nHm#-NO vue>VV;lr=2ITgI2tR~9$!Im2~{O#(7Up}?Axe>S<((R;Dy}9-yBFgd~auPIp diff --git a/src/equipment/router.py b/src/equipment/router.py index 6d5f9a1..213cf94 100644 --- a/src/equipment/router.py +++ b/src/equipment/router.py @@ -23,12 +23,13 @@ from src.equipment.service import ( delete, generate_all_transaction, get_top_10_economic_life, + get_maximo_by_assetnum ) from src.modules.equipment.Prediksi import main as prediksi_main from src.modules.equipment.Eac import Eac from src.database.service import CommonParameters, search_filter_sort_paginate -from src.database.core import DbSession +from src.database.core import DbSession, CollectorDbSession from src.auth.service import CurrentUser, Token from src.models import StandardResponse @@ -55,6 +56,16 @@ async def get_equipments( message="Data retrieved successfully", ) +@router.get("/maximo/{assetnum}", response_model=StandardResponse[List[dict]]) +async def get_maximo_record_by_assetnum(db_session: CollectorDbSession, assetnum: str): + record = await get_maximo_by_assetnum(db_session=db_session, assetnum=assetnum) + if not record: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Maximo record not found") + return StandardResponse( + data=record, + message="Maximo record retrieved successfully", + ) + @router.get("/simulate/{assetnum}") async def simulate_equipment(db_session: DbSession, assetnum: str): """Stream progress events while running the simulation (prediksi + EAC). diff --git a/src/equipment/service.py b/src/equipment/service.py index 6daddf0..4ac3eef 100644 --- a/src/equipment/service.py +++ b/src/equipment/service.py @@ -17,6 +17,7 @@ import httpx from src.modules.equipment.run import main import datetime import math +from sqlalchemy import text async def get_master_by_assetnum( @@ -120,6 +121,52 @@ async def get_master_by_assetnum( ) # return result.scalars().all() +async def get_maximo_by_assetnum(*, db_session: DbSession, assetnum: str) -> Optional[MasterBase]: + """Returns a document based on the given document id.""" + # default worktype; change if you need a different filtering + # worktype = "CM" + + # where_worktype = ( + # "AND a.worktype in ('CM', 'PROACTIVE', 'WA')" + # if worktype == "CM" + # else "AND a.worktype = :worktype" + # ) + # where_wojp8 = "AND a.wojp8 != 'S1'" if worktype == "CM" else "" + + # sql = f""" + # SELECT + # DATE_PART('year', a.reportdate) AS tahun, + # COUNT(a.wonum) AS raw_{worktype.lower()}_interval, + # SUM(a.actmatcost) AS raw_{worktype.lower()}_material_cost, + # ROUND(SUM(EXTRACT(EPOCH FROM (a.actfinish - a.actstart)) / 3600), 2) AS raw_{worktype.lower()}_labor_time, + # CASE WHEN COUNT(b.laborcode) = 0 THEN 3 ELSE COUNT(b.laborcode) END AS raw_{worktype.lower()}_labor_human + # FROM public.wo_maximo AS a + # LEFT JOIN public.wo_maximo_labtrans AS b ON b.wonum = a.wonum + # WHERE a.asset_unit = '3' + # {where_worktype} + # AND a.asset_assetnum = :assetnum + # AND a.wonum NOT LIKE 'T%' + # {where_wojp8} + # GROUP BY DATE_PART('year', a.reportdate); + # """ + + sql = f""" + SELECT + * + FROM public.wo_maximo AS a + WHERE a.asset_unit = '3' + AND a.asset_assetnum = '{assetnum}' + AND a.wonum NOT LIKE 'T%' + """ + + query = text(sql) + # Pass parameters to execute to avoid bindparam/name mismatches + result = await db_session.execute(query) + record = result.mappings().all() + if record: + return record + return None + async def get_by_assetnum(*, db_session: DbSession, assetnum: str) -> Optional[Equipment]: """Returns a document based on the given document id.""" print("assetnum service:", assetnum) diff --git a/src/main.py b/src/main.py index 94d37c2..ca47123 100644 --- a/src/main.py +++ b/src/main.py @@ -29,7 +29,7 @@ from src.enums import ResponseStatus from src.logging import configure_logging from src.rate_limiter import limiter from src.api import api_router -from src.database.core import engine, async_session +from src.database.core import engine, async_session, collector_async_session from src.exceptions import handle_exception @@ -70,11 +70,14 @@ async def db_session_middleware(request: Request, call_next): try: session = async_scoped_session(async_session, scopefunc=get_request_id) request.state.db = session() + collector_session = async_scoped_session(collector_async_session, scopefunc=get_request_id) + request.state.collector_db = collector_session() response = await call_next(request) except Exception as e: raise e from None finally: await request.state.db.close() + await request.state.collector_db.close() _request_id_ctx_var.reset(ctx_token) return response diff --git a/src/modules/equipment/Eac.py b/src/modules/equipment/Eac.py index 01e63d7..f615b7e 100644 --- a/src/modules/equipment/Eac.py +++ b/src/modules/equipment/Eac.py @@ -159,8 +159,8 @@ class Eac: # Total NPV pada titik proyeksi ini = NPV aktual terakhir + biaya proyeksi yang didiskontokan final_value = float(last_npv) + float(discounted_proj) - # Gunakan seq penuh (jumlah periode dari akuisisi) untuk menghitung pembayaran tahunan tingkat - # pemeliharaan. Menggunakan hanya selisih dari seq aktual terakhir + # Gunakan seq penuh (jumlah periode dari akuisisi) untuk menghitung pembayaran tahunan pemeliharaan. + # Menggunakan hanya selisih dari seq aktual terakhir # (sisa_periode) mengamortisasi seluruh nilai sekarang selama # sejumlah periode yang sangat kecil untuk proyeksi pertama dan menghasilkan lonjakan. # Menggunakan row["seq"] menjaga periode amortisasi konsisten dengan perhitungan lain diff --git a/src/modules/equipment/__pycache__/Eac.cpython-311.pyc b/src/modules/equipment/__pycache__/Eac.cpython-311.pyc index 2830049413318000680a4cdf7130203d3dd59f3b..baa1835347296c1ca9e67eab86c17ef4f101e468 100644 GIT binary patch delta 22 ccmbPLHmi(xIWI340}%MHl*+W;$jfUD07;AmwEzGB delta 22 ccmbPLHmi(xIWI340}w1fCzR>5k(bvR08O_BF#rGn diff --git a/src/modules/equipment/__pycache__/run.cpython-311.pyc b/src/modules/equipment/__pycache__/run.cpython-311.pyc index 4cfcbd2e2c8ec566bb4d134e00fe83f982ff31e1..7c85b9b35acdf52dbe15c3a72782d45f066e04f0 100644 GIT binary patch delta 103 zcmaDU+9<}moR^o20SE-Yh-IGK$lJllm@s)7<5Qkyh7^Ws<}ilo%xx^2tC<{G7>y_I yW$P4@ke^sNrE-Ei0&ezU-_9s%yo&(y5dl^g(ajQ^7a0K$RUKFW delta 78 zcmZn^dnwAhoR^o20SG+$1TznBL` iol(@xS(@RnG?xoM^I=ta7cS-_T&ynqn+-TGG6Dc#vK2u9