SHELLAPANDIANGANHUNGING commited on
Commit
41cdbc8
·
verified ·
1 Parent(s): 2c7e819

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -71
app.py CHANGED
@@ -1663,106 +1663,122 @@ else:
1663
  # =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
1664
  st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
1665
 
1666
- # Membagi tampilan menjadi dua kolom
1667
  col_insights, col_recs = st.columns(2)
1668
 
1669
- # Kolom kiri: Insights by Advanced Analytics
 
 
1670
  with col_insights:
1671
  st.subheader("Insights by Advanced Analytics")
1672
 
1673
- # 1. Critical Hour Analysis (2-5 AM)
1674
  critical_hours = [2, 3, 4, 5]
1675
  critical_alerts = df[df['hour'].isin(critical_hours)]
1676
  critical_pct = (len(critical_alerts) / len(df)) * 100 if len(df) > 0 else 0
1677
 
1678
- st.markdown(f"**Critical Hour Risk (3-6 AM)**")
1679
- # Use conditional formatting for background color
1680
- bg_color = "#ffcccc" if critical_pct > 50 else "#ffebcc" if critical_pct > 25 else "#ffffcc" if critical_pct > 10 else "#e6ffe6"
1681
- st.markdown(f'<div style="background-color: {bg_color}; padding: 10px; border-radius: 5px;">Critical Hour Alerts: {len(critical_alerts)} ({critical_pct:.1f}% of total alerts)</div>', unsafe_allow_html=True)
1682
- if critical_pct > 10: # If more than 10% of alerts happen in critical hours
1683
- st.warning(f"High risk: {critical_pct:.1f}% of fatigue alerts occur during critical hours (3-6 AM). This is a known circadian dip period.")
 
 
 
 
 
 
 
 
 
1684
  else:
1685
- st.info(f"{critical_pct:.1f}% of alerts occur during critical hours. This is within acceptable range.")
1686
 
1687
- # 2. High-Speed Fatigue Analysis (Environmental Risk)
1688
  if col_speed and col_speed in df.columns:
1689
- high_speed_threshold = df[col_speed].quantile(0.75) if not df[col_speed].dropna().empty else 0 # Handle empty series
1690
- high_speed_fatigue = df[df[col_speed] >= high_speed_threshold] if high_speed_threshold > 0 else pd.DataFrame()
1691
- high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
1692
-
1693
  st.markdown(f"**High-Speed Fatigue Risk (Speed > {high_speed_threshold:.0f} km/h)**")
1694
  st.metric("High-Speed Fatigue Events", f"{len(high_speed_fatigue)}", f"{high_speed_pct:.1f}% of total alerts")
1695
- if high_speed_pct > 20: # If more than 20% of alerts happen at high speed
1696
- st.warning(f"High risk: {high_speed_pct:.1f}% of fatigue alerts occur at high speeds. This increases accident severity potential.")
 
1697
  else:
1698
- st.info(f"{high_speed_pct:.1f}% of alerts occur at high speeds. This is within acceptable range.")
1699
  else:
1700
  st.info("Speed data not available for High-Speed Fatigue Analysis.")
1701
 
1702
- # 3. Shift Pattern Analysis
1703
- if col_shift and col_shift in df.columns:
1704
- shift_counts = df[col_shift].value_counts()
1705
- st.markdown("**Shift Pattern Risk**")
1706
-
1707
- shift_color = "#d32f2f" # merah gelap
1708
-
1709
- for shift_val in shift_counts.index:
1710
- shift_pct = (shift_counts[shift_val] / len(df)) * 100
1711
-
1712
- st.markdown(
1713
- f"""
1714
- <div style="padding:12px 16px; border-radius:10px; background-color:#fff; border:1px solid #eee; margin-bottom:10px;">
1715
- <div style="font-size:16px; font-weight:600; color:inherit;">Shift {shift_val} Alerts</div>
1716
- <div style="font-size:28px; font-weight:700; color:inherit;">{shift_counts[shift_val]}</div>
1717
- <div style="font-size:14px; font-weight:700;">
1718
- <span style="color:{shift_color};">{shift_pct:.1f}% of total alerts</span>
1719
- </div>
1720
- </div>
1721
- """,
1722
- unsafe_allow_html=True
1723
- )
1724
 
1725
- if shift_pct > 50:
1726
- st.warning(f"Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%). Review shift scheduling and workload.")
1727
- else:
1728
- st.info(f"Shift {shift_val} alert distribution is acceptable ({shift_pct:.1f}%).")
1729
- else:
1730
- st.info("Shift data not available for Shift Pattern Analysis.")
1731
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1732
 
 
 
 
 
 
 
1733
 
1734
- # 4. Operator Risk Profiling
1735
- if col_operator and col_operator in df.columns:
1736
- operator_alerts = df[col_operator].value_counts()
1737
- top_risk_operators = operator_alerts.head(5) # Top 5 operators by alerts
1738
 
1739
- st.markdown("**High-Risk Operator Identification**")
1740
 
1741
- # Warna berdasarkan ranking 1–5
1742
- colors = ["#d32f2f", "#e57373", "#ef9a9a", "#ffcdd2", "#ffe1e4"]
1743
 
1744
- for idx, (op_name, count) in enumerate(top_risk_operators.items()):
1745
- op_pct = (count / len(df)) * 100
1746
- color = colors[idx] if idx < len(colors) else colors[-1]
1747
 
1748
- # Teks normal untuk nama dan jumlah alert
1749
- st.markdown(f"**Operator:** {op_name} \n**Alerts:** {count}")
1750
 
1751
- # Hanya 'Share' yang berwarna sesuai ranking
1752
- st.markdown(
1753
- f"<span style='font-weight:600'>Share:</span> "
1754
- f"<span style='color:{color}; font-weight:700'>{op_pct:.1f}% of total alerts</span>",
1755
- unsafe_allow_html=True
1756
- )
1757
 
1758
- # Risk message (tetap gunakan component Streamlit agar konsisten)
1759
- if op_pct > 5:
1760
- st.warning(f"Operator {op_name} has high fatigue risk ({op_pct:.1f}%). Consider coaching or rest plan.")
1761
- else:
1762
- st.info(f"Operator {op_name} fatigue risk is within acceptable range ({op_pct:.1f}%).")
 
1763
 
1764
- else:
1765
- st.info("Operator data not available for Operator Risk Profiling.")
1766
 
1767
 
1768
  # Kolom kanan: AI Recommendations
 
1663
  # =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
1664
  st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
1665
 
1666
+ # Membagi menjadi 2 kolom
1667
  col_insights, col_recs = st.columns(2)
1668
 
1669
+ # ============================
1670
+ # KIRI — INSIGHTS
1671
+ # ============================
1672
  with col_insights:
1673
  st.subheader("Insights by Advanced Analytics")
1674
 
1675
+ # 1. Critical Hour Analysis
1676
  critical_hours = [2, 3, 4, 5]
1677
  critical_alerts = df[df['hour'].isin(critical_hours)]
1678
  critical_pct = (len(critical_alerts) / len(df)) * 100 if len(df) > 0 else 0
1679
 
1680
+ st.markdown("**Critical Hour Risk (36 AM)**")
1681
+ bg_color = (
1682
+ "#ffcccc" if critical_pct > 50 else
1683
+ "#ffebcc" if critical_pct > 25 else
1684
+ "#ffffcc" if critical_pct > 10 else "#e6ffe6"
1685
+ )
1686
+ st.markdown(
1687
+ f'<div style="background-color:{bg_color}; padding:10px; border-radius:5px;">'
1688
+ f'Critical Hour Alerts: {len(critical_alerts)} ({critical_pct:.1f}% of total alerts)'
1689
+ f'</div>',
1690
+ unsafe_allow_html=True
1691
+ )
1692
+
1693
+ if critical_pct > 10:
1694
+ st.warning(f"High risk: {critical_pct:.1f}% of fatigue alerts occur during critical hours.")
1695
  else:
1696
+ st.info(f"{critical_pct:.1f}% of alerts occur during critical hours. Within acceptable range.")
1697
 
1698
+ # 2. High-Speed Fatigue Analysis
1699
  if col_speed and col_speed in df.columns:
1700
+ high_speed_threshold = df[col_speed].quantile(0.75)
1701
+ high_speed_fatigue = df[df[col_speed] >= high_speed_threshold]
1702
+ high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100
1703
+
1704
  st.markdown(f"**High-Speed Fatigue Risk (Speed > {high_speed_threshold:.0f} km/h)**")
1705
  st.metric("High-Speed Fatigue Events", f"{len(high_speed_fatigue)}", f"{high_speed_pct:.1f}% of total alerts")
1706
+
1707
+ if high_speed_pct > 20:
1708
+ st.warning(f"High risk: {high_speed_pct:.1f}% of alerts occur at high speeds.")
1709
  else:
1710
+ st.info(f"{high_speed_pct:.1f}% occur at high speeds. Acceptable.")
1711
  else:
1712
  st.info("Speed data not available for High-Speed Fatigue Analysis.")
1713
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1714
 
1715
+ # ============================
1716
+ # KANAN RECOMMENDATIONS
1717
+ # ============================
1718
+ with col_recs:
1719
+ st.subheader("AI-Driven Recommendations")
 
1720
 
1721
+ # 3. Shift Pattern Analysis
1722
+ if col_shift and col_shift in df.columns:
1723
+ shift_counts = df[col_shift].value_counts()
1724
+ st.markdown("**Shift Pattern Risk**")
1725
+
1726
+ shift_color = "#d32f2f"
1727
+
1728
+ for shift_val in shift_counts.index:
1729
+ shift_pct = (shift_counts[shift_val] / len(df)) * 100
1730
+
1731
+ st.markdown(
1732
+ f"""
1733
+ <div style="padding:12px 16px; border-radius:10px; background-color:#fff;
1734
+ border:1px solid #eee; margin-bottom:10px;">
1735
+ <div style="font-size:16px; font-weight:600;">Shift {shift_val} Alerts</div>
1736
+ <div style="font-size:28px; font-weight:700;">{shift_counts[shift_val]}</div>
1737
+ <div style="font-size:14px; font-weight:700;">
1738
+ <span style="color:{shift_color};">{shift_pct:.1f}% of total alerts</span>
1739
+ </div>
1740
+ </div>
1741
+ """,
1742
+ unsafe_allow_html=True
1743
+ )
1744
 
1745
+ if shift_pct > 50:
1746
+ st.warning(f"Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%).")
1747
+ else:
1748
+ st.info(f"Shift {shift_val} distribution is acceptable ({shift_pct:.1f}%).")
1749
+ else:
1750
+ st.info("Shift data not available for Shift Pattern Analysis.")
1751
 
1752
+ # 4. Operator Risk Profiling
1753
+ if col_operator and col_operator in df.columns:
1754
+ operator_alerts = df[col_operator].value_counts()
1755
+ top_risk_operators = operator_alerts.head(5)
1756
 
1757
+ st.markdown("**High-Risk Operator Identification**")
1758
 
1759
+ # Warna ranking
1760
+ colors = ["#d32f2f", "#e57373", "#ef9a9a", "#ffcdd2", "#ffe1e4"]
1761
 
1762
+ for idx, (op_name, count) in enumerate(top_risk_operators.items()):
1763
+ op_pct = (count / len(df)) * 100
1764
+ color = colors[idx] if idx < len(colors) else colors[-1]
1765
 
1766
+ # Nama & jumlah
1767
+ st.markdown(f"**Operator:** {op_name} \n**Alerts:** {count}")
1768
 
1769
+ # Share berwarna
1770
+ st.markdown(
1771
+ f"<b>Share:</b> <span style='color:{color}; font-weight:700'>{op_pct:.1f}% of total alerts</span>",
1772
+ unsafe_allow_html=True
1773
+ )
 
1774
 
1775
+ if op_pct > 5:
1776
+ st.warning(f"Operator {op_name} shows high fatigue tendency ({op_pct:.1f}%).")
1777
+ else:
1778
+ st.info(f"Operator {op_name} is within the expected alert range ({op_pct:.1f}%).")
1779
+ else:
1780
+ st.info("Operator data not available for Operator Risk Profiling.")
1781
 
 
 
1782
 
1783
 
1784
  # Kolom kanan: AI Recommendations