From 6c8b06718eaa874be867efc57f7e3edb910a5610 Mon Sep 17 00:00:00 2001 From: averel10 Date: Fri, 3 Apr 2026 08:21:51 +0200 Subject: [PATCH] feat: added sample coverage chart --- src/app/actions/annotation-stats.ts | 3 +- src/components/AnnotationDistribution.tsx | 68 +++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/app/actions/annotation-stats.ts b/src/app/actions/annotation-stats.ts index 3ebf0f1..8fd56a9 100644 --- a/src/app/actions/annotation-stats.ts +++ b/src/app/actions/annotation-stats.ts @@ -180,8 +180,7 @@ export async function getAnnotatedSamples( ) ) .where(eq(dataset_entry.datasetId, datasetId)) - .groupBy(dataset_entry.id) - .having((group) => gt(count(annotation.id), 0)); + .groupBy(dataset_entry.id); // Type assertion for the count return samples diff --git a/src/components/AnnotationDistribution.tsx b/src/components/AnnotationDistribution.tsx index 1166e2b..312e668 100644 --- a/src/components/AnnotationDistribution.tsx +++ b/src/components/AnnotationDistribution.tsx @@ -68,6 +68,26 @@ export default function AnnotationDistribution({ experimentId }: AnnotationDistr ...item, })) || []; + // Calculate per-sample distribution (histogram of annotation counts) + const sampleDistribution: { annotationCount: number; sampleCount: number; percentage: string }[] = []; + const countMap = new Map(); + + samples.forEach((sample) => { + const count = sample.annotationCount; + countMap.set(count, (countMap.get(count) || 0) + 1); + }); + + // Convert to sorted array with percentages + const totalSamples = samples.length; + Array.from(countMap.entries()) + .map(([count, sampleCount]) => ({ + annotationCount: count, + sampleCount, + percentage: totalSamples > 0 ? ((sampleCount / totalSamples) * 100).toFixed(1) : '0', + })) + .sort((a, b) => a.annotationCount - b.annotationCount) + .forEach((item) => sampleDistribution.push(item)); + return (
@@ -157,6 +177,54 @@ export default function AnnotationDistribution({ experimentId }: AnnotationDistr
)} + {/* Sample Annotation Distribution Chart */} + {!samplesLoading && sampleDistribution.length > 0 && ( +
+

Sample Annotation Coverage

+
+ + + + + + { + if (active && payload && payload.length) { + const data = payload[0].payload; + return ( +
+

+ {data.annotationCount} annotation{data.annotationCount !== 1 ? 's' : ''} +

+

{data.percentage}% of samples

+

({data.sampleCount} sample{data.sampleCount !== 1 ? 's' : ''})

+
+ ); + } + return null; + }} + /> + + +
+
+
+
+ )} +
); }