Update app.py
Browse files
app.py
CHANGED
|
@@ -40,12 +40,12 @@ except Exception:
|
|
| 40 |
|
| 41 |
|
| 42 |
# ============================================================
|
| 43 |
-
# 1) KONFIGURASI FILE
|
| 44 |
# ============================================================
|
| 45 |
-
DATA_FILE = "
|
| 46 |
-
META_KAB_FILE = "
|
| 47 |
-
META_SDSMP_FILE = "
|
| 48 |
-
META_SMA_FILE = "
|
| 49 |
|
| 50 |
# ============================================================
|
| 51 |
# 1a) TARGET CAKUPAN (KEBIJAKAN)
|
|
@@ -163,10 +163,12 @@ def clean_prov_display(s):
|
|
| 163 |
return None
|
| 164 |
t = str(s).upper().strip()
|
| 165 |
t = " ".join(t.split())
|
| 166 |
-
# hilangkan prefix PROVINSI berulang
|
| 167 |
while t.startswith("PROVINSI PROVINSI "):
|
| 168 |
t = t.replace("PROVINSI PROVINSI ", "PROVINSI ", 1)
|
| 169 |
t = t.replace("PROVINSI PROVINSI ", "PROVINSI ")
|
|
|
|
|
|
|
|
|
|
| 170 |
return t
|
| 171 |
|
| 172 |
def clean_kab_display(s):
|
|
@@ -174,10 +176,8 @@ def clean_kab_display(s):
|
|
| 174 |
return None
|
| 175 |
t = str(s).upper().strip()
|
| 176 |
t = " ".join(t.split())
|
| 177 |
-
# rapihin kab/kota
|
| 178 |
t = t.replace("KABUPATEN", "KAB.")
|
| 179 |
t = t.replace("KAB ", "KAB. ")
|
| 180 |
-
t = t.replace("KAB.", "KAB.")
|
| 181 |
t = t.replace("KOTA ADMINISTRASI", "KOTA")
|
| 182 |
return t
|
| 183 |
|
|
@@ -207,7 +207,7 @@ def make_pie_plotly(num, den, title):
|
|
| 207 |
DATA_INFO = ""
|
| 208 |
df_all_raw = None
|
| 209 |
|
| 210 |
-
meta_kab_df = None # kab_key -> (Jml_Kecamatan, Jml_DesaKel, Jml_SD, Jml_SMP)
|
| 211 |
meta_sma_df = None # prov_key -> (Jml_SMA)
|
| 212 |
|
| 213 |
prov_col_glob = None
|
|
@@ -229,12 +229,13 @@ try:
|
|
| 229 |
frames = [pd.read_excel(fp, sheet_name=s) for s in xls.sheet_names]
|
| 230 |
df_all_raw = pd.concat(frames, ignore_index=True, sort=False)
|
| 231 |
|
|
|
|
| 232 |
prov_col_glob = pick_col(df_all_raw, ["provinsi", "Provinsi", "PROVINSI"])
|
| 233 |
-
kab_col_glob = pick_col(df_all_raw, ["kab_kota", "
|
| 234 |
kew_col_glob = pick_col(df_all_raw, ["kewenangan", "jenis_kewenangan", "Kewenangan", "KEWENANGAN"])
|
| 235 |
-
jenis_col_glob = pick_col(df_all_raw, ["jenis_perpustakaan", "JENIS_PERPUSTAKAAN", "Jenis Perpustakaan"
|
| 236 |
subjenis_col_glob = pick_col(df_all_raw, ["sub_jenis_perpus", "Sub Jenis", "SubJenis", "subjenis", "jenjang"])
|
| 237 |
-
nama_col_glob = pick_col(df_all_raw, ["
|
| 238 |
|
| 239 |
# kewenangan normal
|
| 240 |
if kew_col_glob:
|
|
@@ -251,6 +252,8 @@ try:
|
|
| 251 |
"PERPUSTAKAAN DAERAH": "umum",
|
| 252 |
"PERPUSTAKAAN KHUSUS": "khusus",
|
| 253 |
"KHUSUS": "khusus",
|
|
|
|
|
|
|
| 254 |
}
|
| 255 |
if jenis_col_glob:
|
| 256 |
df_all_raw["_dataset"] = df_all_raw[jenis_col_glob].apply(_norm_text).map(val_map_jenis)
|
|
@@ -273,33 +276,65 @@ except Exception as e:
|
|
| 273 |
df_all_raw = None
|
| 274 |
DATA_INFO = f"⚠️ Gagal memuat `{DATA_FILE}` | Error: `{e}`"
|
| 275 |
|
| 276 |
-
# ---- Meta Kab (
|
| 277 |
try:
|
| 278 |
meta_kab_raw = pd.read_excel(META_KAB_FILE)
|
| 279 |
-
col_kab = pick_col(meta_kab_raw, ["Kab/Kota", "Kab_Kota", "kab/kota", "kabupaten_kota"])
|
| 280 |
-
col_kec = pick_col(meta_kab_raw, ["Kecamatan", "jml_kecamatan", "jumlah_kecamatan"])
|
| 281 |
-
col_des = pick_col(meta_kab_raw, ["Desa/Kel", "Desa Kelurahan", "Desa", "Desa_kel"])
|
| 282 |
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
meta_kab_df = pd.DataFrame({
|
| 285 |
"Kab_Kota_Label": meta_kab_raw[col_kab].astype(str).str.strip(),
|
| 286 |
-
"Jml_Kecamatan": meta_kab_raw[col_kec].apply(coerce_num),
|
| 287 |
-
"Jml_DesaKel": meta_kab_raw[col_des].apply(coerce_num),
|
| 288 |
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
meta_kab_df["kab_key"] = meta_kab_df["Kab_Kota_Label"].apply(norm_kab_label)
|
| 290 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 291 |
else:
|
| 292 |
meta_kab_df = None
|
| 293 |
-
extra_info.append(f"⚠️ Kolom
|
| 294 |
except Exception as e:
|
| 295 |
meta_kab_df = None
|
| 296 |
extra_info.append(f"⚠️ Gagal memuat `{META_KAB_FILE}` ({e})")
|
| 297 |
|
| 298 |
-
# ---- Meta SD/SMP ----
|
| 299 |
try:
|
| 300 |
sd_smp_raw = pd.read_excel(META_SDSMP_FILE)
|
| 301 |
col_kab2 = pick_col(sd_smp_raw, [
|
| 302 |
-
"Kabupaten/Kota_Kabupaten/Kota", "Kabupaten/Kota",
|
| 303 |
"Kab/Kota", "Kab_Kota", "kab/kota", "kabupaten_kota"
|
| 304 |
])
|
| 305 |
col_sd = pick_col(sd_smp_raw, ["SD", "Jumlah SD", "Total SD", "SD_Total", "jml_sd", "Jml_SD"])
|
|
@@ -319,7 +354,13 @@ try:
|
|
| 319 |
})
|
| 320 |
|
| 321 |
if meta_kab_df is not None:
|
| 322 |
-
meta_kab_df = meta_kab_df.merge(df_sd_smp_grp, on="kab_key", how="left")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 323 |
else:
|
| 324 |
meta_kab_df = df_sd_smp_grp.copy()
|
| 325 |
meta_kab_df["Kab_Kota_Label"] = df_sd_smp.groupby("kab_key")["Kab_Kota_Label_SD"].first().values
|
|
@@ -330,22 +371,28 @@ try:
|
|
| 330 |
except Exception as e:
|
| 331 |
extra_info.append(f"⚠️ Gagal memuat `{META_SDSMP_FILE}` ({e})")
|
| 332 |
|
| 333 |
-
# ---- Meta SMA ----
|
| 334 |
try:
|
| 335 |
meta_sma_raw = pd.read_excel(META_SMA_FILE)
|
|
|
|
|
|
|
|
|
|
| 336 |
col_prov_sma = pick_col(meta_sma_raw, [
|
| 337 |
"Provinsi", "provinsi", "PROVINSI", "NAMA_PROVINSI", "Nama Provinsi",
|
| 338 |
"nm_prov", "nm_provinsi", "prov"
|
| 339 |
])
|
|
|
|
|
|
|
| 340 |
col_sma = pick_col(meta_sma_raw, [
|
| 341 |
-
"Total SMA", "TOTAL_SMA", "TOTAL", "total",
|
| 342 |
-
"Jml_SMA", "Jumlah SMA", "
|
| 343 |
"jumlah_sma", "total_sma", "jml_sma"
|
| 344 |
])
|
|
|
|
| 345 |
if col_prov_sma is None:
|
| 346 |
-
raise ValueError("Kolom provinsi tidak ditemukan di file
|
| 347 |
if col_sma is None:
|
| 348 |
-
raise ValueError("Kolom jumlah SMA tidak ditemukan di file
|
| 349 |
|
| 350 |
meta_sma_df = pd.DataFrame({
|
| 351 |
"Provinsi_Label": meta_sma_raw[col_prov_sma].astype(str).str.strip(),
|
|
@@ -360,7 +407,7 @@ try:
|
|
| 360 |
extra_info.append(f"Meta SMA terbaca: **{META_SMA_FILE}** ({len(meta_sma_df)} provinsi)")
|
| 361 |
except Exception as e:
|
| 362 |
meta_sma_df = None
|
| 363 |
-
extra_info.append(f"⚠️ Gagal memuat file
|
| 364 |
|
| 365 |
if extra_info:
|
| 366 |
DATA_INFO = DATA_INFO + "<br>" + "<br>".join(extra_info)
|
|
@@ -429,7 +476,7 @@ def compute_gap_verification(df_filtered: pd.DataFrame, kew_value: str) -> pd.Da
|
|
| 429 |
tmp_umum = tmp[tmp["_dataset"] == "umum"].copy() if "_dataset" in tmp.columns else tmp.copy()
|
| 430 |
g_umum = tmp_umum.groupby("kab_key").size().rename("Sampel Umum").reset_index()
|
| 431 |
|
| 432 |
-
use_cols = ["kab_key", "Kab_Kota_Label", "Jml_Kecamatan", "Jml_DesaKel", "Jml_SD", "Jml_SMP"]
|
| 433 |
use_cols = [c for c in use_cols if c in meta_kab_df.columns]
|
| 434 |
|
| 435 |
merged = (
|
|
@@ -443,11 +490,20 @@ def compute_gap_verification(df_filtered: pd.DataFrame, kew_value: str) -> pd.Da
|
|
| 443 |
if c in merged.columns:
|
| 444 |
merged[c] = merged[c].fillna(0).astype(int)
|
| 445 |
|
| 446 |
-
|
| 447 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
|
| 449 |
merged["Target Sekolah (68%)"] = np.ceil(merged["Populasi Sekolah (SD+SMP)"] * TARGET_COVERAGE)
|
| 450 |
-
merged["Target Umum (68%)"]
|
| 451 |
|
| 452 |
merged["Kekurangan Sampel Sekolah"] = merged.apply(
|
| 453 |
lambda r: max(int(r["Target Sekolah (68%)"] - r["Sampel Sekolah"]) if pd.notna(r["Target Sekolah (68%)"]) else 0, 0),
|
|
@@ -487,7 +543,6 @@ def compute_gap_verification(df_filtered: pd.DataFrame, kew_value: str) -> pd.Da
|
|
| 487 |
|
| 488 |
tmp["prov_key"] = tmp["prov_clean"].apply(norm_prov_label)
|
| 489 |
|
| 490 |
-
# start dari sampel (tidak bocor prov lain)
|
| 491 |
g_total = tmp.groupby("prov_key").size().rename("Sampel Total (Prov)").reset_index()
|
| 492 |
|
| 493 |
tmp_sek = tmp[tmp["_dataset"] == "sekolah"].copy() if "_dataset" in tmp.columns else tmp.copy()
|
|
@@ -742,7 +797,6 @@ def generate_word_report_gap(verif_df: pd.DataFrame, prov: str, kab: str, kew: s
|
|
| 742 |
if not HAS_KALEIDO:
|
| 743 |
doc.add_paragraph("Grafik pie tidak dibuat karena 'kaleido' tidak tersedia di server.")
|
| 744 |
else:
|
| 745 |
-
# buat pie total capai vs target kalau ada pasangan kolom sampel-target
|
| 746 |
pie_made = False
|
| 747 |
if "Sampel Sekolah" in verif_df.columns and "Target Sekolah (68%)" in verif_df.columns:
|
| 748 |
samp = pd.to_numeric(verif_df["Sampel Sekolah"], errors="coerce").fillna(0).sum()
|
|
@@ -878,9 +932,8 @@ Aplikasi ini menghitung **berapa unit lagi yang harus dikumpulkan** agar memenuh
|
|
| 878 |
|
| 879 |
**File:**
|
| 880 |
- `{DATA_FILE}` (DM)
|
| 881 |
-
- `{META_KAB_FILE}` (
|
| 882 |
-
- `{
|
| 883 |
-
- `{META_SMA_FILE}` (SMA)
|
| 884 |
|
| 885 |
{DATA_INFO}
|
| 886 |
"""
|
|
|
|
| 40 |
|
| 41 |
|
| 42 |
# ============================================================
|
| 43 |
+
# 1) KONFIGURASI FILE (DISESUAIKAN DENGAN FILE UPLOAD KAMU)
|
| 44 |
# ============================================================
|
| 45 |
+
DATA_FILE = "IPLM_clean_manual_131225.xlsx" # DM sampel masuk (multi-sheet)
|
| 46 |
+
META_KAB_FILE = "Data_populasi_Kab_kota.xlsx" # populasi kab/kota (kec, desa, SD, SMP)
|
| 47 |
+
META_SDSMP_FILE = "Data_populasi_Kab_kota.xlsx" # tetap ada (logic sama), sumber sama
|
| 48 |
+
META_SMA_FILE = "Data_populasi_propinsi.xlsx" # populasi provinsi (SMA)
|
| 49 |
|
| 50 |
# ============================================================
|
| 51 |
# 1a) TARGET CAKUPAN (KEBIJAKAN)
|
|
|
|
| 163 |
return None
|
| 164 |
t = str(s).upper().strip()
|
| 165 |
t = " ".join(t.split())
|
|
|
|
| 166 |
while t.startswith("PROVINSI PROVINSI "):
|
| 167 |
t = t.replace("PROVINSI PROVINSI ", "PROVINSI ", 1)
|
| 168 |
t = t.replace("PROVINSI PROVINSI ", "PROVINSI ")
|
| 169 |
+
if not t.startswith("PROVINSI "):
|
| 170 |
+
# di DM kamu kolom 'provinsi' biasanya tanpa prefix PROVINSI, biar konsisten kita tambahin
|
| 171 |
+
t = "PROVINSI " + t
|
| 172 |
return t
|
| 173 |
|
| 174 |
def clean_kab_display(s):
|
|
|
|
| 176 |
return None
|
| 177 |
t = str(s).upper().strip()
|
| 178 |
t = " ".join(t.split())
|
|
|
|
| 179 |
t = t.replace("KABUPATEN", "KAB.")
|
| 180 |
t = t.replace("KAB ", "KAB. ")
|
|
|
|
| 181 |
t = t.replace("KOTA ADMINISTRASI", "KOTA")
|
| 182 |
return t
|
| 183 |
|
|
|
|
| 207 |
DATA_INFO = ""
|
| 208 |
df_all_raw = None
|
| 209 |
|
| 210 |
+
meta_kab_df = None # kab_key -> (Jml_Kecamatan, Jml_DesaKel, Jml_SD, Jml_SMP, optional populasi_umum/pop_sekolah)
|
| 211 |
meta_sma_df = None # prov_key -> (Jml_SMA)
|
| 212 |
|
| 213 |
prov_col_glob = None
|
|
|
|
| 229 |
frames = [pd.read_excel(fp, sheet_name=s) for s in xls.sheet_names]
|
| 230 |
df_all_raw = pd.concat(frames, ignore_index=True, sort=False)
|
| 231 |
|
| 232 |
+
# >>> DISESUAIKAN: DM kamu punya kolom 'provinsi', 'kab_kota', dst.
|
| 233 |
prov_col_glob = pick_col(df_all_raw, ["provinsi", "Provinsi", "PROVINSI"])
|
| 234 |
+
kab_col_glob = pick_col(df_all_raw, ["kab_kota", "kab/kota", "Kab/Kota", "KAB/KOTA", "kabupaten_kota", "kota"])
|
| 235 |
kew_col_glob = pick_col(df_all_raw, ["kewenangan", "jenis_kewenangan", "Kewenangan", "KEWENANGAN"])
|
| 236 |
+
jenis_col_glob = pick_col(df_all_raw, ["jenis_perpustakaan", "JENIS_PERPUSTAKAAN", "Jenis Perpustakaan"])
|
| 237 |
subjenis_col_glob = pick_col(df_all_raw, ["sub_jenis_perpus", "Sub Jenis", "SubJenis", "subjenis", "jenjang"])
|
| 238 |
+
nama_col_glob = pick_col(df_all_raw, ["nm_perpustakaan", "nama_perpustakaan", "nm_instansi_lembaga", "Nama Perpustakaan"])
|
| 239 |
|
| 240 |
# kewenangan normal
|
| 241 |
if kew_col_glob:
|
|
|
|
| 252 |
"PERPUSTAKAAN DAERAH": "umum",
|
| 253 |
"PERPUSTAKAAN KHUSUS": "khusus",
|
| 254 |
"KHUSUS": "khusus",
|
| 255 |
+
"PERPUSTAKAAN PERGURUAN TINGGI": "khusus",
|
| 256 |
+
"PERGURUAN TINGGI": "khusus",
|
| 257 |
}
|
| 258 |
if jenis_col_glob:
|
| 259 |
df_all_raw["_dataset"] = df_all_raw[jenis_col_glob].apply(_norm_text).map(val_map_jenis)
|
|
|
|
| 276 |
df_all_raw = None
|
| 277 |
DATA_INFO = f"⚠️ Gagal memuat `{DATA_FILE}` | Error: `{e}`"
|
| 278 |
|
| 279 |
+
# ---- Meta Kab/Kota (Populasi) ----
|
| 280 |
try:
|
| 281 |
meta_kab_raw = pd.read_excel(META_KAB_FILE)
|
|
|
|
|
|
|
|
|
|
| 282 |
|
| 283 |
+
# >>> DISESUAIKAN: kolom file kamu:
|
| 284 |
+
# 'KABUPATEN_KOTA', 'Jml Kec', 'Jml Desa', 'SD', 'SMP', 'jumlah_populasi_umum', 'jumlah_populasi_sekolah'
|
| 285 |
+
col_kab = pick_col(meta_kab_raw, ["KABUPATEN_KOTA", "KAB/KOTA", "Kab/Kota", "Kab_Kota", "kab/kota", "kabupaten_kota"])
|
| 286 |
+
col_kec = pick_col(meta_kab_raw, ["Jml Kec", "JML KEC", "Jml Kecamatan", "Kecamatan", "jml_kecamatan", "jumlah_kecamatan"])
|
| 287 |
+
col_des = pick_col(meta_kab_raw, ["Jml Desa", "JML DESA", "Jml Desa/Kel", "Desa/Kel", "Desa Kelurahan", "Desa", "Desa_kel"])
|
| 288 |
+
|
| 289 |
+
col_sd = pick_col(meta_kab_raw, ["SD", "Jumlah SD", "Total SD", "SD_Total", "jml_sd", "Jml_SD"])
|
| 290 |
+
col_smp = pick_col(meta_kab_raw, ["SMP", "Jumlah SMP", "Total SMP", "SMP_Total", "jml_smp", "Jml_SMP"])
|
| 291 |
+
|
| 292 |
+
col_pop_umum = pick_col(meta_kab_raw, ["jumlah_populasi_umum", "Populasi Umum", "pop_umum"])
|
| 293 |
+
col_pop_sekolah = pick_col(meta_kab_raw, ["jumlah_populasi_sekolah", "Populasi Sekolah", "pop_sekolah"])
|
| 294 |
+
|
| 295 |
+
if col_kab:
|
| 296 |
meta_kab_df = pd.DataFrame({
|
| 297 |
"Kab_Kota_Label": meta_kab_raw[col_kab].astype(str).str.strip(),
|
|
|
|
|
|
|
| 298 |
})
|
| 299 |
+
meta_kab_df["Jml_Kecamatan"] = meta_kab_raw[col_kec].apply(coerce_num) if col_kec else np.nan
|
| 300 |
+
meta_kab_df["Jml_DesaKel"] = meta_kab_raw[col_des].apply(coerce_num) if col_des else np.nan
|
| 301 |
+
meta_kab_df["Jml_SD"] = meta_kab_raw[col_sd].apply(coerce_num) if col_sd else np.nan
|
| 302 |
+
meta_kab_df["Jml_SMP"] = meta_kab_raw[col_smp].apply(coerce_num) if col_smp else np.nan
|
| 303 |
+
|
| 304 |
+
if col_pop_umum:
|
| 305 |
+
meta_kab_df["Pop_Umum_Meta"] = meta_kab_raw[col_pop_umum].apply(coerce_num)
|
| 306 |
+
else:
|
| 307 |
+
meta_kab_df["Pop_Umum_Meta"] = np.nan
|
| 308 |
+
|
| 309 |
+
if col_pop_sekolah:
|
| 310 |
+
meta_kab_df["Pop_Sekolah_Meta"] = meta_kab_raw[col_pop_sekolah].apply(coerce_num)
|
| 311 |
+
else:
|
| 312 |
+
meta_kab_df["Pop_Sekolah_Meta"] = np.nan
|
| 313 |
+
|
| 314 |
meta_kab_df["kab_key"] = meta_kab_df["Kab_Kota_Label"].apply(norm_kab_label)
|
| 315 |
+
meta_kab_df = meta_kab_df.groupby("kab_key", as_index=False).agg({
|
| 316 |
+
"Kab_Kota_Label": "first",
|
| 317 |
+
"Jml_Kecamatan": "first",
|
| 318 |
+
"Jml_DesaKel": "first",
|
| 319 |
+
"Jml_SD": "first",
|
| 320 |
+
"Jml_SMP": "first",
|
| 321 |
+
"Pop_Umum_Meta": "first",
|
| 322 |
+
"Pop_Sekolah_Meta": "first",
|
| 323 |
+
})
|
| 324 |
+
|
| 325 |
+
extra_info.append(f"Meta Kab/Kota terbaca: **{META_KAB_FILE}** (n={len(meta_kab_df)})")
|
| 326 |
else:
|
| 327 |
meta_kab_df = None
|
| 328 |
+
extra_info.append(f"⚠️ Kolom kab/kota tidak ditemukan di `{META_KAB_FILE}`")
|
| 329 |
except Exception as e:
|
| 330 |
meta_kab_df = None
|
| 331 |
extra_info.append(f"⚠️ Gagal memuat `{META_KAB_FILE}` ({e})")
|
| 332 |
|
| 333 |
+
# ---- Meta SD/SMP (tetap dipertahankan logic-nya, sumber sama) ----
|
| 334 |
try:
|
| 335 |
sd_smp_raw = pd.read_excel(META_SDSMP_FILE)
|
| 336 |
col_kab2 = pick_col(sd_smp_raw, [
|
| 337 |
+
"KABUPATEN_KOTA", "Kabupaten/Kota_Kabupaten/Kota", "Kabupaten/Kota",
|
| 338 |
"Kab/Kota", "Kab_Kota", "kab/kota", "kabupaten_kota"
|
| 339 |
])
|
| 340 |
col_sd = pick_col(sd_smp_raw, ["SD", "Jumlah SD", "Total SD", "SD_Total", "jml_sd", "Jml_SD"])
|
|
|
|
| 354 |
})
|
| 355 |
|
| 356 |
if meta_kab_df is not None:
|
| 357 |
+
meta_kab_df = meta_kab_df.merge(df_sd_smp_grp, on="kab_key", how="left", suffixes=("", "_x"))
|
| 358 |
+
# kalau dari meta_kab_df SD/SMP kosong, isi dari grup
|
| 359 |
+
meta_kab_df["Jml_SD"] = meta_kab_df["Jml_SD"].fillna(meta_kab_df.get("Jml_SD_x"))
|
| 360 |
+
meta_kab_df["Jml_SMP"] = meta_kab_df["Jml_SMP"].fillna(meta_kab_df.get("Jml_SMP_x"))
|
| 361 |
+
for c in ["Jml_SD_x", "Jml_SMP_x"]:
|
| 362 |
+
if c in meta_kab_df.columns:
|
| 363 |
+
meta_kab_df = meta_kab_df.drop(columns=[c])
|
| 364 |
else:
|
| 365 |
meta_kab_df = df_sd_smp_grp.copy()
|
| 366 |
meta_kab_df["Kab_Kota_Label"] = df_sd_smp.groupby("kab_key")["Kab_Kota_Label_SD"].first().values
|
|
|
|
| 371 |
except Exception as e:
|
| 372 |
extra_info.append(f"⚠️ Gagal memuat `{META_SDSMP_FILE}` ({e})")
|
| 373 |
|
| 374 |
+
# ---- Meta SMA (Provinsi) ----
|
| 375 |
try:
|
| 376 |
meta_sma_raw = pd.read_excel(META_SMA_FILE)
|
| 377 |
+
|
| 378 |
+
# >>> DISESUAIKAN: kolom file kamu:
|
| 379 |
+
# 'Provinsi', 'sma ' (ada spasi), dan beberapa kolom lain
|
| 380 |
col_prov_sma = pick_col(meta_sma_raw, [
|
| 381 |
"Provinsi", "provinsi", "PROVINSI", "NAMA_PROVINSI", "Nama Provinsi",
|
| 382 |
"nm_prov", "nm_provinsi", "prov"
|
| 383 |
])
|
| 384 |
+
|
| 385 |
+
# prioritas "sma " (dengan spasi), lalu variasi lain
|
| 386 |
col_sma = pick_col(meta_sma_raw, [
|
| 387 |
+
"sma ", "SMA", "Total SMA", "TOTAL_SMA", "TOTAL", "total",
|
| 388 |
+
"Jml_SMA", "Jumlah SMA", "SMA_Total",
|
| 389 |
"jumlah_sma", "total_sma", "jml_sma"
|
| 390 |
])
|
| 391 |
+
|
| 392 |
if col_prov_sma is None:
|
| 393 |
+
raise ValueError("Kolom provinsi tidak ditemukan di file populasi provinsi.")
|
| 394 |
if col_sma is None:
|
| 395 |
+
raise ValueError("Kolom jumlah SMA tidak ditemukan di file populasi provinsi.")
|
| 396 |
|
| 397 |
meta_sma_df = pd.DataFrame({
|
| 398 |
"Provinsi_Label": meta_sma_raw[col_prov_sma].astype(str).str.strip(),
|
|
|
|
| 407 |
extra_info.append(f"Meta SMA terbaca: **{META_SMA_FILE}** ({len(meta_sma_df)} provinsi)")
|
| 408 |
except Exception as e:
|
| 409 |
meta_sma_df = None
|
| 410 |
+
extra_info.append(f"⚠️ Gagal memuat file populasi provinsi: {e}")
|
| 411 |
|
| 412 |
if extra_info:
|
| 413 |
DATA_INFO = DATA_INFO + "<br>" + "<br>".join(extra_info)
|
|
|
|
| 476 |
tmp_umum = tmp[tmp["_dataset"] == "umum"].copy() if "_dataset" in tmp.columns else tmp.copy()
|
| 477 |
g_umum = tmp_umum.groupby("kab_key").size().rename("Sampel Umum").reset_index()
|
| 478 |
|
| 479 |
+
use_cols = ["kab_key", "Kab_Kota_Label", "Jml_Kecamatan", "Jml_DesaKel", "Jml_SD", "Jml_SMP", "Pop_Umum_Meta", "Pop_Sekolah_Meta"]
|
| 480 |
use_cols = [c for c in use_cols if c in meta_kab_df.columns]
|
| 481 |
|
| 482 |
merged = (
|
|
|
|
| 490 |
if c in merged.columns:
|
| 491 |
merged[c] = merged[c].fillna(0).astype(int)
|
| 492 |
|
| 493 |
+
# Populasi sekolah: prefer SD+SMP, fallback ke Pop_Sekolah_Meta
|
| 494 |
+
pop_sek = merged[["Jml_SD", "Jml_SMP"]].sum(axis=1, skipna=True)
|
| 495 |
+
if "Pop_Sekolah_Meta" in merged.columns:
|
| 496 |
+
pop_sek = pop_sek.where(pop_sek.notna() & (pop_sek > 0), merged["Pop_Sekolah_Meta"])
|
| 497 |
+
merged["Populasi Sekolah (SD+SMP)"] = pop_sek
|
| 498 |
+
|
| 499 |
+
# Populasi umum: prefer Kec+Desa, fallback ke Pop_Umum_Meta
|
| 500 |
+
pop_umum = merged.get("Jml_Kecamatan", np.nan) + merged.get("Jml_DesaKel", np.nan)
|
| 501 |
+
if "Pop_Umum_Meta" in merged.columns:
|
| 502 |
+
pop_umum = pop_umum.where(pop_umum.notna() & (pop_umum > 0), merged["Pop_Umum_Meta"])
|
| 503 |
+
merged["Populasi Admin (Kec+Desa/Kel)"] = pop_umum
|
| 504 |
|
| 505 |
merged["Target Sekolah (68%)"] = np.ceil(merged["Populasi Sekolah (SD+SMP)"] * TARGET_COVERAGE)
|
| 506 |
+
merged["Target Umum (68%)"] = np.ceil(merged["Populasi Admin (Kec+Desa/Kel)"] * TARGET_COVERAGE)
|
| 507 |
|
| 508 |
merged["Kekurangan Sampel Sekolah"] = merged.apply(
|
| 509 |
lambda r: max(int(r["Target Sekolah (68%)"] - r["Sampel Sekolah"]) if pd.notna(r["Target Sekolah (68%)"]) else 0, 0),
|
|
|
|
| 543 |
|
| 544 |
tmp["prov_key"] = tmp["prov_clean"].apply(norm_prov_label)
|
| 545 |
|
|
|
|
| 546 |
g_total = tmp.groupby("prov_key").size().rename("Sampel Total (Prov)").reset_index()
|
| 547 |
|
| 548 |
tmp_sek = tmp[tmp["_dataset"] == "sekolah"].copy() if "_dataset" in tmp.columns else tmp.copy()
|
|
|
|
| 797 |
if not HAS_KALEIDO:
|
| 798 |
doc.add_paragraph("Grafik pie tidak dibuat karena 'kaleido' tidak tersedia di server.")
|
| 799 |
else:
|
|
|
|
| 800 |
pie_made = False
|
| 801 |
if "Sampel Sekolah" in verif_df.columns and "Target Sekolah (68%)" in verif_df.columns:
|
| 802 |
samp = pd.to_numeric(verif_df["Sampel Sekolah"], errors="coerce").fillna(0).sum()
|
|
|
|
| 932 |
|
| 933 |
**File:**
|
| 934 |
- `{DATA_FILE}` (DM)
|
| 935 |
+
- `{META_KAB_FILE}` (Populasi Kab/Kota)
|
| 936 |
+
- `{META_SMA_FILE}` (Populasi Provinsi)
|
|
|
|
| 937 |
|
| 938 |
{DATA_INFO}
|
| 939 |
"""
|