Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1676,23 +1676,26 @@ with col_insights:
|
|
| 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:
|
| 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
|
| 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
|
| 1695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.")
|
|
@@ -1702,13 +1705,16 @@ with col_insights:
|
|
| 1702 |
# 3. Shift Pattern Analysis
|
| 1703 |
if col_shift and col_shift in df.columns:
|
| 1704 |
shift_counts = df[col_shift].value_counts()
|
| 1705 |
-
|
| 1706 |
-
|
| 1707 |
st.markdown(f"**Shift Pattern Risk**")
|
| 1708 |
for shift_val in shift_counts.index:
|
| 1709 |
shift_pct = (shift_counts[shift_val] / len(df)) * 100
|
| 1710 |
-
st.metric
|
| 1711 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1712 |
st.warning(f"Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%). Review shift scheduling and workload.")
|
| 1713 |
else:
|
| 1714 |
st.info(f"Shift {shift_val} alert distribution is acceptable ({shift_pct:.1f}%).")
|
|
@@ -1718,13 +1724,17 @@ with col_insights:
|
|
| 1718 |
# 4. Operator Risk Profiling
|
| 1719 |
if col_operator and col_operator in df.columns:
|
| 1720 |
operator_alerts = df[col_operator].value_counts()
|
| 1721 |
-
top_risk_operators = operator_alerts.head(5)
|
| 1722 |
-
|
| 1723 |
st.markdown(f"**High-Risk Operator Identification**")
|
| 1724 |
for op_name, count in top_risk_operators.items():
|
| 1725 |
op_pct = (count / len(df)) * 100
|
| 1726 |
-
st.metric
|
| 1727 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1728 |
st.warning(f"Operator {op_name} has high fatigue risk ({op_pct:.1f}% of alerts). Consider coaching or rest plan.")
|
| 1729 |
else:
|
| 1730 |
st.info(f"Operator {op_name} fatigue risk is within acceptable range ({op_pct:.1f}%).")
|
|
@@ -1736,7 +1746,7 @@ with col_insights:
|
|
| 1736 |
with col_recs:
|
| 1737 |
st.subheader("Recommendations")
|
| 1738 |
ai_recs = []
|
| 1739 |
-
insights_found = []
|
| 1740 |
|
| 1741 |
# Peak hour
|
| 1742 |
if "hour" in df.columns and not df.empty:
|
|
@@ -1765,7 +1775,6 @@ with col_recs:
|
|
| 1765 |
|
| 1766 |
# Generate recommendations based on found insights
|
| 1767 |
if insights_found:
|
| 1768 |
-
# Contoh rekomendasi berdasarkan insight
|
| 1769 |
if any("circadian low" in i.lower() for i in insights_found):
|
| 1770 |
ai_recs.append({
|
| 1771 |
"recommendation": "Deploy enhanced fatigue monitoring systems (e.g., EOR) specifically during 3-6 AM shifts.",
|
|
@@ -1803,10 +1812,7 @@ with col_recs:
|
|
| 1803 |
"reason": "No specific high-impact insights were automatically identified from the aggregated data in this section."
|
| 1804 |
})
|
| 1805 |
|
| 1806 |
-
# Menampilkan rekomendasi dalam format kotak yang sesuai dengan permintaan
|
| 1807 |
for rec in ai_recs:
|
| 1808 |
-
# Gunakan div dengan class khusus untuk membuat kotak rekomendasi di kolom kanan
|
| 1809 |
-
# Gaya diambil dari .insight-box untuk konsistensi dan menghindari warna ungu
|
| 1810 |
st.markdown(f"""
|
| 1811 |
<div style="
|
| 1812 |
background: #f8f9fa;
|
|
@@ -1847,4 +1853,4 @@ with col_recs:
|
|
| 1847 |
|
| 1848 |
# ================= FOOTER ===========================
|
| 1849 |
st.markdown("---")
|
| 1850 |
-
st.markdown('<div class="footer">FatigueAnalyzer - Transforming Mining Safety with Intelligent Analytics | Contact: info@bukittechnology.com</div>', unsafe_allow_html=True)
|
|
|
|
| 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 |
bg_color = "#ffcccc" if critical_pct > 50 else "#ffebcc" if critical_pct > 25 else "#ffffcc" if critical_pct > 10 else "#e6ffe6"
|
| 1680 |
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)
|
| 1681 |
+
if critical_pct > 10:
|
| 1682 |
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.")
|
| 1683 |
else:
|
| 1684 |
st.info(f"{critical_pct:.1f}% of alerts occur during critical hours. This is within acceptable range.")
|
| 1685 |
|
| 1686 |
# 2. High-Speed Fatigue Analysis (Environmental Risk)
|
| 1687 |
if col_speed and col_speed in df.columns:
|
| 1688 |
+
high_speed_threshold = df[col_speed].quantile(0.75) if not df[col_speed].dropna().empty else 0
|
| 1689 |
high_speed_fatigue = df[df[col_speed] >= high_speed_threshold] if high_speed_threshold > 0 else pd.DataFrame()
|
| 1690 |
high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
|
| 1691 |
|
| 1692 |
st.markdown(f"**High-Speed Fatigue Risk (Speed > {high_speed_threshold:.0f} km/h)**")
|
| 1693 |
+
# Ganti st.metric dengan HTML custom
|
| 1694 |
+
st.markdown(f"""
|
| 1695 |
+
<div style="font-size: 24px; font-weight: bold;">{len(high_speed_fatigue)}</div>
|
| 1696 |
+
<div style="color: red; font-size: 14px; margin-top: -5px;">↑ {high_speed_pct:.1f}% of total alerts</div>
|
| 1697 |
+
""", unsafe_allow_html=True)
|
| 1698 |
+
if high_speed_pct > 20:
|
| 1699 |
st.warning(f"High risk: {high_speed_pct:.1f}% of fatigue alerts occur at high speeds. This increases accident severity potential.")
|
| 1700 |
else:
|
| 1701 |
st.info(f"{high_speed_pct:.1f}% of alerts occur at high speeds. This is within acceptable range.")
|
|
|
|
| 1705 |
# 3. Shift Pattern Analysis
|
| 1706 |
if col_shift and col_shift in df.columns:
|
| 1707 |
shift_counts = df[col_shift].value_counts()
|
| 1708 |
+
|
|
|
|
| 1709 |
st.markdown(f"**Shift Pattern Risk**")
|
| 1710 |
for shift_val in shift_counts.index:
|
| 1711 |
shift_pct = (shift_counts[shift_val] / len(df)) * 100
|
| 1712 |
+
# Ganti st.metric dengan HTML custom
|
| 1713 |
+
st.markdown(f"""
|
| 1714 |
+
<div style="font-size: 24px; font-weight: bold;">{shift_counts[shift_val]}</div>
|
| 1715 |
+
<div style="color: red; font-size: 14px; margin-top: -5px;">↑ {shift_pct:.1f}% of total alerts</div>
|
| 1716 |
+
""", unsafe_allow_html=True)
|
| 1717 |
+
if shift_pct > 50:
|
| 1718 |
st.warning(f"Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%). Review shift scheduling and workload.")
|
| 1719 |
else:
|
| 1720 |
st.info(f"Shift {shift_val} alert distribution is acceptable ({shift_pct:.1f}%).")
|
|
|
|
| 1724 |
# 4. Operator Risk Profiling
|
| 1725 |
if col_operator and col_operator in df.columns:
|
| 1726 |
operator_alerts = df[col_operator].value_counts()
|
| 1727 |
+
top_risk_operators = operator_alerts.head(5)
|
| 1728 |
+
|
| 1729 |
st.markdown(f"**High-Risk Operator Identification**")
|
| 1730 |
for op_name, count in top_risk_operators.items():
|
| 1731 |
op_pct = (count / len(df)) * 100
|
| 1732 |
+
# Ganti st.metric dengan HTML custom
|
| 1733 |
+
st.markdown(f"""
|
| 1734 |
+
<div style="font-size: 24px; font-weight: bold;">{count} alerts</div>
|
| 1735 |
+
<div style="color: red; font-size: 14px; margin-top: -5px;">↑ {op_pct:.1f}% of total alerts</div>
|
| 1736 |
+
""", unsafe_allow_html=True)
|
| 1737 |
+
if op_pct > 5:
|
| 1738 |
st.warning(f"Operator {op_name} has high fatigue risk ({op_pct:.1f}% of alerts). Consider coaching or rest plan.")
|
| 1739 |
else:
|
| 1740 |
st.info(f"Operator {op_name} fatigue risk is within acceptable range ({op_pct:.1f}%).")
|
|
|
|
| 1746 |
with col_recs:
|
| 1747 |
st.subheader("Recommendations")
|
| 1748 |
ai_recs = []
|
| 1749 |
+
insights_found = []
|
| 1750 |
|
| 1751 |
# Peak hour
|
| 1752 |
if "hour" in df.columns and not df.empty:
|
|
|
|
| 1775 |
|
| 1776 |
# Generate recommendations based on found insights
|
| 1777 |
if insights_found:
|
|
|
|
| 1778 |
if any("circadian low" in i.lower() for i in insights_found):
|
| 1779 |
ai_recs.append({
|
| 1780 |
"recommendation": "Deploy enhanced fatigue monitoring systems (e.g., EOR) specifically during 3-6 AM shifts.",
|
|
|
|
| 1812 |
"reason": "No specific high-impact insights were automatically identified from the aggregated data in this section."
|
| 1813 |
})
|
| 1814 |
|
|
|
|
| 1815 |
for rec in ai_recs:
|
|
|
|
|
|
|
| 1816 |
st.markdown(f"""
|
| 1817 |
<div style="
|
| 1818 |
background: #f8f9fa;
|
|
|
|
| 1853 |
|
| 1854 |
# ================= FOOTER ===========================
|
| 1855 |
st.markdown("---")
|
| 1856 |
+
st.markdown('<div class="footer">FatigueAnalyzer - Transforming Mining Safety with Intelligent Analytics | Contact: info@bukittechnology.com</div>', unsafe_allow_html=True)
|