File size: 3,007 Bytes
9483627
 
 
 
 
b2ff085
9483627
 
b1afb5f
 
9483627
b1afb5f
9483627
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
970edc1
9483627
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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)
    }