ariwahyunahar 3 weeks ago
commit 398a2b14a2

@ -2,7 +2,7 @@ import os
import logging
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.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession
@ -392,16 +392,16 @@ async def get_top_10_economic_life(*, db_session: DbSession, common) -> list[Equ
query.add_columns(
case(
(
(current_year - Equipment.minimum_eac_year) >= 0,
(current_year - Equipment.minimum_eac_year),
(Equipment.minimum_eac_year - current_year) >= 0,
(Equipment.minimum_eac_year - current_year),
),
else_=0,
).label("remaining_life")
)
.filter(Equipment.minimum_eac_year != None)
.filter((Equipment.minimum_eac != None) & (Equipment.minimum_eac != 0))
else_=0,
).label("remaining_life")
)
.filter(Equipment.minimum_eac_year != None)
.filter((Equipment.minimum_eac != None) & (Equipment.minimum_eac != 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())
)
@ -424,8 +424,8 @@ async def get_top_10_replacement_priorities(*, db_session: DbSession, common) ->
query.add_columns(
case(
(
(current_year - Equipment.minimum_eac_year) >= 0,
(current_year - Equipment.minimum_eac_year),
(Equipment.minimum_eac_year - current_year) >= 0,
(Equipment.minimum_eac_year - current_year),
),
else_=0,
).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 != None) & (Equipment.minimum_eac != 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())
)

@ -5,8 +5,7 @@ import json
import sys
import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import get_connection
from src.modules.config import get_connection
import argparse
@ -187,7 +186,8 @@ class Eac:
# final_value adalah PV pada titik proyeksi periods
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
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1]
# dimana PV = rc_total_cost_0, r = disc_rate, n = row["seq"]

@ -15,10 +15,8 @@ from dotenv import load_dotenv
import sys
import os
from equipment.formula import rc_labor_cost, rc_lost_cost, rc_total_cost
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import get_connection, get_production_connection
from .formula import rc_labor_cost, rc_lost_cost, rc_total_cost
from src.modules.config import get_connection, get_production_connection
import json
load_dotenv()
@ -97,6 +95,7 @@ class Prediksi:
FROM lcc_equipment_tr_data
WHERE assetnum = %s
and is_actual=1
and seq != 0
;
"""
cursor.execute(query, (equipment_id,))
@ -240,57 +239,39 @@ class Prediksi:
# print(f"HTTP error occurred: {e}")
# return {}
# Menyiapkan data untuk batch insert
# print(f"Data to be inserted: {data}")
# Menyiapkan data untuk batch insert atau update
records_to_insert = []
check_existence_query = """
SELECT id FROM lcc_equipment_tr_data
WHERE assetnum = %s AND tahun = %s AND is_actual = 0
"""
update_query = """
UPDATE lcc_equipment_tr_data
SET
rc_cm_material_cost = %s,
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
"""
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
# 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
# append record using the difference for raw_cm_interval
records_to_insert.append(
(
str(uuid4()), # id
int(max_seq), # seq
int(row["year"]),
equipment_id,
# 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,
@ -299,16 +280,36 @@ class Prediksi:
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(
(
str(uuid4()), # id
int(max_seq), # seq
int(row["year"]),
equipment_id,
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
# Eksekusi batch insert jika ada data baru
if records_to_insert:
cursor.executemany(insert_query, records_to_insert)
# Eksekusi batch insert
cursor.executemany(insert_query, records_to_insert)
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:
print(f"Error saat menyimpan data ke database: {e}")
@ -713,7 +714,6 @@ class Prediksi:
if df is None:
print("Data tidak tersedia untuk prediksi.")
return
# Mendapatkan tahun proyeksi dari DB
par_tahun_target = self.__get_param(assetnum)
@ -771,19 +771,14 @@ class Prediksi:
try:
# Case untuk kolom yang terkait dengan corrective maintenance (cm)
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_n = df.shape[0]
recent_df = df
recent_n = df.shape[0]
recent_n = max(1, recent_n)
recent_vals = (
recent_df.sort_values("year", ascending=True)
recent_df
.sort_values("year", ascending=True)
.head(recent_n)[column]
.dropna()
)
@ -796,18 +791,11 @@ class Prediksi:
if recent_vals.empty:
avg = 0.0
else:
# Pastikan numeric; jika gagal, pakai mean dari yang bisa dikonversi
try:
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)
avg = pd.to_numeric(recent_vals, errors="coerce").fillna(0).mean()
avg = 0.0 if pd.isna(avg) else float(avg)
preds = np.repeat(float(avg), n_future)
print(preds)
# 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
@ -944,7 +932,7 @@ class Prediksi:
predictions_df = pd.DataFrame(predictions)
# print(predictions_df)
# Hapus data prediksi yang ada sebelumnya
self.__delete_predictions_from_db(assetnum)
# self.__delete_predictions_from_db(assetnum)
# Insert hasil prediksi ke database
try:

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

@ -1,17 +1,10 @@
import asyncio
import time
# prefer package-relative imports, but allow running this file directly as a script
try:
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.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
# clean absolute imports
from src.modules.equipment.insert_actual_data import query_data
from src.modules.equipment.Prediksi import Prediksi, main as predict_run
from src.modules.equipment.Eac import Eac, main as eac_run
# Panggil fungsi
async def main():

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

Loading…
Cancel
Save