SHELLAPANDIANGANHUNGING commited on
Commit
30d50ea
·
verified ·
1 Parent(s): dcac95d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +11 -89
app.py CHANGED
@@ -1265,8 +1265,13 @@ except Exception as e:
1265
  # ✅ Panel 3: ONLY Coverage = 100% AND Slope > 0 → Avg/Month
1266
  # ✅ Estetik: Sortable, Hover, Zebra, PLN Blue, No Emoticons
1267
 
1268
- st.markdown("<h3 class='section-title'>OBJECTIVE 6 - Predictive Dashboard & Early Warning Signals</h3>", unsafe_allow_html=True)
 
 
 
1269
 
 
 
1270
 
1271
  # ✅ Enhanced CSS + Minimal Sortable JS
1272
  st.markdown("""
@@ -1404,7 +1409,6 @@ function makeSortable(tableId) {
1404
  }
1405
  setTimeout(() => {
1406
  makeSortable('tbl-locations');
1407
- makeSortable('tbl-divisions');
1408
  makeSortable('tbl-issues');
1409
  }, 800);
1410
  </script>
@@ -1473,58 +1477,14 @@ def predict_locations(df):
1473
  df_res = pd.DataFrame(results)
1474
  return df_res.sort_values('Trend Slope', ascending=True) if not df_res.empty else df_res # most negative first
1475
 
1476
- # # ——————— 2. Divisions ———————
1477
- # def predict_divisions(df):
1478
- # if 'nama' not in df.columns:
1479
- # return pd.DataFrame()
1480
-
1481
- # start_month = df['created_at'].min().to_period('M')
1482
- # end_month = df['created_at'].max().to_period('M')
1483
- # all_months = pd.period_range(start=start_month, end=end_month, freq='M')
1484
-
1485
- # df_monthly = (
1486
- # df.groupby(['nama', df['created_at'].dt.to_period('M')])
1487
- # .size()
1488
- # .unstack(fill_value=0)
1489
- # .reindex(columns=all_months, fill_value=0)
1490
- # .stack()
1491
- # .reset_index(name='count')
1492
- # )
1493
- # df_monthly.columns = ['Division', 'Month', 'Count']
1494
-
1495
- # results = []
1496
- # for div, group in df_monthly.groupby('Division'):
1497
- # ts = group.set_index('Month')['Count']
1498
- # total = len(all_months)
1499
- # active = (ts > 0).sum()
1500
- # gaps = total - active
1501
- # coverage = active / total if total > 0 else 0
1502
-
1503
- # if gaps > 2:
1504
- # status = "<span class='status-inactive'>Inactive</span>"
1505
- # elif gaps == 0:
1506
- # status = "<span class='status-active'>Active</span>"
1507
- # else:
1508
- # status = "<span class='status-neutral'>Neutral</span>"
1509
-
1510
- # bar = ''.join(['●' if c > 0 else '○' for c in ts.values])
1511
- # trend_line = f"<span class='spark' style='color:#003DA5;'>{bar}</span>"
1512
- # results.append({
1513
- # 'Division': div,
1514
- # 'Active Months': int(active),
1515
- # 'Total Months': int(total),
1516
- # 'Coverage (%)': round(coverage * 100, 1),
1517
- # 'Status': status,
1518
- # 'Trend': trend_line
1519
- # })
1520
- # df_res = pd.DataFrame(results)
1521
- # return df_res.sort_values('Coverage (%)', ascending=True) if not df_res.empty else df_res
1522
-
1523
  # ——————— 3. Issues: ONLY Coverage=100% & Trend Slope > 0 → Avg/Month ———————
1524
  def predict_issues(df):
1525
  if 'kategori' not in df.columns or df.empty:
1526
  return pd.DataFrame()
1527
 
 
 
 
1528
  start_month = df['created_at'].min().to_period('M')
1529
  end_month = df['created_at'].max().to_period('M')
1530
  all_months = pd.period_range(start=start_month, end=end_month, freq='M')
@@ -1576,7 +1536,6 @@ def predict_issues(df):
1576
 
1577
  # ——————— RUN ———————
1578
  df_loc = predict_locations(df_filtered)
1579
- df_div = predict_divisions(df_filtered)
1580
  df_issue = predict_issues(df_filtered)
1581
 
1582
  # 🎯 PANEL 1: Locations (FILTERED: Coverage < 90% & Slope < 0)
@@ -1615,46 +1574,11 @@ if not df_loc.empty:
1615
  # )
1616
  st.markdown("</div>", unsafe_allow_html=True)
1617
 
1618
- # # 🎯 PANEL 2: Divisions
1619
- # st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
1620
- # st.markdown("<div class='predictive-header'>2. Which Areas Are Likely to Be Frequently or Rarely Inspected?</div>", unsafe_allow_html=True)
1621
- # if not df_div.empty:
1622
- # cols = ['Division', 'Active Months', 'Total Months', 'Coverage (%)', 'Status', 'Trend']
1623
-
1624
- # # 🔵 Rename ONLY for display (Status → Forecast Inspection)
1625
- # df_display = df_div[cols].rename(columns={
1626
- # "Status": "Forecast Inspection"
1627
- # })
1628
-
1629
- # html = df_display.to_html(escape=False, index=False, table_id="tbl-divisions")
1630
- # st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
1631
-
1632
- # # st.markdown(
1633
- # # "<div class='predictive-note'>"
1634
- # # "<strong>Forecast Inspection:</strong> "
1635
- # # "<span class='status-active'>Active</span> (0 gaps), "
1636
- # # "<span class='status-neutral'>Neutral</span> (1–2 gaps), "
1637
- # # "<span class='status-inactive'>Inactive</span> (>2 gaps)."
1638
- # # "</div>",
1639
- # # unsafe_allow_html=True
1640
- # # )
1641
- # # else:
1642
- # # st.markdown(
1643
- # # "<div class='predictive-table-wrapper'>"
1644
- # # "<p style='text-align:center; color:#666; padding:24px; font-style:italic;'>"
1645
- # # "Insufficient division data (≥2 months required)."
1646
- # # "</p></div>",
1647
- # # unsafe_allow_html=True
1648
- # # )
1649
-
1650
- # st.markdown("</div>", unsafe_allow_html=True)
1651
-
1652
-
1653
- # 🎯 PANEL 3: Issues (FILTERED: Coverage=100% & Rising)
1654
  st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
1655
  st.markdown(
1656
  "<div class='predictive-header'>"
1657
- "3. Which Issue Categories Are Likely to Appear in the Next 3 Months"
1658
  "<span style='font-size:0.75em; font-weight:400; color:#003DA5;'>"
1659
  " &nbsp;&nbsp;(* Categorization uses NLP — Natural Language Processing from random text)"
1660
  "</span>"
@@ -1696,8 +1620,6 @@ if not df_issue.empty:
1696
 
1697
  st.markdown("</div>", unsafe_allow_html=True)
1698
 
1699
-
1700
-
1701
  # =================== 6. ✅ AI INSIGHT ENGINE (BARU - BERDASARKAN DATA & RATIO) ===================
1702
 
1703
  st.markdown("<h3 class='section-title'>OBJECTIVE 7 - Insight and Recommendation</h3>", unsafe_allow_html=True)
 
1265
  # ✅ Panel 3: ONLY Coverage = 100% AND Slope > 0 → Avg/Month
1266
  # ✅ Estetik: Sortable, Hover, Zebra, PLN Blue, No Emoticons
1267
 
1268
+ import streamlit as st
1269
+ import plotly.graph_objects as go
1270
+ import numpy as np
1271
+ import pandas as pd
1272
 
1273
+ # =================== OBJECTIVE 6 - Predictive Dashboard & Early Warning Signals ===================
1274
+ st.markdown("<h3 class='section-title'>OBJECTIVE 6 — Predictive Dashboard & Early Warning Signals</h3>", unsafe_allow_html=True)
1275
 
1276
  # ✅ Enhanced CSS + Minimal Sortable JS
1277
  st.markdown("""
 
1409
  }
1410
  setTimeout(() => {
1411
  makeSortable('tbl-locations');
 
1412
  makeSortable('tbl-issues');
1413
  }, 800);
1414
  </script>
 
1477
  df_res = pd.DataFrame(results)
1478
  return df_res.sort_values('Trend Slope', ascending=True) if not df_res.empty else df_res # most negative first
1479
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1480
  # ——————— 3. Issues: ONLY Coverage=100% & Trend Slope > 0 → Avg/Month ———————
1481
  def predict_issues(df):
1482
  if 'kategori' not in df.columns or df.empty:
1483
  return pd.DataFrame()
1484
 
1485
+ # 🔥 Filter: Hanya yang bukan 'Positive'
1486
+ df = df[df['kategori'] != 'Positive'].copy() # ✅ Filter non-Positive
1487
+
1488
  start_month = df['created_at'].min().to_period('M')
1489
  end_month = df['created_at'].max().to_period('M')
1490
  all_months = pd.period_range(start=start_month, end=end_month, freq='M')
 
1536
 
1537
  # ——————— RUN ———————
1538
  df_loc = predict_locations(df_filtered)
 
1539
  df_issue = predict_issues(df_filtered)
1540
 
1541
  # 🎯 PANEL 1: Locations (FILTERED: Coverage < 90% & Slope < 0)
 
1574
  # )
1575
  st.markdown("</div>", unsafe_allow_html=True)
1576
 
1577
+ # 🎯 PANEL 3: Issues (FILTERED: Coverage=100% & Rising) — Hanya Non-Positive
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1578
  st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
1579
  st.markdown(
1580
  "<div class='predictive-header'>"
1581
+ "3. Which Issue Categories Are Likely to Appear in the Next 3 Months (Non-Positive Only)"
1582
  "<span style='font-size:0.75em; font-weight:400; color:#003DA5;'>"
1583
  " &nbsp;&nbsp;(* Categorization uses NLP — Natural Language Processing from random text)"
1584
  "</span>"
 
1620
 
1621
  st.markdown("</div>", unsafe_allow_html=True)
1622
 
 
 
1623
  # =================== 6. ✅ AI INSIGHT ENGINE (BARU - BERDASARKAN DATA & RATIO) ===================
1624
 
1625
  st.markdown("<h3 class='section-title'>OBJECTIVE 7 - Insight and Recommendation</h3>", unsafe_allow_html=True)