import pandas as pd import numpy as np import numpy_financial as npf # Gunakan numpy-financial untuk fungsi keuangan from statsmodels.tsa.holtwinters import ExponentialSmoothing from sklearn.linear_model import LinearRegression from sklearn.tree import DecisionTreeRegressor import matplotlib.pyplot as plt from uuid import uuid4 from modules.config import get_connection from psycopg2.extras import DictCursor class Prediksi: def __init__(self): pass # Fungsi untuk mengambil data dari database def __get_param(self, equipment_id): try: # Mendapatkan koneksi dari config.py connection = get_connection() if connection is None: print("Koneksi ke database gagal.") return None # Membuat cursor menggunakan DictCursor cursor = connection.cursor(cursor_factory=DictCursor) # Query untuk mendapatkan data query = """ SELECT (select COALESCE(forecasting_target_year, 2060) from lcc_ms_equipment_data where equipment_id = %s) AS forecasting_target_year """ cursor.execute(query, (equipment_id,)) par1 = cursor.fetchone() return par1["forecasting_target_year"] except Exception as e: print(f"Error saat mengambil data dari database: {e}") return None finally: if connection: connection.close() # Fungsi untuk mengambil data dari database def __fetch_data_from_db(self, equipment_id): try: # Mendapatkan koneksi dari config.py connection = get_connection() if connection is None: print("Koneksi ke database gagal.") return None # Membuat cursor menggunakan DictCursor cursor = connection.cursor(cursor_factory=DictCursor) # Query untuk mendapatkan data 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_material_cost AS pm_cost, raw_pm_labor_time AS pm_labor_time, raw_pm_labor_human AS pm_labor_human, raw_oh_material_cost AS oh_cost, raw_oh_labor_time AS oh_labor_time, raw_oh_labor_human AS oh_labor_human, "raw_loss_output_MW" AS loss_output_mw, raw_loss_output_price AS loss_price FROM lcc_tr_data WHERE equipment_id = %s and is_actual=1 ; """ cursor.execute(query, (equipment_id,)) # Mengambil hasil dan mengonversi ke DataFrame pandas data = cursor.fetchall() columns = [desc[0] for desc in cursor.description] # Mengambil nama kolom dari hasil query df = pd.DataFrame(data, columns=columns) return df except Exception as e: print(f"Error saat mengambil data dari database: {e}") return None finally: if connection: connection.close() # Fungsi untuk prediksi menggunakan Future Value (FV) def __future_value_predict(self, rate, nper, pmt, pv, years): # Hitung Future Value untuk tahun-tahun proyeksi fv_values = [] for i in range(len(years)): fv = npf.fv(rate, nper + i, pmt, pv) # Menggunakan numpy_financial.fv fv_values.append(fv) return fv_values # Fungsi untuk menghapus data proyeksi pada tahun tertentu def __delete_predictions_from_db(self, equipment_id): try: connection = get_connection() if connection is None: print("Koneksi ke database gagal.") return cursor = connection.cursor() # Query untuk menghapus data berdasarkan tahun proyeksi delete_query = """ DELETE FROM lcc_tr_data WHERE equipment_id = %s AND is_actual = 0; """ # Asumsikan kolom is_actual digunakan untuk membedakan data proyeksi dan data aktual # Eksekusi query delete cursor.execute(delete_query, (equipment_id,)) connection.commit() print(f"Data proyeksi untuk tahun {equipment_id} berhasil dihapus.") except Exception as e: print(f"Error saat menghapus data proyeksi dari database: {e}") finally: if connection: connection.close() # Fungsi untuk menyimpan data proyeksi ke database def __insert_predictions_to_db(self, data, equipment_id): try: connection = get_connection() if connection is None: print("Koneksi ke database gagal.") return cursor = connection.cursor() # Query untuk mendapatkan nilai maksimum seq get_max_seq_query = """ SELECT COALESCE(MAX(seq), 0) FROM lcc_tr_data WHERE equipment_id = %s """ cursor.execute(get_max_seq_query, (equipment_id,)) max_seq = cursor.fetchone()[0] # Query untuk insert data insert_query = """ INSERT INTO lcc_tr_data ( id, seq, is_actual, raw_pm_interval, tahun, equipment_id, 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_material_cost, raw_oh_labor_time, raw_oh_labor_human, "raw_loss_output_MW", raw_loss_output_price , created_by, created_at ) VALUES ( %s, %s, 0, 1, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'Sys', NOW() ) """ # Menyiapkan data untuk batch insert records_to_insert = [] for _, row in data.iterrows(): max_seq = max_seq + 1 records_to_insert.append(( str(uuid4()), max_seq, row["year"], equipment_id, row["cm_interval"] if row["cm_interval"] >= 1 else 1, row["cm_cost"], row["cm_labor_time"], row["cm_labor_human"], row["pm_cost"], row["pm_labor_time"], row["pm_labor_human"], row["oh_cost"], row["oh_labor_time"], row["oh_labor_human"], row["loss_output_mw"], row["loss_price"] )) # Eksekusi batch insert cursor.executemany(insert_query, records_to_insert) connection.commit() print("Data proyeksi berhasil dimasukkan ke database.") except Exception as e: print(f"Error saat menyimpan data ke database: {e}") finally: if connection: connection.close() # Fungsi untuk menghapus data proyeksi pada tahun tertentu def __update_date_lcc(self, equipment_id): try: connection = get_connection() if connection is None: print("Koneksi ke database gagal.") return cursor = connection.cursor() # Query untuk menghapus data berdasarkan tahun proyeksi up_query = """ update lcc_tr_data set rc_cm_material_cost = raw_cm_material_cost ,rc_cm_labor_cost = (raw_cm_interval * raw_cm_labor_time * raw_cm_labor_human * COALESCE((SELECT man_hour FROM lcc_ms_year_data WHERE year = lcc_tr_data.tahun), coalesce((select value_num from lcc_ms_master where name='manhours_rate'), 0)) ) ,rc_pm_material_cost = raw_pm_material_cost ,rc_pm_labor_cost = (raw_pm_labor_time * raw_pm_labor_human * COALESCE((SELECT man_hour FROM lcc_ms_year_data WHERE year = lcc_tr_data.tahun), coalesce((select value_num from lcc_ms_master where name='manhours_rate'), 0)) ) ,rc_predictive_labor_cost = COALESCE( (raw_predictive_labor_time * raw_predictive_labor_human * COALESCE((SELECT man_hour FROM lcc_ms_year_data WHERE year = lcc_tr_data.tahun), coalesce((select value_num from lcc_ms_master where name='manhours_rate'), 0)) ) , 0) ,rc_oh_material_cost = raw_oh_material_cost ,rc_oh_labor_cost = (raw_oh_labor_time * raw_oh_labor_human * COALESCE((SELECT man_hour FROM lcc_ms_year_data WHERE year = lcc_tr_data.tahun), coalesce((select value_num from lcc_ms_master where name='manhours_rate'), 0)) ) ,rc_project_material_cost = coalesce(raw_project_task_material_cost, 0) ,rc_lost_cost = coalesce(("raw_loss_output_MW" * raw_loss_output_price * raw_cm_interval), 0) * 1000 ,rc_operation_cost = coalesce(raw_operational_cost, 0) ,rc_maintenance_cost = coalesce(raw_maintenance_cost, 0) ,rc_total_cost = ( raw_cm_material_cost + (raw_cm_interval * raw_cm_labor_time * raw_cm_labor_human * COALESCE((SELECT man_hour FROM lcc_ms_year_data WHERE year = lcc_tr_data.tahun), coalesce((select value_num from lcc_ms_master where name='manhours_rate'), 0)) ) + raw_pm_material_cost + (raw_pm_labor_time * raw_pm_labor_human * COALESCE((SELECT man_hour FROM lcc_ms_year_data WHERE year = lcc_tr_data.tahun), coalesce((select value_num from lcc_ms_master where name='manhours_rate'), 0)) ) + COALESCE( (raw_predictive_labor_time * raw_predictive_labor_human * COALESCE((SELECT man_hour FROM lcc_ms_year_data WHERE year = lcc_tr_data.tahun), coalesce((select value_num from lcc_ms_master where name='manhours_rate'), 0)) ) , 0) + raw_oh_material_cost + (raw_oh_labor_time * raw_oh_labor_human * COALESCE((SELECT man_hour FROM lcc_ms_year_data WHERE year = lcc_tr_data.tahun), coalesce((select value_num from lcc_ms_master where name='manhours_rate'), 0)) ) + coalesce(raw_project_task_material_cost, 0) + coalesce(("raw_loss_output_MW" * raw_loss_output_price * raw_cm_interval), 0) * 1000 + coalesce(raw_operational_cost, 0) + coalesce(raw_maintenance_cost, 0) ) , updated_by = 'Sys', updated_at = NOW() where equipment_id = %s; update lcc_tr_data set rc_total_cost = (select acquisition_cost from lcc_ms_equipment_data where equipment_id=lcc_tr_data.equipment_id) where equipment_id = %s and seq=0; """ # Asumsikan kolom is_actual digunakan untuk membedakan data proyeksi dan data aktual # Eksekusi query delete cursor.execute(up_query, (equipment_id, equipment_id)) connection.commit() print(f"Data berhasil diupdate.") except Exception as e: print(f"Error saat update data proyeksi dari database: {e}") finally: if connection: connection.close() # Fungsi untuk mengambil parameter dari database def __get_rate_and_max_year(self, equipment_id): try: connection = get_connection() if connection is None: print("Koneksi ke database gagal.") return None, None cursor = connection.cursor(cursor_factory=DictCursor) # Query untuk mendapatkan rate dan max_year query = """ SELECT (SELECT value_num / 100 FROM lcc_ms_master where name='inflation_rate') AS rate, (SELECT MAX(tahun) FROM lcc_tr_data WHERE is_actual = 1 AND equipment_id = %s) AS max_year """ cursor.execute(query, (equipment_id,)) result = cursor.fetchone() # Debug hasil query print(f"Result: {result}") rate = result["rate"] max_year = result["max_year"] # Validasi nilai rate dan max_year if rate is None: raise Exception("Nilai 'rate' tidak boleh kosong. Periksa tabel 'lcc_ms_master'.") if max_year is None: raise Exception("Nilai 'max_year' tidak boleh kosong. Periksa tabel 'lcc_tr_data'.") return rate, max_year except Exception as e: print(f"Error saat mendapatkan parameter dari database: {e}") raise # Lempar kembali exception agar program berhenti finally: if connection: connection.close() # ====================================================================================================================================================== def predict_equipment_data(self, p_equipment_id): try: # Mengambil data dari database df = self.__fetch_data_from_db(p_equipment_id) if df is None: print("Data tidak tersedia untuk prediksi.") return # Mendapatkan tahun proyeksi dari DB par_tahun_target = self.__get_param(p_equipment_id) # Tahun proyeksi future_years = list(range(df["year"].max() + 1, par_tahun_target + 1)) # Hasil prediksi predictions = {"year": future_years} # Fungsi untuk prediksi menggunakan Linear Regression def linear_regression_predict(column, years): x = df["year"].values.reshape(-1, 1) y = df[column].fillna(0).values model = LinearRegression() model.fit(x, y) future_x = np.array(years).reshape(-1, 1) preds = model.predict(future_x) return np.abs(preds) # Fungsi untuk prediksi menggunakan Exponential Smoothing def exponential_smoothing_predict(column, years): data_series = df[column].fillna(0).values model = ExponentialSmoothing(data_series, trend="add", seasonal=None, seasonal_periods=None) model_fit = model.fit() preds = model_fit.forecast(len(years)) return np.abs(preds) # Fungsi untuk prediksi menggunakan Decision Tree def decision_tree_predict(column, years): x = df["year"].values.reshape(-1, 1) y = df[column].fillna(0).values model = DecisionTreeRegressor() model.fit(x, y) future_x = np.array(years).reshape(-1, 1) preds = model.predict(future_x) return np.abs(preds) # Mendapatkan rate dan tahun maksimal rate, max_year = self.__get_rate_and_max_year(p_equipment_id) pmt = 0 # Prediksi untuk setiap kolom for column in df.columns: if column != "year": if "cost" in column.lower(): # Prediksi Future Value nper = max_year - df["year"].max() pv = -df[column].iloc[-1] predictions[column] = self.__future_value_predict(rate, nper, pmt, pv, future_years) elif df[column].nunique() < 5: predictions[column] = exponential_smoothing_predict(column, future_years) elif df[column].isnull().sum() > 0: predictions[column] = decision_tree_predict(column, future_years) else: predictions[column] = linear_regression_predict(column, future_years) # Konversi hasil ke DataFrame predictions_df = pd.DataFrame(predictions) # Hapus data prediksi yang ada sebelumnya self.__delete_predictions_from_db(p_equipment_id) # Insert hasil prediksi ke database self.__insert_predictions_to_db(predictions_df, p_equipment_id) # Update data untuk total RiskCost per tahun self.__update_date_lcc(p_equipment_id) except Exception as e: print(f"Program dihentikan: {e}")