Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1678,34 +1678,11 @@ with col_insights:
|
|
| 1678 |
|
| 1679 |
st.markdown(f"**Critical Hour Risk (3-6 AM)**")
|
| 1680 |
bg_color = "#ffcccc" if critical_pct > 50 else "#ffebcc" if critical_pct > 25 else "#ffffcc" if critical_pct > 10 else "#e6ffe6"
|
| 1681 |
-
|
| 1682 |
-
# Tampilkan jumlah dan persentase dalam satu div
|
| 1683 |
-
st.markdown(
|
| 1684 |
-
f"""
|
| 1685 |
-
<div style="background-color: {bg_color}; padding: 10px; border-radius: 8px; margin-bottom: 10px;">
|
| 1686 |
-
<div style="font-size: 24px; font-weight: bold; color: #000000;">
|
| 1687 |
-
{len(critical_alerts)}
|
| 1688 |
-
</div>
|
| 1689 |
-
<div style="color: #d32f2f; font-size: 16px; font-weight: bold; margin-top: -10px;">
|
| 1690 |
-
↑ {critical_pct:.1f}% of total alerts
|
| 1691 |
-
</div>
|
| 1692 |
-
</div>
|
| 1693 |
-
""",
|
| 1694 |
-
unsafe_allow_html=True
|
| 1695 |
-
)
|
| 1696 |
-
|
| 1697 |
-
# Gunakan st.markdown untuk warning/info dengan persentase berwarna merah tebal
|
| 1698 |
if critical_pct > 10:
|
| 1699 |
-
st.
|
| 1700 |
-
f"<span style='color:#d32f2f; font-weight:bold;'>⚠️ High risk: {critical_pct:.1f}% of fatigue alerts occur during critical hours (3-6 AM). This is a known circadian dip period.</span>",
|
| 1701 |
-
unsafe_allow_html=True
|
| 1702 |
-
)
|
| 1703 |
else:
|
| 1704 |
-
st.
|
| 1705 |
-
f"<span style='color:#d32f2f; font-weight:bold;'>✅ {critical_pct:.1f}% of alerts occur during critical hours. This is within acceptable range.</span>",
|
| 1706 |
-
unsafe_allow_html=True
|
| 1707 |
-
)
|
| 1708 |
-
|
| 1709 |
|
| 1710 |
# 2. High-Speed Fatigue Analysis (Environmental Risk)
|
| 1711 |
if col_speed and col_speed in df.columns:
|
|
@@ -1714,107 +1691,69 @@ with col_insights:
|
|
| 1714 |
high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
|
| 1715 |
|
| 1716 |
st.markdown(f"**High-Speed Fatigue Risk (Speed > {high_speed_threshold:.0f} km/h)**")
|
| 1717 |
-
|
| 1718 |
-
|
| 1719 |
-
|
| 1720 |
-
|
| 1721 |
-
|
| 1722 |
-
<div style="font-size: 24px; font-weight: bold; color: #000000;">
|
| 1723 |
-
{len(high_speed_fatigue)}
|
| 1724 |
-
</div>
|
| 1725 |
-
<div style="color: #d32f2f; font-size: 16px; font-weight: bold; margin-top: -10px;">
|
| 1726 |
-
↑ {high_speed_pct:.1f}% of total alerts
|
| 1727 |
-
</div>
|
| 1728 |
-
</div>
|
| 1729 |
-
""",
|
| 1730 |
-
unsafe_allow_html=True
|
| 1731 |
-
)
|
| 1732 |
-
|
| 1733 |
-
# Gunakan st.markdown untuk warning/info dengan persentase berwarna merah tebal
|
| 1734 |
if high_speed_pct > 20:
|
| 1735 |
-
st.
|
| 1736 |
-
f"<span style='color:#d32f2f; font-weight:bold;'>⚠️ High risk: {high_speed_pct:.1f}% of fatigue alerts occur at high speeds. This increases accident severity potential.</span>",
|
| 1737 |
-
unsafe_allow_html=True
|
| 1738 |
-
)
|
| 1739 |
else:
|
| 1740 |
-
st.
|
| 1741 |
-
f"<span style='color:#d32f2f; font-weight:bold;'>✅ {high_speed_pct:.1f}% of alerts occur at high speeds. This is within acceptable range.</span>",
|
| 1742 |
-
unsafe_allow_html=True
|
| 1743 |
-
)
|
| 1744 |
else:
|
| 1745 |
st.info("Speed data not available for High-Speed Fatigue Analysis.")
|
| 1746 |
-
|
| 1747 |
-
|
| 1748 |
-
if col_shift and col_shift in df.columns:
|
| 1749 |
shift_counts = df[col_shift].value_counts()
|
| 1750 |
-
st.markdown(f"**Shift Pattern Risk**")
|
| 1751 |
|
|
|
|
| 1752 |
for shift_val in shift_counts.index:
|
| 1753 |
shift_pct = (shift_counts[shift_val] / len(df)) * 100
|
|
|
|
|
|
|
| 1754 |
|
| 1755 |
-
# Tampilkan jumlah dan persentase dalam satu div dengan warna latar belakang seragam
|
| 1756 |
-
st.markdown(
|
| 1757 |
-
f"""
|
| 1758 |
-
<div style="background-color: #f0f8ff; padding: 10px; border-radius: 8px; margin-bottom: 10px; border-left: 5px solid #007BFF;">
|
| 1759 |
-
<div style="font-size: 24px; font-weight: bold; color: #000000;">Shift {shift_val}</div>
|
| 1760 |
-
<div style="font-size: 28px; font-weight: bold; color: #000000; margin: 10px 0;">
|
| 1761 |
-
{shift_counts[shift_val]}
|
| 1762 |
-
</div>
|
| 1763 |
-
<div style="color: #d32f2f; font-size: 16px; font-weight: bold; margin-top: -10px;">
|
| 1764 |
-
↑ {shift_pct:.1f}% of total alerts
|
| 1765 |
-
</div>
|
| 1766 |
-
</div>
|
| 1767 |
-
""",
|
| 1768 |
-
unsafe_allow_html=True
|
| 1769 |
-
)
|
| 1770 |
-
|
| 1771 |
-
# Gunakan st.markdown untuk warning/info dengan persentase berwarna merah tebal
|
| 1772 |
if shift_pct > 50:
|
| 1773 |
-
st.
|
| 1774 |
-
f"<span style='color:#d32f2f; font-weight:bold;'>⚠️ Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%). Review shift scheduling and workload.</span>",
|
| 1775 |
-
unsafe_allow_html=True
|
| 1776 |
-
)
|
| 1777 |
else:
|
| 1778 |
-
st.
|
| 1779 |
-
f"<span style='color:#d32f2f; font-weight:bold;'>✅ Shift {shift_val} alert distribution is acceptable ({shift_pct:.1f}%).</span>",
|
| 1780 |
-
unsafe_allow_html=True
|
| 1781 |
-
)
|
| 1782 |
else:
|
| 1783 |
st.info("Shift data not available for Shift Pattern Analysis.")
|
| 1784 |
|
| 1785 |
|
| 1786 |
# 4. Operator Risk Profiling
|
| 1787 |
-
|
| 1788 |
-
|
| 1789 |
-
|
|
|
|
| 1790 |
|
| 1791 |
-
|
| 1792 |
|
| 1793 |
-
|
| 1794 |
-
|
| 1795 |
|
| 1796 |
-
|
| 1797 |
-
|
| 1798 |
-
|
| 1799 |
|
| 1800 |
-
|
| 1801 |
-
|
| 1802 |
|
| 1803 |
-
|
| 1804 |
-
|
| 1805 |
-
|
| 1806 |
-
|
| 1807 |
-
|
| 1808 |
-
|
| 1809 |
|
| 1810 |
-
|
| 1811 |
-
|
| 1812 |
-
|
| 1813 |
-
|
| 1814 |
-
|
|
|
|
|
|
|
|
|
|
| 1815 |
|
| 1816 |
-
else:
|
| 1817 |
-
st.info("Operator data not available for Operator Risk Profiling.")
|
| 1818 |
|
| 1819 |
|
| 1820 |
# Kolom kanan: AI Recommendations
|
|
@@ -1891,6 +1830,7 @@ with col_recs:
|
|
| 1891 |
# Ambil data_point dan ganti teks persentase di dalamnya menjadi warna merah
|
| 1892 |
data_point_text = rec['data_point']
|
| 1893 |
# Ganti pola persentase (X.X%) dengan span warna merah
|
|
|
|
| 1894 |
# Cari pola seperti "1.6%", "21.2%", dll.
|
| 1895 |
data_point_colored = re.sub(r'(\d+\.?\d*%)', r'<span style="color: red;">\1</span>', data_point_text)
|
| 1896 |
|
|
@@ -1939,4 +1879,4 @@ with col_recs:
|
|
| 1939 |
|
| 1940 |
# ================= FOOTER ===========================
|
| 1941 |
st.markdown("---")
|
| 1942 |
-
st.markdown('<div class="footer">FatigueAnalyzer - Transforming Mining Safety with Intelligent Analytics | Contact: info@bukittechnology.com</div>', unsafe_allow_html=True)
|
|
|
|
| 1678 |
|
| 1679 |
st.markdown(f"**Critical Hour Risk (3-6 AM)**")
|
| 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:
|
|
|
|
| 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 |
+
# Ganti st.metric dengan HTML custom
|
| 1695 |
+
st.markdown(f"""
|
| 1696 |
+
<div style="font-size: 24px; font-weight: bold;">{len(high_speed_fatigue)}</div>
|
| 1697 |
+
<div style="color: red; font-size: 14px; margin-top: -5px;">↑ {high_speed_pct:.1f}% of total alerts</div>
|
| 1698 |
+
""", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1699 |
if high_speed_pct > 20:
|
| 1700 |
+
st.warning(f"High risk: {high_speed_pct:.1f}% of fatigue alerts occur at high speeds. This increases accident severity potential.")
|
|
|
|
|
|
|
|
|
|
| 1701 |
else:
|
| 1702 |
+
st.info(f"{high_speed_pct:.1f}% of alerts occur at high speeds. This is within acceptable range.")
|
|
|
|
|
|
|
|
|
|
| 1703 |
else:
|
| 1704 |
st.info("Speed data not available for High-Speed Fatigue Analysis.")
|
| 1705 |
+
# 3.Objective
|
| 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"""<div style="font-size: 24px; font-weight: bold;">{shift_counts[shift_val]}</div><div style="color: red; font-size: 14px; margin-top: -5px;">↑ {shift_pct:.1f}% of total alerts</div>""", unsafe_allow_html=True)
|
| 1714 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1715 |
if shift_pct > 50:
|
| 1716 |
+
st.warning(f"Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%). Review shift scheduling and workload.")
|
|
|
|
|
|
|
|
|
|
| 1717 |
else:
|
| 1718 |
+
st.info(f"Shift {shift_val} alert distribution is acceptable ({shift_pct:.1f}%).")
|
|
|
|
|
|
|
|
|
|
| 1719 |
else:
|
| 1720 |
st.info("Shift data not available for Shift Pattern Analysis.")
|
| 1721 |
|
| 1722 |
|
| 1723 |
# 4. Operator Risk Profiling
|
| 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) # Top 5 operators by alerts
|
| 1728 |
|
| 1729 |
+
st.markdown("**High-Risk Operator Identification**")
|
| 1730 |
|
| 1731 |
+
# Warna berdasarkan ranking 1–5
|
| 1732 |
+
colors = ["#d32f2f", "#e57373", "#ef9a9a", "#ffcdd2", "#ffe1e4"]
|
| 1733 |
|
| 1734 |
+
for idx, (op_name, count) in enumerate(top_risk_operators.items()):
|
| 1735 |
+
op_pct = (count / len(df)) * 100
|
| 1736 |
+
color = colors[idx] if idx < len(colors) else colors[-1]
|
| 1737 |
|
| 1738 |
+
# Teks normal untuk nama dan jumlah alert
|
| 1739 |
+
st.markdown(f"**Operator:** {op_name} \n**Alerts:** {count}")
|
| 1740 |
|
| 1741 |
+
# Hanya 'Share' yang berwarna sesuai ranking
|
| 1742 |
+
st.markdown(
|
| 1743 |
+
f"<span style='font-weight:600'>Share:</span> "
|
| 1744 |
+
f"<span style='color:{color}; font-weight:700'>{op_pct:.1f}% of total alerts</span>",
|
| 1745 |
+
unsafe_allow_html=True
|
| 1746 |
+
)
|
| 1747 |
|
| 1748 |
+
# Risk message (tetap gunakan component Streamlit agar konsisten)
|
| 1749 |
+
if op_pct > 5:
|
| 1750 |
+
st.warning(f"Operator {op_name} has high fatigue risk ({op_pct:.1f}%). Consider coaching or rest plan.")
|
| 1751 |
+
else:
|
| 1752 |
+
st.info(f"Operator {op_name} fatigue risk is within acceptable range ({op_pct:.1f}%).")
|
| 1753 |
+
|
| 1754 |
+
else:
|
| 1755 |
+
st.info("Operator data not available for Operator Risk Profiling.")
|
| 1756 |
|
|
|
|
|
|
|
| 1757 |
|
| 1758 |
|
| 1759 |
# Kolom kanan: AI Recommendations
|
|
|
|
| 1830 |
# Ambil data_point dan ganti teks persentase di dalamnya menjadi warna merah
|
| 1831 |
data_point_text = rec['data_point']
|
| 1832 |
# Ganti pola persentase (X.X%) dengan span warna merah
|
| 1833 |
+
import re
|
| 1834 |
# Cari pola seperti "1.6%", "21.2%", dll.
|
| 1835 |
data_point_colored = re.sub(r'(\d+\.?\d*%)', r'<span style="color: red;">\1</span>', data_point_text)
|
| 1836 |
|
|
|
|
| 1879 |
|
| 1880 |
# ================= FOOTER ===========================
|
| 1881 |
st.markdown("---")
|
| 1882 |
+
st.markdown('<div class="footer">FatigueAnalyzer - Transforming Mining Safety with Intelligent Analytics | Contact: info@bukittechnology.com</div>', unsafe_allow_html=True)
|