Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1473,52 +1473,52 @@ def predict_locations(df):
|
|
| 1473 |
df_res = pd.DataFrame(results)
|
| 1474 |
return df_res.sort_values('Trend Slope', ascending=True) if not df_res.empty else df_res # most negative first
|
| 1475 |
|
| 1476 |
-
# ——————— 2. Divisions ———————
|
| 1477 |
-
def predict_divisions(df):
|
| 1478 |
-
|
| 1479 |
-
|
| 1480 |
|
| 1481 |
-
|
| 1482 |
-
|
| 1483 |
-
|
| 1484 |
|
| 1485 |
-
|
| 1486 |
-
|
| 1487 |
-
|
| 1488 |
-
|
| 1489 |
-
|
| 1490 |
-
|
| 1491 |
-
|
| 1492 |
-
|
| 1493 |
-
|
| 1494 |
|
| 1495 |
-
|
| 1496 |
-
|
| 1497 |
-
|
| 1498 |
-
|
| 1499 |
-
|
| 1500 |
-
|
| 1501 |
-
|
| 1502 |
|
| 1503 |
-
|
| 1504 |
-
|
| 1505 |
-
|
| 1506 |
-
|
| 1507 |
-
|
| 1508 |
-
|
| 1509 |
|
| 1510 |
-
|
| 1511 |
-
|
| 1512 |
-
|
| 1513 |
-
|
| 1514 |
-
|
| 1515 |
-
|
| 1516 |
-
|
| 1517 |
-
|
| 1518 |
-
|
| 1519 |
-
|
| 1520 |
-
|
| 1521 |
-
|
| 1522 |
|
| 1523 |
# ——————— 3. Issues: ONLY Coverage=100% & Trend Slope > 0 → Avg/Month ———————
|
| 1524 |
def predict_issues(df):
|
|
@@ -1615,39 +1615,39 @@ if not df_loc.empty:
|
|
| 1615 |
# )
|
| 1616 |
st.markdown("</div>", unsafe_allow_html=True)
|
| 1617 |
|
| 1618 |
-
# 🎯 PANEL 2: Divisions
|
| 1619 |
-
st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
|
| 1620 |
-
st.markdown("<div class='predictive-header'>2. Which Areas Are Likely to Be Frequently or Rarely Inspected?</div>", unsafe_allow_html=True)
|
| 1621 |
-
if not df_div.empty:
|
| 1622 |
-
|
| 1623 |
-
|
| 1624 |
-
|
| 1625 |
-
|
| 1626 |
-
|
| 1627 |
-
|
| 1628 |
-
|
| 1629 |
-
|
| 1630 |
-
|
| 1631 |
-
|
| 1632 |
-
# st.markdown(
|
| 1633 |
-
# "<div class='predictive-note'>"
|
| 1634 |
-
# "<strong>Forecast Inspection:</strong> "
|
| 1635 |
-
# "<span class='status-active'>Active</span> (0 gaps), "
|
| 1636 |
-
# "<span class='status-neutral'>Neutral</span> (1–2 gaps), "
|
| 1637 |
-
# "<span class='status-inactive'>Inactive</span> (>2 gaps)."
|
| 1638 |
-
# "</div>",
|
| 1639 |
-
# unsafe_allow_html=True
|
| 1640 |
-
# )
|
| 1641 |
-
# else:
|
| 1642 |
-
# st.markdown(
|
| 1643 |
-
# "<div class='predictive-table-wrapper'>"
|
| 1644 |
-
# "<p style='text-align:center; color:#666; padding:24px; font-style:italic;'>"
|
| 1645 |
-
# "Insufficient division data (≥2 months required)."
|
| 1646 |
-
# "</p></div>",
|
| 1647 |
-
# unsafe_allow_html=True
|
| 1648 |
-
# )
|
| 1649 |
-
|
| 1650 |
-
st.markdown("</div>", unsafe_allow_html=True)
|
| 1651 |
|
| 1652 |
|
| 1653 |
# 🎯 PANEL 3: Issues (FILTERED: Coverage=100% & Rising)
|
|
|
|
| 1473 |
df_res = pd.DataFrame(results)
|
| 1474 |
return df_res.sort_values('Trend Slope', ascending=True) if not df_res.empty else df_res # most negative first
|
| 1475 |
|
| 1476 |
+
# # ——————— 2. Divisions ———————
|
| 1477 |
+
# def predict_divisions(df):
|
| 1478 |
+
# if 'nama' not in df.columns:
|
| 1479 |
+
# return pd.DataFrame()
|
| 1480 |
|
| 1481 |
+
# start_month = df['created_at'].min().to_period('M')
|
| 1482 |
+
# end_month = df['created_at'].max().to_period('M')
|
| 1483 |
+
# all_months = pd.period_range(start=start_month, end=end_month, freq='M')
|
| 1484 |
|
| 1485 |
+
# df_monthly = (
|
| 1486 |
+
# df.groupby(['nama', df['created_at'].dt.to_period('M')])
|
| 1487 |
+
# .size()
|
| 1488 |
+
# .unstack(fill_value=0)
|
| 1489 |
+
# .reindex(columns=all_months, fill_value=0)
|
| 1490 |
+
# .stack()
|
| 1491 |
+
# .reset_index(name='count')
|
| 1492 |
+
# )
|
| 1493 |
+
# df_monthly.columns = ['Division', 'Month', 'Count']
|
| 1494 |
|
| 1495 |
+
# results = []
|
| 1496 |
+
# for div, group in df_monthly.groupby('Division'):
|
| 1497 |
+
# ts = group.set_index('Month')['Count']
|
| 1498 |
+
# total = len(all_months)
|
| 1499 |
+
# active = (ts > 0).sum()
|
| 1500 |
+
# gaps = total - active
|
| 1501 |
+
# coverage = active / total if total > 0 else 0
|
| 1502 |
|
| 1503 |
+
# if gaps > 2:
|
| 1504 |
+
# status = "<span class='status-inactive'>Inactive</span>"
|
| 1505 |
+
# elif gaps == 0:
|
| 1506 |
+
# status = "<span class='status-active'>Active</span>"
|
| 1507 |
+
# else:
|
| 1508 |
+
# status = "<span class='status-neutral'>Neutral</span>"
|
| 1509 |
|
| 1510 |
+
# bar = ''.join(['●' if c > 0 else '○' for c in ts.values])
|
| 1511 |
+
# trend_line = f"<span class='spark' style='color:#003DA5;'>{bar}</span>"
|
| 1512 |
+
# results.append({
|
| 1513 |
+
# 'Division': div,
|
| 1514 |
+
# 'Active Months': int(active),
|
| 1515 |
+
# 'Total Months': int(total),
|
| 1516 |
+
# 'Coverage (%)': round(coverage * 100, 1),
|
| 1517 |
+
# 'Status': status,
|
| 1518 |
+
# 'Trend': trend_line
|
| 1519 |
+
# })
|
| 1520 |
+
# df_res = pd.DataFrame(results)
|
| 1521 |
+
# return df_res.sort_values('Coverage (%)', ascending=True) if not df_res.empty else df_res
|
| 1522 |
|
| 1523 |
# ——————— 3. Issues: ONLY Coverage=100% & Trend Slope > 0 → Avg/Month ———————
|
| 1524 |
def predict_issues(df):
|
|
|
|
| 1615 |
# )
|
| 1616 |
st.markdown("</div>", unsafe_allow_html=True)
|
| 1617 |
|
| 1618 |
+
# # 🎯 PANEL 2: Divisions
|
| 1619 |
+
# st.markdown("<div class='predictive-panel'>", unsafe_allow_html=True)
|
| 1620 |
+
# st.markdown("<div class='predictive-header'>2. Which Areas Are Likely to Be Frequently or Rarely Inspected?</div>", unsafe_allow_html=True)
|
| 1621 |
+
# if not df_div.empty:
|
| 1622 |
+
# cols = ['Division', 'Active Months', 'Total Months', 'Coverage (%)', 'Status', 'Trend']
|
| 1623 |
+
|
| 1624 |
+
# # 🔵 Rename ONLY for display (Status → Forecast Inspection)
|
| 1625 |
+
# df_display = df_div[cols].rename(columns={
|
| 1626 |
+
# "Status": "Forecast Inspection"
|
| 1627 |
+
# })
|
| 1628 |
+
|
| 1629 |
+
# html = df_display.to_html(escape=False, index=False, table_id="tbl-divisions")
|
| 1630 |
+
# st.markdown(f"<div class='predictive-table-wrapper'>{html}</div>", unsafe_allow_html=True)
|
| 1631 |
+
|
| 1632 |
+
# # st.markdown(
|
| 1633 |
+
# # "<div class='predictive-note'>"
|
| 1634 |
+
# # "<strong>Forecast Inspection:</strong> "
|
| 1635 |
+
# # "<span class='status-active'>Active</span> (0 gaps), "
|
| 1636 |
+
# # "<span class='status-neutral'>Neutral</span> (1–2 gaps), "
|
| 1637 |
+
# # "<span class='status-inactive'>Inactive</span> (>2 gaps)."
|
| 1638 |
+
# # "</div>",
|
| 1639 |
+
# # unsafe_allow_html=True
|
| 1640 |
+
# # )
|
| 1641 |
+
# # else:
|
| 1642 |
+
# # st.markdown(
|
| 1643 |
+
# # "<div class='predictive-table-wrapper'>"
|
| 1644 |
+
# # "<p style='text-align:center; color:#666; padding:24px; font-style:italic;'>"
|
| 1645 |
+
# # "Insufficient division data (≥2 months required)."
|
| 1646 |
+
# # "</p></div>",
|
| 1647 |
+
# # unsafe_allow_html=True
|
| 1648 |
+
# # )
|
| 1649 |
+
|
| 1650 |
+
# st.markdown("</div>", unsafe_allow_html=True)
|
| 1651 |
|
| 1652 |
|
| 1653 |
# 🎯 PANEL 3: Issues (FILTERED: Coverage=100% & Rising)
|