Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1700,36 +1700,86 @@ with col_insights:
|
|
| 1700 |
st.info("Speed data not available for High-Speed Fatigue Analysis.")
|
| 1701 |
|
| 1702 |
# 3. Shift Pattern Analysis
|
| 1703 |
-
|
| 1704 |
-
|
| 1705 |
-
|
| 1706 |
-
|
| 1707 |
-
|
| 1708 |
-
|
| 1709 |
-
shift_pct = (shift_counts[shift_val] / len(df)) * 100
|
| 1710 |
-
st.metric(f"Shift {shift_val} Alerts", f"{shift_counts[shift_val]}", f"{shift_pct:.1f}% of total alerts")
|
| 1711 |
-
if shift_pct > 50: # If one shift has more than 50% of alerts
|
| 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}%).")
|
| 1715 |
-
else:
|
| 1716 |
-
st.info("Shift data not available for Shift Pattern Analysis.")
|
| 1717 |
|
| 1718 |
-
|
| 1719 |
-
|
| 1720 |
-
operator_alerts = df[col_operator].value_counts()
|
| 1721 |
-
top_risk_operators = operator_alerts.head(5) # Top 5 operators by alerts
|
| 1722 |
|
| 1723 |
-
|
| 1724 |
-
|
| 1725 |
-
|
| 1726 |
-
|
| 1727 |
-
|
| 1728 |
-
|
| 1729 |
-
|
| 1730 |
-
|
| 1731 |
-
|
| 1732 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1733 |
|
| 1734 |
|
| 1735 |
# Kolom kanan: AI Recommendations
|
|
|
|
| 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 |
+
|
| 1706 |
+
st.markdown("**Shift Pattern Risk**")
|
| 1707 |
+
|
| 1708 |
+
shift_color = "#d32f2f" # warna merah gelap
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1709 |
|
| 1710 |
+
for shift_val in shift_counts.index:
|
| 1711 |
+
shift_pct = (shift_counts[shift_val] / len(df)) * 100
|
|
|
|
|
|
|
| 1712 |
|
| 1713 |
+
# Metric-style card
|
| 1714 |
+
st.markdown(
|
| 1715 |
+
f"""
|
| 1716 |
+
<div style="
|
| 1717 |
+
padding: 12px 16px;
|
| 1718 |
+
border-radius: 10px;
|
| 1719 |
+
background-color: #f9f9f9;
|
| 1720 |
+
border: 1px solid #ddd;
|
| 1721 |
+
margin-bottom: 10px;
|
| 1722 |
+
">
|
| 1723 |
+
<div style="font-size: 16px; font-weight: 600;">
|
| 1724 |
+
Shift {shift_val} Alerts
|
| 1725 |
+
</div>
|
| 1726 |
+
|
| 1727 |
+
<div style="font-size: 28px; font-weight: 700;">
|
| 1728 |
+
{shift_counts[shift_val]}
|
| 1729 |
+
</div>
|
| 1730 |
+
|
| 1731 |
+
<div style="font-size: 14px; font-weight: 700; color:{shift_color};">
|
| 1732 |
+
{shift_pct:.1f}% of total alerts
|
| 1733 |
+
</div>
|
| 1734 |
+
</div>
|
| 1735 |
+
""",
|
| 1736 |
+
unsafe_allow_html=True
|
| 1737 |
+
)
|
| 1738 |
+
|
| 1739 |
+
# Risk Message
|
| 1740 |
+
if shift_pct > 50:
|
| 1741 |
+
st.warning(
|
| 1742 |
+
f"Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%). Review shift scheduling and workload."
|
| 1743 |
+
)
|
| 1744 |
+
else:
|
| 1745 |
+
st.info(
|
| 1746 |
+
f"Shift {shift_val} alert distribution is acceptable ({shift_pct:.1f}%)."
|
| 1747 |
+
)
|
| 1748 |
+
else:
|
| 1749 |
+
st.info("Shift data not available for Shift Pattern Analysis.")
|
| 1750 |
+
|
| 1751 |
+
# 4. Operator Risk Profiling
|
| 1752 |
+
if col_operator and col_operator in df.columns:
|
| 1753 |
+
operator_alerts = df[col_operator].value_counts()
|
| 1754 |
+
top_risk_operators = operator_alerts.head(5) # Top 5 operators by alerts
|
| 1755 |
+
|
| 1756 |
+
st.markdown("**High-Risk Operator Identification**")
|
| 1757 |
+
|
| 1758 |
+
# Warna berdasarkan ranking 1–5
|
| 1759 |
+
colors = ["#d32f2f", "#e57373", "#ef9a9a", "#ffcdd2", "#ffe1e4"]
|
| 1760 |
+
|
| 1761 |
+
for idx, (op_name, count) in enumerate(top_risk_operators.items()):
|
| 1762 |
+
op_pct = (count / len(df)) * 100
|
| 1763 |
+
color = colors[idx] if idx < len(colors) else colors[-1]
|
| 1764 |
+
|
| 1765 |
+
# Teks normal untuk nama dan jumlah alert
|
| 1766 |
+
st.markdown(f"**Operator:** {op_name} \n**Alerts:** {count}")
|
| 1767 |
+
|
| 1768 |
+
# Hanya 'Share' yang berwarna sesuai ranking
|
| 1769 |
+
st.markdown(
|
| 1770 |
+
f"<span style='font-weight:600'>Share:</span> "
|
| 1771 |
+
f"<span style='color:{color}; font-weight:700'>{op_pct:.1f}% of total alerts</span>",
|
| 1772 |
+
unsafe_allow_html=True
|
| 1773 |
+
)
|
| 1774 |
+
|
| 1775 |
+
# Risk message (tetap gunakan component Streamlit agar konsisten)
|
| 1776 |
+
if op_pct > 5:
|
| 1777 |
+
st.warning(f"Operator {op_name} has high fatigue risk ({op_pct:.1f}%). Consider coaching or rest plan.")
|
| 1778 |
+
else:
|
| 1779 |
+
st.info(f"Operator {op_name} fatigue risk is within acceptable range ({op_pct:.1f}%).")
|
| 1780 |
+
|
| 1781 |
+
else:
|
| 1782 |
+
st.info("Operator data not available for Operator Risk Profiling.")
|
| 1783 |
|
| 1784 |
|
| 1785 |
# Kolom kanan: AI Recommendations
|