TIF_E41253368/REFERENSI_APLIKASI.md

652 lines
18 KiB
Markdown

# Referensi Lengkap Aplikasi Panen Harga
Dokumen ini menjelaskan aplikasi **Panen Harga**, yaitu sistem prediksi harga cabai
di Kabupaten Jember dengan visualisasi peta digital interaktif. Dokumen ini dapat
digunakan sebagai referensi untuk menjelaskan tujuan aplikasi, arsitektur, fitur,
alur data, teknologi, API, database, model LSTM, pengelolaan scraping, serta
batasan sistem.
## 1. Identitas dan tujuan aplikasi
### Nama aplikasi
**Panen Harga**
### Tujuan
Aplikasi digunakan untuk:
- menampilkan harga cabai berdasarkan pasar di Kabupaten Jember;
- menampilkan lokasi pasar melalui peta digital;
- menghasilkan prediksi harga menggunakan model LSTM yang sudah dilatih;
- membantu pengguna membandingkan harga aktual dan hasil forecast;
- menyediakan panel admin untuk memperbarui data secara manual;
- menyediakan preprocessing terbatas untuk menangani gap data;
- mencatat riwayat scraping dan proses pengolahan data.
Aplikasi ini bukan marketplace dan tidak melakukan transaksi jual beli. Fokusnya
adalah informasi harga, visualisasi lokasi, dan prediksi harga.
### Komoditas
Sistem mendukung tiga komoditas:
| Slug API | Nama |
|---|---|
| `rawit` | Cabai Rawit |
| `merah` | Cabai Merah Besar |
| `keriting` | Cabai Merah Keriting |
### Pasar
Pasar yang terdaftar:
- Pasar Tanjung
- Pasar Kreongan
- Pasar Tegal Besar
- Pasar Mangli
- Pasar Wirolegi
Ketersediaan model dapat berbeda untuk setiap komoditas. Saat ini model cabai
merah keriting hanya tersedia untuk pasar yang memiliki artifact lengkap.
Aplikasi tidak membuat prediksi palsu untuk pasar tanpa model atau data.
## 2. Teknologi dan tools
### Backend
- **Bahasa:** Python 3.9
- **Framework:** Flask
- **Server development:** Werkzeug melalui `app.run`
- **Database driver:** PyMySQL
- **Data processing:** pandas dan NumPy
- **Machine learning runtime:** TensorFlow 2.10
- **Preprocessing dan scaler:** scikit-learn
- **Pemuat scaler:** joblib atau pickle
- **HTTP scraping:** requests
- **Parsing tabel HTML:** pandas dan lxml
- **Pengujian:** pytest
- **CORS:** flask-cors
Entry point backend adalah [api/main.py](api/main.py). Aplikasi Flask dibuat
melalui application factory `create_app()`.
### Frontend
- **Bahasa:** JavaScript modern dengan JSX
- **Framework:** React 19
- **Build tool:** Vite
- **Lint:** Oxlint
- **Peta:** Leaflet dan React Leaflet
- **Grafik:** Recharts
- **Ikon:** Lucide React
- **Styling:** CSS biasa melalui `website/src/Public.css`
### Database
- **Database:** MySQL/MariaDB
- **Pengelolaan lokal:** phpMyAdmin, XAMPP, Laragon, atau WAMP
- **Nama database default:** `prediksicabe`
### Model dan artifact
Setiap kombinasi komoditas/pasar dapat memiliki:
- `model.h5`: model LSTM yang sudah dilatih;
- `scaler.pkl`: scaler yang digunakan saat training;
- `config.json`: metadata model dan konfigurasi training.
API memuat artifact satu kali saat startup. API tidak menjalankan training ulang
ketika aplikasi berjalan.
## 3. Struktur proyek
Struktur utama:
```text
C:\PrediksiHargaCabe\
├── api\
│ ├── main.py
│ ├── requirements.txt
│ ├── restart_backend.ps1
│ ├── README.md
│ ├── app\
│ │ ├── __init__.py
│ │ ├── config.py
│ │ ├── errors.py
│ │ ├── api\
│ │ │ └── routes.py
│ │ ├── data\
│ │ │ ├── markets.json
│ │ │ └── repository.py
│ │ └── services\
│ │ ├── forecaster.py
│ │ ├── model_registry.py
│ │ ├── prediction_service.py
│ │ └── siskaperbapo_scraper.py
│ ├── models\
│ │ ├── rawit\
│ │ ├── merah\
│ │ └── keriting\
│ └── tests\
├── database\
│ └── panen_harga.sql
├── website\
│ ├── package.json
│ └── src\
│ ├── App.jsx
│ ├── Public.css
│ └── services\
│ └── api.js
├── RINGKASAN_FITUR.md
└── REFERENSI_APLIKASI.md
```
## 4. Arsitektur aplikasi
```text
Pengguna
│
▼
React/Vite frontend :5173
│ HTTP/JSON + session cookie
▼
Flask REST API :8000
├── PredictionService
├── ModelRegistry
├── PriceRepository
└── SiskaperbapoScraper
│
├── Artifact model LSTM
└── MySQL prediksicabe
```
Frontend tidak mengakses database secara langsung. Semua data diambil melalui
service layer pada [website/src/services/api.js](website/src/services/api.js).
Backend bertanggung jawab atas validasi, akses database, scraping, preprocessing,
dan inference model.
## 5. Alur data utama
### 5.1 Data penelitian awal
Data CSV penelitian dapat dimasukkan satu kali ke MySQL menggunakan script import.
Harga CSV dikonversi ke Rupiah penuh sebelum disimpan.
```text
CSV penelitian
↓
import_csv_to_mysql.py
↓
price_history
↓
PriceRepository
```
### 5.2 Data scraping manual
Scraping tidak berjalan otomatis dan tidak memiliki scheduler.
```text
Admin memilih komoditas dan tanggal
↓
POST /api/v1/admin/scraping/jobs
↓
Background thread
↓
SISKAPERBAPO
↓
Normalisasi harga
↓
price_history
↓
scraping_logs
```
Data baru dicatat menggunakan `scraping_log_id`, sehingga satu riwayat scraping
dapat ditelusuri dan dihapus bersama data yang dihasilkannya.
### 5.3 Preprocessing
Preprocessing manual dapat dijalankan terhadap satu log scraping yang dipilih.
Metode yang digunakan adalah interpolasi linear untuk gap internal yang memenuhi
batas sistem. Gap di awal/akhir atau gap terlalu panjang tidak diisi secara
sembarangan dan dilaporkan sebagai warning.
```text
Pilih riwayat scraping
↓
POST /api/v1/admin/preprocessing/jobs
↓
Pemeriksaan gap
↓
Interpolasi linear yang diizinkan
↓
price_history + preprocessing_runs
```
Repository juga melakukan interpolasi internal terbatas ketika membaca series
untuk inference. Perilaku ini berfungsi sebagai validasi/fallback agar gap kecil
tidak langsung membuat prediksi gagal.
### 5.4 Inference prediksi
Alur inference:
```text
Data historis terbaru dari database
↓
Pilih data valid terakhir sesuai time_step
↓
Konversi input ke satuan ribuan untuk model
↓
Transform dengan scaler.pkl
↓
Model H5 menghasilkan prediksi
↓
Inverse transform
↓
Konversi kembali ke Rupiah penuh
↓
Recursive forecast 1, 3, atau 7 hari
↓
Response API
```
Data scraping baru tidak melatih ulang model. Data tersebut hanya menjadi data
aktual terbaru yang digunakan sebagai input inference.
## 6. Satuan harga dan presisi
Aturan penyimpanan database:
> Semua harga positif di `price_history` disimpan dalam Rupiah penuh.
Contoh:
```text
8234 → Rp8.234
8500 → Rp8.500
9275 → Rp9.275
```
Model dapat bekerja dalam satuan ribuan Rupiah sesuai artifact training. API
melakukan penyesuaian satuan ketika inference dan mengembalikan hasil dalam
Rupiah penuh.
Harga prediksi tidak dibulatkan ke ribuan. Frontend menampilkan maksimal dua
angka desimal jika hasil model memiliki pecahan.
Nilai `0`, kosong, dan nilai tidak valid diperlakukan sebagai data kosong, bukan
harga aktual yang valid.
## 7. Fitur dashboard publik
Dashboard publik berada pada route utama website.
### 7.1 Pemilihan filter
Pengguna dapat memilih:
- jenis cabai;
- pasar;
- horizon prediksi 1, 3, atau 7 hari.
Prediksi tidak dijalankan otomatis ketika halaman pertama kali dibuka. Pengguna
harus menekan tombol **Prediksi**.
### 7.2 Data terakhir
Bagian Data terakhir menampilkan tanggal data terbaru untuk setiap komoditas.
Sistem mencari pasar aktif yang memiliki ketersediaan komoditas tersebut, sehingga
komoditas tidak bergantung pada pasar yang kosong.
### 7.3 Peta interaktif
Peta menggunakan Leaflet dan OpenStreetMap tile layer. Fitur peta:
- marker lokasi pasar;
- marker terpilih;
- tooltip nama pasar dan harga;
- popup harga aktual, forecast, perubahan, dan tren;
- pemusatan peta ke pasar yang dipilih;
- mode **Aktual** dan **Forecast**;
- legenda status harga;
- peringkat pasar berdasarkan harga aktual atau forecast.
Pasar yang tidak memiliki model atau data tidak dipaksakan menjadi hasil prediksi.
### 7.4 Grafik dan tabel
Dashboard menampilkan:
- riwayat harga aktual;
- grafik aktual dan forecast;
- tabel forecast per tanggal;
- perubahan harga dalam Rupiah;
- perubahan persentase;
- indikator tren;
- indikator harga di bagian bawah dashboard.
### 7.5 Indikator harga
Indikator harga mengambil data dari response prediksi, bukan membuat prediksi
sendiri di frontend. Indikator menampilkan:
- Naik;
- Turun;
- Stabil;
- perubahan Rupiah pada langkah terakhir;
- perubahan persentase pada langkah terakhir.
## 8. Fitur panel admin
Panel admin menggunakan route `#/admin`.
### 8.1 Autentikasi
Fitur autentikasi:
- register;
- login;
- logout;
- session cookie;
- role `user` dan `admin`.
Endpoint admin memerlukan role admin.
### 8.2 Ringkasan
Ringkasan menampilkan informasi operasional:
- jumlah data harga;
- jumlah komoditas;
- jumlah pasar;
- status pembaruan terakhir;
- jumlah data terbaru dari scraping terakhir.
### 8.3 Dataset harga
Halaman Dataset harga menyediakan:
- tabel data harga;
- filter komoditas;
- filter pasar;
- pagination;
- harga dan tanggal data.
### 8.4 Perbarui data
Fitur Perbarui data menyediakan:
- pilihan komoditas scraping;
- tanggal mulai dan tanggal akhir;
- progress bar scraping;
- log riwayat scraping;
- jenis cabai pada setiap log;
- status scraping;
- jumlah data baru;
- detail data yang masuk;
- filter detail berdasarkan komoditas dan pasar;
- pagination;
- tombol hapus log;
- modal konfirmasi penghapusan.
### 8.5 Preprocessing
Section preprocessing berada di bagian bawah halaman Perbarui data. Admin dapat:
1. memilih satu riwayat scraping;
2. melihat jenis cabai dan jumlah data pada dropdown;
3. menjalankan preprocessing;
4. melihat progress;
5. melihat jumlah baris yang diisi;
6. melihat warning gap yang tidak dapat diisi.
## 9. Endpoint API
Prefix API:
```text
/api/v1
```
### Endpoint publik
| Method | Endpoint | Keterangan |
|---|---|---|
| `GET` | `/` | Status API dasar |
| `GET` | `/api/v1/health` | Status API dan artifact model |
| `GET` | `/api/v1/commodities` | Daftar komoditas tersedia |
| `GET` | `/api/v1/markets` | Daftar pasar dan ketersediaan komoditas |
| `GET` | `/api/v1/history` | Riwayat harga |
| `GET` | `/api/v1/predict` | Prediksi satu pasar |
| `GET` | `/api/v1/predict/map` | Prediksi ringkas seluruh pasar |
Contoh:
```text
GET /api/v1/history?commodity=rawit&market=pasar_tanjung&days=30
GET /api/v1/predict?commodity=rawit&market=pasar_tanjung&horizon=7
GET /api/v1/predict/map?commodity=rawit&horizon=7
```
### Endpoint autentikasi
| Method | Endpoint | Keterangan |
|---|---|---|
| `GET` | `/api/v1/auth/me` | User session saat ini |
| `POST` | `/api/v1/auth/register` | Registrasi user |
| `POST` | `/api/v1/auth/login` | Login |
| `POST` | `/api/v1/auth/logout` | Logout |
### Endpoint admin
| Method | Endpoint | Keterangan |
|---|---|---|
| `GET` | `/api/v1/admin/overview` | Ringkasan operasional |
| `GET` | `/api/v1/admin/models` | Metadata/status model |
| `GET` | `/api/v1/admin/scraping/logs` | Riwayat scraping |
| `GET` | `/api/v1/admin/scraping/logs/<id>/records` | Detail data dari log |
| `POST` | `/api/v1/admin/scraping/jobs` | Memulai scraping |
| `GET` | `/api/v1/admin/scraping/jobs/<id>` | Status scraping |
| `DELETE` | `/api/v1/admin/scraping/logs/<id>` | Hapus log dan data terkait |
| `DELETE` | `/api/v1/admin/scraping/history` | Hapus history scraping |
| `POST` | `/api/v1/admin/preprocessing/jobs` | Memulai preprocessing |
| `GET` | `/api/v1/admin/preprocessing/jobs/<id>` | Status preprocessing |
| `GET` | `/api/v1/prices` | Dataset harga dengan pagination |
Format error API:
```json
{
"error": {
"code": "ERROR_CODE",
"message": "Pesan untuk pengguna",
"details": {}
}
}
```
## 10. Struktur database
Schema lengkap tersedia pada [database/panen_harga.sql](database/panen_harga.sql).
### Tabel utama
| Tabel | Fungsi |
|---|---|
| `commodities` | Master jenis cabai |
| `markets` | Master pasar dan koordinat |
| `price_history` | Harga aktual per komoditas, pasar, dan tanggal |
| `scraping_logs` | Riwayat proses scraping |
| `preprocessing_runs` | Riwayat proses preprocessing |
| `users` | Akun dan role |
| `training_runs` | Metadata training jika dikelola di luar API |
| `model_versions` | Metadata versi model |
| `predictions` | Struktur penyimpanan forecast jika digunakan |
Relasi penting:
```text
commodities 1 ─── * price_history
markets 1 ─── * price_history
scraping_logs 1 ─── * price_history
commodities 1 ─── * training_runs
commodities 1 ─── * model_versions
```
Kunci unik `price_history` adalah kombinasi:
```text
commodity_id + market_id + price_date
```
Karena itu scraping tanggal yang sama akan memperbarui data yang sudah ada,
bukan membuat duplikat baru.
## 11. Konfigurasi environment
Variabel penting backend:
| Variabel | Default | Fungsi |
|---|---|---|
| `DATA_SOURCE` | `mysql` | Sumber data aktif |
| `MYSQL_HOST` | `127.0.0.1` | Host MySQL |
| `MYSQL_PORT` | `3306` | Port MySQL |
| `MYSQL_DATABASE` | `prediksicabe` | Nama database |
| `MYSQL_USER` | `root` | User database |
| `MYSQL_PASSWORD` | kosong | Password database |
| `MODELS_DIR` | `api/models` | Folder artifact |
| `DATA_DIR` | folder api | Folder CSV |
| `PRICE_UNIT_SCALE` | `1000` | Skala CSV lama ke Rupiah |
| `ALLOWED_HORIZONS` | `1,3,7` | Horizon yang diizinkan |
| `MAX_HORIZON` | `7` | Batas horizon |
| `CORS_ORIGINS` | localhost:5173 | Origin frontend |
| `STALE_DATA_DAYS` | `30` | Ambang data lama |
| `TREND_THRESHOLD_PCT` | `1` | Ambang status tren |
| `SISKAPERBAPO_TIMEOUT` | `30` | Timeout request scraper |
Untuk produksi, ganti `SECRET_KEY` default dan batasi `CORS_ORIGINS` hanya pada
origin frontend yang dipercaya.
## 12. Cara menjalankan aplikasi
### 12.1 Menjalankan backend
Gunakan environment Conda yang sudah tersedia:
```powershell
cd C:\PrediksiHargaCabe\api
C:\Users\alfia\anaconda3\envs\tf_gpu39\python.exe main.py
```
Atau gunakan script restart:
```powershell
cd C:\PrediksiHargaCabe\api
powershell -ExecutionPolicy Bypass -File .\restart_backend.ps1
```
Backend berjalan di:
```text
http://127.0.0.1:8000
```
### 12.2 Menjalankan frontend
```powershell
cd C:\PrediksiHargaCabe\website
npm run dev
```
Frontend Vite biasanya berjalan di:
```text
http://localhost:5173
```
### 12.3 Build frontend
```powershell
cd C:\PrediksiHargaCabe\website
npm run lint
npm run build
```
### 12.4 Pengujian backend
```powershell
cd C:\PrediksiHargaCabe\api
C:\Users\alfia\anaconda3\envs\tf_gpu39\python.exe -m pytest
```
## 13. Keamanan dan batasan operasional
- Endpoint admin dilindungi autentikasi dan role admin.
- Password disimpan dalam bentuk hash.
- Frontend tidak terhubung langsung ke database.
- Scheduler scraping otomatis belum diaktifkan.
- Training ulang otomatis belum diaktifkan.
- API tidak menjalankan `model.fit()`.
- Data input scraping tidak boleh dianggap sebagai hasil evaluasi model baru.
- Data terbaru hasil scraping adalah data operasional untuk inference.
- Jangan mengklaim model sebagai “global optimum”. Gunakan istilah:
“konfigurasi terbaik yang diperoleh dari skema eksperimen yang diterapkan pada
dataset penelitian.”
## 14. Batasan model dan interpretasi hasil
### Recursive forecasting
Prediksi lebih dari satu hari menggunakan hasil prediksi sebelumnya sebagai input
berikutnya. Akibatnya, error dapat terakumulasi pada horizon yang lebih panjang.
### Ketersediaan artifact
Tidak semua komoditas harus tersedia pada semua pasar. Kombinasi tanpa artifact
lengkap ditampilkan sebagai tidak tersedia.
### Data lama
API dapat memberikan warning `data_stale` apabila tanggal data terakhir melewati
ambang `STALE_DATA_DAYS`.
### Model deployment tidak sama dengan training ulang
Scraping baru tidak mengubah bobot model. Jika penelitian memerlukan model baru,
training harus dilakukan melalui proses eksperimen terpisah, divalidasi, lalu
artifact baru dideploy secara sadar.
## 15. Cara menjelaskan aplikasi saat presentasi
Urutan penjelasan yang disarankan:
1. Jelaskan masalah: harga cabai berbeda antar pasar dan berubah dari waktu ke
waktu.
2. Jelaskan sumber data: dataset penelitian dan data SISKAPERBAPO.
3. Jelaskan tiga komoditas dan pasar yang diamati.
4. Tunjukkan peta digital pasar.
5. Pilih komoditas dan pasar.
6. Pilih horizon prediksi.
7. Tekan tombol Prediksi.
8. Jelaskan harga aktual terakhir, forecast, grafik, tabel, dan indikator tren.
9. Jelaskan bahwa model menggunakan artifact LSTM yang sudah divalidasi.
10. Jelaskan bahwa scraping memperbarui data aktual, bukan melatih ulang model.
11. Tunjukkan panel admin untuk log scraping dan preprocessing.
12. Jelaskan keterbatasan model dan pasar yang artifact-nya belum tersedia.
## 16. Ringkasan satu paragraf
Panen Harga adalah aplikasi web berbasis React dan Flask yang menggunakan model
LSTM tersimpan untuk memprediksi harga tiga komoditas cabai di pasar-pasar
Kabupaten Jember. Data harga disimpan dalam MySQL menggunakan Rupiah penuh,
sedangkan model dan scaler dimuat dari artifact saat backend dimulai. Pengguna
dapat memilih komoditas, pasar, dan horizon prediksi, kemudian melihat hasilnya
melalui peta Leaflet, ranking pasar, grafik riwayat, grafik forecast, tabel
prediksi, dan indikator tren. Admin dapat melakukan scraping manual dari
SISKAPERBAPO, memantau progress, melihat log, menghapus hasil scraping, dan
menjalankan preprocessing terbatas. Sistem tidak melakukan scheduler maupun
training ulang otomatis sehingga artifact model penelitian tetap terkendali.