feat: prediction script

main
Cizz22 12 months ago
parent caa667f2b8
commit 1d789219dd

1263
poetry.lock generated

File diff suppressed because it is too large Load Diff

@ -7,7 +7,7 @@ license = "MIT"
readme = "README.md" readme = "README.md"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.11" python = "^3.10"
fastapi = {extras = ["standard"], version = "^0.115.6"} fastapi = {extras = ["standard"], version = "^0.115.6"}
sqlalchemy = "^2.0.36" sqlalchemy = "^2.0.36"
httpx = "^0.27.2" httpx = "^0.27.2"

@ -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}")

@ -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.

@ -1,3 +1,8 @@
import asyncio
import os
import logging
from subprocess import PIPE
from sqlalchemy import Select, Delete, cast, String from sqlalchemy import Select, Delete, cast, String
from .model import PlantTransactionData from .model import PlantTransactionData
from .schema import PlantTransactionDataCreate, PlantTransactionDataUpdate from .schema import PlantTransactionDataCreate, PlantTransactionDataUpdate
@ -7,6 +12,8 @@ from typing import Optional
from src.database.core import DbSession from src.database.core import DbSession
from src.auth.service import CurrentUser from src.auth.service import CurrentUser
logger = logging.getLogger(__name__)
async def get( async def get(
*, db_session: DbSession, transaction_data_id: str *, db_session: DbSession, transaction_data_id: str
@ -59,6 +66,41 @@ async def create(
transaction_data = PlantTransactionData(**transaction_data_in.model_dump()) transaction_data = PlantTransactionData(**transaction_data_in.model_dump())
db_session.add(transaction_data) db_session.add(transaction_data)
await db_session.commit() await db_session.commit()
# Get the directory of the current file
# directory_path = "../modules/plant"
directory_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../modules/plant'))
# Construct path to the script
script_path = os.path.join(directory_path, "run.py")
if not os.path.exists(script_path):
logger.error(f"Script not found at path: {script_path}")
raise FileNotFoundError(f"Script not found at path: {script_path}")
# Execute script
try:
process = await asyncio.create_subprocess_exec(
"python",
script_path,
stdout=PIPE,
stderr=PIPE,
cwd=directory_path
)
stdout, stderr = await process.communicate()
if process.returncode != 0:
error_message = stderr.decode()
logger.error(f"Script execution failed: {error_message}")
# Depending on your requirements, you might want to raise an exception here
else:
logger.info(f"Script executed successfully: {stdout.decode()}")
except asyncio.SubprocessError as e:
logger.error(f"Failed to execute script: {e}")
raise Exception(f"Failed to execute script: {e}")
# Handle subprocess error appropriately
return transaction_data return transaction_data

Loading…
Cancel
Save