From 052b94d8e0972ede129f0c2704d8f301599b959a Mon Sep 17 00:00:00 2001 From: MrWaradana Date: Mon, 22 Dec 2025 14:48:22 +0700 Subject: [PATCH] refactor: externalize SQL WHERE clause generation and update cost data column names. --- .../__pycache__/service.cpython-311.pyc | Bin 30775 -> 27991 bytes src/equipment/service.py | 106 ++------ src/modules/equipment/Prediksi.py | 239 +++++++++--------- .../__pycache__/Prediksi.cpython-311.pyc | Bin 49082 -> 41481 bytes .../insert_actual_data.cpython-311.pyc | Bin 52500 -> 51957 bytes .../equipment/__pycache__/run.cpython-311.pyc | Bin 3147 -> 3459 bytes src/modules/equipment/insert_actual_data.py | 36 +-- src/modules/equipment/run.py | 10 +- src/modules/equipment/where_query_sql.py | 38 +++ 9 files changed, 183 insertions(+), 246 deletions(-) create mode 100644 src/modules/equipment/where_query_sql.py diff --git a/src/equipment/__pycache__/service.cpython-311.pyc b/src/equipment/__pycache__/service.cpython-311.pyc index 921a422f8cc64b18e62d97e3f1074dec66075e08..0ce524a131bf2f3d2df4774cee6460e0fa476b21 100644 GIT binary patch delta 4609 zcma)93v64}8NSEY*KyoD+BEN^N!lchUD`Cwt8toVlQd~)(ns1Rb)m-bJx)v=J3YQ> z(vZ}hD5Ya;FwPKYzy{JN+5l;lCtjlr`eK`Sq)Y3#F(Wqv^IvyV#ppt4nXUK>sw-IeGF{?R4$|&0aYM^WH$k>6x^y9 zs#YVpYK>sl0&}xKb%2}#)yL457;1>2MnFwMuQ`TV0JRF<)|g&f47JCk9SgjOx(#5b zFzH%APZI;(M7R5i+WolsY2w;K8)ze~_nC4Q_Cumg>a7hhQgf+H8yETN4j4DbjeBTI zoJInepaS0|=Op)@G`m{W9^rf|@0D{RCUqz9+DsB{I|G`}#=^PUfk*D_SYXg?X9&pQ zYdo$_br(%nchg4j*CjkE@#!umKn+o`iwQ>dfnv8%+)o|z z&Rf8J{-QhDtddEXReUg?RqY4MTfuU{N~UWUZ6JjKkna$ljg)1Yw?uvblPKTIzsW_~ukDo(>PW#Ko{ zvu?6Wm`a$ptE}^yR2l!ynBZ8!*Ldy*?m`$Mc})i-Ri0LkD}7S2v`IoKev3z)3fAyDPR6AP`G7P|7W@UN;dh*9nG3NcsYGJ-X$5^Z zTEVySt-c|nU`@Fr6T^Q&7)TfK5&O&Sa?R1*+isJFG76EB)&;9N`~h^+!IBVcwh$=2SaT+A$J zcPbaF734S-Upc{J2*l2*V9+W18y`F%Fh1pIVE%S!LntLqh*W6VO8#{zf-28Ic zFW{&jw%e5>z<56TsNG4(#^`&FZ>;=hmFwkFt&RV?@@-Pi|4@|=nx9o|F?}KN;_4D; zDqYo25fA^Oy1-)81y8BD%W@PQkMajQAI_9r6iJN zL%gN7-TploL%$1qMa~RQ7Q>0_3DEvLn#M%4V|Dzw+M#SLCEXmP6Pm+r%!i?VH?P?I zWyvVQF(l(ijv<*uau<>*B-6aPuA3a^_tia?csDS@b|8F{v%~^L+XD4E;LNw21>Rdy zbkcd$OltX!#!E&g?A@BlXPQnVL_pl@AUq`}tHB`~S%^tt{4lmh@Wm?NSI@+YW5hGz z9|^e!kGVZzXuQF(5%wDp*7HZ3n`54%H<~rF@8$&pHHJm$FfWGD6>$f#0pN%6)f1x4 zhfBq9@o`=Tv5UXddJ^`xacdDd^a3z?9SO#stb~Es6W!!j=eGn|@eyA^#!E>4PrStW zcwl`FR6P8*Z47FpyuEZRt^J4YFzA7=vP#RSijRaOhJp~ z^vIZhbc6jD&)5`C+Z$dMvC6))3y*?DrrTj$Y|p zho%0XuCL_w^XNvTl=&S}Viz!=RrP38^N&zn4u;0r7%%V1%=!sRlz?ZD28;@S2t9wF zAMDB7B@%uU$GC=il6QiBf8xz(-qwY-=Zt4vMLnD5#JK*}o-kNXw(_lmPg;zdB04*Gl9YFX5MC2(@D9qf@VH+ahNg#G zrjx@uIl83bu?hZDwU9i-Us0WDV&mrf<8_Si5m#uLSlvleeUWkYGK{VJE6T-nw-%8| z=tV>=NbsIw!$<~^c#+fq(JkQ+)2Pb6Le7tne1UB9QMdwi#jnwkFv~?YE-vc>;z&z3 zwhI@DeU9WKevXz}jJSN3j#~V{)zkT?H=Q$Idn$HvBQQqqj9&8fE6uMWQ&b{GT(|~e z-EzsQ=oV;y6jon>?R^qzJUjsvm&uO=d}13M1N={cm&vZ^BO{H3e9S)x7Dhe+uX<^i zdCOtj#{x859&3r^aj#+cpNR3wCQkAFm{lo~O0J<$4nNp-dgA`_t4hwC0>U|IDY+y1{MadzxO4vpRYrF> delta 6212 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 diff --git a/src/equipment/service.py b/src/equipment/service.py index f7bd855..c18b2f1 100644 --- a/src/equipment/service.py +++ b/src/equipment/service.py @@ -23,6 +23,7 @@ import httpx from src.modules.equipment.run import main from src.modules.equipment.Prediksi import main as predict_main from src.modules.equipment.Eac import main as eac_main +from src.modules.equipment.where_query_sql import get_where_query_sql_all_worktype import datetime import math @@ -120,99 +121,26 @@ CATEGORY_ROLLUP_CHILDREN = _build_category_rollup_children() logger = logging.getLogger(__name__) -MAXIMO_SQL = text( - """ - SELECT - * - FROM public.wo_maximo AS a - WHERE a.asset_unit = '3' - AND a.asset_assetnum = :assetnum - AND a.wonum NOT LIKE 'T%' - AND ( - (a.worktype = 'CM' AND a.wojp8 != 'S1') - OR (a.worktype <> 'CM') - ); - """ -) - -JOINED_MAXIMO_SQL = text( - """ - SELECT * - FROM public.wo_maximo a - LEFT JOIN public.wo_maximo_labtrans b - ON b.wonum = a.wonum - LEFT JOIN lcc_ms_manpower emp - ON UPPER(TRIM(emp."ID Number")) = UPPER(TRIM(b.laborcode)) - WHERE - a.asset_unit = '3' - AND a.wonum NOT LIKE 'T%' - AND a.asset_assetnum = :assetnum - AND ( - a.actfinish IS NULL - OR a.actstart IS NULL - OR (EXTRACT(EPOCH FROM (a.actfinish - a.actstart)) / 3600.0) <= 730 - ) - AND ( - (a.worktype = 'CM' AND a.wojp8 != 'S1') - OR (a.worktype <> 'CM') - ) - AND ( - a.description NOT ILIKE '%U4%' - OR ( - a.description ILIKE '%U3%' - AND a.description ILIKE '%U4%' - ) - ); - """ -) - -async def _fetch_maximo_records( - *, session: AsyncSession, assetnum: str -) -> list[dict[str, Any]]: - """Fetch Maximo rows with a retry to mask transient collector failures.""" - - query = MAXIMO_SQL.bindparams(assetnum=assetnum) - - try: - result = await session.execute(query) - return result.mappings().all() - except AsyncpgInterfaceError as exc: - logger.warning( - "Collector session closed while fetching Maximo data for %s. Retrying once.", - assetnum, - ) - try: - async with collector_async_session() as retry_session: - retry_result = await retry_session.execute(query) - return retry_result.mappings().all() - except Exception as retry_exc: - logger.error( - "Retrying Maximo query failed for %s: %s", - assetnum, - retry_exc, - exc_info=True, - ) - except SQLAlchemyError as exc: - logger.error( - "Failed to fetch Maximo data for %s: %s", assetnum, exc, exc_info=True - ) - except Exception as exc: - logger.exception( - "Unexpected error while fetching Maximo data for %s", assetnum - ) - - return [] - async def _fetch_joined_maximo_records( *, session: AsyncSession, assetnum: str ) -> list[dict[str, Any]]: """Fetch Joined Maximo rows with a retry to mask transient collector failures.""" - - query = JOINED_MAXIMO_SQL.bindparams(assetnum=assetnum) - + where_query = get_where_query_sql_all_worktype(assetnum) + + JOINED_MAXIMO_SQL = text( + f""" + SELECT * + FROM public.wo_maximo a + LEFT JOIN public.wo_maximo_labtrans b + ON b.wonum = a.wonum + LEFT JOIN lcc_ms_manpower emp + ON UPPER(TRIM(emp."ID Number")) = UPPER(TRIM(b.laborcode)) + {where_query} + """ + ) try: - result = await session.execute(query) + result = await session.execute(JOINED_MAXIMO_SQL) return result.mappings().all() except AsyncpgInterfaceError as exc: logger.warning( @@ -221,7 +149,7 @@ async def _fetch_joined_maximo_records( ) try: async with collector_async_session() as retry_session: - retry_result = await retry_session.execute(query) + retry_result = await retry_session.execute(JOINED_MAXIMO_SQL) return retry_result.mappings().all() except Exception as retry_exc: logger.error( @@ -358,7 +286,7 @@ async def get_master_by_assetnum( min_seq = equipment_record.minimum_eac_seq if equipment_record else None min_eac_year = equipment_record.minimum_eac_year if equipment_record else None - maximo_record = await _fetch_maximo_records( + maximo_record = await _fetch_joined_maximo_records( session=collector_db_session, assetnum=assetnum ) joined_maximo_record = await _fetch_joined_maximo_records( diff --git a/src/modules/equipment/Prediksi.py b/src/modules/equipment/Prediksi.py index a07f067..38ae297 100644 --- a/src/modules/equipment/Prediksi.py +++ b/src/modules/equipment/Prediksi.py @@ -86,22 +86,14 @@ class Prediksi: query = """ SELECT tahun AS year, - raw_cm_interval AS cm_interval, - raw_cm_material_cost AS cm_cost, - raw_cm_labor_time AS cm_labor_time, - raw_cm_labor_human AS cm_labor_human, - raw_pm_interval AS pm_interval, - raw_pm_material_cost AS pm_cost, - raw_pm_labor_time AS pm_labor_time, - raw_pm_labor_human AS pm_labor_human, - raw_oh_interval AS oh_interval, - raw_oh_material_cost AS oh_cost, - raw_oh_labor_time AS oh_labor_time, - raw_oh_labor_human AS oh_labor_human, - raw_predictive_material_cost AS predictive_material_cost, - raw_predictive_labor_time AS predictive_labor_time, - raw_predictive_labor_human AS predictive_labor_human, - raw_predictive_interval AS predictive_interval + rc_cm_material_cost, + rc_cm_labor_cost, + rc_pm_material_cost, + rc_pm_labor_cost, + rc_oh_material_cost, + rc_oh_labor_cost, + rc_predictive_material_cost, + rc_predictive_labor_cost FROM lcc_equipment_tr_data WHERE assetnum = %s and is_actual=1 @@ -188,62 +180,65 @@ class Prediksi: # Query untuk insert data insert_query = """ INSERT INTO lcc_equipment_tr_data ( - id, + id, seq, is_actual, - raw_pm_interval, - tahun, assetnum, - raw_cm_interval, raw_cm_material_cost, raw_cm_labor_time, raw_cm_labor_human, - raw_pm_material_cost, raw_pm_labor_time, raw_pm_labor_human, - raw_oh_interval, raw_oh_material_cost, raw_oh_labor_time, raw_oh_labor_human, - raw_predictive_interval, raw_predictive_material_cost, raw_predictive_labor_time, raw_predictive_labor_human, - created_by, created_at + tahun, assetnum, + rc_cm_material_cost, + rc_cm_labor_cost, + rc_pm_material_cost, + rc_pm_labor_cost, + rc_oh_material_cost, + rc_oh_labor_cost, + rc_predictive_material_cost, + rc_predictive_labor_cost, + created_by, created_at ) VALUES ( - %s, %s, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'Sys', NOW() + %s, %s, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'Sys', NOW() ) """ # If a token was provided, store locally so fetch_api_data can use/refresh it - if token: - self.access_token = token - - # Fetch data from external API (uses instance access_token and will try refresh on 403) - async def fetch_api_data(assetnum: str, year: int) -> dict: - url = self.RELIABILITY_APP_URL - endpoint = f"{url}/main/number-of-failures/{assetnum}/{int(year)}/{int(year)}" - async with httpx.AsyncClient() as client: - try: - current_token = getattr(self, "access_token", None) - response = await client.get( - endpoint, - timeout=30.0, - headers={"Authorization": f"Bearer {current_token}"} if current_token else {}, - ) - response.raise_for_status() - return response.json() - except httpx.HTTPStatusError as e: - status = getattr(e.response, "status_code", None) - # If we get a 403, try to refresh the access token and retry once - if status == 403: - print("Received 403 from reliability API, attempting to refresh access token...") - new_access = await self.refresh_access_token() - if new_access: - try: - response = await client.get( - endpoint, - timeout=30.0, - headers={"Authorization": f"Bearer {new_access}"}, - ) - response.raise_for_status() - return response.json() - except httpx.HTTPError as e2: - print(f"HTTP error occurred after refresh: {e2}") - return {} - print(f"HTTP error occurred: {e}") - return {} - except httpx.HTTPError as e: - print(f"HTTP error occurred: {e}") - return {} + # if token: + # self.access_token = token + + # # Fetch data from external API (uses instance access_token and will try refresh on 403) + # async def fetch_api_data(assetnum: str, year: int) -> dict: + # url = self.RELIABILITY_APP_URL + # endpoint = f"{url}/main/number-of-failures/{assetnum}/{int(year)}/{int(year)}" + # async with httpx.AsyncClient() as client: + # try: + # current_token = getattr(self, "access_token", None) + # response = await client.get( + # endpoint, + # timeout=30.0, + # headers={"Authorization": f"Bearer {current_token}"} if current_token else {}, + # ) + # response.raise_for_status() + # return response.json() + # except httpx.HTTPStatusError as e: + # status = getattr(e.response, "status_code", None) + # # If we get a 403, try to refresh the access token and retry once + # if status == 403: + # print("Received 403 from reliability API, attempting to refresh access token...") + # new_access = await self.refresh_access_token() + # if new_access: + # try: + # response = await client.get( + # endpoint, + # timeout=30.0, + # headers={"Authorization": f"Bearer {new_access}"}, + # ) + # response.raise_for_status() + # return response.json() + # except httpx.HTTPError as e2: + # print(f"HTTP error occurred after refresh: {e2}") + # return {} + # print(f"HTTP error occurred: {e}") + # return {} + # except httpx.HTTPError as e: + # print(f"HTTP error occurred: {e}") + # return {} # Menyiapkan data untuk batch insert # print(f"Data to be inserted: {data}") @@ -252,71 +247,63 @@ class Prediksi: max_seq = max_seq + 1 # (token already stored before defining fetch_api_data) # maintain previous cm_interval between iterations using attribute on fetch_api_data - if not hasattr(fetch_api_data, "prev_cm"): - fetch_api_data.prev_cm = None + # if not hasattr(fetch_api_data, "prev_cm"): + # fetch_api_data.prev_cm = None # Update values from API (current year) - api_data = await fetch_api_data(equipment_id, row["year"]) - if api_data and "data" in api_data and isinstance(api_data["data"], list) and len(api_data["data"]) > 0: - try: - cur_cm = float(api_data["data"][0].get("num_fail", row.get("cm_interval", 1))) - except Exception: - cur_cm = float(row.get("cm_interval", 1)) if not pd.isna(row.get("cm_interval", None)) else 1.0 - else: - try: - val = float(row.get("cm_interval", 1)) - cur_cm = val if val >= 1 else 1.0 - except Exception: - cur_cm = 1.0 + # api_data = await fetch_api_data(equipment_id, row["year"]) + # if api_data and "data" in api_data and isinstance(api_data["data"], list) and len(api_data["data"]) > 0: + # try: + # cur_cm = float(api_data["data"][0].get("num_fail", row.get("cm_interval", 1))) + # except Exception: + # cur_cm = float(row.get("cm_interval", 1)) if not pd.isna(row.get("cm_interval", None)) else 1.0 + # else: + # try: + # val = float(row.get("cm_interval", 1)) + # cur_cm = val if val >= 1 else 1.0 + # except Exception: + # cur_cm = 1.0 # Determine previous cm_interval: prefer stored prev_cm, otherwise try API for previous year, else fallback to cur_cm - if fetch_api_data.prev_cm is not None: - prev_cm = float(fetch_api_data.prev_cm) - else: - try: - api_prev = await fetch_api_data(equipment_id, int(row["year"]) - 1) - if api_prev and "data" in api_prev and isinstance(api_prev["data"], list) and len(api_prev["data"]) > 0: - prev_cm = float(api_prev["data"][0].get("num_fail", cur_cm)) - else: - # attempt to use any available previous value from the row if present, otherwise fallback to current - prev_cm = float(row.get("cm_interval", cur_cm)) if not pd.isna(row.get("cm_interval", None)) else cur_cm - except Exception: - prev_cm = cur_cm + # if fetch_api_data.prev_cm is not None: + # prev_cm = float(fetch_api_data.prev_cm) + # else: + # try: + # api_prev = await fetch_api_data(equipment_id, int(row["year"]) - 1) + # if api_prev and "data" in api_prev and isinstance(api_prev["data"], list) and len(api_prev["data"]) > 0: + # prev_cm = float(api_prev["data"][0].get("num_fail", cur_cm)) + # else: + # # attempt to use any available previous value from the row if present, otherwise fallback to current + # prev_cm = float(row.get("cm_interval", cur_cm)) if not pd.isna(row.get("cm_interval", None)) else cur_cm + # except Exception: + # prev_cm = cur_cm # compute difference: current year interval minus previous year interval - try: - cm_interval_diff = float(cur_cm) - float(prev_cm) - except Exception: - cm_interval_diff = 0.0 + # try: + # cm_interval_diff = float(cur_cm) - float(prev_cm) + # except Exception: + # cm_interval_diff = 0.0 # append record using the difference for raw_cm_interval records_to_insert.append( ( str(uuid4()), # id int(max_seq), # seq - float(row["pm_interval"]) if not pd.isna(row.get("pm_interval", None)) else 0.0, - float(row["year"]) if not pd.isna(row.get("year", None)) else 0.0, + int(row["year"]), equipment_id, - cm_interval_diff, - float(row["cm_cost"]) if not pd.isna(row.get("cm_cost", None)) else 0.0, - float(row["cm_labor_time"]) if not pd.isna(row.get("cm_labor_time", None)) else 0.0, - float(row["cm_labor_human"]) if not pd.isna(row.get("cm_labor_human", None)) else 0.0, - float(row["pm_cost"]) if not pd.isna(row.get("pm_cost", None)) else 0.0, - float(row["pm_labor_time"]) if not pd.isna(row.get("pm_labor_time", None)) else 0.0, - float(row["pm_labor_human"]) if not pd.isna(row.get("pm_labor_human", None)) else 0.0, - float(row["oh_interval"]) if not pd.isna(row.get("oh_interval", None)) else 0.0, - float(row["oh_cost"]) if not pd.isna(row.get("oh_cost", None)) else 0.0, - float(row["oh_labor_time"]) if not pd.isna(row.get("oh_labor_time", None)) else 0.0, - float(row["oh_labor_human"]) if not pd.isna(row.get("oh_labor_human", None)) else 0.0, - float(row["predictive_interval"]) if not pd.isna(row.get("predictive_interval", None)) else 0.0, - float(row["predictive_material_cost"]) if not pd.isna(row.get("predictive_material_cost", None)) else 0.0, - float(row["predictive_labor_time"]) if not pd.isna(row.get("predictive_labor_time", None)) else 0.0, - float(row["predictive_labor_human"]) if not pd.isna(row.get("predictive_labor_human", None)) else 0.0, + float(row.get("rc_cm_material_cost", 0)) if not pd.isna(row.get("rc_cm_material_cost", 0)) else 0.0, + float(row.get("rc_cm_labor_cost", 0)) if not pd.isna(row.get("rc_cm_labor_cost", 0)) else 0.0, + float(row.get("rc_pm_material_cost", 0)) if not pd.isna(row.get("rc_pm_material_cost", 0)) else 0.0, + float(row.get("rc_pm_labor_cost", 0)) if not pd.isna(row.get("rc_pm_labor_cost", 0)) else 0.0, + float(row.get("rc_oh_material_cost", 0)) if not pd.isna(row.get("rc_oh_material_cost", 0)) else 0.0, + float(row.get("rc_oh_labor_cost", 0)) if not pd.isna(row.get("rc_oh_labor_cost", 0)) else 0.0, + float(row.get("rc_predictive_material_cost", 0)) if not pd.isna(row.get("rc_predictive_material_cost", 0)) else 0.0, + float(row.get("rc_predictive_labor_cost", 0)) if not pd.isna(row.get("rc_predictive_labor_cost", 0)) else 0.0, ) ) # store current cm for next iteration - fetch_api_data.prev_cm = cur_cm + # fetch_api_data.prev_cm = cur_cm # Eksekusi batch insert cursor.executemany(insert_query, records_to_insert) @@ -788,24 +775,26 @@ class Prediksi: if "is_actual" in df.columns: recent_df = df[df["is_actual"] == 1] recent_n = recent_df.shape[0] + avg_recent = recent_df[column].mean() + print(f"avg_recent: {avg_recent}") else: recent_df = df recent_n = df.shape[0] recent_n = max(1, recent_n) recent_vals = ( - recent_df.sort_values("year", ascending=False) + recent_df.sort_values("year", ascending=True) .head(recent_n)[column] .dropna() ) - + # print(f"Recent Vals: {recent_vals}") # Fallback ke semua nilai non-na jika tidak ada recent_vals if recent_vals.empty: recent_vals = df[column].dropna() # Jika masih kosong, pakai default (interval minimal 1, lainnya 0) if recent_vals.empty: - avg = 0.0 + avg = 0.0 else: # Pastikan numeric; jika gagal, pakai mean dari yang bisa dikonversi try: @@ -818,7 +807,7 @@ class Prediksi: avg = max(0.0, avg) preds = np.repeat(float(avg), n_future) - + print(preds) else: # Untuk kolom non-cm, gunakan nilai dari last actual year bila ada, # jika tidak ada gunakan last available non-NA value, jika tidak ada pakai 0.0 @@ -980,14 +969,14 @@ async def main(RELIABILITY_APP_URL=RELIABILITY_APP_URL, assetnum=None, token=Non prediksi = Prediksi(RELIABILITY_APP_URL) # If token not provided, sign in to obtain access_token/refresh_token - if token is None: - signin_res = await prediksi.sign_in() - if not getattr(prediksi, "access_token", None): - print("Failed to obtain access token; aborting.") - return - else: - # Use provided token as access token - prediksi.access_token = token + # if token is None: + # signin_res = await prediksi.sign_in() + # if not getattr(prediksi, "access_token", None): + # print("Failed to obtain access token; aborting.") + # return + # else: + # # Use provided token as access token + # prediksi.access_token = token # If an assetnum was provided, run only for that assetnum if assetnum: diff --git a/src/modules/equipment/__pycache__/Prediksi.cpython-311.pyc b/src/modules/equipment/__pycache__/Prediksi.cpython-311.pyc index 91779118bfbbf2b8573f9b30c913f2645d9f9ea1..c445d9e896699e7b3889c68e96bee4e90ce65d61 100644 GIT binary patch delta 6248 zcmc&&4RBM}m45d<$&&SFTR)a8Tef9mKo}dF)D&_Ffz7GuAU*7 zCk!OBfrb{?6TpPfn5013K$}!kLb^!^BxxYifJD?nUzQ}zblWn!P0S?I-OaY!Jy-HS z(9)Tm-D~T-^UgW}CBl=f zk>6WC5y-B{=Hc&(>#GV?@i&@~b+6`jXpq%3*9aI(A?Gi6$4-4B2gFLE_ zyE7F&m5;!V!aTimP?4xPq^fr2WvTx{g z4+Q;kXy^mn;NV+urk1N?!&nTV}DON}7|n z@?ON>pk7kKC;6GMzbhSVC6)}ms2bF9oG)F{!{>XfdM>Qrt`Dkxi6^Wp*+McvE(!4) zd1xq|Px9elX$|~)X*%rMVAlR`Y(@ASx8(7%M__9Cgq~ZhJF;S{-$P{+9ktip-p-Kt zyFriT^rW`kv#ygSR0;b{!?7VLWM(KFW7fH0D1;1~p%8AiX%uoc^+Huy7BAair_ToA zs!7BQ7fg9A6R~ybVjijk7=UQ!mJprKUWJfIDH}~$2n0o1$v8zI6!geKt6!9r>)Sg$ zqOA2l>i2eu{ti!YD{Vv@Ithu(0CQ$H6v-LX@9m_%kX!6@2ZA9#6=mX<1#E6-|vU()e9?(IqAC>_gC|rvoNZ2+_2|9 zTk>?tktxqi>E~wXjkg&&kg}ibf8d7Q*;jLy0hf8e5_(I-;GMVvUc)8y|_)(Rdw=+O{22_IjUfeY!O|zVh6% zSnlk2?(CSYI&Q0u;{T)DZq_b%yXDgIt1a)fM4MZqfwtI^jqxQLW3}z^+V-feYqiP=zwH5B@+HgO| zRr59cZH`+>=I~!8ANaP6%w=!vfhJjt`QhQ6`zFrLf_UzNn5`~utBcy|&|)FKn7_HC z;paFvE3P*`y!u-6>gbv+(XHLF=56ujZLuX%e2K)6=8r(CeiKJ&UOabR%vKw>)kbZ# zXfc61Sc&l-Oyq(BXNM`dDonWk)P6W@IuW-4JP-Ktk|g z(^Nz;G&c|m-)s(&>d5=eucTWOO)BXGW;zrF(1WAv50Q(JsjYADqzP^Z?4$*BZJV>{ zH<;lHMjmG5n~bbxq%(4>Z2?K&#&|Cy9!7jn-QG(65ngD|%R9&fhZs4`NG~Ht7N_6UYI0zhfapS&}w^_d;ue8Vb#_avN#gi`X$lGDbz1^(V$m= z@+ZuZi`(*)1wUn0uOb-;sa4f3o=W&GtHeNuzq3n(|JpH~ya8X^xdzg|Zs}2M!uU&B z%7d}j6A1a;>oM|%M32}NN>rqU405<JI_g)na`cD|V+hQok>-F%&2rKy@fJYO48k*G5x!Y%t9h-g8nUTkJsxDg&1L$bOIYDPQWI5t_{$UA%`OW*nGl zkkTaGaFz)>9~f5}&Jcw`6if&-QpQp47(X`22&V0Egz*Q<4^$Ez#QLnM+S9sZssX<~ zkPGi0a1aB_fAhme$=H@Q2ubaG93$K8OBhv+F%o?GuqoJ{%8AawiX11hl{HY>jDwtH z>@%^U62yECY6`4y`j`QKX+@@Bc_AmVZ8}iKadaFRg(w+Wm*MDBhUsC`N-n6_!aXXi zK{DV#rHk6Z$~Y8y$uv5b3Y)}2Xe=}vMtUik;O3#c8Rn6rD4EBO-+mH&;f{Iv z?!dg{rVo!-=!t~zHWUjM$pU##wGaz9vzXJ4#DXYe?i<6I!3kpdu(LQbv3&{E%y1?{ z@bSa*EM;y?NwX(Oz%#ERi2+A;@uDQk51LWQFzZmd3R z3mWg38YRII4%yvKyn;?a=iND|-%G(#*JQhsx=6 zOzLw|!e+_b=U|%$E+5vC58)SwCy;FT^Wj=;wnQZUByP|b1y~!&93OPu;aE;MXXM)R zWk@*#DSgiNoJ2~GPdL+C=^VW`9AoMkll8K>FxvaL% zZGlu1%MI(`0j_xgZHf5X9%@Ai;SB3aKqz)y>_ zx~a*gt}}o{%Sy(vrV`E54;lB*jIgHYM;z=rzL9(mpB?{kNw?0kxz&yC;CVAMzsW`9 zR8I(Be1TvqZ9#KP`Gc7AZ}zm)U%>h29@oo?_5iv*he2(JhUcr4iVnXg2z#F|tYbMw zvdgNVC&3|R2Aw!NdnId=y>nw1LfGi$ zRyKx~6IDuCKoP7sQK-nps<88f9lDz}@Z5>%rgt!0g!CyK_aB3o4GI2m;tSPEqOZ8k zt%Tp5oI)DFdTM6b4cF9{ro~+|ue)YnbIp#qYT~Y%-ujz4&S=4`m+VOJKbBJ!(g z{$cs4V$Hm0PI)Y6VLWFc>^`+DrJ$&HZtq<9_|(&BH*Jny{}J6Yx?bH8UB5aKexXL_ zX)<$X=9D9SD{t=jMkoJn%8WXWe|I)P{t{PQSE{^J%rm}(Fut_bjxSeI)0SrOS8UAa zN|tWvMCFwtW^`o&Vf;h}bT!T1Fp0nFVm4PN=^ADzuTEn&SEm!k&zOLGG{1*n(@|&M zGC>*Rc;sU|VZ2Z?6<@ARb1XM=ADEcY2WHLknZgIT#^n{l2W3hosZcV>OdIlXv!x=h)P}%J>Xf?REz}9e%f4*0|lc?z`IIwbNGBSIp!;Ign3} zE7cI>wT$#Jsg7}fV%%Cr5}6pwm}rcVJeYi@DQ$|fu%8iV zJvdj{)5W2D13!SJi0R;b7(bq+_>@EE0v9AwN?4GD9g`5^!Iz4#dKr{2v5XU0<~LAI zt45z}VUjpSc@8lNCTS^N1#hjS$0bqFkR2M5@w;^G;ra8gJ&l_*v>?HZx^L_RJjv} zLP(FY{E>y)nU$i$6A03BMEe<@x}i$~{OPQd*ds5#@_nIaA6ooF;X)Jl180qnyPze= zUeIZ3DufG}mYOo*La~xb%9Kn}VMP97K^4-sD(B9?$Ga8IIxBxElU2K9)hy(cmz6)C;j+V6uNN*)QPi8txcBr*6uoB@Q2kz6)}mDS z?4Qbr40*3zB*SC|hRs+E}b$5C@A^&ou Kwh88)-~2xZtaVcW delta 12166 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 delta 4710 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 aJi96>;PX^{>iJ4T>iIP|ka+XFkNgKJP>#d^ diff --git a/src/modules/equipment/__pycache__/run.cpython-311.pyc b/src/modules/equipment/__pycache__/run.cpython-311.pyc index 0d30746bc1d102e30d3ad1985c7a4bb2c198fe44..c376c1f2757acbfcfefc30bee80437f78e37ac4c 100644 GIT binary patch delta 879 zcma)3&1(}u6rb7I@3@-=8@q8WNldYzv9VATDoaUqAtynKgbUe>!t_= zK@cx<=%EA@{G=jCKt#MWS}$HAh=+jqFL?7NPWnMi;-QZ>!+US${pR<3^Tk* zRTj}l+^NNNmfS(JU<2Lbt6ac2Tp8d<7Z7>|0oY<|&milVISc{5pMcp#paLsk1 zsNAooXP|$O6;f^pZH{=7nDjHB91@Ay;T#B#vMRFAQRx~#G^MuVs_cXUaT7R40*UQf zAUpad?}TeoRq->dL)}L_*@Lu;akhie-7T{DC38ij5vvR`9&^VnT37YW@GDXh~eDVpMWA^bx(?iU{voAUAhI=wC%#@N%si{#^l zCkst|jQLkN^G(h)BXa=-uw?1M2{Xkc<|=MNz=Mr1pN&&y!Yn!e(&R1^HZI_<~#rk|7ka# zd&l1(yhhA>bf=yojRYEitSCArOdzoNfE#n}z7Nm_c`C&P4pA)s*VwpZhT}CXU;zZA z)-q4I5&uRBP(aH=;C z4&ZJfn``d`2$GA(LH&Cm@4o}RaxQoM2fKCTylU*L186GzRoiK?YU=*Qu^k9z5?VXM zR;``-BG9fKprk$f9@4(x8;+RT^ir0bwyJEe+A-BIU!CdO-Z=IrvzS9|j%W4guEya_ zlCpyv)2!GX0?iiSWch~qPQT3Z%u(^9C@b}4B;qBtCbNFm%clL6o xgJ6V$&UrE_wK^dgg>-CR>RjlCuoDKoHrbhP^;+a(i}u>)XD~#3dr+FfzX1r dict: """Return yearly labor cost totals for a worktype using the standardized query.""" if not assetnum or not worktype: return {} - + where_query = get_where_query_sql(assetnum, worktype) query = f""" SELECT EXTRACT(YEAR FROM x.reportdate)::int AS tahun, @@ -173,25 +168,12 @@ FROM ( a.wonum, a.reportdate, CASE - WHEN (EXTRACT(EPOCH FROM (a.actfinish - a.actstart)) / 3600.0) = 0 - THEN 1 - ELSE (EXTRACT(EPOCH FROM (a.actfinish - a.actstart)) / 3600.0) + WHEN EXTRACT(EPOCH FROM (a.actfinish - a.actstart)) / 3600.0 = 0 THEN 1 + WHEN EXTRACT(EPOCH FROM (a.actfinish - a.actstart)) / 3600.0 > 730 THEN 1 + ELSE EXTRACT(EPOCH FROM (a.actfinish - a.actstart)) / 3600.0 END AS jumlah_jam_kerja - FROM public.wo_maximo a - WHERE - a.asset_unit = '3' - AND a.wonum NOT LIKE 'T%' - AND a.asset_assetnum = '{assetnum}' - AND (EXTRACT(EPOCH FROM (a.actfinish - a.actstart)) / 3600.0) <= 730 - AND a.worktype = '{worktype}' - AND ( - a.description NOT ILIKE '%U4%' - OR ( - a.description ILIKE '%U3%' - AND a.description ILIKE '%U4%' - ) - ) + {where_query} ) bw LEFT JOIN public.wo_maximo_labtrans b ON b.wonum = bw.wonum diff --git a/src/modules/equipment/run.py b/src/modules/equipment/run.py index 941f4d0..5773e38 100644 --- a/src/modules/equipment/run.py +++ b/src/modules/equipment/run.py @@ -17,11 +17,11 @@ except ImportError: async def main(): start_time = time.time() - # try: - # await query_data() - # except Exception as e: - # print(f"Error in query_data: {str(e)}") - # return + try: + await query_data() + except Exception as e: + print(f"Error in query_data: {str(e)}") + return try: prediction_result = await predict_run() diff --git a/src/modules/equipment/where_query_sql.py b/src/modules/equipment/where_query_sql.py new file mode 100644 index 0000000..f8bb101 --- /dev/null +++ b/src/modules/equipment/where_query_sql.py @@ -0,0 +1,38 @@ +def get_where_query_sql(assetnum, worktype): + where_query = f""" + where + a.asset_unit = '3' + and a.wonum not like 'T%' + AND a.asset_assetnum = '{assetnum}' + {f"AND a.worktype = '{worktype}'" if worktype != 'CM' else "AND a.worktype in ('CM', 'PROACTIVE', 'EM')"} + {f"AND a.wojp8 != 'S1'" if worktype == 'CM' else ""} + AND ( + a.description NOT ILIKE '%U4%' + OR ( + a.description ILIKE '%U3%' + AND a.description ILIKE '%U4%' + ) + ) + """ + + return where_query + +def get_where_query_sql_all_worktype(assetnum): + where_query = f""" + where + a.asset_unit = '3' + and a.wonum not like 'T%' + AND a.asset_assetnum = '{assetnum}' + AND ( + (a.worktype = 'CM' AND a.wojp8 != 'S1') + OR (a.worktype <> 'CM') + ) + AND ( + a.description NOT ILIKE '%U4%' + OR ( + a.description ILIKE '%U3%' + AND a.description ILIKE '%U4%' + ) + ) + """ + return where_query \ No newline at end of file