From 15e3b05a8177ac8e011f7fcf5e20f155b865c3f9 Mon Sep 17 00:00:00 2001 From: Aditya Sanjeev Date: Sat, 18 Jul 2026 00:25:37 -0700 Subject: [PATCH] Fix CV viewer hang and add dataset counts to filter panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VisualizationPage: detect CV cases (id starts with "CV") and set segUrl to null — skips the /api/get-segmentations call that returns JSON {"masks_available":false} instead of a binary NIfTI, which previously hung the loader indefinitely. Also guards the HuggingFace fallback URL so getPanTSId's garbage output for CV ids never produces a bad URL. Homepage/api_blueprint: /api/facets now returns dataset_counts {PanTS, CancerVerse} (total row counts from DF / DF_CV). Dataset filter buttons display the count badge matching the style of every other filter option. --- PanTS-Demo/src/routes/Homepage.tsx | 3 +++ PanTS-Demo/src/routes/VisualizationPage.tsx | 14 ++++++++++++-- flask-server/api/api_blueprint.py | 5 +++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/PanTS-Demo/src/routes/Homepage.tsx b/PanTS-Demo/src/routes/Homepage.tsx index 251367c..c548ba8 100644 --- a/PanTS-Demo/src/routes/Homepage.tsx +++ b/PanTS-Demo/src/routes/Homepage.tsx @@ -37,6 +37,7 @@ type FacetData = { counts: Record; unknown: Record; total: number; + datasetCounts: Record; }; // Filter groups whose available values are discovered from /api/facets (not hardcoded). @@ -287,6 +288,7 @@ export default function Homepage() { counts: data.facets ?? {}, unknown: data.unknown_counts ?? {}, total: data.total ?? 0, + datasetCounts: data.dataset_counts ?? {}, }); } catch (e) { console.error(e); @@ -725,6 +727,7 @@ export default function Homepage() { onClick={() => toggleMulti("dataset", opt.value)} > {opt.label} + {countBadge(facetData?.datasetCounts[opt.value] ?? null)} ))} diff --git a/PanTS-Demo/src/routes/VisualizationPage.tsx b/PanTS-Demo/src/routes/VisualizationPage.tsx index d8cd741..2e928f4 100644 --- a/PanTS-Demo/src/routes/VisualizationPage.tsx +++ b/PanTS-Demo/src/routes/VisualizationPage.tsx @@ -341,7 +341,10 @@ function VisualizationPage() { return; } const id = pantsCase ?? "1"; - const p = getPanTSId(id); + const isCvCase = String(id).toUpperCase().startsWith("CV"); + // getPanTSId produces a garbage value for CV ids, but it's only used in the HF + // fallback URLs which are never reached for CV (CT is always on the JHU server). + const p = isCvCase ? "" : getPanTSId(id); const localCt = `${API_BASE}/api/get-main-nifti/${id}.nii.gz`; const localSeg = `${API_BASE}/api/get-segmentations/${id}.nii.gz`; const hfCt = `https://huggingface.co/datasets/BodyMaps/iPanTSMini/resolve/main/image_only/${p}/ct.nii.gz?download=true`; @@ -354,7 +357,14 @@ function VisualizationPage() { // full res when ?hd=1. HuggingFace fallback is full res only. const resParam = isHd ? "" : "?res=low"; setCtUrl(localOk ? `${localCt}${resParam}` : hfCt); - setSegUrl(localOk ? `${localSeg}${resParam}` : hfSeg); + // CancerVerse cases have no masks yet — /api/get-segmentations returns + // {"masks_available": false} (JSON, HTTP 200) which hangs the nifti loader. + // Skip the seg URL entirely so the viewer opens CT-only without hanging. + if (isCvCase) { + setSegUrl(null); + } else { + setSegUrl(localOk ? `${localSeg}${resParam}` : hfSeg); + } }; resolveSources(); return () => { cancelled = true; }; diff --git a/flask-server/api/api_blueprint.py b/flask-server/api/api_blueprint.py index 5583141..ddd31be 100644 --- a/flask-server/api/api_blueprint.py +++ b/flask-server/api/api_blueprint.py @@ -1871,12 +1871,17 @@ def _minmax(series: pd.Series): if len(yr): year_min, year_max = int(yr.min()), int(yr.max()) + dataset_counts = { + "PanTS": int(len(DF)), + "CancerVerse": int(len(DF_CV)) if DF_CV is not None else 0, + } return jsonify({ "facets": facets, "unknown_counts": unknown_counts, "age_range": {"min": age_min, "max": age_max}, "year_range": {"min": year_min, "max": year_max}, "total": int(len(df_now)), + "dataset_counts": dataset_counts, }) except Exception as e: return jsonify({"error": str(e)}), 400