# 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//records` | Detail data dari log | | `POST` | `/api/v1/admin/scraping/jobs` | Memulai scraping | | `GET` | `/api/v1/admin/scraping/jobs/` | Status scraping | | `DELETE` | `/api/v1/admin/scraping/logs/` | 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/` | 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.