irhamni commited on
Commit
31796e6
·
verified ·
1 Parent(s): 2bf7d22

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -39
app.py CHANGED
@@ -40,12 +40,12 @@ except Exception:
40
 
41
 
42
  # ============================================================
43
- # 1) KONFIGURASI FILE
44
  # ============================================================
45
- DATA_FILE = "IPLM_clean_Manual.xlsx" # DM sampel masuk (multi-sheet)
46
- META_KAB_FILE = "jumlahdesa_fixed (1).xlsx" # kecamatan & desa/kel per kab/kota
47
- META_SDSMP_FILE = "SD-SMP-kab.xlsx" # jumlah SD & SMP per kab/kota
48
- META_SMA_FILE = "SMA.xlsx" # jumlah SMA per provinsi
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", "Kab_Kota", "Kab/Kota", "KAB/KOTA", "kabupaten_kota", "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", "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, ["nama_perpustakaan", "nm_perpustakaan", "nm_instansi_lembaga", "Nama Perpustakaan"])
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 (Kec/Desa) ----
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
- if col_kab and col_kec and col_des:
 
 
 
 
 
 
 
 
 
 
 
 
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
- extra_info.append(f"Meta Kab/Kota (Kec/Desa) terbaca: **{META_KAB_FILE}** (n={len(meta_kab_df)})")
 
 
 
 
 
 
 
 
 
 
291
  else:
292
  meta_kab_df = None
293
- extra_info.append(f"⚠️ Kolom kunci meta kab tidak lengkap di `{META_KAB_FILE}`")
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", "SMA", "SMA_Total",
343
  "jumlah_sma", "total_sma", "jml_sma"
344
  ])
 
345
  if col_prov_sma is None:
346
- raise ValueError("Kolom provinsi tidak ditemukan di file SMA.")
347
  if col_sma is None:
348
- raise ValueError("Kolom jumlah SMA tidak ditemukan di file SMA.")
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 SMA: {e}")
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
- merged["Populasi Sekolah (SD+SMP)"] = merged[["Jml_SD", "Jml_SMP"]].sum(axis=1, skipna=True)
447
- merged["Populasi Admin (Kec+Desa/Kel)"] = merged.get("Jml_Kecamatan", np.nan) + merged.get("Jml_DesaKel", np.nan)
 
 
 
 
 
 
 
 
 
448
 
449
  merged["Target Sekolah (68%)"] = np.ceil(merged["Populasi Sekolah (SD+SMP)"] * TARGET_COVERAGE)
450
- merged["Target Umum (68%)"] = np.ceil(merged["Populasi Admin (Kec+Desa/Kel)"] * TARGET_COVERAGE)
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}` (Kecamatan + Desa/Kel)
882
- - `{META_SDSMP_FILE}` (SD + SMP)
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
  """