Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -549,7 +549,7 @@ if 'temuan_kode_distrik' in df_local.columns:
|
|
| 549 |
f"(<strong>{top_company_pg['avg_monthly_ratio']:.2f}</strong>), indicating potentially high exposure or active reporting. "
|
| 550 |
f"Consider reviewing their operational procedures. "
|
| 551 |
f"Conversely, <strong>{low_company_pg['nama_perusahaan']}</strong> has the lowest ratio "
|
| 552 |
-
f"(<strong>{low_company_pg['avg_monthly_ratio']:.2f}</strong>), suggesting
|
| 553 |
f"</div>"
|
| 554 |
)
|
| 555 |
st.markdown(insight_text, unsafe_allow_html=True)
|
|
@@ -570,10 +570,10 @@ if 'temuan_kode_distrik' in df_local.columns:
|
|
| 570 |
st.markdown("### Insight")
|
| 571 |
insight_text = (
|
| 572 |
f"<div class='ai-insight'>"
|
| 573 |
-
f"In UM Area,
|
| 574 |
-
f"(<strong>{top_company_um['avg_monthly_ratio']:.2f}</strong>), warranting a focused safety audit. "
|
| 575 |
-
f"<strong>{low_company_um['nama_perusahaan']}</strong> shows the lowest ratio "
|
| 576 |
-
f"(<strong>{low_company_um['avg_monthly_ratio']:.2f}</strong>), which could reflect strong safety practices or requires verification of reporting completeness."
|
| 577 |
f"</div>"
|
| 578 |
)
|
| 579 |
st.markdown(insight_text, unsafe_allow_html=True)
|
|
@@ -848,11 +848,11 @@ with col_3a:
|
|
| 848 |
low = sorted_data.iloc[-1] if sort_option_3a == "Top 10" else sorted_data_all.iloc[0]
|
| 849 |
insight_text = (
|
| 850 |
f"<div class='ai-insight'>"
|
| 851 |
-
f"The division <strong>{top['nama']}</strong> has the
|
| 852 |
f"(<strong>{top['avg_monthly_ratio']:.2f}</strong>). "
|
| 853 |
-
f"<strong>{low['nama']}</strong> has the
|
| 854 |
f"(<strong>{low['avg_monthly_ratio']:.2f}</strong>). "
|
| 855 |
-
f"Monitor
|
| 856 |
f"</div>"
|
| 857 |
)
|
| 858 |
st.markdown(insight_text, unsafe_allow_html=True)
|
|
@@ -911,9 +911,9 @@ with col_3c:
|
|
| 911 |
low = sorted_data.iloc[-1] if sort_option_3c == "Top 10" else sorted_data_all.iloc[0]
|
| 912 |
insight_text = (
|
| 913 |
f"<div class='ai-insight'>"
|
| 914 |
-
f"The reporter <strong>{top['creator_name']}</strong> has the
|
| 915 |
f"(<strong>{top['avg_monthly_rate']:.2f}</strong>). "
|
| 916 |
-
f"<strong>{low['creator_name']}</strong> has the
|
| 917 |
f"(<strong>{low['avg_monthly_rate']:.2f}</strong>). "
|
| 918 |
f"Recognize high performers and investigate low performers."
|
| 919 |
f"</div>"
|
|
@@ -1124,7 +1124,7 @@ else:
|
|
| 1124 |
|
| 1125 |
|
| 1126 |
# =================== 5. Matrix (Tetap Dipertahankan) ===================
|
| 1127 |
-
st.markdown("<h3 class='section-title'>OBJECTIVE 5 - Findings vs Lead Time: Which
|
| 1128 |
|
| 1129 |
import math
|
| 1130 |
import plotly.express as px
|
|
@@ -1185,7 +1185,7 @@ try:
|
|
| 1185 |
# 6. Merge Risk Matrix
|
| 1186 |
# ============================
|
| 1187 |
risk_matrix = operator_avg.merge(operator_lead, on='nama', how='left')
|
| 1188 |
-
risk_matrix = risk_matrix.rename(columns={'nama': '
|
| 1189 |
# Handle operator tanpa lead time (e.g., belum closed)
|
| 1190 |
risk_matrix['Average Lead Time'] = risk_matrix['Average Lead Time'].fillna(0)
|
| 1191 |
# ============================
|
|
@@ -1638,14 +1638,14 @@ df_category = predict_categories(df_filtered)
|
|
| 1638 |
|
| 1639 |
# 🎯 PANEL 1: Creators (FILTERED: Coverage < 90% & Slope < 0)
|
| 1640 |
st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
|
| 1641 |
-
st.markdown("<div class='predictive-header'>1. Which Reporters Are Predicted to Have
|
| 1642 |
if not df_creator.empty:
|
| 1643 |
-
cols = ['Creator', 'Avg Reports/Month', 'Coverage (%)', 'Trend Slope', 'Trend'
|
| 1644 |
|
| 1645 |
# 🔥 Rename hanya untuk DISPLAY, bukan data asli
|
| 1646 |
-
df_display = df_creator[cols].rename(columns={
|
| 1647 |
-
|
| 1648 |
-
})
|
| 1649 |
|
| 1650 |
html = df_display.to_html(escape=False, index=False, table_id="tbl-creators")
|
| 1651 |
st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
|
|
@@ -1674,14 +1674,14 @@ st.markdown("</div>", unsafe_allow_html=True)
|
|
| 1674 |
|
| 1675 |
# 🎯 PANEL 2: Locations (FILTERED: Coverage < 90% & Slope < 0)
|
| 1676 |
st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
|
| 1677 |
-
st.markdown("<div class='predictive-header'>2. Which Locations Are Predicted to Have
|
| 1678 |
if not df_location.empty:
|
| 1679 |
-
cols = ['Location', 'Avg Reports/Month', 'Coverage (%)', 'Trend Slope', 'Trend'
|
| 1680 |
|
| 1681 |
-
# 🔥 Rename hanya untuk DISPLAY, bukan data asli
|
| 1682 |
-
df_display = df_location[cols].rename(columns={
|
| 1683 |
-
|
| 1684 |
-
})
|
| 1685 |
|
| 1686 |
html = df_display.to_html(escape=False, index=False, table_id="tbl-locations")
|
| 1687 |
st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
|
|
@@ -1710,14 +1710,14 @@ st.markdown("</div>", unsafe_allow_html=True)
|
|
| 1710 |
|
| 1711 |
# 🎯 PANEL 3: Divisions (FILTERED: Coverage < 90% & Slope < 0)
|
| 1712 |
st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
|
| 1713 |
-
st.markdown("<div class='predictive-header'>3. Which Divisions Are Predicted to Have
|
| 1714 |
if not df_division.empty:
|
| 1715 |
-
cols = ['Division', 'Avg Reports/Month', 'Coverage (%)', 'Trend Slope', 'Trend'
|
| 1716 |
|
| 1717 |
-
# 🔥 Rename hanya untuk DISPLAY, bukan data asli
|
| 1718 |
-
df_display = df_division[cols].rename(columns={
|
| 1719 |
-
|
| 1720 |
-
})
|
| 1721 |
|
| 1722 |
html = df_display.to_html(escape=False, index=False, table_id="tbl-divisions")
|
| 1723 |
st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
|
|
@@ -1748,7 +1748,7 @@ st.markdown("</div>", unsafe_allow_html=True)
|
|
| 1748 |
# st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
|
| 1749 |
st.markdown(
|
| 1750 |
"<div class='predictive-header'>"
|
| 1751 |
-
"4. Which Issue Categories Are Likely to Appear in the Next 3 Months (
|
| 1752 |
"<span style='font-size:0.75em; font-weight:400; color:#003DA5;'>"
|
| 1753 |
" (* Categorization uses NLP — Natural Language Processing from random text)"
|
| 1754 |
"</span>"
|
|
@@ -1808,9 +1808,9 @@ if not df_category.empty:
|
|
| 1808 |
fig.add_trace(go.Scatter(
|
| 1809 |
x=df_plot['Category'],
|
| 1810 |
y=df_plot['Y'],
|
| 1811 |
-
mode='markers
|
| 1812 |
marker=dict(
|
| 1813 |
-
size=df_plot['Size'] *
|
| 1814 |
color='#003DA5',
|
| 1815 |
line=dict(width=2, color='white'),
|
| 1816 |
opacity=0.8
|
|
@@ -1885,15 +1885,15 @@ if not df_category.empty:
|
|
| 1885 |
st.plotly_chart(fig, use_container_width=True)
|
| 1886 |
|
| 1887 |
# Insight
|
| 1888 |
-
st.markdown("### 💡 Insight")
|
| 1889 |
-
insight_text = (
|
| 1890 |
-
|
| 1891 |
-
|
| 1892 |
-
|
| 1893 |
-
|
| 1894 |
-
|
| 1895 |
-
)
|
| 1896 |
-
st.markdown(insight_text, unsafe_allow_html=True)
|
| 1897 |
else:
|
| 1898 |
st.info("No data available for non-positive issue categories with 100% coverage and positive trend.")
|
| 1899 |
|
|
|
|
| 549 |
f"(<strong>{top_company_pg['avg_monthly_ratio']:.2f}</strong>), indicating potentially high exposure or active reporting. "
|
| 550 |
f"Consider reviewing their operational procedures. "
|
| 551 |
f"Conversely, <strong>{low_company_pg['nama_perusahaan']}</strong> has the lowest ratio "
|
| 552 |
+
f"(<strong>{low_company_pg['avg_monthly_ratio']:.2f}</strong>), suggesting the need to actively improve reporting frequency."
|
| 553 |
f"</div>"
|
| 554 |
)
|
| 555 |
st.markdown(insight_text, unsafe_allow_html=True)
|
|
|
|
| 570 |
st.markdown("### Insight")
|
| 571 |
insight_text = (
|
| 572 |
f"<div class='ai-insight'>"
|
| 573 |
+
f"In UM Area, all companies show almost the same average finding per person ration"
|
| 574 |
+
# f"(<strong>{top_company_um['avg_monthly_ratio']:.2f}</strong>), warranting a focused safety audit. "
|
| 575 |
+
# f"<strong>{low_company_um['nama_perusahaan']}</strong> shows the lowest ratio "
|
| 576 |
+
# f"(<strong>{low_company_um['avg_monthly_ratio']:.2f}</strong>), which could reflect strong safety practices or requires verification of reporting completeness."
|
| 577 |
f"</div>"
|
| 578 |
)
|
| 579 |
st.markdown(insight_text, unsafe_allow_html=True)
|
|
|
|
| 848 |
low = sorted_data.iloc[-1] if sort_option_3a == "Top 10" else sorted_data_all.iloc[0]
|
| 849 |
insight_text = (
|
| 850 |
f"<div class='ai-insight'>"
|
| 851 |
+
f"The division <strong>{top['nama']}</strong> has the lowest average finding-to-person ratio "
|
| 852 |
f"(<strong>{top['avg_monthly_ratio']:.2f}</strong>). "
|
| 853 |
+
f"<strong>{low['nama']}</strong> has the highest ratio "
|
| 854 |
f"(<strong>{low['avg_monthly_ratio']:.2f}</strong>). "
|
| 855 |
+
f"Monitor low-ratio divisions for potential systemic issues and verify reporting completeness."
|
| 856 |
f"</div>"
|
| 857 |
)
|
| 858 |
st.markdown(insight_text, unsafe_allow_html=True)
|
|
|
|
| 911 |
low = sorted_data.iloc[-1] if sort_option_3c == "Top 10" else sorted_data_all.iloc[0]
|
| 912 |
insight_text = (
|
| 913 |
f"<div class='ai-insight'>"
|
| 914 |
+
f"The reporter <strong>{top['creator_name']}</strong> has the lowest average monthly finding rate "
|
| 915 |
f"(<strong>{top['avg_monthly_rate']:.2f}</strong>). "
|
| 916 |
+
f"<strong>{low['creator_name']}</strong> has the highest rate "
|
| 917 |
f"(<strong>{low['avg_monthly_rate']:.2f}</strong>). "
|
| 918 |
f"Recognize high performers and investigate low performers."
|
| 919 |
f"</div>"
|
|
|
|
| 1124 |
|
| 1125 |
|
| 1126 |
# =================== 5. Matrix (Tetap Dipertahankan) ===================
|
| 1127 |
+
st.markdown("<h3 class='section-title'>OBJECTIVE 5 - Findings vs Lead Time: Which Divisions Move Slow?</h3>", unsafe_allow_html=True)
|
| 1128 |
|
| 1129 |
import math
|
| 1130 |
import plotly.express as px
|
|
|
|
| 1185 |
# 6. Merge Risk Matrix
|
| 1186 |
# ============================
|
| 1187 |
risk_matrix = operator_avg.merge(operator_lead, on='nama', how='left')
|
| 1188 |
+
risk_matrix = risk_matrix.rename(columns={'nama': 'Division'})
|
| 1189 |
# Handle operator tanpa lead time (e.g., belum closed)
|
| 1190 |
risk_matrix['Average Lead Time'] = risk_matrix['Average Lead Time'].fillna(0)
|
| 1191 |
# ============================
|
|
|
|
| 1638 |
|
| 1639 |
# 🎯 PANEL 1: Creators (FILTERED: Coverage < 90% & Slope < 0)
|
| 1640 |
st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
|
| 1641 |
+
st.markdown("<div class='predictive-header'>1. Which Reporters Are Predicted to Have Less Future Inspections? (Top 10 Most Declining)</div>", unsafe_allow_html=True)
|
| 1642 |
if not df_creator.empty:
|
| 1643 |
+
cols = ['Creator', 'Avg Reports/Month', 'Coverage (%)', 'Trend Slope', 'Trend']
|
| 1644 |
|
| 1645 |
# 🔥 Rename hanya untuk DISPLAY, bukan data asli
|
| 1646 |
+
# df_display = df_creator[cols].rename(columns={
|
| 1647 |
+
# "Reason": "Reason Forecast"
|
| 1648 |
+
# })
|
| 1649 |
|
| 1650 |
html = df_display.to_html(escape=False, index=False, table_id="tbl-creators")
|
| 1651 |
st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
|
|
|
|
| 1674 |
|
| 1675 |
# 🎯 PANEL 2: Locations (FILTERED: Coverage < 90% & Slope < 0)
|
| 1676 |
st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
|
| 1677 |
+
st.markdown("<div class='predictive-header'>2. Which Locations Are Predicted to Have Less Future Inspections? (Top 10 Most Declining)</div>", unsafe_allow_html=True)
|
| 1678 |
if not df_location.empty:
|
| 1679 |
+
cols = ['Location', 'Avg Reports/Month', 'Coverage (%)', 'Trend Slope', 'Trend']
|
| 1680 |
|
| 1681 |
+
# # 🔥 Rename hanya untuk DISPLAY, bukan data asli
|
| 1682 |
+
# df_display = df_location[cols].rename(columns={
|
| 1683 |
+
# "Reason": "Reason Forecast"
|
| 1684 |
+
# })
|
| 1685 |
|
| 1686 |
html = df_display.to_html(escape=False, index=False, table_id="tbl-locations")
|
| 1687 |
st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
|
|
|
|
| 1710 |
|
| 1711 |
# 🎯 PANEL 3: Divisions (FILTERED: Coverage < 90% & Slope < 0)
|
| 1712 |
st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
|
| 1713 |
+
st.markdown("<div class='predictive-header'>3. Which Divisions Are Predicted to Have Less Future Inspections? (Top 10 Most Declining)</div>", unsafe_allow_html=True)
|
| 1714 |
if not df_division.empty:
|
| 1715 |
+
cols = ['Division', 'Avg Reports/Month', 'Coverage (%)', 'Trend Slope', 'Trend']
|
| 1716 |
|
| 1717 |
+
# # 🔥 Rename hanya untuk DISPLAY, bukan data asli
|
| 1718 |
+
# df_display = df_division[cols].rename(columns={
|
| 1719 |
+
# "Reason": "Reason Forecast"
|
| 1720 |
+
# })
|
| 1721 |
|
| 1722 |
html = df_display.to_html(escape=False, index=False, table_id="tbl-divisions")
|
| 1723 |
st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
|
|
|
|
| 1748 |
# st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
|
| 1749 |
st.markdown(
|
| 1750 |
"<div class='predictive-header'>"
|
| 1751 |
+
"4. Which Issue Categories Are Likely to Appear in the Next 3 Months (Increasing Trend Only)"
|
| 1752 |
"<span style='font-size:0.75em; font-weight:400; color:#003DA5;'>"
|
| 1753 |
" (* Categorization uses NLP — Natural Language Processing from random text)"
|
| 1754 |
"</span>"
|
|
|
|
| 1808 |
fig.add_trace(go.Scatter(
|
| 1809 |
x=df_plot['Category'],
|
| 1810 |
y=df_plot['Y'],
|
| 1811 |
+
mode='markers',
|
| 1812 |
marker=dict(
|
| 1813 |
+
size=df_plot['Size'] * 1.5, # Skala ukuran agar tidak terlalu besar
|
| 1814 |
color='#003DA5',
|
| 1815 |
line=dict(width=2, color='white'),
|
| 1816 |
opacity=0.8
|
|
|
|
| 1885 |
st.plotly_chart(fig, use_container_width=True)
|
| 1886 |
|
| 1887 |
# Insight
|
| 1888 |
+
# st.markdown("### 💡 Insight")
|
| 1889 |
+
# insight_text = (
|
| 1890 |
+
# f"<div class='ai-insight'>"
|
| 1891 |
+
# f"The scatter plot visualizes issue categories with 100% coverage and positive trend. "
|
| 1892 |
+
# f"Categories higher on the Y-axis indicate stronger upward trends, while larger circles indicate higher average monthly frequency. "
|
| 1893 |
+
# f"This helps identify which non-positive issues are both increasing in trend and frequently reported — these should be prioritized for intervention."
|
| 1894 |
+
# f"</div>"
|
| 1895 |
+
# )
|
| 1896 |
+
# st.markdown(insight_text, unsafe_allow_html=True)
|
| 1897 |
else:
|
| 1898 |
st.info("No data available for non-positive issue categories with 100% coverage and positive trend.")
|
| 1899 |
|