ALAMDIENG commited on
Commit
bd4bcef
·
verified ·
1 Parent(s): 09b93a5

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +208 -0
app.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.concurrency import run_in_threadpool
4
+ from pydantic import BaseModel, Field
5
+ from typing import Optional, List
6
+ import pandas as pd
7
+ import numpy as np
8
+ import torch
9
+ from chronos import ChronosPipeline
10
+ from datetime import datetime, timedelta
11
+ import os
12
+ import logging
13
+ import asyncio
14
+
15
+ # ==========================================
16
+ # 1. KONFIGURASI & METADATA API
17
+ # ==========================================
18
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
19
+ logger = logging.getLogger(__name__)
20
+
21
+ app = FastAPI(
22
+ title="Waste Intelligence API - Jakarta Pusat 2025",
23
+ description="""
24
+ API Prediksi Volume Sampah Berbasis AI untuk tantangan CASE 2.
25
+ Sistem menggunakan Model Transformer (Amazon Chronos) untuk memprediksi tumpukan sampah
26
+ berdasarkan anomali cuaca (BMKG) dan izin keramaian (Event Data).
27
+
28
+ Fitur Utama:
29
+ - Prediksi Volume Total (Ton)
30
+ - Dekomposisi Sampah (Organik vs Plastik) berdasarkan SIPSN KLHK 2025
31
+ - Rekomendasi Jumlah Armada Truk
32
+ - Status Risiko Operasional (Safe, Warning, Critical)
33
+ - Integrasi Jadwal Event Otomatis
34
+ """,
35
+ version="1.1.0",
36
+ contact={
37
+ "name": "Faril Putra Pratama - SMK Taruna Bangsa",
38
+ "url": "https://github.com/vibe-coder",
39
+ }
40
+ )
41
+
42
+ # Menambahkan dukungan CORS agar Frontend bisa mengakses API
43
+ app.add_middleware(
44
+ CORSMiddleware,
45
+ allow_origins=["*"],
46
+ allow_credentials=True,
47
+ allow_methods=["*"],
48
+ allow_headers=["*"],
49
+ )
50
+
51
+ # ==========================================
52
+ # 2. MODEL & DATA LOADING (STARTUP)
53
+ # ==========================================
54
+ pipeline = None
55
+ df_history = None
56
+ events_data = {}
57
+
58
+ @app.on_event("startup")
59
+ def load_assets():
60
+ global pipeline, df_history, events_data
61
+ logger.info("⏳ Menyiapkan AI Engine (Chronos-T5)...")
62
+ try:
63
+ pipeline = ChronosPipeline.from_pretrained(
64
+ "amazon/chronos-t5-tiny",
65
+ device_map="cpu",
66
+ torch_dtype=torch.float32,
67
+ )
68
+
69
+ dataset_path = 'dataset_vibe_coder_2025.csv'
70
+ if os.path.exists(dataset_path):
71
+ df_history = pd.read_csv(dataset_path)
72
+ logger.info("✅ Dataset & Model AI berhasil dimuat.")
73
+ else:
74
+ logger.warning(f"⚠️ Warning: {dataset_path} tidak ditemukan!")
75
+
76
+ # Memuat jadwal event jika ada
77
+ event_path = 'event_jakarta_2025.txt'
78
+ if os.path.exists(event_path):
79
+ df_events = pd.read_csv(event_path)
80
+ for _, row in df_events.iterrows():
81
+ if str(row['Ada_Event']) == '1':
82
+ events_data[str(row['Tanggal'])] = {
83
+ 'Nama_Event': row['Nama_Event'],
84
+ 'Lokasi': row['Lokasi_Utama']
85
+ }
86
+ logger.info(f"✅ Jadwal {len(events_data)} event otomatis berhasil dimuat.")
87
+ else:
88
+ logger.warning(f"⚠️ Warning: {event_path} tidak ditemukan!")
89
+
90
+ except Exception as e:
91
+ logger.error(f"❌ Gagal memuat asset: {e}")
92
+
93
+ # ==========================================
94
+ # 3. SCHEMA VALIDATION (DATA MODELS)
95
+ # ==========================================
96
+ class PredictionRequest(BaseModel):
97
+ hari_ke_depan: int = Field(7, ge=1, le=30, description="Durasi prediksi (1-30 hari)")
98
+ prediksi_hujan_bmkg: float = Field(0.0, ge=0, description="Estimasi curah hujan (mm)")
99
+ skala_keramaian: int = Field(0, ge=0, le=3, description="Skala event manual (0=Normal, 1=Kecil, 2=Menengah, 3=Besar) jika jadwal otomatis tidak ada.")
100
+
101
+ model_config = {
102
+ "json_schema_extra": {
103
+ "examples": [
104
+ {
105
+ "hari_ke_depan": 7,
106
+ "prediksi_hujan_bmkg": 25.5,
107
+ "skala_keramaian": 0
108
+ }
109
+ ]
110
+ }
111
+ }
112
+
113
+ class PredictionResult(BaseModel):
114
+ tanggal: str
115
+ total_volume_ton: float
116
+ sisa_makanan_ton: float
117
+ plastik_ton: float
118
+ rekomendasi_truk: int
119
+ status_risiko: str
120
+ info_event: Optional[str] = Field(None, description="Informasi jika ada event besar di hari ini")
121
+
122
+ # ==========================================
123
+ # 4. ENDPOINT LOGIC (BUSINESS LAYER)
124
+ # ==========================================
125
+ @app.get("/", tags=["Sistem"])
126
+ def status_check():
127
+ return {
128
+ "status": "Online",
129
+ "model": "Chronos-T5 Tiny",
130
+ "region": "Jakarta Pusat",
131
+ "events_loaded": len(events_data)
132
+ }
133
+
134
+ def perform_inference(context_tensor, steps):
135
+ """Fungsi sync untuk inference model yang akan dijalankan di threadpool"""
136
+ forecast = pipeline.predict(context_tensor.unsqueeze(0), steps)
137
+ return np.quantile(forecast[0].numpy(), 0.5, axis=0)
138
+
139
+ @app.post("/api/v1/predict", response_model=List[PredictionResult], tags=["Prediksi Sampah"])
140
+ async def get_waste_forecast(request: PredictionRequest):
141
+ if df_history is None or pipeline is None:
142
+ raise HTTPException(status_code=503, detail="Model atau Dataset belum siap.")
143
+
144
+ try:
145
+ # 1. Konteks Data Historis
146
+ context = torch.tensor(df_history['Volume_Total_Ton'].values)
147
+
148
+ # 2. Forecasting Probabilistik (Asynchronous / Non-blocking)
149
+ logger.info(f"⏳ Memprediksi {request.hari_ke_depan} hari ke depan...")
150
+ median_forecast = await run_in_threadpool(perform_inference, context, request.hari_ke_depan)
151
+
152
+ # 3. Integrasi Faktor Luar (Case 2: Cuaca & Event Otomatis)
153
+ results = []
154
+ last_date = pd.to_datetime(df_history['TANGGAL'].iloc[-1])
155
+
156
+ for i, val in enumerate(median_forecast):
157
+ current_date = last_date + timedelta(days=i+1)
158
+ date_str = current_date.strftime('%Y-%m-%d')
159
+
160
+ # Logika tambahan berat sampah basah karena hujan
161
+ rain_impact = (request.prediksi_hujan_bmkg * 2) if request.prediksi_hujan_bmkg > 20 else 0
162
+
163
+ # Logika otomatis vs manual untuk Event
164
+ event_info = events_data.get(date_str)
165
+ if event_info:
166
+ # Jika ada di jadwal kalender otomatis (misal Konser Maroon 5), asumsikan lonjakan super besar
167
+ event_impact = 350 # Ton ekstra
168
+ info_text = f"{event_info['Nama_Event']} di {event_info['Lokasi']}"
169
+ else:
170
+ # Fallback ke skala input manual
171
+ event_impact = request.skala_keramaian * 150
172
+ info_text = None
173
+
174
+ total_vol = float(val + rain_impact + event_impact)
175
+
176
+ # Dekomposisi berdasarkan Data SIPSN KLHK 2025 Jakarta Pusat
177
+ food_waste = total_vol * 0.4987
178
+ plastic_waste = total_vol * 0.2295
179
+
180
+ # Rekomendasi Armada (Kapasitas Truk Standar: 10 Ton)
181
+ num_trucks = int(np.ceil(total_vol / 10))
182
+
183
+ # Penentuan Status Risiko
184
+ if total_vol > 1300:
185
+ risk = "CRITICAL ⚠️"
186
+ elif total_vol > 1100:
187
+ risk = "WARNING ⚡"
188
+ else:
189
+ risk = "SAFE ✅"
190
+
191
+ results.append(
192
+ PredictionResult(
193
+ tanggal=date_str,
194
+ total_volume_ton=round(total_vol, 2),
195
+ sisa_makanan_ton=round(food_waste, 2),
196
+ plastik_ton=round(plastic_waste, 2),
197
+ rekomendasi_truk=num_trucks,
198
+ status_risiko=risk,
199
+ info_event=info_text
200
+ )
201
+ )
202
+
203
+ logger.info("✅ Prediksi berhasil digenerate.")
204
+ return results
205
+
206
+ except Exception as e:
207
+ logger.error(f"❌ Gagal memproses prediksi: {e}")
208
+ raise HTTPException(status_code=500, detail=str(e))