|
|
|
|
@ -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
|
|
|
|
|
@ -191,59 +183,62 @@ class Prediksi:
|
|
|
|
|
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
|
|
|
|
|
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:
|
|
|
|
|
|