From 98d13a8d17993e572adf3017f69fd65f343ec938 Mon Sep 17 00:00:00 2001 From: hlomzik Date: Mon, 17 Nov 2025 20:07:48 +0000 Subject: [PATCH 1/2] fix: BROS-594: Count empty values in Task Summary No value is also a value and this should be counted in agreement calculations. Removed all controls, making `countEmpty` and `showEmpty` true by default. --- .../components/TaskSummary/Aggregation.tsx | 23 +++--- .../TaskSummary/LabelingSummary.tsx | 73 ++----------------- 2 files changed, 14 insertions(+), 82 deletions(-) diff --git a/web/libs/editor/src/components/TaskSummary/Aggregation.tsx b/web/libs/editor/src/components/TaskSummary/Aggregation.tsx index efbbefaa4fbe..9b8ddda5d98d 100644 --- a/web/libs/editor/src/components/TaskSummary/Aggregation.tsx +++ b/web/libs/editor/src/components/TaskSummary/Aggregation.tsx @@ -22,17 +22,15 @@ const resultValue = (result: RawResult) => { export const AggregationCell = ({ control, annotations, - countEmpty, isExpanded, -}: { control: ControlTag; annotations: AnnotationSummary[]; countEmpty: boolean; isExpanded: boolean }) => { +}: { control: ControlTag; annotations: AnnotationSummary[]; isExpanded: boolean }) => { const allResults = annotations.flatMap((ann) => ann.results.filter((r) => r.from_name === control.name)); + const totalAnnotations = annotations.length; if (!allResults.length) { return No data; } - const totalAnnotations = countEmpty ? annotations.length : allResults.length; - // Handle labels-type controls (rectanglelabels, polygonlabels, labels, etc.) if (control.type.endsWith("labels")) { const allLabels = allResults.flatMap((r) => resultValue(r)).flat(); @@ -145,7 +143,7 @@ export const AggregationCell = ({ const ratings = allResults.map((r) => resultValue(r)).filter(Boolean); if (!ratings.length) return No ratings; - const avgRating = ratings.reduce((sum, val) => sum + val, 0) / (countEmpty ? totalAnnotations : ratings.length); + const avgRating = ratings.reduce((sum, val) => sum + val, 0) / totalAnnotations; return ( Avg: {avgRating.toFixed(1)} @@ -158,7 +156,7 @@ export const AggregationCell = ({ const numbers = allResults.map((r) => resultValue(r)).filter((v) => v !== null && v !== undefined); if (!numbers.length) return No data; - const avg = numbers.reduce((sum, val) => sum + Number(val), 0) / (countEmpty ? totalAnnotations : numbers.length); + const avg = numbers.reduce((sum, val) => sum + Number(val), 0) / totalAnnotations; return ( Avg: {avg.toFixed(1)} @@ -167,7 +165,7 @@ export const AggregationCell = ({ } // Default: show N/A - return N/A; + return N/A; }; /** @@ -177,14 +175,12 @@ export const AggregationCell = ({ */ export const AggregationTableRow = ({ headers, - processedControls, + controls, annotations, - countEmpty, }: { headers: Header[]; - processedControls: ControlTag[]; + controls: ControlTag[]; annotations: AnnotationSummary[]; - countEmpty: boolean; }) => { const [isExpanded, setIsExpanded] = useState(false); const [hasOverflow, setHasOverflow] = useState(false); @@ -200,7 +196,7 @@ export const AggregationTableRow = ({ }); setHasOverflow(hasOverflowingCells); - }, [annotations, processedControls]); + }, [annotations, controls]); return ( @@ -234,9 +230,8 @@ export const AggregationTableRow = ({ style={{ width: header.getSize() }} > diff --git a/web/libs/editor/src/components/TaskSummary/LabelingSummary.tsx b/web/libs/editor/src/components/TaskSummary/LabelingSummary.tsx index 63cf5cb74006..9d49c048aa6e 100644 --- a/web/libs/editor/src/components/TaskSummary/LabelingSummary.tsx +++ b/web/libs/editor/src/components/TaskSummary/LabelingSummary.tsx @@ -46,9 +46,6 @@ const columnHelper = createColumnHelper(); export const LabelingSummary = ({ hideInfo, annotations: all, controls, onSelect }: Props) => { const currentUser = window.APP_SETTINGS?.user; - const [showEmpty, setShowEmpty] = useState(true); - const [countEmpty, setCountEmpty] = useState(false); - const [popularFirst, setPopularFirst] = useState(false); const [columnWidths, setColumnWidths] = useState>({}); const tableRef = useRef(null); @@ -70,34 +67,6 @@ export const LabelingSummary = ({ hideInfo, annotations: all, controls, onSelect : (annotation.versions.result ?? []), })); - // Filter and sort controls based on toggles - const processedControls = useMemo(() => { - let result = [...controls]; - - // Filter out empty columns if showEmpty is false - if (!showEmpty) { - result = result.filter((control) => { - const hasResults = annotations.some((ann) => ann.results.some((r) => r.from_name === control.name)); - return hasResults; - }); - } - - // Sort by popularity if enabled - if (popularFirst) { - result = result.sort((a, b) => { - const aCount = annotations.reduce((sum, ann) => { - return sum + ann.results.filter((r) => r.from_name === a.name).length; - }, 0); - const bCount = annotations.reduce((sum, ann) => { - return sum + ann.results.filter((r) => r.from_name === b.name).length; - }, 0); - return bCount - aCount; - }); - } - - return result; - }, [controls, annotations, showEmpty, popularFirst]); - // Measure initial column widths after first render useEffect(() => { if (tableRef.current && Object.keys(columnWidths).length === 0) { @@ -105,7 +74,7 @@ export const LabelingSummary = ({ hideInfo, annotations: all, controls, onSelect const widths: Record = {}; headers.forEach((header, index) => { - const columnId = index === 0 ? "id" : processedControls[index - 1]?.name; + const columnId = index === 0 ? "id" : controls[index - 1]?.name; if (columnId) { // Get the computed width const width = header.getBoundingClientRect().width; @@ -115,10 +84,10 @@ export const LabelingSummary = ({ hideInfo, annotations: all, controls, onSelect setColumnWidths(widths); } - }, [processedControls, columnWidths]); + }, [controls, columnWidths]); const columns = useMemo(() => { - const columns: ColumnDef[] = processedControls.map((control) => + const columns: ColumnDef[] = controls.map((control) => columnHelper.display({ id: control.name, header: () => ( @@ -165,7 +134,7 @@ export const LabelingSummary = ({ hideInfo, annotations: all, controls, onSelect }, }); return columns; - }, [processedControls, onSelect, hideInfo, columnWidths]); + }, [controls, onSelect, hideInfo, columnWidths]); const table = useReactTable({ data: annotations, @@ -182,37 +151,6 @@ export const LabelingSummary = ({ hideInfo, annotations: all, controls, onSelect return (
- {/* Controls */} -
- - - -
-
{/* Annotation Rows */} {table.getRowModel().rows.map((row, rowIndex) => ( From 3f63c6bf5fe0b762f0d5cb6c57f12aee327f1539 Mon Sep 17 00:00:00 2001 From: hlomzik Date: Tue, 18 Nov 2025 19:55:41 +0000 Subject: [PATCH 2/2] Apply pre-commit linters Workflow run: https://github.com/HumanSignal/label-studio/actions/runs/19479308561 --- web/libs/editor/src/components/TaskSummary/Aggregation.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/web/libs/editor/src/components/TaskSummary/Aggregation.tsx b/web/libs/editor/src/components/TaskSummary/Aggregation.tsx index 9b8ddda5d98d..7c9597b7c671 100644 --- a/web/libs/editor/src/components/TaskSummary/Aggregation.tsx +++ b/web/libs/editor/src/components/TaskSummary/Aggregation.tsx @@ -229,11 +229,7 @@ export const AggregationTableRow = ({ className="px-4 py-2.5 overflow-hidden border-y-2 border-neutral-border-bold" style={{ width: header.getSize() }} > - + ), )}