SHELLAPANDIANGANHUNGING commited on
Commit
8d770d6
·
verified ·
1 Parent(s): e94306c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +20 -559
app.py CHANGED
@@ -437,14 +437,30 @@ with st.sidebar.form("filters_form"):
437
  format_func=lambda x: "All" if x is None else x
438
  )
439
  filter_dict['site'] = selected_site
440
-
441
  # ---------------- Group Model Filter ✅ NOW WORKING ----------------
442
  all_models = sorted(df['group_model'].dropna().unique())
443
- selected_model = st.selectbox(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
  "Filter Group Model",
445
- options=[None] + all_models,
446
  format_func=lambda x: "All" if x is None else x
447
  )
 
 
 
448
  filter_dict['group_model'] = selected_model
449
 
450
  # ---------------- Shift ----------------
@@ -1724,562 +1740,7 @@ else:
1724
  st.error(f"Error in Top 10 Operator analysis: {str(e)}")
1725
  st.exception(e)
1726
 
1727
- # # =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
1728
- # st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
1729
-
1730
- # # Membagi tampilan menjadi dua kolom
1731
- # col_insights, col_recs = st.columns(2)
1732
-
1733
- # # =====================================================================
1734
- # # 🔹 KOLOM KIRI — INSIGHTS BY ADVANCED ANALYTICS
1735
- # # =====================================================================
1736
- # with col_insights:
1737
- # st.subheader("Insights by Advanced Analytics")
1738
-
1739
- # # ===================== 1. Critical Hour Analysis =====================
1740
- # critical_hours = [2, 3, 4, 5]
1741
- # critical_alerts = df[df['hour'].isin(critical_hours)]
1742
- # critical_pct = (len(critical_alerts) / len(df)) * 100 if len(df) > 0 else 0
1743
-
1744
- # st.markdown(f"**Critical Hour Risk (3-6 AM)**")
1745
- # bg_color = (
1746
- # "#ffcccc" if critical_pct > 50 else
1747
- # "#ffebcc" if critical_pct > 25 else
1748
- # "#ffffcc" if critical_pct > 10 else
1749
- # "#e6ffe6"
1750
- # )
1751
- # st.markdown(
1752
- # f'<div style="background-color: {bg_color}; padding: 10px; border-radius: 5px;">'
1753
- # f'Critical Hour Alerts: {len(critical_alerts)} ({critical_pct:.1f}% of total alerts)</div>',
1754
- # unsafe_allow_html=True
1755
- # )
1756
-
1757
- # if critical_pct > 10:
1758
- # st.warning(
1759
- # f"High risk: {critical_pct:.1f}% of fatigue alerts occur during critical hours (3-6 AM). "
1760
- # f"This is a known circadian dip period."
1761
- # )
1762
- # else:
1763
- # st.info(
1764
- # f"{critical_pct:.1f}% of alerts occur during critical hours. This is within acceptable range."
1765
- # )
1766
-
1767
- # # ===================== 2. High-Speed Fatigue Analysis =====================
1768
- # if col_speed and col_speed in df.columns:
1769
- # high_speed_threshold = 20
1770
- # high_speed_fatigue = df[df[col_speed] >= high_speed_threshold]
1771
- # high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
1772
-
1773
- # st.markdown(f"**High-Speed Fatigue Risk (Speed > {high_speed_threshold} km/h)**")
1774
- # st.markdown(
1775
- # f"""
1776
- # <div style="font-size: 24px; font-weight: bold;">{len(high_speed_fatigue)}</div>
1777
- # <div style="color: red; font-size: 14px; margin-top: -5px;">↑ {high_speed_pct:.1f}% of total alerts</div>
1778
- # """,
1779
- # unsafe_allow_html=True
1780
- # )
1781
-
1782
- # if high_speed_pct > 20:
1783
- # st.warning(
1784
- # f"High risk: {high_speed_pct:.1f}% of fatigue alerts occur at high speeds. "
1785
- # f"This increases accident severity potential."
1786
- # )
1787
- # else:
1788
- # st.info(
1789
- # f"{high_speed_pct:.1f}% of alerts occur at high speeds. This is within acceptable range."
1790
- # )
1791
- # else:
1792
- # st.info("Speed data not available for High-Speed Fatigue Analysis.")
1793
-
1794
- # # ===================== 3. Shift Pattern Analysis =====================
1795
- # if col_shift and col_shift in df.columns:
1796
- # shift_counts = df[col_shift].value_counts()
1797
- # st.markdown(f"**Shift Pattern Risk**")
1798
-
1799
- # for shift_val in shift_counts.index:
1800
- # shift_pct = (shift_counts[shift_val] / len(df)) * 100
1801
-
1802
- # st.markdown(
1803
- # f"""
1804
- # <div style="font-size: 24px; font-weight: bold;">{shift_counts[shift_val]}</div>
1805
- # <div style="color: red; font-size: 14px; margin-top: -5px;">↑ {shift_pct:.1f}% of total alerts</div>
1806
- # """,
1807
- # unsafe_allow_html=True
1808
- # )
1809
-
1810
- # if shift_pct > 50:
1811
- # st.warning(
1812
- # f"Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%). "
1813
- # f"Review shift scheduling and workload."
1814
- # )
1815
- # else:
1816
- # st.info(
1817
- # f"Shift {shift_val} alert distribution is acceptable ({shift_pct:.1f}%)."
1818
- # )
1819
- # else:
1820
- # st.info("Shift data not available for Shift Pattern Analysis.")
1821
-
1822
- # # ===================== 4. Operator Risk Profiling =====================
1823
- # if col_operator and col_operator in df.columns:
1824
- # operator_alerts = df[col_operator].value_counts()
1825
- # top_risk_operators = operator_alerts.head(5)
1826
-
1827
- # st.markdown("**High-Risk Operator Identification**")
1828
- # colors = ["#d32f2f", "#e57373", "#ef9a9a", "#ffcdd2", "#ffe1e4"]
1829
-
1830
- # for idx, (op_name, count) in enumerate(top_risk_operators.items()):
1831
- # op_pct = (count / len(df)) * 100
1832
- # color = colors[idx] if idx < len(colors) else colors[-1]
1833
-
1834
- # st.markdown(
1835
- # f"**Operator:** {op_name} \n**Alerts:** {count}"
1836
- # )
1837
- # st.markdown(
1838
- # f"<span style='font-weight:600'>Share:</span> "
1839
- # f"<span style='color:{color}; font-weight:700'>{op_pct:.1f}% of total alerts</span>",
1840
- # unsafe_allow_html=True
1841
- # )
1842
-
1843
- # if op_pct > 5:
1844
- # st.warning(
1845
- # f"Operator {op_name} has high fatigue risk ({op_pct:.1f}%). "
1846
- # f"Consider coaching or rest plan."
1847
- # )
1848
- # else:
1849
- # st.info(
1850
- # f"Operator {op_name} fatigue risk is within acceptable range ({op_pct:.1f}%)."
1851
- # )
1852
- # else:
1853
- # st.info("Operator data not available for Operator Risk Profiling.")
1854
-
1855
- # # =====================================================================
1856
- # # 🔹 KOLOM KANAN — AI RECOMMENDATIONS (PER INSIGHT)
1857
- # # =====================================================================
1858
- # with col_recs:
1859
- # st.subheader("Recommendations")
1860
-
1861
- # # Reset list to collect recommendations per insight
1862
- # ai_recommendations = []
1863
-
1864
- # # 1. Critical Hour Insight → AI Rec
1865
- # if "hour" in df.columns and not df.empty:
1866
- # peak_hour = df["hour"].value_counts().idxmax()
1867
- # critical_hours = [2, 3, 4, 5]
1868
-
1869
- # if peak_hour in critical_hours:
1870
- # ai_recommendations.append({
1871
- # "action": "Deploy enhanced fatigue monitoring systems during 3-6 AM.",
1872
- # "data_point": f"Critical Hour Alerts: {len(critical_alerts)} ({critical_pct:.1f}%)",
1873
- # "reasoning": "High percentage of alerts during circadian low period."
1874
- # })
1875
- # else:
1876
- # ai_recommendations.append({
1877
- # "action": "Monitor fatigue patterns around peak hour (Hour {peak_hour}).",
1878
- # "data_point": f"Peak Hour: {peak_hour}:00 — {df['hour'].value_counts()[peak_hour]} alerts",
1879
- # "reasoning": "This hour shows highest fatigue occurrence."
1880
- # })
1881
-
1882
- # # 2. High-Speed Insight → AI Rec
1883
- # if col_speed and col_speed in df.columns and not df.empty:
1884
- # high_speed_threshold = 20
1885
- # high_speed_fatigue = df[df[col_speed] >= high_speed_threshold]
1886
- # high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
1887
-
1888
- # if high_speed_pct > 20:
1889
- # ai_recommendations.append({
1890
- # "action": "Implement speed-reduction protocols during fatigue-prone hours.",
1891
- # "data_point": f"High-Speed Alerts: {len(high_speed_fatigue)} ({high_speed_pct:.1f}%)",
1892
- # "reasoning": "High-speed alerts increase accident severity potential."
1893
- # })
1894
- # else:
1895
- # ai_recommendations.append({
1896
- # "action": "Maintain current speed monitoring — risk level is acceptable.",
1897
- # "data_point": f"High-Speed Alerts: {len(high_speed_fatigue)} ({high_speed_pct:.1f}%)",
1898
- # "reasoning": "Current high-speed fatigue rate is within acceptable range."
1899
- # })
1900
-
1901
- # # 3. Shift Pattern Insight → AI Rec
1902
- # if col_shift and col_shift in df.columns and not df.empty:
1903
- # worst_shift = df[col_shift].value_counts().idxmax()
1904
- # shift_pct = (df[col_shift].value_counts()[worst_shift] / len(df)) * 100
1905
-
1906
- # if shift_pct > 50:
1907
- # ai_recommendations.append({
1908
- # "action": "Review shift rotation schedules for Shift {worst_shift}.",
1909
- # "data_point": f"Shift {worst_shift}: {df[col_shift].value_counts()[worst_shift]} alerts ({shift_pct:.1f}%)",
1910
- # "reasoning": "Disproportionately high fatigue alerts indicate scheduling imbalance."
1911
- # })
1912
- # else:
1913
- # ai_recommendations.append({
1914
- # "action": "Continue monitoring all shifts — no dominant risk identified.",
1915
- # "data_point": f"Shift {worst_shift}: {df[col_shift].value_counts()[worst_shift]} alerts ({shift_pct:.1f}%)",
1916
- # "reasoning": "Shift distribution is balanced."
1917
- # })
1918
-
1919
- # # 4. Operator Risk Insight → AI Rec
1920
- # if col_operator and col_operator in df.columns and not df.empty:
1921
- # worst_operator = df[col_operator].value_counts().idxmax()
1922
- # op_pct = (df[col_operator].value_counts()[worst_operator] / len(df)) * 100
1923
-
1924
- # if op_pct > 5:
1925
- # ai_recommendations.append({
1926
- # "action": "Coaching or mandatory rest for the identified high-risk operator.",
1927
- # "data_point": f"Operator {worst_operator}: {df[col_operator].value_counts()[worst_operator]} alerts ({op_pct:.1f}%)",
1928
- # "reasoning": "Operator has highest fatigue alerts — requires individual intervention."
1929
- # })
1930
- # else:
1931
- # ai_recommendations.append({
1932
- # "action": "Continue general monitoring — no single operator dominates risk.",
1933
- # "data_point": f"Top Operator: {worst_operator} — {df[col_operator].value_counts()[worst_operator]} alerts ({op_pct:.1f}%)",
1934
- # "reasoning": "Risk is distributed across operators — no urgent individual action needed."
1935
- # })
1936
-
1937
- # # Render each recommendation as a card
1938
- # for rec in ai_recommendations:
1939
- # # Highlight percentages in red
1940
- # data_point_colored = rec['data_point'].replace(
1941
- # f"({rec['data_point'].split('(')[-1]}",
1942
- # f"(<span style='color: red;'>{rec['data_point'].split('(')[-1]}"
1943
- # ).replace(")", "</span>)")
1944
-
1945
- # reasoning_colored = rec['reasoning'].replace(
1946
- # f"({rec['reasoning'].split('(')[-1]}",
1947
- # f"(<span style='color: red;'>{rec['reasoning'].split('(')[-1]}"
1948
- # ).replace(")", "</span>)")
1949
-
1950
- # st.markdown(
1951
- # f"""
1952
- # <div style="
1953
- # background: #f8f9fa;
1954
- # border: 1px solid #dee2e6;
1955
- # border-radius: 8px;
1956
- # padding: 15px;
1957
- # margin: 10px 0;
1958
- # box-shadow: 0 2px 8px rgba(0,0,0,0.05);
1959
- # ">
1960
- # <div style="
1961
- # font-weight: bold;
1962
- # background: #e9ecef;
1963
- # padding: 8px;
1964
- # border-radius: 5px;
1965
- # margin-bottom: 8px;
1966
- # border-left: 4px solid #495057;
1967
- # ">
1968
- # AI Recommendation
1969
- # </div>
1970
- # <div style="padding: 8px 0;">
1971
- # <strong>Action:</strong> {rec['action']}
1972
- # </div>
1973
- # <div style="
1974
- # padding: 8px;
1975
- # background: #f1f1f1;
1976
- # border-radius: 5px;
1977
- # margin: 8px 0;
1978
- # ">
1979
- # <strong>Data Point:</strong> {data_point_colored}
1980
- # </div>
1981
- # <div style="
1982
- # padding: 8px;
1983
- # background: #f1f1f1;
1984
- # border-radius: 5px;
1985
- # ">
1986
- # <strong>AI Reasoning:</strong> {reasoning_colored}
1987
- # </div>
1988
- # </div>
1989
- # """,
1990
- # unsafe_allow_html=True
1991
- # )
1992
-
1993
- # if not ai_recommendations:
1994
- # st.info(
1995
- # "No specific data points available for AI recommendations. "
1996
- # "Ensure relevant columns are present (hour, shift, operator, duration, speed)."
1997
- # )
1998
-
1999
- # # ================= FOOTER ===========================
2000
- # st.markdown("---")
2001
- # st.markdown(
2002
- # '<div class="footer">FatigueAnalyzer - Transforming Mining Safety with Intelligent Analytics | Contact: info@bukittechnology.com</div>',
2003
- # unsafe_allow_html=True
2004
- # =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
2005
- # st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
2006
-
2007
- # # Membagi tampilan menjadi dua kolom
2008
- # col_insights, col_recs = st.columns(2)
2009
-
2010
- # # =====================================================================
2011
- # # 🔹 KOLOM KIRI — INSIGHTS BY ADVANCED ANALYTICS
2012
- # # =====================================================================
2013
- # with col_insights:
2014
- # st.subheader("Insights by Advanced Analytics")
2015
-
2016
- # # ===================== 1. Critical Hour Analysis =====================
2017
- # critical_hours = [2, 3, 4, 5]
2018
- # critical_alerts = df[df['hour'].isin(critical_hours)]
2019
- # critical_pct = (len(critical_alerts) / len(df)) * 100 if len(df) > 0 else 0
2020
-
2021
- # st.markdown(f"**Critical Hour Risk (3-6 AM)**")
2022
- # bg_color = (
2023
- # "#ffcccc" if critical_pct > 50 else
2024
- # "#ffebcc" if critical_pct > 25 else
2025
- # "#ffffcc" if critical_pct > 10 else
2026
- # "#e6ffe6"
2027
- # )
2028
- # st.markdown(
2029
- # f'<div style="background-color: {bg_color}; padding: 10px; border-radius: 5px;">'
2030
- # f'Critical Hour Alerts: {len(critical_alerts)} ({critical_pct:.1f}% of total alerts)</div>',
2031
- # unsafe_allow_html=True
2032
- # )
2033
-
2034
- # if critical_pct > 10:
2035
- # st.warning(
2036
- # f"High risk: {critical_pct:.1f}% of fatigue alerts occur during critical hours (3-6 AM). "
2037
- # f"This is a known circadian dip period."
2038
- # )
2039
- # else:
2040
- # st.info(
2041
- # f"{critical_pct:.1f}% of alerts occur during critical hours. This is within acceptable range."
2042
- # )
2043
-
2044
- # # ===================== 2. High-Speed Fatigue Analysis =====================
2045
- # if col_speed and col_speed in df.columns:
2046
- # high_speed_threshold = 20
2047
- # high_speed_fatigue = df[df[col_speed] >= high_speed_threshold]
2048
- # high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
2049
-
2050
- # st.markdown(f"**High-Speed Fatigue Risk (Speed > {high_speed_threshold} km/h)**")
2051
- # st.markdown(
2052
- # f"""
2053
- # <div style="font-size: 24px; font-weight: bold;">{len(high_speed_fatigue)}</div>
2054
- # <div style="color: red; font-size: 14px; margin-top: -5px;">↑ {high_speed_pct:.1f}% of total alerts</div>
2055
- # """,
2056
- # unsafe_allow_html=True
2057
- # )
2058
-
2059
- # if high_speed_pct > 20:
2060
- # st.warning(
2061
- # f"High risk: {high_speed_pct:.1f}% of fatigue alerts occur at high speeds. "
2062
- # f"This increases accident severity potential."
2063
- # )
2064
- # else:
2065
- # st.info(
2066
- # f"{high_speed_pct:.1f}% of alerts occur at high speeds. This is within acceptable range."
2067
- # )
2068
- # else:
2069
- # st.info("Speed data not available for High-Speed Fatigue Analysis.")
2070
-
2071
- # # ===================== 3. Shift Pattern Analysis =====================
2072
- # if col_shift and col_shift in df.columns:
2073
- # shift_counts = df[col_shift].value_counts()
2074
- # st.markdown(f"**Shift Pattern Risk**")
2075
-
2076
- # for shift_val in shift_counts.index:
2077
- # shift_pct = (shift_counts[shift_val] / len(df)) * 100
2078
-
2079
- # st.markdown(
2080
- # f"""
2081
- # <div style="font-size: 24px; font-weight: bold;">{shift_counts[shift_val]}</div>
2082
- # <div style="color: red; font-size: 14px; margin-top: -5px;">↑ {shift_pct:.1f}% of total alerts</div>
2083
- # """,
2084
- # unsafe_allow_html=True
2085
- # )
2086
-
2087
- # if shift_pct > 50:
2088
- # st.warning(
2089
- # f"Shift {shift_val} has disproportionately high alerts ({shift_pct:.1f}%). "
2090
- # f"Review shift scheduling and workload."
2091
- # )
2092
- # else:
2093
- # st.info(
2094
- # f"Shift {shift_val} alert distribution is acceptable ({shift_pct:.1f}%)."
2095
- # )
2096
- # else:
2097
- # st.info("Shift data not available for Shift Pattern Analysis.")
2098
-
2099
- # # ===================== 4. Operator Risk Profiling =====================
2100
- # if col_operator and col_operator in df.columns:
2101
- # operator_alerts = df[col_operator].value_counts()
2102
- # top_risk_operators = operator_alerts.head(5)
2103
-
2104
- # st.markdown("**High-Risk Operator Identification**")
2105
- # colors = ["#d32f2f", "#e57373", "#ef9a9a", "#ffcdd2", "#ffe1e4"]
2106
-
2107
- # for idx, (op_name, count) in enumerate(top_risk_operators.items()):
2108
- # op_pct = (count / len(df)) * 100
2109
- # color = colors[idx] if idx < len(colors) else colors[-1]
2110
-
2111
- # st.markdown(
2112
- # f"**Operator:** {op_name} \n**Alerts:** {count}"
2113
- # )
2114
- # st.markdown(
2115
- # f"<span style='font-weight:600'>Share:</span> "
2116
- # f"<span style='color:{color}; font-weight:700'>{op_pct:.1f}% of total alerts</span>",
2117
- # unsafe_allow_html=True
2118
- # )
2119
-
2120
- # if op_pct > 5:
2121
- # st.warning(
2122
- # f"Operator {op_name} has high fatigue risk ({op_pct:.1f}%). "
2123
- # f"Consider coaching or rest plan."
2124
- # )
2125
- # else:
2126
- # st.info(
2127
- # f"Operator {op_name} fatigue risk is within acceptable range ({op_pct:.1f}%)."
2128
- # )
2129
- # else:
2130
- # st.info("Operator data not available for Operator Risk Profiling.")
2131
-
2132
- # # =====================================================================
2133
- # # 🔹 KOLOM KANAN — AI RECOMMENDATIONS (PER INSIGHT)
2134
- # # =====================================================================
2135
- # with col_recs:
2136
- # st.subheader("Recommendations")
2137
-
2138
- # # Reset list to collect recommendations per insight
2139
- # ai_recommendations = []
2140
-
2141
- # # 1. Critical Hour Insight → AI Rec
2142
- # if "hour" in df.columns and not df.empty:
2143
- # peak_hour = df["hour"].value_counts().idxmax()
2144
- # critical_hours = [2, 3, 4, 5]
2145
-
2146
- # if peak_hour in critical_hours:
2147
- # ai_recommendations.append({
2148
- # "action": "Deploy enhanced fatigue monitoring systems during 3-6 AM.",
2149
- # "data_point": f"Critical Hour Alerts: {len(critical_alerts)} ({critical_pct:.1f}%)",
2150
- # "reasoning": "High percentage of alerts during circadian low period."
2151
- # })
2152
- # else:
2153
- # ai_recommendations.append({
2154
- # "action": "Monitor fatigue patterns around peak hour (Hour {peak_hour}).",
2155
- # "data_point": f"Peak Hour: {peak_hour}:00 — {df['hour'].value_counts()[peak_hour]} alerts",
2156
- # "reasoning": "This hour shows highest fatigue occurrence."
2157
- # })
2158
-
2159
- # # 2. High-Speed Insight → AI Rec
2160
- # if col_speed and col_speed in df.columns and not df.empty:
2161
- # high_speed_threshold = 20
2162
- # high_speed_fatigue = df[df[col_speed] >= high_speed_threshold]
2163
- # high_speed_pct = (len(high_speed_fatigue) / len(df)) * 100 if len(df) > 0 else 0
2164
-
2165
- # if high_speed_pct > 20:
2166
- # ai_recommendations.append({
2167
- # "action": "Implement speed-reduction protocols during fatigue-prone hours.",
2168
- # "data_point": f"High-Speed Alerts: {len(high_speed_fatigue)} ({high_speed_pct:.1f}%)",
2169
- # "reasoning": "High-speed alerts increase accident severity potential."
2170
- # })
2171
- # else:
2172
- # ai_recommendations.append({
2173
- # "action": "Maintain current speed monitoring — risk level is acceptable.",
2174
- # "data_point": f"High-Speed Alerts: {len(high_speed_fatigue)} ({high_speed_pct:.1f}%)",
2175
- # "reasoning": "Current high-speed fatigue rate is within acceptable range."
2176
- # })
2177
-
2178
- # # 3. Shift Pattern Insight → AI Rec
2179
- # if col_shift and col_shift in df.columns and not df.empty:
2180
- # worst_shift = df[col_shift].value_counts().idxmax()
2181
- # shift_pct = (df[col_shift].value_counts()[worst_shift] / len(df)) * 100
2182
-
2183
- # if shift_pct > 50:
2184
- # ai_recommendations.append({
2185
- # "action": "Review shift rotation schedules for Shift {worst_shift}.",
2186
- # "data_point": f"Shift {worst_shift}: {df[col_shift].value_counts()[worst_shift]} alerts ({shift_pct:.1f}%)",
2187
- # "reasoning": "Disproportionately high fatigue alerts indicate scheduling imbalance."
2188
- # })
2189
- # else:
2190
- # ai_recommendations.append({
2191
- # "action": "Continue monitoring all shifts — no dominant risk identified.",
2192
- # "data_point": f"Shift {worst_shift}: {df[col_shift].value_counts()[worst_shift]} alerts ({shift_pct:.1f}%)",
2193
- # "reasoning": "Shift distribution is balanced."
2194
- # })
2195
-
2196
- # # 4. Operator Risk Insight → AI Rec
2197
- # if col_operator and col_operator in df.columns and not df.empty:
2198
- # worst_operator = df[col_operator].value_counts().idxmax()
2199
- # op_pct = (df[col_operator].value_counts()[worst_operator] / len(df)) * 100
2200
-
2201
- # if op_pct > 5:
2202
- # ai_recommendations.append({
2203
- # "action": "Coaching or mandatory rest for the identified high-risk operator.",
2204
- # "data_point": f"Operator {worst_operator}: {df[col_operator].value_counts()[worst_operator]} alerts ({op_pct:.1f}%)",
2205
- # "reasoning": "Operator has highest fatigue alerts — requires individual intervention."
2206
- # })
2207
- # else:
2208
- # ai_recommendations.append({
2209
- # "action": "Continue general monitoring — no single operator dominates risk.",
2210
- # "data_point": f"Top Operator: {worst_operator} — {df[col_operator].value_counts()[worst_operator]} alerts ({op_pct:.1f}%)",
2211
- # "reasoning": "Risk is distributed across operators — no urgent individual action needed."
2212
- # })
2213
-
2214
- # # Render each recommendation as a card
2215
- # for rec in ai_recommendations:
2216
- # # Highlight percentages in red
2217
- # data_point_colored = rec['data_point'].replace(
2218
- # f"({rec['data_point'].split('(')[-1]}",
2219
- # f"(<span style='color: red;'>{rec['data_point'].split('(')[-1]}"
2220
- # ).replace(")", "</span>)")
2221
-
2222
- # reasoning_colored = rec['reasoning'].replace(
2223
- # f"({rec['reasoning'].split('(')[-1]}",
2224
- # f"(<span style='color: red;'>{rec['reasoning'].split('(')[-1]}"
2225
- # ).replace(")", "</span>)")
2226
-
2227
- # st.markdown(
2228
- # f"""
2229
- # <div style="
2230
- # background: #f8f9fa;
2231
- # border: 1px solid #dee2e6;
2232
- # border-radius: 8px;
2233
- # padding: 15px;
2234
- # margin: 10px 0;
2235
- # box-shadow: 0 2px 8px rgba(0,0,0,0.05);
2236
- # ">
2237
- # <div style="
2238
- # font-weight: bold;
2239
- # background: #e9ecef;
2240
- # padding: 8px;
2241
- # border-radius: 5px;
2242
- # margin-bottom: 8px;
2243
- # border-left: 4px solid #495057;
2244
- # ">
2245
- # AI Recommendation
2246
- # </div>
2247
- # <div style="padding: 8px 0;">
2248
- # <strong>Action:</strong> {rec['action']}
2249
- # </div>
2250
- # <div style="
2251
- # padding: 8px;
2252
- # background: #f1f1f1;
2253
- # border-radius: 5px;
2254
- # margin: 8px 0;
2255
- # ">
2256
- # <strong>Data Point:</strong> {data_point_colored}
2257
- # </div>
2258
- # <div style="
2259
- # padding: 8px;
2260
- # background: #f1f1f1;
2261
- # border-radius: 5px;
2262
- # ">
2263
- # <strong>AI Reasoning:</strong> {reasoning_colored}
2264
- # </div>
2265
- # </div>
2266
- # """,
2267
- # unsafe_allow_html=True
2268
- # )
2269
-
2270
- # if not ai_recommendations:
2271
- # st.info(
2272
- # "No specific data points available for AI recommendations. "
2273
- # "Ensure relevant columns are present (hour, shift, operator, duration, speed)."
2274
- # )
2275
-
2276
- # # ================= FOOTER ===========================
2277
- # st.markdown("---")
2278
- # st.markdown(
2279
- # '<div class="footer">FatigueAnalyzer - Transforming Mining Safety with Intelligent Analytics | Contact: info@bukittechnology.com</div>',
2280
- # unsafe_allow_html=True
2281
- # )
2282
- # # )
2283
 
2284
  # =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
2285
  st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")
 
437
  format_func=lambda x: "All" if x is None else x
438
  )
439
  filter_dict['site'] = selected_site
 
440
  # ---------------- Group Model Filter ✅ NOW WORKING ----------------
441
  all_models = sorted(df['group_model'].dropna().unique())
442
+
443
+ # Define display names for specific values
444
+ display_map = {
445
+ "OB HAULLER": "OB HAULER",
446
+ "HAULING COAL": "COAL HAULING"
447
+ }
448
+
449
+ # Create display options
450
+ display_options = [display_map.get(model, model) for model in all_models]
451
+
452
+ # Create reverse map to get original value back
453
+ reverse_map = {v: k for k, v in display_map.items()}
454
+
455
+ # Create selectbox with display names
456
+ selected_display = st.selectbox(
457
  "Filter Group Model",
458
+ options=[None] + display_options,
459
  format_func=lambda x: "All" if x is None else x
460
  )
461
+
462
+ # Map back to original value for filtering
463
+ selected_model = reverse_map.get(selected_display, selected_display) if selected_display else None
464
  filter_dict['group_model'] = selected_model
465
 
466
  # ---------------- Shift ----------------
 
1740
  st.error(f"Error in Top 10 Operator analysis: {str(e)}")
1741
  st.exception(e)
1742
 
1743
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1744
 
1745
  # =================== OBJECTIVE 6: Automated Insights & AI Recommendations =====================
1746
  st.subheader("OBJECTIVE 6: Instant Insights & Recommendations")