feat: prediction script
parent
caa667f2b8
commit
1d789219dd
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,180 @@
|
||||
from modules.config import get_connection
|
||||
from psycopg2.extras import DictCursor
|
||||
import numpy_financial as npf
|
||||
import json
|
||||
|
||||
class Eac:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __calculate_npv_with_db_inflation_rate(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 data2 dasar
|
||||
query_inflation_rate = """
|
||||
select
|
||||
(SELECT value_num / 100 FROM lcc_ms_master WHERE name = 'inflation_rate') as inflation_rate
|
||||
, (SELECT value_num / 100 FROM lcc_ms_master WHERE name = 'discount_rate') as discount_rate
|
||||
, (select COALESCE(rc_total_cost,0) from lcc_tr_data ltd where equipment_id = %s and seq = 0) as rc_total_cost_0
|
||||
;
|
||||
"""
|
||||
cursor.execute(query_inflation_rate, (equipment_id,))
|
||||
inflation_rate_result = cursor.fetchone()
|
||||
|
||||
if not inflation_rate_result:
|
||||
print("Inflation rate tidak ditemukan.")
|
||||
return None
|
||||
|
||||
inflation_rate = inflation_rate_result['inflation_rate']
|
||||
disc_rate = inflation_rate_result['discount_rate']
|
||||
rc_total_cost_0 = inflation_rate_result['rc_total_cost_0']
|
||||
|
||||
last_seq = 0
|
||||
last_npv = 0
|
||||
# Query untuk mendapatkan data dengan seq dan is_actual
|
||||
query_data_actual = """
|
||||
SELECT equipment_id, tahun, seq, is_actual, rc_total_cost
|
||||
FROM lcc_tr_data
|
||||
WHERE is_actual = 1 AND seq != 0
|
||||
AND equipment_id = %s
|
||||
ORDER BY seq;
|
||||
"""
|
||||
cursor.execute(query_data_actual, (equipment_id,))
|
||||
data_actual = cursor.fetchall()
|
||||
|
||||
# Variabel untuk menyimpan hasil NPV dan PMT per baris
|
||||
npv_results = []
|
||||
cumulative_values = [] # Menyimpan nilai kumulatif hingga baris ke-n
|
||||
# Menghitung NPV dan PMT secara bertahap untuk data aktual
|
||||
for idx, row in enumerate(data_actual):
|
||||
cumulative_values.append(row['rc_total_cost'])
|
||||
# Menghitung NPV menggunakan rumus diskonto
|
||||
final_value = sum(
|
||||
value / ((1 + inflation_rate) ** (i + 1)) for i, value in enumerate(cumulative_values))
|
||||
# Menghitung PMT
|
||||
pmt_value = -npf.pmt(inflation_rate, row['seq'], final_value)
|
||||
pmt_aq_cost = -npf.pmt(disc_rate, row['seq'], rc_total_cost_0)
|
||||
eac = pmt_value + pmt_aq_cost
|
||||
|
||||
npv_results.append({
|
||||
'seq': row['seq'],
|
||||
'year': row['tahun'],
|
||||
'npv': final_value,
|
||||
'pmt': pmt_value,
|
||||
'pmt_aq_cost': pmt_aq_cost,
|
||||
'eac': eac,
|
||||
'is_actual': 1
|
||||
})
|
||||
|
||||
# Update lcc_tr_data
|
||||
update_query = """
|
||||
UPDATE lcc_tr_data
|
||||
SET eac_npv = %s, eac_annual_mnt_cost = %s, eac_annual_acq_cost = %s, eac_eac = %s
|
||||
, updated_by = 'Sys', updated_at = NOW()
|
||||
WHERE equipment_id = %s AND tahun = %s;
|
||||
"""
|
||||
cursor.execute(update_query, (final_value, pmt_value, pmt_aq_cost, eac, equipment_id, row['tahun']))
|
||||
|
||||
last_seq = row['seq']
|
||||
last_npv = final_value
|
||||
|
||||
# Commit perubahan
|
||||
connection.commit()
|
||||
|
||||
# Query untuk mendapatkan data dengan seq dan is_actual = 0
|
||||
query_data_proyeksi = """
|
||||
SELECT equipment_id, tahun, seq, is_actual, rc_total_cost
|
||||
FROM lcc_tr_data
|
||||
WHERE is_actual = 0
|
||||
ORDER BY seq;
|
||||
"""
|
||||
cursor.execute(query_data_proyeksi)
|
||||
data_proyeksi = cursor.fetchall()
|
||||
cumulative_values = []
|
||||
|
||||
# Menghitung NPV dan PMT secara bertahap untuk data proyeksi
|
||||
for idx, row in enumerate(data_proyeksi):
|
||||
cumulative_values.append(row['rc_total_cost'])
|
||||
npv_value = sum(value / ((1 + inflation_rate) ** (i + 1)) for i, value in enumerate(cumulative_values))
|
||||
pv_value = npf.pv(inflation_rate, last_seq, 0, npv_value)
|
||||
final_value = -pv_value + last_npv
|
||||
# Menghitung PMT
|
||||
pmt_value = -npf.pmt(inflation_rate, row['seq'], final_value)
|
||||
pmt_aq_cost = -npf.pmt(disc_rate, row['seq'], rc_total_cost_0)
|
||||
eac = pmt_value + pmt_aq_cost
|
||||
|
||||
npv_results.append({
|
||||
'seq': row['seq'],
|
||||
'year': row['tahun'],
|
||||
'npv': final_value,
|
||||
'pmt': pmt_value,
|
||||
'pmt_aq_cost': pmt_aq_cost,
|
||||
'eac': eac,
|
||||
'is_actual': 0
|
||||
})
|
||||
|
||||
# Update lcc_tr_data
|
||||
update_query = """
|
||||
UPDATE lcc_tr_data
|
||||
SET eac_npv = %s, eac_annual_mnt_cost = %s, eac_annual_acq_cost = %s, eac_eac = %s
|
||||
, updated_by = 'Sys', updated_at = NOW()
|
||||
WHERE equipment_id = %s AND tahun = %s;
|
||||
"""
|
||||
cursor.execute(update_query, (final_value, pmt_value, pmt_aq_cost, eac, equipment_id, row['tahun']))
|
||||
|
||||
# Commit perubahan
|
||||
connection.commit()
|
||||
|
||||
# Menutup koneksi
|
||||
cursor.close()
|
||||
connection.close()
|
||||
|
||||
# Menampilkan hasil
|
||||
for result in npv_results:
|
||||
print(
|
||||
f"Seq: {result['seq']}, Is Actual: {result['is_actual']}, NPV: {result['npv']:.2f}, PMT: {result['pmt']:.2f}, AQ_COST: {result['pmt_aq_cost']:.2f}, EAC: {result['eac']:.2f}")
|
||||
|
||||
return npv_results
|
||||
|
||||
except Exception as e:
|
||||
print("Terjadi kesalahan:", str(e))
|
||||
|
||||
# ======================================================================================================================================================
|
||||
|
||||
def hitung_eac_equipment(self, p_equipment_id):
|
||||
try:
|
||||
# Mendapatkan koneksi dari config.py
|
||||
connection = get_connection()
|
||||
if connection is None:
|
||||
print("Koneksi ke database gagal.")
|
||||
return None
|
||||
|
||||
cursor = connection.cursor(cursor_factory=DictCursor)
|
||||
|
||||
rslt = self.__calculate_npv_with_db_inflation_rate(p_equipment_id)
|
||||
|
||||
lowest_eac_record = min(rslt, key=lambda x: x['eac'])
|
||||
print(json.dumps(lowest_eac_record))
|
||||
# Update lcc_tr_data
|
||||
update_query = """
|
||||
UPDATE lcc_ms_equipment_data
|
||||
SET min_eac_info = %s, updated_by = 'Sys', updated_at = NOW()
|
||||
WHERE equipment_id = %s;
|
||||
"""
|
||||
cursor.execute(update_query, (json.dumps(lowest_eac_record), p_equipment_id))
|
||||
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
connection.close()
|
||||
|
||||
except Exception as e:
|
||||
print("Terjadi kesalahan saat memproses semua equipment:", str(e))
|
||||
@ -0,0 +1,367 @@
|
||||
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}")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,24 @@
|
||||
import psycopg2
|
||||
|
||||
def get_connection():
|
||||
try:
|
||||
# Konfigurasi koneksi database
|
||||
|
||||
# connection = psycopg2.connect(
|
||||
# host="localhost",
|
||||
# port=5432,
|
||||
# database="postgres",
|
||||
# user="postgres",
|
||||
# password="ariwa"
|
||||
# )
|
||||
connection = psycopg2.connect(
|
||||
dbname = "digital_twin",
|
||||
user = "postgres",
|
||||
password = "postgres",
|
||||
host = "192.168.1.85",
|
||||
port = "5432"
|
||||
)
|
||||
return connection
|
||||
except Exception as e:
|
||||
print("Error saat koneksi ke database:", e)
|
||||
return None
|
||||
Binary file not shown.
@ -0,0 +1,343 @@
|
||||
import subprocess
|
||||
|
||||
import openpyxl
|
||||
from openpyxl.utils import get_column_letter
|
||||
from openpyxl.styles import PatternFill
|
||||
from datetime import datetime
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
from config import get_connection
|
||||
ROOT_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
file_ref = f"{ROOT_DIR}/wlc.xlsx"
|
||||
file_hasil = f"{ROOT_DIR}/hasil/wlc.xlsx"
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
conn = get_connection()
|
||||
if conn is None:
|
||||
print("Koneksi ke database gagal.")
|
||||
sys.exit(1)
|
||||
|
||||
cur = conn.cursor()
|
||||
|
||||
|
||||
# Fungsi untuk mengambil data dari PostgreSQL
|
||||
def fetch_data_from_postgresql(q=""):
|
||||
try:
|
||||
cur.execute(q) # Jalankan query yang diberikan
|
||||
col_names = [desc[0] for desc in cur.description] # Ambil nama kolom
|
||||
rows = cur.fetchall() # Ambil semua baris data
|
||||
|
||||
# Buat list dictionary berdasarkan nama kolom
|
||||
result = [dict(zip(col_names, row)) for row in rows]
|
||||
return result
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return None
|
||||
|
||||
def insert_and_shift_right(file_path, sheet_name):
|
||||
# Load workbook and select sheet
|
||||
wb = openpyxl.load_workbook(file_path)
|
||||
sheet = wb[sheet_name]
|
||||
|
||||
# Get the last two columns
|
||||
max_col = sheet.max_column
|
||||
last_two_cols = [max_col - 1, max_col]
|
||||
|
||||
# Insert a new column after the last two columns
|
||||
new_col_idx = max_col + 1
|
||||
sheet.insert_cols(new_col_idx)
|
||||
|
||||
# Shift formulas, values, and styles
|
||||
for row in range(1, sheet.max_row + 1):
|
||||
for col in last_two_cols:
|
||||
current_cell = sheet.cell(row=row, column=col)
|
||||
current_value = current_cell.value
|
||||
if isinstance(current_value, str) and current_value.startswith("="):
|
||||
# Adjust formula to the new column
|
||||
formula = current_value.replace(get_column_letter(col), get_column_letter(new_col_idx))
|
||||
sheet.cell(row=row, column=new_col_idx, value=formula)
|
||||
else:
|
||||
sheet.cell(row=row, column=new_col_idx, value=current_value)
|
||||
|
||||
# Copy cell style (fill color)
|
||||
new_cell = sheet.cell(row=row, column=new_col_idx)
|
||||
if current_cell.fill:
|
||||
new_cell.fill = PatternFill(start_color=current_cell.fill.start_color.rgb,
|
||||
end_color=current_cell.fill.end_color.rgb,
|
||||
fill_type=current_cell.fill.fill_type)
|
||||
|
||||
# Save workbook with changes
|
||||
wb.save(file_path)
|
||||
print(f"Column inserted and shifted to the right in {file_path}")
|
||||
|
||||
|
||||
def insert_column_data(file_path, sheet_name, col_name, data):
|
||||
"""
|
||||
Memasukkan data ke Excel per kolom.
|
||||
|
||||
Args:
|
||||
file_path (str): Path file Excel.
|
||||
sheet_name (str): Nama sheet Excel.
|
||||
col_name (str): Nama kolom untuk dicari di baris pertama.
|
||||
data (dict): Data yang akan dimasukkan, dengan kunci adalah baris (row_name) dan nilai adalah isi sel.
|
||||
"""
|
||||
# Load workbook dan pilih sheet
|
||||
wb = openpyxl.load_workbook(file_path, data_only=False)
|
||||
sheet = wb[sheet_name]
|
||||
|
||||
# Temukan indeks kolom berdasarkan col_name di baris pertama
|
||||
col_index = None
|
||||
for col in range(1, sheet.max_column + 1):
|
||||
if sheet.cell(row=1, column=col).value == col_name:
|
||||
col_index = col
|
||||
break
|
||||
|
||||
# Jika kolom ditemukan, masukkan data per baris
|
||||
if col_index:
|
||||
for row_name, value in data.items():
|
||||
row_index = None
|
||||
# Cari indeks baris berdasarkan nama di kolom pertama
|
||||
for row in range(2, sheet.max_row + 1): # Mulai dari baris ke-2 untuk menghindari header
|
||||
if sheet.cell(row=row, column=1).value == row_name:
|
||||
row_index = row
|
||||
break
|
||||
# Isi nilai jika baris ditemukan
|
||||
if row_index:
|
||||
sheet.cell(row=row_index, column=col_index, value=value)
|
||||
else:
|
||||
print(f"Column name '{col_name}' not found in the sheet.")
|
||||
|
||||
# Simpan workbook setelah semua data kolom dimasukkan
|
||||
wb.save(file_path)
|
||||
wb.close()
|
||||
wb.save(file_path)
|
||||
|
||||
def insert_param(file_path):
|
||||
wb = openpyxl.load_workbook(file_path, data_only=False)
|
||||
sheet = wb["Params"]
|
||||
col_name = "Value"
|
||||
|
||||
# Temukan indeks kolom berdasarkan col_name di baris pertama
|
||||
col_index = None
|
||||
for col in range(1, sheet.max_column + 1):
|
||||
if sheet.cell(row=1, column=col).value == col_name:
|
||||
col_index = col
|
||||
break
|
||||
|
||||
query = """SELECT
|
||||
value_str,
|
||||
CASE
|
||||
WHEN unit_of_measurement = '%' THEN value_num / 100
|
||||
ELSE value_num
|
||||
END AS value_num
|
||||
FROM lcc_ms_master"""
|
||||
data_map = fetch_data_from_postgresql(query)
|
||||
mapping_data = {}
|
||||
|
||||
for item in data_map:
|
||||
mapping_data[item['value_str']] = round(item['value_num'], 2)
|
||||
|
||||
# Jika kolom ditemukan, masukkan data per baris
|
||||
if col_index:
|
||||
for row in range(2, sheet.max_row + 1): # Mulai dari baris ke-2 untuk menghindari header
|
||||
param_name = sheet.cell(row=row, column=1).value # Kolom 1 == value_str
|
||||
if param_name in mapping_data:
|
||||
sheet.cell(row=row, column=col_index, value=mapping_data[param_name])
|
||||
else:
|
||||
print(f"Params: Baris name '{col_name}' not found in the sheet.")
|
||||
|
||||
# Simpan workbook setelah semua data diperbarui
|
||||
wb.save(file_path)
|
||||
wb.close()
|
||||
|
||||
def get_abjad(i):
|
||||
if i < 0:
|
||||
raise ValueError("Indeks harus berupa angka 0 atau lebih.")
|
||||
result = ""
|
||||
while i >= 0:
|
||||
i, remainder = divmod(i, 26)
|
||||
result = chr(65 + remainder) + result
|
||||
i -= 1 # Mengurangi 1 untuk menangani offset ke basis 0
|
||||
return result
|
||||
def validate_number(n):
|
||||
return n if n is not None else 0
|
||||
|
||||
# Example usage
|
||||
# insert_and_shift_right(file_ref, "Calc")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ====================================================== 00000 =================================================
|
||||
# =============================================== SET DATA KE EXCEL =================================================
|
||||
# ====================================================== 00000 =================================================
|
||||
# insert PARAM
|
||||
insert_param(file_ref)
|
||||
print("Insert Params Sukses")
|
||||
|
||||
# Pengolahan data dari PostgreSQL
|
||||
current_year = datetime.now().year
|
||||
query = "SELECT * FROM lcc_plant_tr_data ORDER BY seq"
|
||||
data = fetch_data_from_postgresql(query)
|
||||
if data:
|
||||
for record in data:
|
||||
# print(f"Processing column for year {record['tahun']}")
|
||||
|
||||
# Kumpulkan semua data untuk satu kolom
|
||||
col_data = {}
|
||||
|
||||
# Tambahkan data tambahan berdasarkan kondisi
|
||||
if record['is_actual'] == 1:
|
||||
col_data.update({
|
||||
"Net Capacity Factor": validate_number(record['net_capacity_factor']),
|
||||
"EAF": validate_number(record['eaf']),
|
||||
"Biaya Investasi Tambahan": validate_number(record['cost_a_pinjaman'])/1000000,
|
||||
"O & M Cost": validate_number(record['cost_bd_om'])/1000000,
|
||||
"Periodic Maintenance Cost (Non MI)": validate_number(record['cost_bd_pm_nonmi'])/1000000,
|
||||
|
||||
"Production (Bruto)": validate_number(record['production_bruto']),
|
||||
"Production (Netto)": validate_number(record['production_netto']),
|
||||
"Fuel Consumption": validate_number(record['fuel_consumption']),
|
||||
"Revenue A": validate_number(record['revenue_a'])/1000000,
|
||||
"Revenue B": validate_number(record['revenue_b'])/1000000,
|
||||
"Revenue C": validate_number(record['revenue_c'])/1000000,
|
||||
"Revenue D": validate_number(record['revenue_d'])/1000000,
|
||||
"Fuel Cost": validate_number(record['cost_c_fuel'])/1000000
|
||||
})
|
||||
else:
|
||||
seq_offset = record['seq'] + 2
|
||||
col_data.update({
|
||||
"Net Capacity Factor": validate_number(record['net_capacity_factor']),
|
||||
"EAF": validate_number(record['eaf']),
|
||||
"Biaya Investasi Tambahan": validate_number(record['cost_a_pinjaman'])/1000000,
|
||||
"O & M Cost": validate_number(record['cost_bd_om'])/1000000,
|
||||
"Periodic Maintenance Cost (Non MI)": validate_number(record['cost_bd_pm_nonmi'])/1000000,
|
||||
|
||||
"Production (Bruto)": f"={get_abjad(seq_offset)}7/(100-SUM(Params!$C$16:$C$17))*100",
|
||||
"Production (Netto)": f"={get_abjad(seq_offset)}4*8760*Params!$C$15/100",
|
||||
"Fuel Consumption": f"={get_abjad(seq_offset)}6*Params!$C$18",
|
||||
"Revenue A": f"=(Params!$C$19*{get_abjad(seq_offset)}5*Params!$C$15*1000*12/100)/1000000",
|
||||
"Revenue B": f"=(Params!$C$20*{get_abjad(seq_offset)}5*Params!$C$15*1000*12/100)/1000000",
|
||||
"Revenue C": f"=Params!$C$21*{get_abjad(seq_offset)}7*1000/1000000",
|
||||
"Revenue D": f"=Params!$C$22*{get_abjad(seq_offset)}7*1000/1000000",
|
||||
"Fuel Cost": f"={get_abjad(seq_offset)}9*Params!$C$23/10^6"
|
||||
})
|
||||
|
||||
# Masukkan data ke Excel
|
||||
insert_column_data(file_ref, "Calc", record['tahun'], col_data)
|
||||
else:
|
||||
print("No data found.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ====================================================== 00000 =================================================
|
||||
# =============================================== SIMPAN UNTUK CHART =================================================
|
||||
# ====================================================== 00000 =================================================
|
||||
# Mapping dari kolom Excel ke nama kolom database sesuai gambar 2
|
||||
|
||||
libreoffice_path = "soffice" # Sesuaikan dengan lokasi di Linux/Windows
|
||||
command = f'"{libreoffice_path}" --headless --convert-to xlsx {file_ref} --outdir {ROOT_DIR}/hasil'
|
||||
subprocess.run(command, shell=True, check=True)
|
||||
|
||||
print("recalculate OK")
|
||||
|
||||
# def read_excel_with_pandas(file_path, sheet_name):
|
||||
# df = pd.read_excel(file_path, sheet_name=sheet_name, engine='openpyxl')
|
||||
# print(df.head()) # Periksa apakah nilai diambil dengan benar
|
||||
# read_excel_with_pandas(f"{file_hasil}", "Upload")
|
||||
#
|
||||
|
||||
column_mapping = {
|
||||
"Total Revenue": "chart_total_revenue",
|
||||
"Revenue A": "chart_revenue_a",
|
||||
"Revenue B": "chart_revenue_b",
|
||||
"Revenue C": "chart_revenue_c",
|
||||
"Revenue D": "chart_revenue_d",
|
||||
"Revenue Annualized": "chart_revenue_annualized",
|
||||
"Fuel Cost (Component C)": "chart_fuel_cost_component_c",
|
||||
"Fuel Cost": "chart_fuel_cost",
|
||||
"Fuel Cost Annualized": "chart_fuel_cost_annualized",
|
||||
"O and M Cost (Component B and D)": "chart_oem_component_bd",
|
||||
"O and M Cost": "chart_oem_bd_cost",
|
||||
"Periodic Maintenance Cost (NonMI)": "chart_oem_periodic_maintenance_cost",
|
||||
"O and M Cost Annualized": "chart_oem_annualized",
|
||||
"Capex (Component A)": "chart_capex_component_a",
|
||||
"Biaya Investasi Tambahan": "chart_capex_biaya_investasi_tambahan",
|
||||
"Acquisition Cost": "chart_capex_acquisition_cost",
|
||||
"Capex Annualized": "chart_capex_annualized"
|
||||
}
|
||||
# Fungsi untuk memperbarui database
|
||||
def update_database_from_excel():
|
||||
# Buka koneksi ke PostgreSQL
|
||||
|
||||
try:
|
||||
# Load workbook dan pilih sheet
|
||||
wb = openpyxl.load_workbook(f"{file_hasil}", data_only=True)
|
||||
sheet = wb["Upload"]
|
||||
|
||||
# Ambil tahun dari baris pertama mulai dari kolom ketiga
|
||||
years = [sheet.cell(row=1, column=col).value for col in range(3, sheet.max_column + 1)]
|
||||
|
||||
# Loop melalui setiap baris di Excel mulai dari baris kedua
|
||||
for row in range(2, sheet.max_row + 1):
|
||||
row_name = sheet.cell(row=row, column=1).value # Nama data di kolom pertama
|
||||
row_name = row_name if row_name else sheet.cell(row=row,column=2).value # Nama data di kolom kedua jika kolom pertama kosong
|
||||
db_column = column_mapping.get(row_name)
|
||||
|
||||
|
||||
if db_column:
|
||||
for col_index, year in enumerate(years, start=3): # Iterasi mulai dari kolom ke-3
|
||||
value = sheet.cell(row=row, column=col_index).value
|
||||
|
||||
if value is None:
|
||||
continue
|
||||
|
||||
# Lakukan update data ke database
|
||||
query = f"""
|
||||
UPDATE lcc_plant_tr_data
|
||||
SET {db_column} = %s
|
||||
WHERE tahun = %s
|
||||
"""
|
||||
seq = sheet.cell(row=row, column=2).value # Ambil seq dari kolom kedua jika dibutuhkan
|
||||
cur.execute(query, (value, year)) # Jalankan query dengan parameter
|
||||
|
||||
conn.commit()
|
||||
print("Data successfully updated in database.")
|
||||
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
print(f"Error: {e}")
|
||||
update_database_from_excel()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
end_time = time.time()
|
||||
elapsed_time = end_time - start_time
|
||||
minutes = int(elapsed_time // 60)
|
||||
seconds = int(elapsed_time % 60)
|
||||
# Cetak hasil
|
||||
print(f"Execution time: {minutes} minutes and {seconds} seconds")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Loading…
Reference in New Issue