lstm / api.py
Bima Ardhia
sort
970edc1
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import pandas as pd
from prophet import Prophet
import psycopg2
import os
app = FastAPI()
db = os.getenv('db')
# Database URL
DATABASE_URL = db
# Request body schema
class PredictionRequest(BaseModel):
user_id: str
tanggal_prediksi: str # Format YYYY-MM-DD
# Fungsi ambil data transaksi
def get_transactions(user_id):
try:
conn = psycopg2.connect(DATABASE_URL)
cur = conn.cursor()
cur.execute("""
SELECT id, user_id, amount, type, description, transaction_date
FROM transactions
WHERE user_id = %s
ORDER BY transaction_date
""", (user_id,))
rows = cur.fetchall()
columns = ['id', 'user_id', 'amount', 'type', 'description', 'transaction_date']
df = pd.DataFrame(rows, columns=columns)
cur.close()
conn.close()
return df
except Exception as e:
return None
# Hitung grand total harian
def prepare_data(df):
df['transaction_date'] = pd.to_datetime(df['transaction_date'])
df = df.sort_values(by=['transaction_date', 'id'])
grand_total = 0
grand_totals = []
for _, row in df.iterrows():
if row['type'] == 'pemasukan':
grand_total += row['amount']
elif row['type'] == 'pengeluaran':
grand_total -= row['amount']
grand_totals.append(grand_total)
df['grand_total'] = grand_totals
return df
@app.post("/predict")
def predict_saldo(request: PredictionRequest):
try:
future_date = pd.to_datetime(request.tanggal_prediksi)
except:
raise HTTPException(status_code=400, detail="Format tanggal tidak valid. Gunakan YYYY-MM-DD.")
df = get_transactions(request.user_id)
if df is None or df.empty:
raise HTTPException(status_code=404, detail="Data transaksi tidak ditemukan untuk user_id tersebut.")
df = prepare_data(df)
df_prophet = df[['transaction_date', 'grand_total']].rename(
columns={'transaction_date': 'ds', 'grand_total': 'y'}
)
model = Prophet()
model.fit(df_prophet)
last_date = df_prophet['ds'].max()
days_ahead = (future_date - last_date).days
if days_ahead <= 0:
raise HTTPException(status_code=400, detail=f"Tanggal prediksi ({future_date.date()}) harus setelah data terakhir ({last_date.date()}).")
future = model.make_future_dataframe(periods=days_ahead, freq='D')
forecast = model.predict(future)
prediction = forecast[forecast['ds'] == future_date]
if prediction.empty:
raise HTTPException(status_code=404, detail="Tanggal tidak ditemukan dalam hasil prediksi.")
result = prediction[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].iloc[0]
return {
"tanggal": result['ds'].date().isoformat(),
"prediksi_saldo": round(result['yhat'], 2),
"batas_bawah": round(result['yhat_lower'], 2),
"batas_atas": round(result['yhat_upper'], 2)
}