BTA_Prediction / src /streamlit_app.py
Rendhaputra's picture
Update src/streamlit_app.py
62b92d8 verified
Raw
History Blame Contribute Delete
8.28 kB
import os
import sqlite3
import datetime
import streamlit as st
import pandas as pd
from huggingface_hub import hf_hub_download
from xgboost import XGBRegressor
# --- KONFIGURASI HALAMAN ---
st.set_page_config(page_title="Sistem Monitoring BTA v2.1", page_icon="🏗️", layout="wide")
# --- KONEKSI DATABASE ---
def init_db():
conn = sqlite3.connect('furnace_data.db', check_same_thread=False)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS cycles
(id INTEGER PRIMARY KEY, start_date TEXT, initial_thickness REAL, active INTEGER)''')
c.execute('''CREATE TABLE IF NOT EXISTS daily_logs
(id INTEGER PRIMARY KEY, cycle_id INTEGER, log_date TEXT, raw_temp REAL)''')
conn.commit()
return conn
# --- LOAD MODEL ---
HF_TOKEN = os.environ.get("HF_TOKEN")
REPO_MODEL = "Rendhaputra/BTA_predictive"
FILE_MODEL = "xgboost_bta.json"
@st.cache_resource
def load_model():
try:
path = hf_hub_download(repo_id=REPO_MODEL, filename=FILE_MODEL, token=HF_TOKEN)
m = XGBRegressor()
m.load_model(path)
return m
except Exception as e:
st.error(f"Gagal muat model: {e}")
return None
model = load_model()
db = init_db()
# --- FUNGSI HELPER ---
def get_active_cycle():
c = db.cursor()
c.execute("SELECT id, start_date FROM cycles WHERE active = 1 ORDER BY id DESC LIMIT 1")
return c.fetchone()
# (Fungsi ini tetap disimpan sebagai cadangan/helper, meski logika utamanya sekarang menggunakan Pandas)
def get_rolling_30(cycle_id, current_temp):
c = db.cursor()
c.execute("SELECT raw_temp FROM daily_logs WHERE cycle_id = ? ORDER BY log_date DESC LIMIT 29", (cycle_id,))
past_temps = []
for row in c.fetchall():
try:
if row[0] is not None:
past_temps.append(float(row[0]))
except ValueError:
pass
all_temps = past_temps + [float(current_temp)]
if len(all_temps) == 0:
return 0.0
return sum(all_temps) / len(all_temps)
# --- ANTARMUKA (UI) ---
st.title("🏗️ Smart Monitoring BTA Furnace (v2.1 - Data Migration Enabled)")
# SIDEBAR
with st.sidebar:
st.header("⚙️ Pengaturan & Import")
active_cycle = get_active_cycle()
if active_cycle:
st.success(f"Siklus Aktif: {active_cycle[1]}")
# --- FITUR IMPORT CSV (Versi Diperbarui) ---
st.markdown("---")
st.subheader("📤 Bulk Import Data Historis")
st.caption("Gunakan CSV dengan kolom: 'Tanggal' dan 'Bodi Tengah (°C)'")
uploaded_file = st.file_uploader("Pilih file CSV", type="csv")
if uploaded_file is not None:
try:
# 1. Melewati baris judul (skiprows=1) & Penanganan Encoding
try:
data_import = pd.read_csv(uploaded_file, encoding='utf-8-sig', skiprows=1)
except UnicodeDecodeError:
uploaded_file.seek(0)
data_import = pd.read_csv(uploaded_file, encoding='latin1', skiprows=1)
# 2. Cek apakah pemisahnya ternyata titik koma (;)
if len(data_import.columns) == 1:
uploaded_file.seek(0)
data_import = pd.read_csv(uploaded_file, encoding='utf-8-sig', sep=';', skiprows=1)
# 3. Bersihkan nama kolom dari spasi
data_import.columns = data_import.columns.str.strip()
# 4. Samakan nama kolom di CSV ("Bodi Tengah (°C)") ke "Suhu"
if 'Bodi Tengah (°C)' in data_import.columns:
data_import.rename(columns={'Bodi Tengah (°C)': 'Suhu'}, inplace=True)
st.info("Preview Data yang akan diimpor:")
st.dataframe(data_import[['Tanggal', 'Suhu']].head(3))
if st.button("Konfirmasi Import Data"):
count = 0
# Abaikan data kosong pada kolom vital
for index, row in data_import.dropna(subset=['Tanggal', 'Suhu']).iterrows():
db.execute("INSERT INTO daily_logs (cycle_id, log_date, raw_temp) VALUES (?, ?, ?)",
(active_cycle[0], str(row['Tanggal']), float(row['Suhu'])))
count += 1
db.commit()
st.success(f"✅ Berhasil mengimpor {count} data historis!")
st.rerun()
except KeyError as e:
st.error(f"Kolom tidak ditemukan: {e}. Pastikan file memiliki kolom 'Tanggal' dan 'Bodi Tengah (°C)'.")
except Exception as e:
st.error(f"Gagal memproses CSV: {e}")
st.markdown("---")
if st.button("Reset / Pasang BTA Baru"):
db.execute("UPDATE cycles SET active = 0 WHERE active = 1")
db.commit()
st.rerun()
else:
st.warning("Buat siklus dulu sebelum import!")
new_date = st.date_input("Tanggal Pemasangan BTA Baru", datetime.date.today())
if st.button("Mulai Siklus Baru"):
db.execute("INSERT INTO cycles (start_date, initial_thickness, active) VALUES (?, ?, ?)",
(new_date.isoformat(), 230.0, 1))
db.commit()
st.rerun()
# --- MAIN AREA (Versi Diperbarui: Otomatis Update) ---
if active_cycle:
cycle_id, start_date_str = active_cycle
# Pastikan start_date dikonversi dengan aman menggunakan Pandas
try:
start_date = pd.to_datetime(start_date_str).date()
except Exception:
start_date = datetime.date.fromisoformat(start_date_str)
col1, col2 = st.columns([1, 2])
with col1:
st.subheader("📥 Input Harian Tunggal")
tgl_skrg = st.date_input("Tanggal Hari Ini", datetime.date.today())
temp_raw = st.number_input("Suhu Bodi Tengah (°C)", 200.0, 500.0, 350.0)
if st.button("Simpan & Prediksi", type="primary"):
# Hanya menyimpan data ke DB, perhitungannya ditangani di Col2
db.execute("INSERT INTO daily_logs (cycle_id, log_date, raw_temp) VALUES (?, ?, ?)",
(cycle_id, tgl_skrg.isoformat(), temp_raw))
db.commit()
st.rerun()
with col2:
st.subheader("📊 Visualisasi & Analisis")
# Tarik semua data dari DB menggunakan Pandas
df_hist = pd.read_sql_query(f"SELECT log_date, raw_temp FROM daily_logs WHERE cycle_id={cycle_id}", db)
if not df_hist.empty and model is not None:
# Rapikan dan urutkan tanggal (antisipasi beda format CSV "MM/DD/YYYY" vs Streamlit "YYYY-MM-DD")
df_hist['log_date'] = pd.to_datetime(df_hist['log_date'], format="mixed", dayfirst=False)
df_hist = df_hist.sort_values('log_date').reset_index(drop=True)
# Ambil data hari terakhir yang ada di database
latest_row = df_hist.iloc[-1]
latest_date = latest_row['log_date'].date()
# Perhitungan Hari Operasi
hari_ops = (latest_date - start_date).days
# Hitung Rolling 30 dari Pandas
tail_30 = pd.to_numeric(df_hist['raw_temp'].tail(30), errors='coerce').dropna()
roll30 = tail_30.mean() if not tail_30.empty else 0.0
# Lakukan Prediksi Model
pred_mm = float(model.predict([[hari_ops, roll30]])[0])
sisa_hari = max(0, int((pred_mm - 100) / 0.191))
# Tampilkan ke Layar secara Otomatis
m1, m2, m3 = st.columns(3)
m1.metric("Ketebalan BTA", f"{pred_mm:.1f} mm")
m2.metric("Umur Operasi", f"{hari_ops} Hari")
m3.metric("Est. Sisa Umur", f"{sisa_hari} Hari")
# Kembalikan tipe data ke string untuk chart agar rapi
df_hist['log_date'] = df_hist['log_date'].dt.strftime('%Y-%m-%d')
st.line_chart(df_hist.set_index('log_date')['raw_temp'])
st.write(f"Total data dalam database: **{len(df_hist)} baris**")
else:
st.info("Belum ada data untuk dianalisis. Silakan input harian atau import CSV.")