ariwahyunahar 3 weeks ago
commit 398a2b14a2

@ -2,7 +2,7 @@ import os
import logging import logging
from typing import Optional, TypedDict, Any from typing import Optional, TypedDict, Any
from sqlalchemy import Select, Delete, Float, func, cast, String, text, case from sqlalchemy import Select, Delete, Float, func, cast, String, text, case, asc, desc
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@ -392,8 +392,8 @@ async def get_top_10_economic_life(*, db_session: DbSession, common) -> list[Equ
query.add_columns( query.add_columns(
case( case(
( (
(current_year - Equipment.minimum_eac_year) >= 0, (Equipment.minimum_eac_year - current_year) >= 0,
(current_year - Equipment.minimum_eac_year), (Equipment.minimum_eac_year - current_year),
), ),
else_=0, else_=0,
).label("remaining_life") ).label("remaining_life")
@ -401,7 +401,7 @@ async def get_top_10_economic_life(*, db_session: DbSession, common) -> list[Equ
.filter(Equipment.minimum_eac_year != None) .filter(Equipment.minimum_eac_year != None)
.filter((Equipment.minimum_eac != None) & (Equipment.minimum_eac != 0)) .filter((Equipment.minimum_eac != None) & (Equipment.minimum_eac != 0))
# .filter((current_year - Equipment.minimum_eac_year) >= 0) # .filter((current_year - Equipment.minimum_eac_year) >= 0)
.order_by((current_year - Equipment.minimum_eac_year).desc()) .order_by(desc("remaining_life"))
.order_by(func.abs(Equipment.minimum_eac).desc()) .order_by(func.abs(Equipment.minimum_eac).desc())
) )
@ -424,8 +424,8 @@ async def get_top_10_replacement_priorities(*, db_session: DbSession, common) ->
query.add_columns( query.add_columns(
case( case(
( (
(current_year - Equipment.minimum_eac_year) >= 0, (Equipment.minimum_eac_year - current_year) >= 0,
(current_year - Equipment.minimum_eac_year), (Equipment.minimum_eac_year - current_year),
), ),
else_=0, else_=0,
).label("remaining_life") ).label("remaining_life")
@ -433,7 +433,7 @@ async def get_top_10_replacement_priorities(*, db_session: DbSession, common) ->
.filter(Equipment.minimum_eac_year != None) .filter(Equipment.minimum_eac_year != None)
.filter((Equipment.minimum_eac != None) & (Equipment.minimum_eac != 0)) .filter((Equipment.minimum_eac != None) & (Equipment.minimum_eac != 0))
# .filter((current_year - Equipment.minimum_eac_year) >= 0) # .filter((current_year - Equipment.minimum_eac_year) >= 0)
.order_by((current_year - Equipment.minimum_eac_year).asc()) .order_by(asc("remaining_life"))
.order_by(func.abs(Equipment.minimum_eac).desc()) .order_by(func.abs(Equipment.minimum_eac).desc())
) )

@ -5,8 +5,7 @@ import json
import sys import sys
import os import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) from src.modules.config import get_connection
from config import get_connection
import argparse import argparse
@ -187,7 +186,8 @@ class Eac:
# final_value adalah PV pada titik proyeksi periods # final_value adalah PV pada titik proyeksi periods
pmt_mnt_cost = -float(npf.pmt(disc_rate, periods, final_value)) pmt_mnt_cost = -float(npf.pmt(disc_rate, periods, final_value))
eac_disposal_cost_proyeksi = 0.07 * pmt_mnt_cost eac_disposal_cost_proyeksi = -npf.pmt(disc_rate, row["seq"], 0, 0.05 * rc_total_cost_0) if row["seq"] > 0 else 0.0
# menghitung PMT biaya akuisisi # menghitung PMT biaya akuisisi
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1] # Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1]
# dimana PV = rc_total_cost_0, r = disc_rate, n = row["seq"] # dimana PV = rc_total_cost_0, r = disc_rate, n = row["seq"]

@ -15,10 +15,8 @@ from dotenv import load_dotenv
import sys import sys
import os import os
from equipment.formula import rc_labor_cost, rc_lost_cost, rc_total_cost from .formula import rc_labor_cost, rc_lost_cost, rc_total_cost
from src.modules.config import get_connection, get_production_connection
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import get_connection, get_production_connection
import json import json
load_dotenv() load_dotenv()
@ -97,6 +95,7 @@ class Prediksi:
FROM lcc_equipment_tr_data FROM lcc_equipment_tr_data
WHERE assetnum = %s WHERE assetnum = %s
and is_actual=1 and is_actual=1
and seq != 0
; ;
""" """
cursor.execute(query, (equipment_id,)) cursor.execute(query, (equipment_id,))
@ -240,51 +239,52 @@ class Prediksi:
# print(f"HTTP error occurred: {e}") # print(f"HTTP error occurred: {e}")
# return {} # return {}
# Menyiapkan data untuk batch insert # Menyiapkan data untuk batch insert atau update
# print(f"Data to be inserted: {data}")
records_to_insert = [] records_to_insert = []
for _, row in data.iterrows():
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
# 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
# Determine previous cm_interval: prefer stored prev_cm, otherwise try API for previous year, else fallback to cur_cm check_existence_query = """
# if fetch_api_data.prev_cm is not None: SELECT id FROM lcc_equipment_tr_data
# prev_cm = float(fetch_api_data.prev_cm) WHERE assetnum = %s AND tahun = %s AND is_actual = 0
# 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 update_query = """
# try: UPDATE lcc_equipment_tr_data
# cm_interval_diff = float(cur_cm) - float(prev_cm) SET
# except Exception: rc_cm_material_cost = %s,
# cm_interval_diff = 0.0 rc_cm_labor_cost = %s,
rc_pm_material_cost = %s,
rc_pm_labor_cost = %s,
rc_oh_material_cost = %s,
rc_oh_labor_cost = %s,
rc_predictive_material_cost = %s,
rc_predictive_labor_cost = %s,
updated_by = 'Sys',
updated_at = NOW()
WHERE id = %s
"""
# append record using the difference for raw_cm_interval for _, row in data.iterrows():
# Check if data exists
cursor.execute(check_existence_query, (equipment_id, int(row["year"])))
existing_record = cursor.fetchone()
if existing_record:
# Update existing record
record_id = existing_record[0]
cursor.execute(update_query, (
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,
record_id
))
else:
max_seq = max_seq + 1
# Prepare for insert
records_to_insert.append( records_to_insert.append(
( (
str(uuid4()), # id str(uuid4()), # id
@ -302,13 +302,14 @@ class Prediksi:
) )
) )
# store current cm for next iteration # Eksekusi batch insert jika ada data baru
# fetch_api_data.prev_cm = cur_cm if records_to_insert:
# Eksekusi batch insert
cursor.executemany(insert_query, records_to_insert) cursor.executemany(insert_query, records_to_insert)
connection.commit() connection.commit()
# print("Data proyeksi berhasil dimasukkan ke database.")
# Recalculate total costs and update asset criticality
self.__update_data_lcc(equipment_id)
except Exception as e: except Exception as e:
print(f"Error saat menyimpan data ke database: {e}") print(f"Error saat menyimpan data ke database: {e}")
@ -713,7 +714,6 @@ class Prediksi:
if df is None: if df is None:
print("Data tidak tersedia untuk prediksi.") print("Data tidak tersedia untuk prediksi.")
return return
# Mendapatkan tahun proyeksi dari DB # Mendapatkan tahun proyeksi dari DB
par_tahun_target = self.__get_param(assetnum) par_tahun_target = self.__get_param(assetnum)
@ -771,19 +771,14 @@ class Prediksi:
try: try:
# Case untuk kolom yang terkait dengan corrective maintenance (cm) # Case untuk kolom yang terkait dengan corrective maintenance (cm)
if "cm" in col_lower: if "cm" in col_lower:
# Tentukan jumlah baris recent yang dianggap actual jika kolom is_actual ada
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_df = df
recent_n = df.shape[0] recent_n = df.shape[0]
recent_n = max(1, recent_n) recent_n = max(1, recent_n)
recent_vals = ( recent_vals = (
recent_df.sort_values("year", ascending=True) recent_df
.sort_values("year", ascending=True)
.head(recent_n)[column] .head(recent_n)[column]
.dropna() .dropna()
) )
@ -796,18 +791,11 @@ class Prediksi:
if recent_vals.empty: if recent_vals.empty:
avg = 0.0 avg = 0.0
else: else:
# Pastikan numeric; jika gagal, pakai mean dari yang bisa dikonversi avg = pd.to_numeric(recent_vals, errors="coerce").fillna(0).mean()
try: avg = 0.0 if pd.isna(avg) else float(avg)
avg = float(np.nanmean(recent_vals.astype(float)))
except Exception:
# jika conversion gagal gunakan mean pandas (objek mungkin numeric-like)
avg = float(recent_vals.mean())
if "interval" in col_lower:
avg = max(0.0, avg)
preds = np.repeat(float(avg), n_future) preds = np.repeat(float(avg), n_future)
print(preds) # print(preds)
else: else:
# Untuk kolom non-cm, gunakan nilai dari last actual year bila ada, # 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 # jika tidak ada gunakan last available non-NA value, jika tidak ada pakai 0.0
@ -944,7 +932,7 @@ class Prediksi:
predictions_df = pd.DataFrame(predictions) predictions_df = pd.DataFrame(predictions)
# print(predictions_df) # print(predictions_df)
# Hapus data prediksi yang ada sebelumnya # Hapus data prediksi yang ada sebelumnya
self.__delete_predictions_from_db(assetnum) # self.__delete_predictions_from_db(assetnum)
# Insert hasil prediksi ke database # Insert hasil prediksi ke database
try: try:

@ -8,10 +8,8 @@ from datetime import datetime
import sys import sys
import os import os
import httpx import httpx
from where_query_sql import get_where_query_sql from src.modules.config import get_connection, get_production_connection
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) from .where_query_sql import get_where_query_sql
from config import get_connection, get_production_connection
async def fetch_api_data( async def fetch_api_data(
assetnum: str, year: int, RELIABILITY_APP_URL: str, token: str assetnum: str, year: int, RELIABILITY_APP_URL: str, token: str
@ -112,20 +110,11 @@ def get_recursive_query(cursor, assetnum, worktype="CM"):
select select
DATE_PART('year', a.reportdate) as tahun, DATE_PART('year', a.reportdate) as tahun,
COUNT(DISTINCT a.wonum) as raw_{worktype.lower()}_interval, COUNT(DISTINCT a.wonum) as raw_{worktype.lower()}_interval,
sum(a.actmatcost) as raw_{worktype.lower()}_material_cost, sum(a.actmatcost) as raw_{worktype.lower()}_material_cost
( from public.wo_maximo as a
ROUND(SUM(EXTRACT(EPOCH FROM (a.actfinish - a.actstart)) / 3600), 2)
) AS raw_{worktype.lower()}_labor_time,
CASE
WHEN COUNT(DISTINCT b.laborcode) = 0 THEN 3
ELSE COUNT(DISTINCT 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_query} {where_query}
group by DATE_PART('year', a.reportdate); group by DATE_PART('year', a.reportdate);
""" """
# Eksekusi query dan fetch hasil # Eksekusi query dan fetch hasil
cursor.execute(query) cursor.execute(query)
return cursor.fetchall() return cursor.fetchall()
@ -459,7 +448,7 @@ async def insert_ms_equipment_data():
try: try:
connection, connection_wo_db = get_connection() connection, connection_wo_db = get_connection()
cursor_db_app = connection.cursor(cursor_factory=DictCursor) cursor_db_app = connection.cursor(cursor_factory=DictCursor)
query_main = "SELECT DISTINCT(assetnum) FROM ms_equipment_master" query_main = f"SELECT DISTINCT(assetnum) FROM ms_equipment_master WHERE assetnum = 'A27860'"
cursor_db_app.execute(query_main) cursor_db_app.execute(query_main)
results = cursor_db_app.fetchall() results = cursor_db_app.fetchall()
@ -570,7 +559,7 @@ async def insert_lcca_maximo_corrective_data():
data_corrective_maintenance = get_recursive_query( data_corrective_maintenance = get_recursive_query(
cursor_production, assetnum, worktype="CM" cursor_production, assetnum, worktype="CM"
) )
print(data_corrective_maintenance) # print(data_corrective_maintenance)
start_year = 2015 start_year = 2015
end_year = 2056 end_year = 2056
seq = 0 seq = 0
@ -870,7 +859,7 @@ async def insert_acquisition_cost_data():
except Exception: except Exception:
pass pass
async def query_data(): async def query_data(target_assetnum: str = None):
connection = None connection = None
connection_wo_db = None connection_wo_db = None
connection_production_wo = None connection_production_wo = None
@ -950,6 +939,10 @@ async def query_data():
# Query untuk mendapatkan semua data dari tabel `lcc_ms_equipment_data` # Query untuk mendapatkan semua data dari tabel `lcc_ms_equipment_data`
# query_main = "SELECT * FROM lcc_ms_equipment_data" # query_main = "SELECT * FROM lcc_ms_equipment_data"
query_main = "SELECT DISTINCT(assetnum) FROM ms_equipment_master" query_main = "SELECT DISTINCT(assetnum) FROM ms_equipment_master"
if target_assetnum:
query_main += " WHERE assetnum = %s"
cursor.execute(query_main, (target_assetnum,))
else:
cursor.execute(query_main) cursor.execute(query_main)
# Fetch semua hasil query # Fetch semua hasil query
@ -1046,7 +1039,6 @@ async def query_data():
year=year, year=year,
labour_cost_lookup=labour_cost_lookup, labour_cost_lookup=labour_cost_lookup,
) )
if not data_exists: if not data_exists:
cursor.execute( cursor.execute(
insert_query, insert_query,

@ -1,17 +1,10 @@
import asyncio import asyncio
import time import time
# prefer package-relative imports, but allow running this file directly as a script # clean absolute imports
try: from src.modules.equipment.insert_actual_data import query_data
from src.modules.equipment.insert_actual_data import query_data, insert_lcca_maximo_corrective_data, insert_ms_equipment_data, insert_acquisition_cost_data from src.modules.equipment.Prediksi import Prediksi, main as predict_run
from src.modules.equipment.Prediksi import Prediksi, main as predict_run from src.modules.equipment.Eac import Eac, main as eac_run
from src.modules.equipment.Eac import Eac, main as eac_run
except ImportError:
# fallback when there's no parent package (e.g., python run.py)
from insert_actual_data import query_data, insert_lcca_maximo_corrective_data, insert_ms_equipment_data, insert_acquisition_cost_data
from Prediksi import Prediksi, main as predict_run
from Eac import Eac, main as eac_run
# Panggil fungsi # Panggil fungsi
async def main(): async def main():

@ -14,7 +14,7 @@ def get_where_query_sql(assetnum, worktype):
) )
) )
""" """
# EM = Emergency Maintainance
return where_query return where_query
def get_where_query_sql_all_worktype(assetnum): def get_where_query_sql_all_worktype(assetnum):

Loading…
Cancel
Save