Spaces:
Running
Running
| """ | |
| Generator sample data CSV untuk 38 kabupaten/kota Jawa Timur. | |
| Data sources: | |
| - Kode wilayah & nama: Permendagri 137/2017 | |
| - IPM 2024: BPS Jatim (https://jatim.bps.go.id/) | |
| ringkasan: jatimtimes.com & manadopost berdasarkan Berita Resmi Statistik | |
| - Koordinat: Approximate kabupaten/kota center (lat/lon) | |
| - Population 2024: BPS Jawa Timur Dalam Angka 2025 | |
| - 8 Kota IHK: PIHPS Bank Indonesia (https://www.bi.go.id/hargapangan/) | |
| Catatan: Beberapa IPM di proposal v7/v8 (e.g., Sampang 61.6) berasal dari | |
| data tahun lebih lawas. v9 pakai IPM 2024 BPS. Tim wajib refresh tahunan | |
| saat BPS publish data baru (biasanya November-Desember). | |
| """ | |
| import csv | |
| import os | |
| import sys | |
| # Force UTF-8 stdout di Windows (default cp1252 crash di "β"). | |
| if sys.platform == "win32": | |
| try: | |
| sys.stdout.reconfigure(encoding="utf-8") | |
| except (AttributeError, OSError): | |
| pass | |
| OUTPUT_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| # ============================================================================= | |
| # 38 Kabupaten/Kota Jawa Timur β Master Data | |
| # ============================================================================= | |
| # Kolom: kab_id, nama, latitude, longitude, ipm_2024, population_2024, tier | |
| # Tier: TIER_1_HIGH untuk 8 kota IHK, TIER_2_MEDIUM untuk sisanya | |
| # IPM mengikuti sumber BPS 2024 (atau estimasi konservatif jika tidak tersedia) | |
| KABUPATEN_DATA = [ | |
| # ===== TIER 1 β 8 Kota IHK Jatim ===== | |
| ("3578", "Kota Surabaya", -7.2575, 112.7521, 84.69, 2949496, "TIER_1_HIGH"), | |
| ("3573", "Kota Malang", -7.9666, 112.6326, 84.68, 844614, "TIER_1_HIGH"), | |
| ("3571", "Kota Kediri", -7.8166, 112.0114, 81.48, 287609, "TIER_1_HIGH"), | |
| ("3577", "Kota Madiun", -7.6298, 111.5300, 81.67, 178540, "TIER_1_HIGH"), | |
| ("3574", "Kota Probolinggo", -7.7543, 113.2159, 79.20, 239649, "TIER_1_HIGH"), | |
| ("3510", "Banyuwangi", -8.2192, 114.3691, 73.45, 1719287, "TIER_1_HIGH"), | |
| ("3529", "Sumenep", -7.0067, 113.8525, 68.79, 1144719, "TIER_1_HIGH"), | |
| ("3509", "Jember", -8.1727, 113.7000, 71.86, 2606656, "TIER_1_HIGH"), | |
| # ===== TIER 2 β 30 Kabupaten Non-IHK ===== | |
| ("3501", "Pacitan", -8.1985, 111.1014, 71.40, 592786, "TIER_2_MEDIUM"), | |
| ("3502", "Ponorogo", -7.8674, 111.4625, 73.20, 956000, "TIER_2_MEDIUM"), | |
| ("3503", "Trenggalek", -8.0497, 111.7100, 73.85, 733700, "TIER_2_MEDIUM"), | |
| ("3504", "Tulungagung", -8.0658, 111.9028, 75.30, 1077500, "TIER_2_MEDIUM"), | |
| ("3505", "Blitar", -8.0950, 112.1658, 73.85, 1213000, "TIER_2_MEDIUM"), | |
| ("3506", "Kediri", -7.7960, 112.1700, 74.50, 1626100, "TIER_2_MEDIUM"), | |
| ("3507", "Malang", -8.1706, 112.5290, 75.50, 2716000, "TIER_2_MEDIUM"), | |
| ("3508", "Lumajang", -8.1336, 113.2245, 70.10, 1129500, "TIER_2_MEDIUM"), | |
| ("3511", "Bondowoso", -7.9134, 113.8217, 69.62, 790180, "TIER_2_MEDIUM"), | |
| ("3512", "Situbondo", -7.7068, 114.0093, 71.20, 690600, "TIER_2_MEDIUM"), | |
| ("3513", "Probolinggo", -7.8742, 113.4675, 69.40, 1208400, "TIER_2_MEDIUM"), | |
| ("3514", "Pasuruan", -7.7460, 112.9237, 71.30, 1683700, "TIER_2_MEDIUM"), | |
| ("3515", "Sidoarjo", -7.4549, 112.7178, 80.13, 2185300, "TIER_2_MEDIUM"), | |
| ("3516", "Mojokerto", -7.4720, 112.4390, 76.50, 1175000, "TIER_2_MEDIUM"), | |
| ("3517", "Jombang", -7.5460, 112.2308, 75.10, 1357300, "TIER_2_MEDIUM"), | |
| ("3518", "Nganjuk", -7.6050, 111.9023, 75.20, 1075800, "TIER_2_MEDIUM"), | |
| ("3519", "Madiun", -7.5570, 111.6890, 73.55, 743200, "TIER_2_MEDIUM"), | |
| ("3520", "Magetan", -7.6515, 111.3287, 75.80, 679000, "TIER_2_MEDIUM"), | |
| ("3521", "Ngawi", -7.4080, 111.4458, 74.40, 889600, "TIER_2_MEDIUM"), | |
| ("3522", "Bojonegoro", -7.1500, 111.8800, 73.10, 1357000, "TIER_2_MEDIUM"), | |
| ("3523", "Tuban", -6.8970, 111.9750, 74.30, 1218000, "TIER_2_MEDIUM"), | |
| ("3524", "Lamongan", -7.1155, 112.4170, 75.60, 1378200, "TIER_2_MEDIUM"), | |
| ("3525", "Gresik", -7.1568, 112.6510, 77.61, 1330000, "TIER_2_MEDIUM"), | |
| ("3526", "Bangkalan", -7.0317, 112.7491, 67.70, 1062200, "TIER_2_MEDIUM"), | |
| ("3527", "Sampang", -7.1924, 113.2473, 66.72, 984000, "TIER_2_MEDIUM"), | |
| ("3528", "Pamekasan", -7.1565, 113.4785, 70.43, 892800, "TIER_2_MEDIUM"), | |
| ("3572", "Kota Blitar", -8.0980, 112.1681, 80.20, 148800, "TIER_2_MEDIUM"), | |
| ("3575", "Kota Pasuruan", -7.6469, 112.9070, 79.50, 211800, "TIER_2_MEDIUM"), | |
| ("3576", "Kota Mojokerto", -7.4719, 112.4341, 80.85, 146100, "TIER_2_MEDIUM"), | |
| ("3579", "Kota Batu", -7.8740, 112.5240, 78.30, 213700, "TIER_2_MEDIUM"), | |
| ] | |
| assert len(KABUPATEN_DATA) == 38, f"Expected 38 kab, got {len(KABUPATEN_DATA)}" | |
| # ============================================================================= | |
| # 19 Komoditas Bapanas + spec konstrain | |
| # ============================================================================= | |
| # Kolom: code, nama, max_distance_km, min_viable_tons, max_fresh_age_days, | |
| # baseline_price_idr_per_kg | |
| KOMODITAS_DATA = [ | |
| ("cabai_merah", "Cabai Merah Besar", 200, 1.0, 5, 45000), | |
| ("cabai_rawit", "Cabai Rawit Merah", 200, 1.0, 5, 60000), | |
| ("bawang_merah", "Bawang Merah", 400, 2.0, 30, 35000), | |
| ("bawang_putih", "Bawang Putih (Bonggol)", 600, 2.0, 60, 40000), | |
| ("tomat", "Tomat Sayur", 150, 1.0, 7, 12000), | |
| ("kentang", "Kentang", 400, 2.0, 21, 14000), | |
| ("kol", "Kol/Kubis", 300, 2.0, 14, 8000), | |
| ("wortel", "Wortel", 300, 2.0, 14, 11000), | |
| ("beras_premium", "Beras Premium", 800, 5.0, 180, 15500), | |
| ("beras_medium", "Beras Medium", 800, 5.0, 180, 13000), | |
| ("jagung", "Jagung Pipilan Kering", 600, 5.0, 120, 6000), | |
| ("kedelai", "Kedelai Lokal", 600, 3.0, 120, 11000), | |
| ("telur_ayam", "Telur Ayam Ras", 300, 1.0, 21, 29000), | |
| ("daging_ayam", "Daging Ayam Ras", 200, 0.5, 3, 37000), | |
| ("daging_sapi", "Daging Sapi Murni", 200, 0.5, 3, 138000), | |
| ("gula_pasir", "Gula Pasir Lokal", 600, 3.0, 365, 18000), | |
| ("minyak_goreng", "Minyak Goreng Curah", 600, 3.0, 180, 16500), | |
| ("ikan_segar", "Ikan Segar (Bandeng/Tongkol)", 150, 0.5, 2, 35000), | |
| ("tepung_terigu", "Tepung Terigu", 600, 3.0, 180, 13000), | |
| ] | |
| assert len(KOMODITAS_DATA) == 19, f"Expected 19 komoditas, got {len(KOMODITAS_DATA)}" | |
| # ============================================================================= | |
| # Sample Surplus / Deficit per kabupaten (realistic seasonal pattern) | |
| # ============================================================================= | |
| # Pattern berbasis observasi pasar Jatim April 2026: | |
| # - Kediri, Tulungagung, Blitar: sentra cabai (surplus) | |
| # - Probolinggo, Pasuruan, Lumajang: sentra bawang (surplus) | |
| # - Madura (Bangkalan/Sampang/Pamekasan/Sumenep): sentra bawang (surplus) | |
| # - Banyuwangi, Jember: sentra hortikultura mixed | |
| # - Tuban, Lamongan, Bojonegoro, Ngawi: sentra padi (surplus beras) | |
| # - Surabaya, Sidoarjo, Gresik, Malang Kota: pasar besar (deficit) | |
| # - Pacitan, Trenggalek: relatif kecil, mostly subsisten | |
| # | |
| # Format: (kab_id, commodity_code, role, volume_tons, price_idr_per_kg, harvest_age_days) | |
| # role: "SURPLUS" atau "DEFICIT" | |
| SURPLUS_DEFICIT_DATA = [ | |
| # ===== Cabai merah ===== | |
| # Sentra cabai Jatim | |
| ("3506", "cabai_merah", "SURPLUS", 85.0, 38000, 2), # Kab Kediri | |
| ("3504", "cabai_merah", "SURPLUS", 60.0, 39000, 1), # Tulungagung | |
| ("3505", "cabai_merah", "SURPLUS", 45.0, 40000, 2), # Kab Blitar | |
| ("3503", "cabai_merah", "SURPLUS", 20.0, 41000, 1), # Trenggalek | |
| ("3508", "cabai_merah", "SURPLUS", 15.0, 42000, 2), # Lumajang | |
| # Pasar besar (deficit) | |
| ("3578", "cabai_merah", "DEFICIT", 80.0, 65000, 0), # Surabaya | |
| ("3515", "cabai_merah", "DEFICIT", 35.0, 62000, 0), # Sidoarjo | |
| ("3525", "cabai_merah", "DEFICIT", 30.0, 60000, 0), # Gresik | |
| ("3573", "cabai_merah", "DEFICIT", 25.0, 58000, 0), # Kota Malang | |
| # Skenario A3: Sampang surplus mini, demand jauh | |
| ("3527", "cabai_merah", "SURPLUS", 2.0, 42000, 3), # Sampang (mini) | |
| # ===== Cabai rawit ===== | |
| ("3506", "cabai_rawit", "SURPLUS", 40.0, 52000, 2), # Kab Kediri | |
| ("3504", "cabai_rawit", "SURPLUS", 30.0, 53000, 1), # Tulungagung | |
| ("3508", "cabai_rawit", "SURPLUS", 25.0, 54000, 2), # Lumajang | |
| ("3578", "cabai_rawit", "DEFICIT", 60.0, 75000, 0), # Surabaya | |
| ("3573", "cabai_rawit", "DEFICIT", 20.0, 72000, 0), # Kota Malang | |
| # ===== Bawang merah ===== | |
| # Sentra Probolinggo / Pasuruan / Madura | |
| ("3513", "bawang_merah", "SURPLUS", 120.0, 28000, 5), # Kab Probolinggo | |
| ("3514", "bawang_merah", "SURPLUS", 80.0, 29000, 6), # Kab Pasuruan | |
| ("3526", "bawang_merah", "SURPLUS", 40.0, 30000, 4), # Bangkalan (cluster Madura) | |
| ("3527", "bawang_merah", "SURPLUS", 35.0, 30000, 3), # Sampang (cluster Madura) | |
| ("3528", "bawang_merah", "SURPLUS", 45.0, 29500, 5), # Pamekasan (cluster Madura) | |
| ("3529", "bawang_merah", "SURPLUS", 60.0, 29000, 6), # Sumenep (cluster Madura) | |
| ("3578", "bawang_merah", "DEFICIT", 150.0, 42000, 0), # Surabaya | |
| ("3515", "bawang_merah", "DEFICIT", 60.0, 40000, 0), # Sidoarjo | |
| ("3573", "bawang_merah", "DEFICIT", 35.0, 41000, 0), # Kota Malang | |
| ("3525", "bawang_merah", "DEFICIT", 40.0, 39000, 0), # Gresik | |
| # ===== Tomat ===== | |
| ("3507", "tomat", "SURPLUS", 50.0, 8000, 2), # Kab Malang (Pujon-Batu area) | |
| ("3579", "tomat", "SURPLUS", 30.0, 8500, 1), # Kota Batu | |
| ("3505", "tomat", "SURPLUS", 25.0, 9000, 2), # Kab Blitar | |
| ("3578", "tomat", "DEFICIT", 60.0, 14000, 0), # Surabaya | |
| ("3525", "tomat", "DEFICIT", 25.0, 13500, 0), # Gresik | |
| # ===== Kentang ===== | |
| ("3507", "kentang", "SURPLUS", 35.0, 10000, 5), # Kab Malang | |
| ("3579", "kentang", "SURPLUS", 25.0, 10500, 4), # Kota Batu | |
| ("3578", "kentang", "DEFICIT", 45.0, 16000, 0), # Surabaya | |
| ("3573", "kentang", "DEFICIT", 15.0, 15000, 0), # Kota Malang | |
| # ===== Kol ===== | |
| ("3579", "kol", "SURPLUS", 40.0, 5500, 3), # Kota Batu | |
| ("3507", "kol", "SURPLUS", 30.0, 5800, 4), # Kab Malang | |
| ("3578", "kol", "DEFICIT", 50.0, 9500, 0), # Surabaya | |
| # ===== Wortel ===== | |
| ("3579", "wortel", "SURPLUS", 28.0, 8000, 3), # Kota Batu | |
| ("3507", "wortel", "SURPLUS", 22.0, 8200, 4), # Kab Malang | |
| ("3578", "wortel", "DEFICIT", 35.0, 12500, 0), # Surabaya | |
| # ===== Beras premium ===== | |
| ("3523", "beras_premium", "SURPLUS", 850.0, 14500, 30), # Tuban | |
| ("3524", "beras_premium", "SURPLUS", 700.0, 14600, 25), # Lamongan | |
| ("3522", "beras_premium", "SURPLUS", 650.0, 14400, 35), # Bojonegoro | |
| ("3521", "beras_premium", "SURPLUS", 500.0, 14300, 28), # Ngawi | |
| ("3578", "beras_premium", "DEFICIT", 800.0, 17000, 0), # Surabaya | |
| ("3525", "beras_premium", "DEFICIT", 350.0, 16800, 0), # Gresik | |
| ("3515", "beras_premium", "DEFICIT", 400.0, 16500, 0), # Sidoarjo | |
| # ===== Beras medium (Bulog priority candidate) ===== | |
| ("3519", "beras_medium", "SURPLUS", 1200.0, 12500, 20), # Madiun (Bulog priority) | |
| ("3520", "beras_medium", "SURPLUS", 800.0, 12600, 22), # Magetan | |
| ("3521", "beras_medium", "SURPLUS", 900.0, 12400, 25), # Ngawi | |
| ("3578", "beras_medium", "DEFICIT", 1000.0, 14500, 0), # Surabaya | |
| # ===== Jagung ===== | |
| ("3522", "jagung", "SURPLUS", 800.0, 5500, 15), # Bojonegoro | |
| ("3521", "jagung", "SURPLUS", 600.0, 5400, 18), # Ngawi | |
| ("3502", "jagung", "SURPLUS", 400.0, 5600, 20), # Ponorogo | |
| ("3578", "jagung", "DEFICIT", 700.0, 7200, 0), # Surabaya (industri pakan) | |
| ("3525", "jagung", "DEFICIT", 400.0, 7000, 0), # Gresik | |
| # ===== Telur ayam ===== | |
| ("3505", "telur_ayam", "SURPLUS", 80.0, 26000, 1), # Kab Blitar (sentra) | |
| ("3506", "telur_ayam", "SURPLUS", 50.0, 26500, 2), # Kab Kediri | |
| ("3578", "telur_ayam", "DEFICIT", 90.0, 32000, 0), # Surabaya | |
| ("3573", "telur_ayam", "DEFICIT", 25.0, 30500, 0), # Kota Malang | |
| # ===== Daging ayam ===== | |
| ("3505", "daging_ayam", "SURPLUS", 30.0, 32000, 1), # Kab Blitar | |
| ("3506", "daging_ayam", "SURPLUS", 20.0, 33000, 1), # Kab Kediri | |
| ("3578", "daging_ayam", "DEFICIT", 40.0, 40000, 0), # Surabaya | |
| # ===== Ikan segar ===== | |
| ("3525", "ikan_segar", "SURPLUS", 25.0, 28000, 1), # Gresik (pesisir) | |
| ("3525", "ikan_segar", "SURPLUS", 20.0, 30000, 1), # Lamongan | |
| ("3573", "ikan_segar", "DEFICIT", 15.0, 38000, 0), # Kota Malang | |
| ("3577", "ikan_segar", "DEFICIT", 8.0, 36000, 0), # Kota Madiun (pegunungan) | |
| # ===== Deficits di kab IPM rendah (demonstrate equity multiplier v9) ===== | |
| # Threshold v9: <68β1.30 | <72β1.15 | <78β1.05 | β₯78β1.00 (kalibrasi BPS 2024) | |
| # Madura tidak produksi padi β impor beras | |
| ("3527", "beras_premium", "DEFICIT", 200.0, 16500, 0), # Sampang IPM 66.72 β +30% | |
| ("3526", "beras_premium", "DEFICIT", 250.0, 16400, 0), # Bangkalan IPM 67.70 β +30% | |
| ("3528", "beras_medium", "DEFICIT", 180.0, 14000, 0), # Pamekasan IPM 70.43 β +15% | |
| # Bondowoso pegunungan β defisit ikan & sayur | |
| ("3511", "ikan_segar", "DEFICIT", 6.0, 38000, 0), # Bondowoso IPM 69.62 β +15% | |
| ("3511", "tomat", "DEFICIT", 12.0, 13500, 0), # Bondowoso β +15% | |
| # Sumenep (Tier 1 IHK tapi IPM rendah β equity boost +15%) | |
| ("3529", "cabai_merah", "DEFICIT", 8.0, 60000, 0), # Sumenep IPM 68.79 β +15% | |
| ] | |
| # ============================================================================= | |
| # Weather forecast sample untuk sebagian rute (BMKG-style) | |
| # ============================================================================= | |
| # Skenario D1 Banjir Rute: hujan deras di rute Kediri β Surabaya (April monsoon) | |
| # Format: origin_kab_id, dest_kab_id, max_rain_mm, transit_window_days, source | |
| WEATHER_DATA = [ | |
| # Hujan deras di rute Kediri-Surabaya (skenario D1) | |
| ("3506", "3578", 75.0, 1, "BMKG"), # Kab Kediri β Surabaya | |
| ("3571", "3578", 70.0, 1, "BMKG"), # Kota Kediri β Surabaya | |
| # Rute Madura cerah | |
| ("3527", "3578", 5.0, 1, "BMKG"), # Sampang β Surabaya | |
| ("3526", "3578", 8.0, 1, "BMKG"), # Bangkalan β Surabaya | |
| # Rute pegunungan moderate | |
| ("3507", "3578", 25.0, 1, "BMKG"), # Kab Malang β Surabaya | |
| ("3579", "3578", 30.0, 1, "BMKG"), # Kota Batu β Surabaya | |
| # Rute Probolinggo cerah | |
| ("3513", "3578", 10.0, 1, "BMKG"), # Probolinggo β Surabaya | |
| ("3514", "3578", 12.0, 1, "BMKG"), # Pasuruan β Surabaya | |
| # Rute Lumbung Padi (default cerah) | |
| ("3523", "3578", 8.0, 1, "BMKG"), # Tuban β Surabaya | |
| ("3524", "3578", 6.0, 1, "BMKG"), # Lamongan β Surabaya | |
| ] | |
| # ============================================================================= | |
| # Historical price stats untuk anomaly detection (skenario D3) | |
| # ============================================================================= | |
| # 30-day rolling median + std per komoditas. Kalau current price > 3Ο β outlier. | |
| # Format: commodity_code, median_idr_per_kg, std_idr_per_kg | |
| HISTORICAL_PRICE_STATS = [ | |
| ("cabai_merah", 45000, 8000), | |
| ("cabai_rawit", 60000, 12000), | |
| ("bawang_merah", 35000, 5000), | |
| ("bawang_putih", 40000, 6000), | |
| ("tomat", 12000, 2500), | |
| ("kentang", 14000, 2000), | |
| ("kol", 8000, 1500), | |
| ("wortel", 11000, 2000), | |
| ("beras_premium", 15500, 1500), | |
| ("beras_medium", 13000, 1200), | |
| ("jagung", 6000, 800), | |
| ("kedelai", 11000, 1500), | |
| ("telur_ayam", 29000, 3500), | |
| ("daging_ayam", 37000, 4500), | |
| ("daging_sapi", 138000,12000), | |
| ("gula_pasir", 18000, 1500), | |
| ("minyak_goreng", 16500, 2000), | |
| ("ikan_segar", 35000, 5000), | |
| ("tepung_terigu", 13000, 1200), | |
| ] | |
| # ============================================================================= | |
| # WRITE CSV FILES | |
| # ============================================================================= | |
| def write_csv(filename, header, rows): | |
| path = os.path.join(OUTPUT_DIR, filename) | |
| with open(path, "w", newline="", encoding="utf-8") as f: | |
| w = csv.writer(f) | |
| w.writerow(header) | |
| w.writerows(rows) | |
| print(f" β {filename} ({len(rows)} rows)") | |
| def main(): | |
| print("Generating AgriFlow sample data CSVs...") | |
| write_csv( | |
| "kabupaten_jatim.csv", | |
| ["kab_id", "nama", "latitude", "longitude", "ipm_2024", | |
| "population_2024", "tier"], | |
| KABUPATEN_DATA, | |
| ) | |
| write_csv( | |
| "komoditas_constraints.csv", | |
| ["code", "nama", "max_distance_km", "min_viable_tons", | |
| "max_fresh_age_days", "baseline_price_idr_per_kg"], | |
| KOMODITAS_DATA, | |
| ) | |
| write_csv( | |
| "surplus_deficit.csv", | |
| ["kab_id", "commodity_code", "role", "volume_tons", | |
| "price_idr_per_kg", "harvest_age_days"], | |
| SURPLUS_DEFICIT_DATA, | |
| ) | |
| write_csv( | |
| "weather_forecast.csv", | |
| ["origin_kab_id", "dest_kab_id", "max_rain_mm", | |
| "transit_window_days", "source"], | |
| WEATHER_DATA, | |
| ) | |
| write_csv( | |
| "historical_price_stats.csv", | |
| ["commodity_code", "median_idr_per_kg", "std_idr_per_kg"], | |
| HISTORICAL_PRICE_STATS, | |
| ) | |
| # Summary | |
| n_surplus = sum(1 for r in SURPLUS_DEFICIT_DATA if r[2] == "SURPLUS") | |
| n_deficit = sum(1 for r in SURPLUS_DEFICIT_DATA if r[2] == "DEFICIT") | |
| print(f"\nSummary:") | |
| print(f" - {len(KABUPATEN_DATA)} kabupaten/kota") | |
| print(f" - Tier 1 (HIGH): {sum(1 for r in KABUPATEN_DATA if r[6] == 'TIER_1_HIGH')}") | |
| print(f" - Tier 2 (MEDIUM): {sum(1 for r in KABUPATEN_DATA if r[6] == 'TIER_2_MEDIUM')}") | |
| print(f" - {len(KOMODITAS_DATA)} komoditas") | |
| print(f" - {n_surplus} surplus + {n_deficit} deficit nodes " | |
| f"({len(SURPLUS_DEFICIT_DATA)} total)") | |
| print(f" - {len(WEATHER_DATA)} weather forecast entries") | |
| print(f" - {len(HISTORICAL_PRICE_STATS)} historical price stats") | |
| if __name__ == "__main__": | |
| main() | |