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