Add robust error handling and data validation for KPI health check panel with automatic fallback selection for sites and KPIs
Browse files
panel_app/kpi_health_check_panel.py
CHANGED
|
@@ -222,13 +222,6 @@ def _update_kpi_options() -> None:
|
|
| 222 |
|
| 223 |
|
| 224 |
def _update_site_view(event=None) -> None:
|
| 225 |
-
if current_status_df is None or current_status_df.empty:
|
| 226 |
-
site_kpi_table.value = pd.DataFrame()
|
| 227 |
-
trend_plot_pane.object = None
|
| 228 |
-
heatmap_plot_pane.object = None
|
| 229 |
-
hist_plot_pane.object = None
|
| 230 |
-
return
|
| 231 |
-
|
| 232 |
code = site_select.value
|
| 233 |
rat = rat_select.value
|
| 234 |
kpi = kpi_select.value
|
|
@@ -240,20 +233,62 @@ def _update_site_view(event=None) -> None:
|
|
| 240 |
hist_plot_pane.object = None
|
| 241 |
return
|
| 242 |
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
site_kpi_table.value = site_df
|
| 248 |
|
| 249 |
daily = current_daily_by_rat.get(rat)
|
| 250 |
-
if daily is None or daily.empty
|
| 251 |
trend_plot_pane.object = None
|
| 252 |
heatmap_plot_pane.object = None
|
| 253 |
hist_plot_pane.object = None
|
| 254 |
return
|
| 255 |
|
| 256 |
d = _filtered_daily(daily)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
s = d[d["site_code"] == int(code)].copy().sort_values("date_only")
|
| 258 |
if s.empty:
|
| 259 |
trend_plot_pane.object = None
|
|
@@ -290,6 +325,15 @@ def _update_site_view(event=None) -> None:
|
|
| 290 |
tmp = tmp.sort_values(by=["_sev", "KPI"], ascending=[False, True])
|
| 291 |
kpis_for_heatmap = tmp["KPI"].astype(str).tolist()[:30]
|
| 292 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
heatmap_plot_pane.object = _build_site_heatmap(
|
| 294 |
d, rules_df, int(code), rat, kpis_for_heatmap
|
| 295 |
)
|
|
@@ -781,6 +825,12 @@ def load_datasets(event=None) -> None:
|
|
| 781 |
if not loaded_any:
|
| 782 |
raise ValueError("Please upload at least one KPI report")
|
| 783 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 784 |
datasets_table.value = pd.DataFrame(rows)
|
| 785 |
|
| 786 |
rules_df = (
|
|
|
|
| 222 |
|
| 223 |
|
| 224 |
def _update_site_view(event=None) -> None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
code = site_select.value
|
| 226 |
rat = rat_select.value
|
| 227 |
kpi = kpi_select.value
|
|
|
|
| 233 |
hist_plot_pane.object = None
|
| 234 |
return
|
| 235 |
|
| 236 |
+
status_df = (
|
| 237 |
+
current_status_df
|
| 238 |
+
if isinstance(current_status_df, pd.DataFrame)
|
| 239 |
+
else pd.DataFrame()
|
| 240 |
+
)
|
| 241 |
+
if status_df is None or status_df.empty:
|
| 242 |
+
site_df = pd.DataFrame()
|
| 243 |
+
else:
|
| 244 |
+
site_df = status_df[
|
| 245 |
+
(status_df["site_code"] == int(code)) & (status_df["RAT"] == rat)
|
| 246 |
+
].copy()
|
| 247 |
site_kpi_table.value = site_df
|
| 248 |
|
| 249 |
daily = current_daily_by_rat.get(rat)
|
| 250 |
+
if daily is None or daily.empty:
|
| 251 |
trend_plot_pane.object = None
|
| 252 |
heatmap_plot_pane.object = None
|
| 253 |
hist_plot_pane.object = None
|
| 254 |
return
|
| 255 |
|
| 256 |
d = _filtered_daily(daily)
|
| 257 |
+
if (
|
| 258 |
+
d is None
|
| 259 |
+
or d.empty
|
| 260 |
+
or "site_code" not in d.columns
|
| 261 |
+
or "date_only" not in d.columns
|
| 262 |
+
):
|
| 263 |
+
trend_plot_pane.object = None
|
| 264 |
+
heatmap_plot_pane.object = None
|
| 265 |
+
hist_plot_pane.object = None
|
| 266 |
+
return
|
| 267 |
+
|
| 268 |
+
try:
|
| 269 |
+
available_sites = set(
|
| 270 |
+
pd.to_numeric(d["site_code"], errors="coerce").dropna().astype(int).tolist()
|
| 271 |
+
)
|
| 272 |
+
except Exception: # noqa: BLE001
|
| 273 |
+
available_sites = set()
|
| 274 |
+
if available_sites and int(code) not in available_sites:
|
| 275 |
+
site_select.value = next(iter(sorted(available_sites)))
|
| 276 |
+
return
|
| 277 |
+
|
| 278 |
+
if not kpi or kpi not in d.columns:
|
| 279 |
+
candidate_kpis = [
|
| 280 |
+
c
|
| 281 |
+
for c in d.columns
|
| 282 |
+
if c
|
| 283 |
+
not in {"site_code", "date_only", "Longitude", "Latitude", "City", "RAT"}
|
| 284 |
+
]
|
| 285 |
+
candidate_kpis = sorted([str(c) for c in candidate_kpis])
|
| 286 |
+
if candidate_kpis:
|
| 287 |
+
kpi_select.value = candidate_kpis[0]
|
| 288 |
+
trend_plot_pane.object = None
|
| 289 |
+
heatmap_plot_pane.object = None
|
| 290 |
+
hist_plot_pane.object = None
|
| 291 |
+
return
|
| 292 |
s = d[d["site_code"] == int(code)].copy().sort_values("date_only")
|
| 293 |
if s.empty:
|
| 294 |
trend_plot_pane.object = None
|
|
|
|
| 325 |
tmp = tmp.sort_values(by=["_sev", "KPI"], ascending=[False, True])
|
| 326 |
kpis_for_heatmap = tmp["KPI"].astype(str).tolist()[:30]
|
| 327 |
|
| 328 |
+
if not kpis_for_heatmap:
|
| 329 |
+
try:
|
| 330 |
+
if isinstance(rules_df, pd.DataFrame) and not rules_df.empty:
|
| 331 |
+
kpis_for_heatmap = (
|
| 332 |
+
rules_df[rules_df["RAT"] == rat]["KPI"].astype(str).tolist()[:30]
|
| 333 |
+
)
|
| 334 |
+
except Exception: # noqa: BLE001
|
| 335 |
+
kpis_for_heatmap = []
|
| 336 |
+
|
| 337 |
heatmap_plot_pane.object = _build_site_heatmap(
|
| 338 |
d, rules_df, int(code), rat, kpis_for_heatmap
|
| 339 |
)
|
|
|
|
| 825 |
if not loaded_any:
|
| 826 |
raise ValueError("Please upload at least one KPI report")
|
| 827 |
|
| 828 |
+
rats_loaded = [r for r in ["2G", "3G", "LTE"] if r in current_daily_by_rat]
|
| 829 |
+
if rats_loaded:
|
| 830 |
+
rat_select.options = rats_loaded
|
| 831 |
+
if rat_select.value not in rats_loaded:
|
| 832 |
+
rat_select.value = rats_loaded[-1]
|
| 833 |
+
|
| 834 |
datasets_table.value = pd.DataFrame(rows)
|
| 835 |
|
| 836 |
rules_df = (
|