Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2024,6 +2024,308 @@ else:
|
|
| 2024 |
|
| 2025 |
# )
|
| 2026 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2027 |
# =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
|
| 2028 |
st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
|
| 2029 |
|
|
@@ -2031,7 +2333,7 @@ st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
|
|
| 2031 |
col_insights, col_recs = st.columns(2)
|
| 2032 |
|
| 2033 |
# =====================================================================
|
| 2034 |
-
# 🔹 KOLOM KIRI — INSIGHTS BY ADVANCED ANALYTICS
|
| 2035 |
# =====================================================================
|
| 2036 |
with col_insights:
|
| 2037 |
st.subheader("Insights by Advanced Analytics")
|
|
@@ -2146,14 +2448,17 @@ with col_insights:
|
|
| 2146 |
f"Consider coaching or rest plan."
|
| 2147 |
)
|
| 2148 |
else:
|
| 2149 |
-
|
| 2150 |
-
|
|
|
|
|
|
|
|
|
|
| 2151 |
)
|
| 2152 |
else:
|
| 2153 |
st.info("Operator data not available for Operator Risk Profiling.")
|
| 2154 |
|
| 2155 |
# =====================================================================
|
| 2156 |
-
# 🔹 KOLOM KANAN — AI RECOMMENDATIONS
|
| 2157 |
# =====================================================================
|
| 2158 |
with col_recs:
|
| 2159 |
st.subheader("Recommendations")
|
|
|
|
| 2024 |
|
| 2025 |
# )
|
| 2026 |
|
| 2027 |
+
# # =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
|
| 2028 |
+
# st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
|
| 2029 |
+
|
| 2030 |
+
# # Membagi tampilan menjadi dua kolom
|
| 2031 |
+
# col_insights, col_recs = st.columns(2)
|
| 2032 |
+
|
| 2033 |
+
# # =====================================================================
|
| 2034 |
+
# # 🔹 KOLOM KIRI — INSIGHTS BY ADVANCED ANALYTICS
|
| 2035 |
+
# # =====================================================================
|
| 2036 |
+
# with col_insights:
|
| 2037 |
+
# st.subheader("Insights by Advanced Analytics")
|
| 2038 |
+
|
| 2039 |
+
# # ===================== 1. Critical Hour Analysis =====================
|
| 2040 |
+
# critical_hours = [2, 3, 4, 5]
|
| 2041 |
+
# critical_alerts = df[df['hour'].isin(critical_hours)]
|
| 2042 |
+
# critical_pct = (len(critical_alerts) / len(df)) * 100 if len(df) > 0 else 0
|
| 2043 |
+
|
| 2044 |
+
# st.markdown(f"**Critical Hour Risk (3-6 AM)**")
|
| 2045 |
+
# bg_color = (
|
| 2046 |
+
# "#ffcccc" if critical_pct > 50 else
|
| 2047 |
+
# "#ffebcc" if critical_pct > 25 else
|
| 2048 |
+
# "#ffffcc" if critical_pct > 10 else
|
| 2049 |
+
# "#e6ffe6"
|
| 2050 |
+
# )
|
| 2051 |
+
# st.markdown(
|
| 2052 |
+
# f'<div style="background-color: {bg_color}; padding: 10px; border-radius: 5px;">'
|
| 2053 |
+
# f'Critical Hour Alerts: {len(critical_alerts)} ({critical_pct:.1f}% of total alerts)</div>',
|
| 2054 |
+
# unsafe_allow_html=True
|
| 2055 |
+
# )
|
| 2056 |
+
|
| 2057 |
+
# if critical_pct > 10:
|
| 2058 |
+
# st.warning(
|
| 2059 |
+
# f"High risk: {critical_pct:.1f}% of fatigue alerts occur during critical hours (3-6 AM). "
|
| 2060 |
+
# f"This is a known circadian dip period."
|
| 2061 |
+
# )
|
| 2062 |
+
# else:
|
| 2063 |
+
# st.info(
|
| 2064 |
+
# f"{critical_pct:.1f}% of alerts occur during critical hours. This is within acceptable range."
|
| 2065 |
+
# )
|
| 2066 |
+
|
| 2067 |
+
# # ===================== 2. High-Speed Fatigue Analysis =====================
|
| 2068 |
+
# if col_speed and col_speed in df.columns:
|
| 2069 |
+
# high_speed_threshold = 20
|
| 2070 |
+
# high_speed_fatigue = df[df[col_speed] >= high_speed_threshold]
|
| 2071 |
+
# high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
|
| 2072 |
+
|
| 2073 |
+
# st.markdown(f"**High-Speed Fatigue Risk (Speed > {high_speed_threshold} km/h)**")
|
| 2074 |
+
# st.markdown(
|
| 2075 |
+
# f"""
|
| 2076 |
+
# <div style="font-size: 24px; font-weight: bold;">{len(high_speed_fatigue)}</div>
|
| 2077 |
+
# <div style="color: red; font-size: 14px; margin-top: -5px;">↑ {high_speed_pct:.1f}% of total alerts</div>
|
| 2078 |
+
# """,
|
| 2079 |
+
# unsafe_allow_html=True
|
| 2080 |
+
# )
|
| 2081 |
+
|
| 2082 |
+
# if high_speed_pct > 20:
|
| 2083 |
+
# st.warning(
|
| 2084 |
+
# f"High risk: {high_speed_pct:.1f}% of fatigue alerts occur at high speeds. "
|
| 2085 |
+
# f"This increases accident severity potential."
|
| 2086 |
+
# )
|
| 2087 |
+
# else:
|
| 2088 |
+
# st.info(
|
| 2089 |
+
# f"{high_speed_pct:.1f}% of alerts occur at high speeds. This is within acceptable range."
|
| 2090 |
+
# )
|
| 2091 |
+
# else:
|
| 2092 |
+
# st.info("Speed data not available for High-Speed Fatigue Analysis.")
|
| 2093 |
+
|
| 2094 |
+
# # ===================== 3. Shift Pattern Analysis =====================
|
| 2095 |
+
# if col_shift and col_shift in df.columns:
|
| 2096 |
+
# shift_counts = df[col_shift].value_counts()
|
| 2097 |
+
# st.markdown(f"**Shift Pattern Risk**")
|
| 2098 |
+
|
| 2099 |
+
# for shift_val in shift_counts.index:
|
| 2100 |
+
# shift_pct = (shift_counts[shift_val] / len(df)) * 100
|
| 2101 |
+
|
| 2102 |
+
# st.markdown(
|
| 2103 |
+
# f"""
|
| 2104 |
+
# <div style="font-size: 24px; font-weight: bold;">{shift_counts[shift_val]}</div>
|
| 2105 |
+
# <div style="color: red; font-size: 14px; margin-top: -5px;">↑ {shift_pct:.1f}% of total alerts</div>
|
| 2106 |
+
# """,
|
| 2107 |
+
# unsafe_allow_html=True
|
| 2108 |
+
# )
|
| 2109 |
+
|
| 2110 |
+
# if shift_pct > 50:
|
| 2111 |
+
# st.warning(
|
| 2112 |
+
# f"Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%). "
|
| 2113 |
+
# f"Review shift scheduling and workload."
|
| 2114 |
+
# )
|
| 2115 |
+
# else:
|
| 2116 |
+
# st.info(
|
| 2117 |
+
# f"Shift {shift_val} alert distribution is acceptable ({shift_pct:.1f}%)."
|
| 2118 |
+
# )
|
| 2119 |
+
# else:
|
| 2120 |
+
# st.info("Shift data not available for Shift Pattern Analysis.")
|
| 2121 |
+
|
| 2122 |
+
# # ===================== 4. Operator Risk Profiling =====================
|
| 2123 |
+
# if col_operator and col_operator in df.columns:
|
| 2124 |
+
# operator_alerts = df[col_operator].value_counts()
|
| 2125 |
+
# top_risk_operators = operator_alerts.head(5)
|
| 2126 |
+
|
| 2127 |
+
# st.markdown("**High-Risk Operator Identification**")
|
| 2128 |
+
# colors = ["#d32f2f", "#e57373", "#ef9a9a", "#ffcdd2", "#ffe1e4"]
|
| 2129 |
+
|
| 2130 |
+
# for idx, (op_name, count) in enumerate(top_risk_operators.items()):
|
| 2131 |
+
# op_pct = (count / len(df)) * 100
|
| 2132 |
+
# color = colors[idx] if idx < len(colors) else colors[-1]
|
| 2133 |
+
|
| 2134 |
+
# st.markdown(
|
| 2135 |
+
# f"**Operator:** {op_name} \n**Alerts:** {count}"
|
| 2136 |
+
# )
|
| 2137 |
+
# st.markdown(
|
| 2138 |
+
# f"<span style='font-weight:600'>Share:</span> "
|
| 2139 |
+
# f"<span style='color:{color}; font-weight:700'>{op_pct:.1f}% of total alerts</span>",
|
| 2140 |
+
# unsafe_allow_html=True
|
| 2141 |
+
# )
|
| 2142 |
+
|
| 2143 |
+
# if op_pct > 5:
|
| 2144 |
+
# st.warning(
|
| 2145 |
+
# f"Operator {op_name} has high fatigue risk ({op_pct:.1f}%). "
|
| 2146 |
+
# f"Consider coaching or rest plan."
|
| 2147 |
+
# )
|
| 2148 |
+
# else:
|
| 2149 |
+
# st.info(
|
| 2150 |
+
# f"Operator {op_name} fatigue risk is within acceptable range ({op_pct:.1f}%)."
|
| 2151 |
+
# )
|
| 2152 |
+
# else:
|
| 2153 |
+
# st.info("Operator data not available for Operator Risk Profiling.")
|
| 2154 |
+
|
| 2155 |
+
# # =====================================================================
|
| 2156 |
+
# # 🔹 KOLOM KANAN — AI RECOMMENDATIONS
|
| 2157 |
+
# # =====================================================================
|
| 2158 |
+
# with col_recs:
|
| 2159 |
+
# st.subheader("Recommendations")
|
| 2160 |
+
|
| 2161 |
+
# ai_recommendations = []
|
| 2162 |
+
|
| 2163 |
+
# # 1. Critical Hour Insight → AI Rec
|
| 2164 |
+
# if "hour" in df.columns and not df.empty:
|
| 2165 |
+
# peak_hour = df["hour"].value_counts().idxmax()
|
| 2166 |
+
# critical_hours = [2, 3, 4, 5]
|
| 2167 |
+
|
| 2168 |
+
# if peak_hour in critical_hours:
|
| 2169 |
+
# ai_recommendations.append({
|
| 2170 |
+
# "type": "critical_hour",
|
| 2171 |
+
# "action": "Deploy enhanced fatigue monitoring systems during 3-6 AM.",
|
| 2172 |
+
# "data_point": f"Critical Hour Alerts: {len(critical_alerts)} ({critical_pct:.1f}%)",
|
| 2173 |
+
# "reasoning": "High percentage of alerts during circadian low period."
|
| 2174 |
+
# })
|
| 2175 |
+
# else:
|
| 2176 |
+
# ai_recommendations.append({
|
| 2177 |
+
# "type": "critical_hour",
|
| 2178 |
+
# "action": "Monitor fatigue patterns around peak hour (Hour {peak_hour}).",
|
| 2179 |
+
# "data_point": f"Peak Hour: {peak_hour}:00 — {df['hour'].value_counts()[peak_hour]} alerts",
|
| 2180 |
+
# "reasoning": "This hour shows highest fatigue occurrence."
|
| 2181 |
+
# })
|
| 2182 |
+
|
| 2183 |
+
# # 2. High-Speed Insight → AI Rec
|
| 2184 |
+
# if col_speed and col_speed in df.columns and not df.empty:
|
| 2185 |
+
# high_speed_threshold = 20
|
| 2186 |
+
# high_speed_fatigue = df[df[col_speed] >= high_speed_threshold]
|
| 2187 |
+
# high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
|
| 2188 |
+
|
| 2189 |
+
# if high_speed_pct > 20:
|
| 2190 |
+
# ai_recommendations.append({
|
| 2191 |
+
# "type": "high_speed",
|
| 2192 |
+
# "action": "Implement speed-reduction protocols during fatigue-prone hours.",
|
| 2193 |
+
# "data_point": f"High-Speed Alerts: {len(high_speed_fatigue)} ({high_speed_pct:.1f}%)",
|
| 2194 |
+
# "reasoning": "High-speed alerts increase accident severity potential."
|
| 2195 |
+
# })
|
| 2196 |
+
# else:
|
| 2197 |
+
# ai_recommendations.append({
|
| 2198 |
+
# "type": "high_speed",
|
| 2199 |
+
# "action": "Maintain current speed monitoring — risk level is acceptable.",
|
| 2200 |
+
# "data_point": f"High-Speed Alerts: {len(high_speed_fatigue)} ({high_speed_pct:.1f}%)",
|
| 2201 |
+
# "reasoning": "Current high-speed fatigue rate is within acceptable range."
|
| 2202 |
+
# })
|
| 2203 |
+
|
| 2204 |
+
# # 3. Shift Pattern Insight → AI Rec
|
| 2205 |
+
# if col_shift and col_shift in df.columns and not df.empty:
|
| 2206 |
+
# worst_shift = df[col_shift].value_counts().idxmax()
|
| 2207 |
+
# shift_pct = (df[col_shift].value_counts()[worst_shift] / len(df)) * 100
|
| 2208 |
+
|
| 2209 |
+
# if shift_pct > 50:
|
| 2210 |
+
# ai_recommendations.append({
|
| 2211 |
+
# "type": "shift_pattern",
|
| 2212 |
+
# "action": "Review shift rotation schedules for Shift {worst_shift}.",
|
| 2213 |
+
# "data_point": f"Shift {worst_shift}: {df[col_shift].value_counts()[worst_shift]} alerts ({shift_pct:.1f}%)",
|
| 2214 |
+
# "reasoning": "Disproportionately high fatigue alerts indicate scheduling imbalance."
|
| 2215 |
+
# })
|
| 2216 |
+
# else:
|
| 2217 |
+
# ai_recommendations.append({
|
| 2218 |
+
# "type": "shift_pattern",
|
| 2219 |
+
# "action": "Continue monitoring all shifts — no dominant risk identified.",
|
| 2220 |
+
# "data_point": f"Shift {worst_shift}: {df[col_shift].value_counts()[worst_shift]} alerts ({shift_pct:.1f}%)",
|
| 2221 |
+
# "reasoning": "Shift distribution is balanced."
|
| 2222 |
+
# })
|
| 2223 |
+
|
| 2224 |
+
# # 4. Operator Risk Profiling → Simple Recommendations (No AI Reasoning, No Box)
|
| 2225 |
+
# if col_operator and col_operator in df.columns and not df.empty:
|
| 2226 |
+
# top_operators = df[col_operator].value_counts().head(5)
|
| 2227 |
+
# for op_name, count in top_operators.items():
|
| 2228 |
+
# op_pct = (count / len(df)) * 100
|
| 2229 |
+
|
| 2230 |
+
# if op_pct > 5:
|
| 2231 |
+
# ai_recommendations.append({
|
| 2232 |
+
# "type": "operator",
|
| 2233 |
+
# "action": f"Coaching or mandatory rest for Operator {op_name}.",
|
| 2234 |
+
# "data_point": f"Operator {op_name}: {count} alerts ({op_pct:.1f}%)"
|
| 2235 |
+
# })
|
| 2236 |
+
# else:
|
| 2237 |
+
# ai_recommendations.append({
|
| 2238 |
+
# "type": "operator",
|
| 2239 |
+
# "action": f"Continue general monitoring for Operator {op_name}.",
|
| 2240 |
+
# "data_point": f"Operator {op_name}: {count} alerts ({op_pct:.1f}%)"
|
| 2241 |
+
# })
|
| 2242 |
+
|
| 2243 |
+
# # Render each recommendation based on type
|
| 2244 |
+
# for rec in ai_recommendations:
|
| 2245 |
+
# if rec["type"] == "operator":
|
| 2246 |
+
# # Simple format: Action + Data Point only
|
| 2247 |
+
# data_point_colored = rec['data_point'].replace(
|
| 2248 |
+
# f"({rec['data_point'].split('(')[-1]}",
|
| 2249 |
+
# f"(<span style='color: red;'>{rec['data_point'].split('(')[-1]}"
|
| 2250 |
+
# ).replace(")", "</span>)")
|
| 2251 |
+
|
| 2252 |
+
# st.markdown(
|
| 2253 |
+
# f"""
|
| 2254 |
+
# <div style="margin: 10px 0; padding: 10px; background: #f8f9fa; border-left: 4px solid #495057; border-radius: 5px;">
|
| 2255 |
+
# <strong>Action:</strong> {rec['action']}<br>
|
| 2256 |
+
# <strong>Data Point:</strong> {data_point_colored}
|
| 2257 |
+
# </div>
|
| 2258 |
+
# """,
|
| 2259 |
+
# unsafe_allow_html=True
|
| 2260 |
+
# )
|
| 2261 |
+
# else:
|
| 2262 |
+
# # Standard format with AI Reasoning and box
|
| 2263 |
+
# data_point_colored = rec['data_point'].replace(
|
| 2264 |
+
# f"({rec['data_point'].split('(')[-1]}",
|
| 2265 |
+
# f"(<span style='color: red;'>{rec['data_point'].split('(')[-1]}"
|
| 2266 |
+
# ).replace(")", "</span>)")
|
| 2267 |
+
|
| 2268 |
+
# reasoning_colored = rec['reasoning'].replace(
|
| 2269 |
+
# f"({rec['reasoning'].split('(')[-1]}",
|
| 2270 |
+
# f"(<span style='color: red;'>{rec['reasoning'].split('(')[-1]}"
|
| 2271 |
+
# ).replace(")", "</span>)")
|
| 2272 |
+
|
| 2273 |
+
# st.markdown(
|
| 2274 |
+
# f"""
|
| 2275 |
+
# <div style="
|
| 2276 |
+
# background: #f8f9fa;
|
| 2277 |
+
# border: 1px solid #dee2e6;
|
| 2278 |
+
# border-radius: 8px;
|
| 2279 |
+
# padding: 15px;
|
| 2280 |
+
# margin: 10px 0;
|
| 2281 |
+
# box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
| 2282 |
+
# ">
|
| 2283 |
+
# <div style="
|
| 2284 |
+
# font-weight: bold;
|
| 2285 |
+
# background: #e9ecef;
|
| 2286 |
+
# padding: 8px;
|
| 2287 |
+
# border-radius: 5px;
|
| 2288 |
+
# margin-bottom: 8px;
|
| 2289 |
+
# border-left: 4px solid #495057;
|
| 2290 |
+
# ">
|
| 2291 |
+
# AI Recommendation
|
| 2292 |
+
# </div>
|
| 2293 |
+
# <div style="padding: 8px 0;">
|
| 2294 |
+
# <strong>Action:</strong> {rec['action']}
|
| 2295 |
+
# </div>
|
| 2296 |
+
# <div style="
|
| 2297 |
+
# padding: 8px;
|
| 2298 |
+
# background: #f1f1f1;
|
| 2299 |
+
# border-radius: 5px;
|
| 2300 |
+
# margin: 8px 0;
|
| 2301 |
+
# ">
|
| 2302 |
+
# <strong>Data Point:</strong> {data_point_colored}
|
| 2303 |
+
# </div>
|
| 2304 |
+
# <div style="
|
| 2305 |
+
# padding: 8px;
|
| 2306 |
+
# background: #f1f1f1;
|
| 2307 |
+
# border-radius: 5px;
|
| 2308 |
+
# ">
|
| 2309 |
+
# <strong>AI Reasoning:</strong> {reasoning_colored}
|
| 2310 |
+
# </div>
|
| 2311 |
+
# </div>
|
| 2312 |
+
# """,
|
| 2313 |
+
# unsafe_allow_html=True
|
| 2314 |
+
# )
|
| 2315 |
+
|
| 2316 |
+
# if not ai_recommendations:
|
| 2317 |
+
# st.info(
|
| 2318 |
+
# "No specific data points available for AI recommendations. "
|
| 2319 |
+
# "Ensure relevant columns are present (hour, shift, operator, duration, speed)."
|
| 2320 |
+
# )
|
| 2321 |
+
|
| 2322 |
+
# # ================= FOOTER ===========================
|
| 2323 |
+
# st.markdown("---")
|
| 2324 |
+
# st.markdown(
|
| 2325 |
+
# '<div class="footer">FatigueAnalyzer - Transforming Mining Safety with Intelligent Analytics | Contact: info@bukittechnology.com</div>',
|
| 2326 |
+
# unsafe_allow_html=True
|
| 2327 |
+
# )
|
| 2328 |
+
|
| 2329 |
# =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
|
| 2330 |
st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
|
| 2331 |
|
|
|
|
| 2333 |
col_insights, col_recs = st.columns(2)
|
| 2334 |
|
| 2335 |
# =====================================================================
|
| 2336 |
+
# 🔹 KOLOM KIRI — INSIGHTS BY ADVANCED ANALYTICS (TANPA SEMUA KOTAK BIRU)
|
| 2337 |
# =====================================================================
|
| 2338 |
with col_insights:
|
| 2339 |
st.subheader("Insights by Advanced Analytics")
|
|
|
|
| 2448 |
f"Consider coaching or rest plan."
|
| 2449 |
)
|
| 2450 |
else:
|
| 2451 |
+
# ❌ HILANGKAN TEKS "is within acceptable range" DAN KOTAK BIRU
|
| 2452 |
+
# Hanya tampilkan nama operator + alert count — tanpa tambahan teks
|
| 2453 |
+
st.markdown(
|
| 2454 |
+
f"<span style='color: #2c3e50;'>Operator {op_name}: {count} alerts ({op_pct:.1f}%)</span>",
|
| 2455 |
+
unsafe_allow_html=True
|
| 2456 |
)
|
| 2457 |
else:
|
| 2458 |
st.info("Operator data not available for Operator Risk Profiling.")
|
| 2459 |
|
| 2460 |
# =====================================================================
|
| 2461 |
+
# 🔹 KOLOM KANAN — AI RECOMMENDATIONS (TIDAK BERUBAH)
|
| 2462 |
# =====================================================================
|
| 2463 |
with col_recs:
|
| 2464 |
st.subheader("Recommendations")
|