Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1657,6 +1657,18 @@ else:
|
|
| 1657 |
except Exception as e:
|
| 1658 |
st.error(f"Error in Top 10 Operator analysis: {str(e)}")
|
| 1659 |
st.exception(e) # optionally show full traceback during dev
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1660 |
# =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
|
| 1661 |
st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
|
| 1662 |
|
|
@@ -1674,11 +1686,34 @@ with col_insights:
|
|
| 1674 |
|
| 1675 |
st.markdown(f"**Critical Hour Risk (3-6 AM)**")
|
| 1676 |
bg_color = "#ffcccc" if critical_pct > 50 else "#ffebcc" if critical_pct > 25 else "#ffffcc" if critical_pct > 10 else "#e6ffe6"
|
| 1677 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1678 |
if critical_pct > 10:
|
| 1679 |
-
st.
|
|
|
|
|
|
|
|
|
|
| 1680 |
else:
|
| 1681 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1682 |
|
| 1683 |
# 2. High-Speed Fatigue Analysis (Environmental Risk)
|
| 1684 |
if col_speed and col_speed in df.columns:
|
|
@@ -1687,69 +1722,107 @@ with col_insights:
|
|
| 1687 |
high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
|
| 1688 |
|
| 1689 |
st.markdown(f"**High-Speed Fatigue Risk (Speed > {high_speed_threshold:.0f} km/h)**")
|
| 1690 |
-
|
| 1691 |
-
|
| 1692 |
-
|
| 1693 |
-
|
| 1694 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1695 |
if high_speed_pct > 20:
|
| 1696 |
-
st.
|
|
|
|
|
|
|
|
|
|
| 1697 |
else:
|
| 1698 |
-
st.
|
|
|
|
|
|
|
|
|
|
| 1699 |
else:
|
| 1700 |
st.info("Speed data not available for High-Speed Fatigue Analysis.")
|
| 1701 |
-
# 3.Objective
|
| 1702 |
-
if col_shift and col_shift in df.columns:
|
| 1703 |
-
shift_counts = df[col_shift].value_counts()
|
| 1704 |
|
|
|
|
|
|
|
|
|
|
| 1705 |
st.markdown(f"**Shift Pattern Risk**")
|
|
|
|
| 1706 |
for shift_val in shift_counts.index:
|
| 1707 |
shift_pct = (shift_counts[shift_val] / len(df)) * 100
|
| 1708 |
-
# Ganti st.metric dengan HTML custom
|
| 1709 |
-
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)
|
| 1710 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1711 |
if shift_pct > 50:
|
| 1712 |
-
st.
|
|
|
|
|
|
|
|
|
|
| 1713 |
else:
|
| 1714 |
-
st.
|
|
|
|
|
|
|
|
|
|
| 1715 |
else:
|
| 1716 |
st.info("Shift data not available for Shift Pattern Analysis.")
|
| 1717 |
|
| 1718 |
|
| 1719 |
# 4. Operator Risk Profiling
|
| 1720 |
-
|
| 1721 |
-
|
| 1722 |
-
|
| 1723 |
-
top_risk_operators = operator_alerts.head(5) # Top 5 operators by alerts
|
| 1724 |
-
|
| 1725 |
-
st.markdown("**High-Risk Operator Identification**")
|
| 1726 |
|
| 1727 |
-
|
| 1728 |
-
colors = ["#d32f2f", "#e57373", "#ef9a9a", "#ffcdd2", "#ffe1e4"]
|
| 1729 |
|
| 1730 |
-
|
| 1731 |
-
|
| 1732 |
-
color = colors[idx] if idx < len(colors) else colors[-1]
|
| 1733 |
|
| 1734 |
-
|
| 1735 |
-
|
|
|
|
| 1736 |
|
| 1737 |
-
|
| 1738 |
-
|
| 1739 |
-
f"<span style='font-weight:600'>Share:</span> "
|
| 1740 |
-
f"<span style='color:{color}; font-weight:700'>{op_pct:.1f}% of total alerts</span>",
|
| 1741 |
-
unsafe_allow_html=True
|
| 1742 |
-
)
|
| 1743 |
|
| 1744 |
-
|
| 1745 |
-
|
| 1746 |
-
|
| 1747 |
-
|
| 1748 |
-
|
|
|
|
| 1749 |
|
| 1750 |
-
|
| 1751 |
-
|
|
|
|
|
|
|
|
|
|
| 1752 |
|
|
|
|
|
|
|
| 1753 |
|
| 1754 |
|
| 1755 |
# Kolom kanan: AI Recommendations
|
|
@@ -1826,7 +1899,6 @@ with col_recs:
|
|
| 1826 |
# Ambil data_point dan ganti teks persentase di dalamnya menjadi warna merah
|
| 1827 |
data_point_text = rec['data_point']
|
| 1828 |
# Ganti pola persentase (X.X%) dengan span warna merah
|
| 1829 |
-
import re
|
| 1830 |
# Cari pola seperti "1.6%", "21.2%", dll.
|
| 1831 |
data_point_colored = re.sub(r'(\d+\.?\d*%)', r'<span style="color: red;">\1</span>', data_point_text)
|
| 1832 |
|
|
@@ -1875,4 +1947,4 @@ with col_recs:
|
|
| 1875 |
|
| 1876 |
# ================= FOOTER ===========================
|
| 1877 |
st.markdown("---")
|
| 1878 |
-
st.markdown('<div class="footer">FatigueAnalyzer - Transforming Mining Safety with Intelligent Analytics | Contact: info@bukittechnology.com</div>', unsafe_allow_html=True)
|
|
|
|
| 1657 |
except Exception as e:
|
| 1658 |
st.error(f"Error in Top 10 Operator analysis: {str(e)}")
|
| 1659 |
st.exception(e) # optionally show full traceback during dev
|
| 1660 |
+
Berikut adalah kode Objective 6 Anda yang telah diperbaiki. Kini, bagian **Critical Hour (1)**, **High-Speed Fatigue (2)**, dan **Shift Pattern (3)** menggunakan tampilan yang seragam: **jumlah alert dalam ukuran besar dan tebal, diikuti persentase dalam warna merah**. Warna latar belakang untuk bagian Shift Pattern (3) disamakan dengan bagian lainnya, dan warna teks persentase untuk Shift 1 dan 2 diatur menjadi merah.
|
| 1661 |
+
|
| 1662 |
+
Perubahan utama:
|
| 1663 |
+
1. Ganti `st.metric` dan `st.markdown` yang sebelumnya tidak seragam.
|
| 1664 |
+
2. Gunakan `st.markdown` dengan `unsafe_allow_html=True` untuk menampilkan jumlah dan persentase.
|
| 1665 |
+
3. Terapkan `st.markdown` untuk `st.warning` dan `st.info` agar teks persentase berwarna merah.
|
| 1666 |
+
4. Gunakan warna latar belakang yang konsisten untuk Shift Pattern (mengikuti pola Critical Hour).
|
| 1667 |
+
5. Tambahkan `import re` di awal file jika belum ada.
|
| 1668 |
+
|
| 1669 |
+
|
| 1670 |
+
import re # Tambahkan ini jika belum ada
|
| 1671 |
+
|
| 1672 |
# =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
|
| 1673 |
st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
|
| 1674 |
|
|
|
|
| 1686 |
|
| 1687 |
st.markdown(f"**Critical Hour Risk (3-6 AM)**")
|
| 1688 |
bg_color = "#ffcccc" if critical_pct > 50 else "#ffebcc" if critical_pct > 25 else "#ffffcc" if critical_pct > 10 else "#e6ffe6"
|
| 1689 |
+
|
| 1690 |
+
# Tampilkan jumlah dan persentase dalam satu div
|
| 1691 |
+
st.markdown(
|
| 1692 |
+
f"""
|
| 1693 |
+
<div style="background-color: {bg_color}; padding: 10px; border-radius: 8px; margin-bottom: 10px;">
|
| 1694 |
+
<div style="font-size: 24px; font-weight: bold; color: #000000;">
|
| 1695 |
+
{len(critical_alerts)}
|
| 1696 |
+
</div>
|
| 1697 |
+
<div style="color: #d32f2f; font-size: 16px; font-weight: bold; margin-top: -10px;">
|
| 1698 |
+
↑ {critical_pct:.1f}% of total alerts
|
| 1699 |
+
</div>
|
| 1700 |
+
</div>
|
| 1701 |
+
""",
|
| 1702 |
+
unsafe_allow_html=True
|
| 1703 |
+
)
|
| 1704 |
+
|
| 1705 |
+
# Gunakan st.markdown untuk warning/info dengan persentase berwarna merah tebal
|
| 1706 |
if critical_pct > 10:
|
| 1707 |
+
st.markdown(
|
| 1708 |
+
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>",
|
| 1709 |
+
unsafe_allow_html=True
|
| 1710 |
+
)
|
| 1711 |
else:
|
| 1712 |
+
st.markdown(
|
| 1713 |
+
f"<span style='color:#d32f2f; font-weight:bold;'>✅ {critical_pct:.1f}% of alerts occur during critical hours. This is within acceptable range.</span>",
|
| 1714 |
+
unsafe_allow_html=True
|
| 1715 |
+
)
|
| 1716 |
+
|
| 1717 |
|
| 1718 |
# 2. High-Speed Fatigue Analysis (Environmental Risk)
|
| 1719 |
if col_speed and col_speed in df.columns:
|
|
|
|
| 1722 |
high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
|
| 1723 |
|
| 1724 |
st.markdown(f"**High-Speed Fatigue Risk (Speed > {high_speed_threshold:.0f} km/h)**")
|
| 1725 |
+
|
| 1726 |
+
# Tampilkan jumlah dan persentase dalam satu div
|
| 1727 |
+
st.markdown(
|
| 1728 |
+
f"""
|
| 1729 |
+
<div style="background-color: #f0f8ff; padding: 10px; border-radius: 8px; margin-bottom: 10px; border-left: 5px solid #007BFF;">
|
| 1730 |
+
<div style="font-size: 24px; font-weight: bold; color: #000000;">
|
| 1731 |
+
{len(high_speed_fatigue)}
|
| 1732 |
+
</div>
|
| 1733 |
+
<div style="color: #d32f2f; font-size: 16px; font-weight: bold; margin-top: -10px;">
|
| 1734 |
+
↑ {high_speed_pct:.1f}% of total alerts
|
| 1735 |
+
</div>
|
| 1736 |
+
</div>
|
| 1737 |
+
""",
|
| 1738 |
+
unsafe_allow_html=True
|
| 1739 |
+
)
|
| 1740 |
+
|
| 1741 |
+
# Gunakan st.markdown untuk warning/info dengan persentase berwarna merah tebal
|
| 1742 |
if high_speed_pct > 20:
|
| 1743 |
+
st.markdown(
|
| 1744 |
+
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>",
|
| 1745 |
+
unsafe_allow_html=True
|
| 1746 |
+
)
|
| 1747 |
else:
|
| 1748 |
+
st.markdown(
|
| 1749 |
+
f"<span style='color:#d32f2f; font-weight:bold;'>✅ {high_speed_pct:.1f}% of alerts occur at high speeds. This is within acceptable range.</span>",
|
| 1750 |
+
unsafe_allow_html=True
|
| 1751 |
+
)
|
| 1752 |
else:
|
| 1753 |
st.info("Speed data not available for High-Speed Fatigue Analysis.")
|
|
|
|
|
|
|
|
|
|
| 1754 |
|
| 1755 |
+
# 3. Shift Pattern Analysis
|
| 1756 |
+
if col_shift and col_shift in df.columns:
|
| 1757 |
+
shift_counts = df[col_shift].value_counts()
|
| 1758 |
st.markdown(f"**Shift Pattern Risk**")
|
| 1759 |
+
|
| 1760 |
for shift_val in shift_counts.index:
|
| 1761 |
shift_pct = (shift_counts[shift_val] / len(df)) * 100
|
|
|
|
|
|
|
| 1762 |
|
| 1763 |
+
# Tampilkan jumlah dan persentase dalam satu div dengan warna latar belakang seragam
|
| 1764 |
+
st.markdown(
|
| 1765 |
+
f"""
|
| 1766 |
+
<div style="background-color: #f0f8ff; padding: 10px; border-radius: 8px; margin-bottom: 10px; border-left: 5px solid #007BFF;">
|
| 1767 |
+
<div style="font-size: 24px; font-weight: bold; color: #000000;">Shift {shift_val}</div>
|
| 1768 |
+
<div style="font-size: 28px; font-weight: bold; color: #000000; margin: 10px 0;">
|
| 1769 |
+
{shift_counts[shift_val]}
|
| 1770 |
+
</div>
|
| 1771 |
+
<div style="color: #d32f2f; font-size: 16px; font-weight: bold; margin-top: -10px;">
|
| 1772 |
+
↑ {shift_pct:.1f}% of total alerts
|
| 1773 |
+
</div>
|
| 1774 |
+
</div>
|
| 1775 |
+
""",
|
| 1776 |
+
unsafe_allow_html=True
|
| 1777 |
+
)
|
| 1778 |
+
|
| 1779 |
+
# Gunakan st.markdown untuk warning/info dengan persentase berwarna merah tebal
|
| 1780 |
if shift_pct > 50:
|
| 1781 |
+
st.markdown(
|
| 1782 |
+
f"<span style='color:#d32f2f; font-weight:bold;'>⚠️ Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%). Review shift scheduling and workload.</span>",
|
| 1783 |
+
unsafe_allow_html=True
|
| 1784 |
+
)
|
| 1785 |
else:
|
| 1786 |
+
st.markdown(
|
| 1787 |
+
f"<span style='color:#d32f2f; font-weight:bold;'>✅ Shift {shift_val} alert distribution is acceptable ({shift_pct:.1f}%).</span>",
|
| 1788 |
+
unsafe_allow_html=True
|
| 1789 |
+
)
|
| 1790 |
else:
|
| 1791 |
st.info("Shift data not available for Shift Pattern Analysis.")
|
| 1792 |
|
| 1793 |
|
| 1794 |
# 4. Operator Risk Profiling
|
| 1795 |
+
if col_operator and col_operator in df.columns:
|
| 1796 |
+
operator_alerts = df[col_operator].value_counts()
|
| 1797 |
+
top_risk_operators = operator_alerts.head(5) # Top 5 operators by alerts
|
|
|
|
|
|
|
|
|
|
| 1798 |
|
| 1799 |
+
st.markdown("**High-Risk Operator Identification**")
|
|
|
|
| 1800 |
|
| 1801 |
+
# Warna berdasarkan ranking 1–5
|
| 1802 |
+
colors = ["#d32f2f", "#e57373", "#ef9a9a", "#ffcdd2", "#ffe1e4"]
|
|
|
|
| 1803 |
|
| 1804 |
+
for idx, (op_name, count) in enumerate(top_risk_operators.items()):
|
| 1805 |
+
op_pct = (count / len(df)) * 100
|
| 1806 |
+
color = colors[idx] if idx < len(colors) else colors[-1]
|
| 1807 |
|
| 1808 |
+
# Teks normal untuk nama dan jumlah alert
|
| 1809 |
+
st.markdown(f"**Operator:** {op_name} \n**Alerts:** {count}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1810 |
|
| 1811 |
+
# Hanya 'Share' yang berwarna sesuai ranking
|
| 1812 |
+
st.markdown(
|
| 1813 |
+
f"<span style='font-weight:600'>Share:</span> "
|
| 1814 |
+
f"<span style='color:{color}; font-weight:700'>{op_pct:.1f}% of total alerts</span>",
|
| 1815 |
+
unsafe_allow_html=True
|
| 1816 |
+
)
|
| 1817 |
|
| 1818 |
+
# Risk message (tetap gunakan component Streamlit agar konsisten)
|
| 1819 |
+
if op_pct > 5:
|
| 1820 |
+
st.warning(f"Operator {op_name} has high fatigue risk ({op_pct:.1f}%). Consider coaching or rest plan.")
|
| 1821 |
+
else:
|
| 1822 |
+
st.info(f"Operator {op_name} fatigue risk is within acceptable range ({op_pct:.1f}%).")
|
| 1823 |
|
| 1824 |
+
else:
|
| 1825 |
+
st.info("Operator data not available for Operator Risk Profiling.")
|
| 1826 |
|
| 1827 |
|
| 1828 |
# Kolom kanan: AI Recommendations
|
|
|
|
| 1899 |
# Ambil data_point dan ganti teks persentase di dalamnya menjadi warna merah
|
| 1900 |
data_point_text = rec['data_point']
|
| 1901 |
# Ganti pola persentase (X.X%) dengan span warna merah
|
|
|
|
| 1902 |
# Cari pola seperti "1.6%", "21.2%", dll.
|
| 1903 |
data_point_colored = re.sub(r'(\d+\.?\d*%)', r'<span style="color: red;">\1</span>', data_point_text)
|
| 1904 |
|
|
|
|
| 1947 |
|
| 1948 |
# ================= FOOTER ===========================
|
| 1949 |
st.markdown("---")
|
| 1950 |
+
st.markdown('<div class="footer">FatigueAnalyzer - Transforming Mining Safety with Intelligent Analytics | Contact: info@bukittechnology.com</div>', unsafe_allow_html=True)
|