SHELLAPANDIANGANHUNGING commited on
Commit
4aedee4
·
verified ·
1 Parent(s): 208a8d1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +23 -66
app.py CHANGED
@@ -334,9 +334,8 @@ with st.container():
334
  with col2:
335
  st.markdown("""
336
  <div class="header-title">
337
- <h1 style="margin-bottom:6px;">PLN Audit Insight & Intelligence Dashboard</h1>
338
  <p style="color:#546e7a; font-size:1.05em; margin-top:-8px;">
339
- Operational Risk Intelligence for Audit & Compliance
340
  </p>
341
  </div>
342
  """, unsafe_allow_html=True)
@@ -1264,7 +1263,6 @@ except Exception as e:
1264
  # ✅ Panel 1: ONLY Coverage < 90% AND Slope < 0
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
@@ -1408,7 +1406,7 @@ function makeSortable(tableId) {
1408
  });
1409
  }
1410
  setTimeout(() => {
1411
- makeSortable('tbl-locations');
1412
  makeSortable('tbl-issues');
1413
  }, 800);
1414
  </script>
@@ -1431,9 +1429,12 @@ def ascii_sparkline_pln(data):
1431
  except:
1432
  return "<span class='spark' style='color:#999;'>▁▁▁</span>"
1433
 
1434
- # ——————— 1. Locations: ONLY Coverage < 90% AND Slope < 0 ———————
1435
- def predict_locations(df):
1436
- if 'nama_lokasi_full' not in df.columns or df.empty:
 
 
 
1437
  return pd.DataFrame()
1438
 
1439
  start_month = df['created_at'].min().to_period('M')
@@ -1441,17 +1442,17 @@ def predict_locations(df):
1441
  all_months = pd.period_range(start=start_month, end=end_month, freq='M')
1442
 
1443
  df_monthly = (
1444
- df.groupby(['nama_lokasi_full', df['created_at'].dt.to_period('M')])
1445
  .size()
1446
  .unstack(fill_value=0)
1447
  .reindex(columns=all_months, fill_value=0)
1448
  .stack()
1449
  .reset_index(name='count')
1450
  )
1451
- df_monthly.columns = ['Location', 'Month', 'Count']
1452
 
1453
  results = []
1454
- for lokasi, group in df_monthly.groupby('Location'):
1455
  ts = group.set_index('Month')['Count']
1456
  total = len(all_months)
1457
  active = (ts > 0).sum()
@@ -1465,7 +1466,7 @@ def predict_locations(df):
1465
  if slope < 0 and coverage < 0.9:
1466
  reason = f"Slope = {slope:.3f}, Coverage = {coverage*100:.1f}%. Avg: {avg_rate:.2f}/mo."
1467
  results.append({
1468
- 'Location': lokasi,
1469
  'Avg Reports/Month': round(avg_rate, 2),
1470
  'Coverage (%)': round(coverage * 100, 1),
1471
  'Trend Slope': round(slope, 3),
@@ -1535,21 +1536,21 @@ def predict_issues(df):
1535
  return df_res.reset_index(drop=True)
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)
1542
  st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
1543
- st.markdown("<div class='predictive-header'>1. Which areas are predicted to have no future inspections?</div>", unsafe_allow_html=True)
1544
- if not df_loc.empty:
1545
- cols = ['Location', 'Avg Reports/Month', 'Coverage (%)', 'Trend Slope', 'Trend', 'Reason']
1546
 
1547
  # 🔥 Rename hanya untuk DISPLAY, bukan data asli
1548
- df_display = df_loc[cols].rename(columns={
1549
  "Reason": "Reason Forecast"
1550
  })
1551
 
1552
- html = df_display.to_html(escape=False, index=False, table_id="tbl-locations")
1553
  st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
1554
 
1555
  # st.markdown(
@@ -1564,65 +1565,20 @@ if not df_loc.empty:
1564
  # st.markdown(
1565
  # "<div class='predictive-table-wrapper'>"
1566
  # "<p style='text-align:center; color:#666; padding:24px; font-style:italic;'>"
1567
- # "No locations meet criteria: Coverage < 90% and negative trend."
1568
  # "</p>"
1569
  # "<div class='warning-box'>"
1570
- # "💡 Note: Locations with Coverage ≥ 90% are excluded — they are considered stable reporting zones."
1571
  # "</div>"
1572
  # "</div>",
1573
  # unsafe_allow_html=True
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>"
1585
- "</div>",
1586
- unsafe_allow_html=True
1587
- )
1588
-
1589
- if not df_issue.empty:
1590
- cols = ['Category', 'Avg/Month', 'Coverage (%)', 'Trend Slope', 'Status', 'Trend']
1591
-
1592
- # 🔵 Rename ONLY for display
1593
- df_display = df_issue[cols].rename(columns={
1594
- "Status": "Status Issue for Next Month"
1595
- })
1596
-
1597
- html = df_display.to_html(escape=False, index=False, table_id="tbl-issues")
1598
- st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
1599
-
1600
- # st.markdown(
1601
- # "<div class='predictive-note'>"
1602
- # "<strong>Filtered:</strong> Reported every month (100% coverage) with increasing trend. "
1603
- # "<strong>Avg/Month</strong> = total ÷ months. "
1604
- # "<span class='trend-rising'>High-Risk Rising</span> = slope > 0.2."
1605
- # "</div>",
1606
- # unsafe_allow_html=True
1607
- # )
1608
-
1609
- # else:
1610
- # st.markdown(
1611
- # "<div class='predictive-table-wrapper'>"
1612
- # "<p style='text-align:center; color:#c62828; padding:24px; font-weight:500;'>"
1613
- # "⚠️ No rising categories with 100% monthly coverage."
1614
- # "</p>"
1615
- # "<p style='text-align:center; color:#666; font-size:0.9em;'>"
1616
- # "Consider relaxing coverage filter if data is sparse."
1617
- # "</p></div>",
1618
- # unsafe_allow_html=True
1619
- # )
1620
-
1621
- st.markdown("</div>", unsafe_allow_html=True)
1622
 
1623
 
1624
  # =================== WHITEBOARD STYLE CHART FOR PANEL 3 ===================
1625
- st.markdown("<h4 style='text-align: center; color: #2c3e50;'>Whiteboard Insight: Trend vs Frequency</h4>", unsafe_allow_html=True)
1626
 
1627
  # Buat chart scatter dengan gaya whiteboard
1628
  if not df_issue.empty:
@@ -1726,6 +1682,7 @@ if not df_issue.empty:
1726
  st.markdown(insight_text, unsafe_allow_html=True)
1727
  else:
1728
  st.info("No data available for non-positive issues with 100% coverage and positive trend.")
 
1729
  # =================== 6. ✅ AI INSIGHT ENGINE (BARU - BERDASARKAN DATA & RATIO) ===================
1730
 
1731
  st.markdown("<h3 class='section-title'>OBJECTIVE 7 - Insight and Recommendation</h3>", unsafe_allow_html=True)
 
334
  with col2:
335
  st.markdown("""
336
  <div class="header-title">
337
+ <h1 style="margin-bottom:6px;">Proactive Safety Intelligence & Analytics Dashboard</h1>
338
  <p style="color:#546e7a; font-size:1.05em; margin-top:-8px;">
 
339
  </p>
340
  </div>
341
  """, unsafe_allow_html=True)
 
1263
  # ✅ Panel 1: ONLY Coverage < 90% AND Slope < 0
1264
  # ✅ Panel 3: ONLY Coverage = 100% AND Slope > 0 → Avg/Month
1265
  # ✅ Estetik: Sortable, Hover, Zebra, PLN Blue, No Emoticons
 
1266
  import streamlit as st
1267
  import plotly.graph_objects as go
1268
  import numpy as np
 
1406
  });
1407
  }
1408
  setTimeout(() => {
1409
+ makeSortable('tbl-creators');
1410
  makeSortable('tbl-issues');
1411
  }, 800);
1412
  </script>
 
1429
  except:
1430
  return "<span class='spark' style='color:#999;'>▁▁▁</span>"
1431
 
1432
+ # ——————— 1. Creators: ONLY Coverage < 90% AND Slope < 0 (Non-Positive Only) ———————
1433
+ def predict_creators(df):
1434
+ # 🔥 Filter: Hanya yang bukan 'Positive'
1435
+ df = df[df['temuan_kategori'] != 'Positive'].copy() # ✅ Filter non-Positive
1436
+
1437
+ if 'creator_name' not in df.columns or df.empty:
1438
  return pd.DataFrame()
1439
 
1440
  start_month = df['created_at'].min().to_period('M')
 
1442
  all_months = pd.period_range(start=start_month, end=end_month, freq='M')
1443
 
1444
  df_monthly = (
1445
+ df.groupby(['creator_name', df['created_at'].dt.to_period('M')])
1446
  .size()
1447
  .unstack(fill_value=0)
1448
  .reindex(columns=all_months, fill_value=0)
1449
  .stack()
1450
  .reset_index(name='count')
1451
  )
1452
+ df_monthly.columns = ['Creator', 'Month', 'Count']
1453
 
1454
  results = []
1455
+ for creator, group in df_monthly.groupby('Creator'):
1456
  ts = group.set_index('Month')['Count']
1457
  total = len(all_months)
1458
  active = (ts > 0).sum()
 
1466
  if slope < 0 and coverage < 0.9:
1467
  reason = f"Slope = {slope:.3f}, Coverage = {coverage*100:.1f}%. Avg: {avg_rate:.2f}/mo."
1468
  results.append({
1469
+ 'Creator': creator,
1470
  'Avg Reports/Month': round(avg_rate, 2),
1471
  'Coverage (%)': round(coverage * 100, 1),
1472
  'Trend Slope': round(slope, 3),
 
1536
  return df_res.reset_index(drop=True)
1537
 
1538
  # ——————— RUN ———————
1539
+ df_creator = predict_creators(df_filtered)
1540
  df_issue = predict_issues(df_filtered)
1541
 
1542
+ # 🎯 PANEL 1: Creators (FILTERED: Coverage < 90% & Slope < 0) — Non-Positive Only
1543
  st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
1544
+ st.markdown("<div class='predictive-header'>1. Which Reporters Are Predicted to Have No Future Inspections? (Non-Positive Only)</div>", unsafe_allow_html=True)
1545
+ if not df_creator.empty:
1546
+ cols = ['Creator', 'Avg Reports/Month', 'Coverage (%)', 'Trend Slope', 'Trend', 'Reason']
1547
 
1548
  # 🔥 Rename hanya untuk DISPLAY, bukan data asli
1549
+ df_display = df_creator[cols].rename(columns={
1550
  "Reason": "Reason Forecast"
1551
  })
1552
 
1553
+ html = df_display.to_html(escape=False, index=False, table_id="tbl-creators")
1554
  st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
1555
 
1556
  # st.markdown(
 
1565
  # st.markdown(
1566
  # "<div class='predictive-table-wrapper'>"
1567
  # "<p style='text-align:center; color:#666; padding:24px; font-style:italic;'>"
1568
+ # "No creators meet criteria: Coverage < 90% and negative trend."
1569
  # "</p>"
1570
  # "<div class='warning-box'>"
1571
+ # "💡 Note: Creators with Coverage ≥ 90% are excluded — they are considered stable reporters."
1572
  # "</div>"
1573
  # "</div>",
1574
  # unsafe_allow_html=True
1575
  # )
1576
  st.markdown("</div>", unsafe_allow_html=True)
1577
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1578
 
1579
 
1580
  # =================== WHITEBOARD STYLE CHART FOR PANEL 3 ===================
1581
+ st.markdown("<h4 style='text-align: center; color: #2c3e50;'>2. Which Issue Categories Are Likely to Appear in the Next 3 Months</h4>", unsafe_allow_html=True)
1582
 
1583
  # Buat chart scatter dengan gaya whiteboard
1584
  if not df_issue.empty:
 
1682
  st.markdown(insight_text, unsafe_allow_html=True)
1683
  else:
1684
  st.info("No data available for non-positive issues with 100% coverage and positive trend.")
1685
+
1686
  # =================== 6. ✅ AI INSIGHT ENGINE (BARU - BERDASARKAN DATA & RATIO) ===================
1687
 
1688
  st.markdown("<h3 class='section-title'>OBJECTIVE 7 - Insight and Recommendation</h3>", unsafe_allow_html=True)