diff --git a/frontend/packages/data-portal/app/graphql/getRunByIdDiffer.ts b/frontend/packages/data-portal/app/graphql/getRunByIdDiffer.ts index e414a2ba3..930d99bd5 100644 --- a/frontend/packages/data-portal/app/graphql/getRunByIdDiffer.ts +++ b/frontend/packages/data-portal/app/graphql/getRunByIdDiffer.ts @@ -54,6 +54,41 @@ export function logIfHasDiff( // There are no frames in V1. delete run.framesAggregate } + // Don't care about counts. + for (const aggregate of v2.uniqueAnnotationSoftwares.aggregate ?? []) { + delete aggregate.count + } + for (const aggregate of v2.uniqueObjectNames.aggregate ?? []) { + delete aggregate.count + } + for (const aggregate of v2.uniqueShapeTypes.aggregate ?? []) { + delete aggregate.count + } + for (const aggregate of v2.uniqueResolutions.aggregate ?? []) { + delete aggregate.count + } + for (const aggregate of v2.uniqueProcessingMethods.aggregate ?? []) { + delete aggregate.count + } + // Consistent sort order. + v2.uniqueAnnotationSoftwares.aggregate?.sort((gropuByA, groupByB) => + gropuByA.groupBy!.annotationSoftware!.localeCompare( + groupByB.groupBy!.annotationSoftware!, + ), + ) + v2.uniqueObjectNames.aggregate?.sort((groupA, groupB) => + groupA.groupBy!.objectName!.localeCompare(groupB.groupBy!.objectName!), + ) + v2.uniqueShapeTypes.aggregate?.sort((groupA, groupB) => + groupA.groupBy!.shapeType!.localeCompare(groupB.groupBy!.shapeType!), + ) + v2.uniqueResolutions.aggregate?.sort( + (groupA, groupB) => + groupA.groupBy!.voxelSpacing! - groupB.groupBy!.voxelSpacing!, + ) + v2.uniqueProcessingMethods.aggregate?.sort((groupA, groupB) => + groupA.groupBy!.processing!.localeCompare(groupB.groupBy!.processing!), + ) const v1Transformed: GetRunByIdV2Query = { runs: v1.runs.map((run) => ({ @@ -304,6 +339,93 @@ export function logIfHasDiff( })), }, })), + uniqueAnnotationSoftwares: { + aggregate: v1.annotations_for_softwares + .map((annotation) => ({ + groupBy: { + annotationSoftware: annotation.annotation_software, + }, + })) + .sort((groupByA, groupByB) => + groupByA.groupBy.annotationSoftware!.localeCompare( + groupByB.groupBy.annotationSoftware!, + ), + ), + }, + uniqueObjectNames: { + aggregate: v1.annotations_for_object_names + .map((annotation) => ({ + groupBy: { + objectName: annotation.object_name, + }, + })) + .sort((groupA, groupB) => + groupA.groupBy.objectName.localeCompare(groupB.groupBy.objectName), + ), + }, + uniqueShapeTypes: { + aggregate: v1.annotation_files_for_shape_types + .map((file) => ({ + groupBy: { + shapeType: file.shape_type as Annotation_File_Shape_Type_Enum, + }, + })) + .sort((groupA, groupB) => + groupA.groupBy.shapeType.localeCompare(groupB.groupBy.shapeType), + ), + }, + uniqueResolutions: { + aggregate: v1.tomograms_for_resolutions.map((tomogram) => ({ + groupBy: { + voxelSpacing: tomogram.voxel_spacing, + }, + })), + }, + uniqueProcessingMethods: { + aggregate: v1.tomograms_for_distinct_processing_methods.map( + (tomogram) => ({ + groupBy: { + processing: tomogram.processing as Tomogram_Processing_Enum, + }, + }), + ), + }, + numTotalAnnotationRows: { + aggregate: [ + { + count: v1.annotation_files_aggregate_for_total.aggregate?.count, + }, + ], + }, + numFilteredAnnotationRows: { + aggregate: [ + { + count: v1.annotation_files_aggregate_for_filtered.aggregate?.count, + }, + ], + }, + numFilteredGroundTruthAnnotationRows: { + aggregate: [ + { + count: + v1.annotation_files_aggregate_for_ground_truth.aggregate?.count, + }, + ], + }, + numFilteredOtherAnnotationRows: { + aggregate: [ + { + count: v1.annotation_files_aggregate_for_other.aggregate?.count, + }, + ], + }, + tomogramsAggregate: { + aggregate: [ + { + count: v1.tomograms_aggregate.aggregate?.count, + }, + ], + }, depositions: v1.deposition != null ? [ diff --git a/frontend/packages/data-portal/app/graphql/getRunByIdV2.server.ts b/frontend/packages/data-portal/app/graphql/getRunByIdV2.server.ts index 503d72226..8fc352226 100644 --- a/frontend/packages/data-portal/app/graphql/getRunByIdV2.server.ts +++ b/frontend/packages/data-portal/app/graphql/getRunByIdV2.server.ts @@ -20,6 +20,8 @@ const GET_RUN_BY_ID_QUERY_V2 = gql(` $limit: Int $annotationShapesOffset: Int $annotationShapesFilter: AnnotationShapeWhereClause + $annotationShapesFilterGroundTruthTrue: AnnotationShapeWhereClause + $annotationShapesFilterGroundTruthFalse: AnnotationShapeWhereClause $depositionId: Int ) { runs(where: { id: { _eq: $id } }) { @@ -336,6 +338,79 @@ const GET_RUN_BY_ID_QUERY_V2 = gql(` } } + # Annotation metadata: + uniqueAnnotationSoftwares: annotationsAggregate(where: { runId: { _eq: $id }}) { + aggregate { + count + groupBy { + annotationSoftware + } + } + } + uniqueObjectNames: annotationsAggregate(where: { runId: { _eq: $id }}) { + aggregate { + count + groupBy { + objectName + } + } + } + uniqueShapeTypes: annotationShapesAggregate(where: { annotation: { runId: { _eq: $id }}}) { + aggregate { + count + groupBy { + shapeType + } + } + } + + # Tomogram metadata: + uniqueResolutions: tomogramVoxelSpacingsAggregate(where: { runId: { _eq: $id }}) { + aggregate { + count + groupBy { + voxelSpacing + } + } + } + uniqueProcessingMethods: tomogramsAggregate(where: { runId: { _eq: $id }}) { + aggregate { + count + groupBy { + processing + } + } + } + + # Annotation counts: + numTotalAnnotationRows: annotationShapesAggregate(where: { annotation: { runId: { _eq: $id }}}) { + aggregate { + count + } + } + numFilteredAnnotationRows: annotationShapesAggregate(where: $annotationShapesFilter) { + aggregate { + count + } + } + numFilteredGroundTruthAnnotationRows: annotationShapesAggregate(where: $annotationShapesFilterGroundTruthTrue) { + aggregate { + count + } + } + numFilteredOtherAnnotationRows: annotationShapesAggregate(where: $annotationShapesFilterGroundTruthFalse) { + aggregate { + count + } + } + + # Tomogram counts: + tomogramsAggregate(where: { runId: { _eq: $id }}) { + aggregate { + count + } + } + # Deposition banner # Returns empty array if $depositionId not defined depositions(where: { id: { _eq: $depositionId }}) { @@ -348,6 +423,7 @@ const GET_RUN_BY_ID_QUERY_V2 = gql(` function getAnnotationShapesFilter( runId: number, filterState: FilterState, + groundTruthStatus?: boolean, ): AnnotationShapeWhereClause { const where: AnnotationShapeWhereClause = { annotation: { @@ -419,6 +495,13 @@ function getAnnotationShapesFilter( } } + // Ground truth dividers + if (groundTruthStatus !== undefined) { + where.annotation!.groundTruthStatus = { + _eq: groundTruthStatus, + } + } + return where } @@ -447,6 +530,16 @@ export async function getRunByIdV2({ id, getFilterState(params), ), + annotationShapesFilterGroundTruthTrue: getAnnotationShapesFilter( + id, + getFilterState(params), + /* groundTruthStatus */ true, + ), + annotationShapesFilterGroundTruthFalse: getAnnotationShapesFilter( + id, + getFilterState(params), + /* groundTruthStatus */ false, + ), depositionId, }, }) diff --git a/frontend/packages/data-portal/app/hooks/useRunById.ts b/frontend/packages/data-portal/app/hooks/useRunById.ts index 34ed37bcf..80c2d4c5e 100644 --- a/frontend/packages/data-portal/app/hooks/useRunById.ts +++ b/frontend/packages/data-portal/app/hooks/useRunById.ts @@ -40,6 +40,7 @@ export function useRunById() { return t2.id - t1.id }) + // TODO(bchu): Sort manually in V2. const processingMethods = v1.tomograms_for_distinct_processing_methods.map( (tomogram) => tomogram.processing, ) @@ -56,6 +57,7 @@ export function useRunById() { .map((annotation) => annotation.annotation_software) .filter(isDefined) + // TODO(bchu): Sort manually in V2. const resolutions = v1.tomograms_for_resolutions.map( (tomogram) => tomogram.voxel_spacing, ) @@ -73,7 +75,7 @@ export function useRunById() { const alignmentsCount = v2.alignmentsAggregate.aggregate?.[0]?.count ?? 0 - const { deposition } = v1 + const deposition = v2.depositions[0] return { run,