Update app.py
Browse files
app.py
CHANGED
|
@@ -605,8 +605,8 @@ def load_default_files(force=False):
|
|
| 605 |
|
| 606 |
# ============================================================
|
| 607 |
# 6) FAKTOR WILAYAH (TOTAL) — hanya untuk faktor/target/pop/coverage
|
| 608 |
-
#
|
| 609 |
-
#
|
| 610 |
# ============================================================
|
| 611 |
|
| 612 |
def build_faktor_wilayah(
|
|
@@ -631,15 +631,16 @@ def build_faktor_wilayah(
|
|
| 631 |
pop_field = "Pop_Total"
|
| 632 |
name_field = "Kab_Kota_Label"
|
| 633 |
|
| 634 |
-
#
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
khs_kab = khs.groupby("kab_key", as_index=False).agg({
|
| 638 |
"Target68_Total_Jenis": "sum",
|
| 639 |
"Pop_Total_Jenis": "sum"
|
| 640 |
}).set_index("kab_key")
|
| 641 |
else:
|
| 642 |
-
|
|
|
|
|
|
|
| 643 |
|
| 644 |
elif "PROV" in kew_norm:
|
| 645 |
key_col = "prov_key"
|
|
@@ -650,17 +651,19 @@ def build_faktor_wilayah(
|
|
| 650 |
pop_field = "Pop_Total_Prov"
|
| 651 |
name_field = "Provinsi_Label"
|
| 652 |
|
| 653 |
-
#
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
khs_prov = khs.groupby("prov_key", as_index=False).agg({
|
| 657 |
"Target68_Total_Jenis": "sum",
|
| 658 |
"Pop_Total_Jenis": "sum"
|
| 659 |
}).set_index("prov_key")
|
| 660 |
else:
|
| 661 |
-
|
|
|
|
|
|
|
| 662 |
|
| 663 |
else:
|
|
|
|
| 664 |
key_col = "kab_key"
|
| 665 |
label_col = "KAB_DISP"
|
| 666 |
label_name = "Kab/Kota"
|
|
@@ -669,14 +672,15 @@ def build_faktor_wilayah(
|
|
| 669 |
pop_field = "Pop_Total"
|
| 670 |
name_field = "Kab_Kota_Label"
|
| 671 |
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
khs_kab = khs.groupby("kab_key", as_index=False).agg({
|
| 675 |
"Target68_Total_Jenis": "sum",
|
| 676 |
"Pop_Total_Jenis": "sum"
|
| 677 |
}).set_index("kab_key")
|
| 678 |
else:
|
| 679 |
-
|
|
|
|
|
|
|
| 680 |
|
| 681 |
base = df.groupby([key_col, label_col], dropna=False).agg(
|
| 682 |
n_total=("Indeks_Dasar_0_100", "size"),
|
|
@@ -686,7 +690,7 @@ def build_faktor_wilayah(
|
|
| 686 |
for _, r in base.iterrows():
|
| 687 |
gk = r["group_key"]
|
| 688 |
|
| 689 |
-
#
|
| 690 |
if gk in pop.index:
|
| 691 |
target_total = pop.loc[gk, target_field] if target_field in pop.columns else np.nan
|
| 692 |
pop_total = pop.loc[gk, pop_field] if pop_field in pop.columns else np.nan
|
|
@@ -694,35 +698,25 @@ def build_faktor_wilayah(
|
|
| 694 |
else:
|
| 695 |
target_total, pop_total, nm = np.nan, np.nan, r[label_name]
|
| 696 |
|
| 697 |
-
#
|
| 698 |
add_target, add_pop = 0.0, 0.0
|
| 699 |
if "PROV" in kew_norm:
|
| 700 |
-
if
|
| 701 |
-
add_target =
|
| 702 |
-
add_pop =
|
| 703 |
else:
|
| 704 |
-
if
|
| 705 |
-
add_target =
|
| 706 |
-
add_pop =
|
| 707 |
-
|
| 708 |
-
|
| 709 |
-
add_target = float(pd.to_numeric(add_target, errors="coerce") or 0.0)
|
| 710 |
-
except Exception:
|
| 711 |
-
add_target = 0.0
|
| 712 |
-
try:
|
| 713 |
-
add_pop = float(pd.to_numeric(add_pop, errors="coerce") or 0.0)
|
| 714 |
-
except Exception:
|
| 715 |
-
add_pop = 0.0
|
| 716 |
-
|
| 717 |
-
# jumlahkan (kalau NaN -> treat 0 dulu)
|
| 718 |
t0 = float(pd.to_numeric(target_total, errors="coerce")) if pd.notna(target_total) else 0.0
|
| 719 |
p0 = float(pd.to_numeric(pop_total, errors="coerce")) if pd.notna(pop_total) else 0.0
|
|
|
|
|
|
|
| 720 |
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
target_vals.append(target_total)
|
| 725 |
-
pop_vals.append(pop_total)
|
| 726 |
label_fix.append(nm)
|
| 727 |
|
| 728 |
base[label_name] = label_fix
|
|
@@ -733,7 +727,7 @@ def build_faktor_wilayah(
|
|
| 733 |
m = base["pop_total"].isna() & base["target_total_68"].notna() & (base["target_total_68"] > 0)
|
| 734 |
base.loc[m, "pop_total"] = base.loc[m, "target_total_68"] / float(FALLBACK_TARGET_RATIO)
|
| 735 |
|
| 736 |
-
# faktor
|
| 737 |
base["faktor_penyesuaian"] = [
|
| 738 |
faktor_penyesuaian_total(n, t)
|
| 739 |
for n, t in zip(
|
|
@@ -750,7 +744,7 @@ def build_faktor_wilayah(
|
|
| 750 |
)
|
| 751 |
]
|
| 752 |
|
| 753 |
-
# ======
|
| 754 |
base["target_total_68"] = pd.to_numeric(base["target_total_68"], errors="coerce").fillna(0).round(0).astype(int)
|
| 755 |
base["pop_total"] = pd.to_numeric(base["pop_total"], errors="coerce").fillna(0).round(0).astype(int)
|
| 756 |
base["coverage_total_%"] = pd.to_numeric(base["coverage_total_%"], errors="coerce").fillna(0.0).round(2)
|
|
@@ -758,7 +752,6 @@ def build_faktor_wilayah(
|
|
| 758 |
|
| 759 |
return base
|
| 760 |
|
| 761 |
-
|
| 762 |
# ============================================================
|
| 763 |
# 7) AGREGAT WILAYAH × JENIS (Final pakai faktor wilayah)
|
| 764 |
# ============================================================
|
|
@@ -1495,6 +1488,17 @@ def generate_word_report(wilayah, summary_jenis, agg_total, agg_jenis, analysis_
|
|
| 1495 |
# 15) CORE RUN
|
| 1496 |
# ============================================================
|
| 1497 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1498 |
def run_calc(prov_value, kab_value, kew_value, df_all, df_raw, pop_kab, pop_prov, pop_khusus, meta):
|
| 1499 |
try:
|
| 1500 |
if df_all is None or df_all.empty or df_raw is None or df_raw.empty:
|
|
@@ -1513,7 +1517,6 @@ def run_calc(prov_value, kab_value, kew_value, df_all, df_raw, pop_kab, pop_prov
|
|
| 1513 |
return _empty_outputs("Tidak ada data untuk filter ini.")
|
| 1514 |
|
| 1515 |
# ==== PIPELINE BARU (KUNCI KONSISTENSI) ====
|
| 1516 |
-
# UPDATE: faktor_wilayah sekarang menambahkan POP_KHUSUS ke target_total_68 & pop_total (total wilayah)
|
| 1517 |
faktor_wilayah = build_faktor_wilayah(df, pop_kab, pop_prov, pop_khusus, kew_value or "(Semua)")
|
| 1518 |
agg_jenis_full = build_agg_wilayah_jenis(df, faktor_wilayah, pop_khusus, kew_value or "(Semua)")
|
| 1519 |
agg_total = build_agg_wilayah_total_from_jenis(agg_jenis_full, faktor_wilayah, kew_value or "(Semua)")
|
|
@@ -1540,7 +1543,6 @@ def run_calc(prov_value, kab_value, kew_value, df_all, df_raw, pop_kab, pop_prov
|
|
| 1540 |
cols_upto = [c for c in cols_upto if c in agg_jenis_full.columns]
|
| 1541 |
agg_jenis_view = agg_jenis_full[cols_upto].copy()
|
| 1542 |
|
| 1543 |
-
|
| 1544 |
# FILTER RAW DOWNLOAD (df_raw)
|
| 1545 |
raw = df_raw.copy()
|
| 1546 |
if prov_value and prov_value != "(Semua)":
|
|
|
|
| 605 |
|
| 606 |
# ============================================================
|
| 607 |
# 6) FAKTOR WILAYAH (TOTAL) — hanya untuk faktor/target/pop/coverage
|
| 608 |
+
# UPDATE: pop_total & target_total_68 wilayah ditambah data KHUSUS (POP_KHUSUS)
|
| 609 |
+
# TANPA ubah logika faktor (tetap min(n_total/target,1))
|
| 610 |
# ============================================================
|
| 611 |
|
| 612 |
def build_faktor_wilayah(
|
|
|
|
| 631 |
pop_field = "Pop_Total"
|
| 632 |
name_field = "Kab_Kota_Label"
|
| 633 |
|
| 634 |
+
# agregat KHUSUS per kab_key (sum)
|
| 635 |
+
if pop_khusus is not None and not pop_khusus.empty and "kab_key" in pop_khusus.columns:
|
| 636 |
+
pk_kab = pop_khusus.groupby("kab_key", as_index=False).agg({
|
|
|
|
| 637 |
"Target68_Total_Jenis": "sum",
|
| 638 |
"Pop_Total_Jenis": "sum"
|
| 639 |
}).set_index("kab_key")
|
| 640 |
else:
|
| 641 |
+
pk_kab = pd.DataFrame().set_index(pd.Index([]))
|
| 642 |
+
|
| 643 |
+
pk_prov = None # tidak dipakai
|
| 644 |
|
| 645 |
elif "PROV" in kew_norm:
|
| 646 |
key_col = "prov_key"
|
|
|
|
| 651 |
pop_field = "Pop_Total_Prov"
|
| 652 |
name_field = "Provinsi_Label"
|
| 653 |
|
| 654 |
+
# agregat KHUSUS per prov_key (sum)
|
| 655 |
+
if pop_khusus is not None and not pop_khusus.empty and "prov_key" in pop_khusus.columns:
|
| 656 |
+
pk_prov = pop_khusus.groupby("prov_key", as_index=False).agg({
|
|
|
|
| 657 |
"Target68_Total_Jenis": "sum",
|
| 658 |
"Pop_Total_Jenis": "sum"
|
| 659 |
}).set_index("prov_key")
|
| 660 |
else:
|
| 661 |
+
pk_prov = pd.DataFrame().set_index(pd.Index([]))
|
| 662 |
+
|
| 663 |
+
pk_kab = None # tidak dipakai
|
| 664 |
|
| 665 |
else:
|
| 666 |
+
# fallback: perlakukan seperti kab/kota
|
| 667 |
key_col = "kab_key"
|
| 668 |
label_col = "KAB_DISP"
|
| 669 |
label_name = "Kab/Kota"
|
|
|
|
| 672 |
pop_field = "Pop_Total"
|
| 673 |
name_field = "Kab_Kota_Label"
|
| 674 |
|
| 675 |
+
if pop_khusus is not None and not pop_khusus.empty and "kab_key" in pop_khusus.columns:
|
| 676 |
+
pk_kab = pop_khusus.groupby("kab_key", as_index=False).agg({
|
|
|
|
| 677 |
"Target68_Total_Jenis": "sum",
|
| 678 |
"Pop_Total_Jenis": "sum"
|
| 679 |
}).set_index("kab_key")
|
| 680 |
else:
|
| 681 |
+
pk_kab = pd.DataFrame().set_index(pd.Index([]))
|
| 682 |
+
|
| 683 |
+
pk_prov = None
|
| 684 |
|
| 685 |
base = df.groupby([key_col, label_col], dropna=False).agg(
|
| 686 |
n_total=("Indeks_Dasar_0_100", "size"),
|
|
|
|
| 690 |
for _, r in base.iterrows():
|
| 691 |
gk = r["group_key"]
|
| 692 |
|
| 693 |
+
# --- ambil dari POP_KAB/POP_PROV (existing) ---
|
| 694 |
if gk in pop.index:
|
| 695 |
target_total = pop.loc[gk, target_field] if target_field in pop.columns else np.nan
|
| 696 |
pop_total = pop.loc[gk, pop_field] if pop_field in pop.columns else np.nan
|
|
|
|
| 698 |
else:
|
| 699 |
target_total, pop_total, nm = np.nan, np.nan, r[label_name]
|
| 700 |
|
| 701 |
+
# --- TAMBAH KHUSUS (ini inti request kamu) ---
|
| 702 |
add_target, add_pop = 0.0, 0.0
|
| 703 |
if "PROV" in kew_norm:
|
| 704 |
+
if pk_prov is not None and (gk in pk_prov.index):
|
| 705 |
+
add_target = pk_prov.loc[gk, "Target68_Total_Jenis"] if "Target68_Total_Jenis" in pk_prov.columns else 0.0
|
| 706 |
+
add_pop = pk_prov.loc[gk, "Pop_Total_Jenis"] if "Pop_Total_Jenis" in pk_prov.columns else 0.0
|
| 707 |
else:
|
| 708 |
+
if pk_kab is not None and (gk in pk_kab.index):
|
| 709 |
+
add_target = pk_kab.loc[gk, "Target68_Total_Jenis"] if "Target68_Total_Jenis" in pk_kab.columns else 0.0
|
| 710 |
+
add_pop = pk_kab.loc[gk, "Pop_Total_Jenis"] if "Pop_Total_Jenis" in pk_kab.columns else 0.0
|
| 711 |
+
|
| 712 |
+
# amanin numeric
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 713 |
t0 = float(pd.to_numeric(target_total, errors="coerce")) if pd.notna(target_total) else 0.0
|
| 714 |
p0 = float(pd.to_numeric(pop_total, errors="coerce")) if pd.notna(pop_total) else 0.0
|
| 715 |
+
at = float(pd.to_numeric(add_target, errors="coerce")) if pd.notna(add_target) else 0.0
|
| 716 |
+
ap = float(pd.to_numeric(add_pop, errors="coerce")) if pd.notna(add_pop) else 0.0
|
| 717 |
|
| 718 |
+
target_vals.append(t0 + at)
|
| 719 |
+
pop_vals.append(p0 + ap)
|
|
|
|
|
|
|
|
|
|
| 720 |
label_fix.append(nm)
|
| 721 |
|
| 722 |
base[label_name] = label_fix
|
|
|
|
| 727 |
m = base["pop_total"].isna() & base["target_total_68"].notna() & (base["target_total_68"] > 0)
|
| 728 |
base.loc[m, "pop_total"] = base.loc[m, "target_total_68"] / float(FALLBACK_TARGET_RATIO)
|
| 729 |
|
| 730 |
+
# logika faktor TIDAK DIUBAH
|
| 731 |
base["faktor_penyesuaian"] = [
|
| 732 |
faktor_penyesuaian_total(n, t)
|
| 733 |
for n, t in zip(
|
|
|
|
| 744 |
)
|
| 745 |
]
|
| 746 |
|
| 747 |
+
# ====== DISPLAY (sesuai permintaan awal kamu) ======
|
| 748 |
base["target_total_68"] = pd.to_numeric(base["target_total_68"], errors="coerce").fillna(0).round(0).astype(int)
|
| 749 |
base["pop_total"] = pd.to_numeric(base["pop_total"], errors="coerce").fillna(0).round(0).astype(int)
|
| 750 |
base["coverage_total_%"] = pd.to_numeric(base["coverage_total_%"], errors="coerce").fillna(0.0).round(2)
|
|
|
|
| 752 |
|
| 753 |
return base
|
| 754 |
|
|
|
|
| 755 |
# ============================================================
|
| 756 |
# 7) AGREGAT WILAYAH × JENIS (Final pakai faktor wilayah)
|
| 757 |
# ============================================================
|
|
|
|
| 1488 |
# 15) CORE RUN
|
| 1489 |
# ============================================================
|
| 1490 |
|
| 1491 |
+
def _empty_outputs(msg="⚠️ Data belum siap."):
|
| 1492 |
+
empty = pd.DataFrame()
|
| 1493 |
+
empty_fig = go.Figure()
|
| 1494 |
+
return (
|
| 1495 |
+
"", # kpi_md
|
| 1496 |
+
empty, empty, empty, empty, empty,
|
| 1497 |
+
None, None, None, None, None,
|
| 1498 |
+
empty_fig, empty_fig, empty_fig,
|
| 1499 |
+
msg, "Analisis belum tersedia."
|
| 1500 |
+
)
|
| 1501 |
+
|
| 1502 |
def run_calc(prov_value, kab_value, kew_value, df_all, df_raw, pop_kab, pop_prov, pop_khusus, meta):
|
| 1503 |
try:
|
| 1504 |
if df_all is None or df_all.empty or df_raw is None or df_raw.empty:
|
|
|
|
| 1517 |
return _empty_outputs("Tidak ada data untuk filter ini.")
|
| 1518 |
|
| 1519 |
# ==== PIPELINE BARU (KUNCI KONSISTENSI) ====
|
|
|
|
| 1520 |
faktor_wilayah = build_faktor_wilayah(df, pop_kab, pop_prov, pop_khusus, kew_value or "(Semua)")
|
| 1521 |
agg_jenis_full = build_agg_wilayah_jenis(df, faktor_wilayah, pop_khusus, kew_value or "(Semua)")
|
| 1522 |
agg_total = build_agg_wilayah_total_from_jenis(agg_jenis_full, faktor_wilayah, kew_value or "(Semua)")
|
|
|
|
| 1543 |
cols_upto = [c for c in cols_upto if c in agg_jenis_full.columns]
|
| 1544 |
agg_jenis_view = agg_jenis_full[cols_upto].copy()
|
| 1545 |
|
|
|
|
| 1546 |
# FILTER RAW DOWNLOAD (df_raw)
|
| 1547 |
raw = df_raw.copy()
|
| 1548 |
if prov_value and prov_value != "(Semua)":
|