Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Migrate annotations table to V2 #1190

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { ConfigureTomogramDownloadContent } from './ConfigureTomogramDownloadCon

export function ConfigureDownloadContent() {
const { t } = useI18n()
const { datasetTitle, runName, objectName } = useDownloadModalContext()

const { annotationToDownload, datasetTitle, runName, objectName } =
useDownloadModalContext()

const { annotationName, annotationId, objectShapeType } =
useDownloadModalQueryParamState()

Expand All @@ -32,6 +35,12 @@ export function ConfigureDownloadContent() {
{objectShapeType && (
<ModalSubtitle label={t('objectShapeType')} value={objectShapeType} />
)}
{annotationToDownload !== undefined && (
<ModalSubtitle
label={t('alignmentId')}
value={`${IdPrefix.Alignment}-${annotationToDownload}`}
/>
)}

<p className="mt-sds-xl text-sds-body-m leading-sds-body-m font-semibold">
{t('selectDownload')}
Expand Down
65 changes: 65 additions & 0 deletions frontend/packages/data-portal/app/graphql/getRunByIdDiffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { diff } from 'deep-object-diff'

import { GetRunByIdQuery } from 'app/__generated__/graphql'
import {
Annotation_File_Shape_Type_Enum,
Annotation_Method_Type_Enum,
Fiducial_Alignment_Status_Enum,
GetRunByIdV2Query,
Sample_Type_Enum,
Expand All @@ -21,6 +23,12 @@ export function logIfHasDiff(
v2 = structuredClone(v2)
// There are no alignments in V1.
delete v2.alignmentsAggregate.aggregate
// Sort annotation files for consistency.
for (const annotationShape of v2.annotationShapes) {
annotationShape.annotationFiles.edges.sort((edge) =>
edge.node.format.localeCompare(edge.node.format),
)
}
// Tomogram deposition relations in V1 are incomplete.
for (const tomogram of v2.tomograms) {
// There are no alignments in V1.
Expand Down Expand Up @@ -169,6 +177,63 @@ export function logIfHasDiff(
},
})),
alignmentsAggregate: {},
annotationShapes: v1.annotation_files.map((file) => ({
shapeType: file.shape_type as Annotation_File_Shape_Type_Enum,
annotationFiles: {
edges: file.annotation.files
.map((nestedFile) => ({
node: {
format: nestedFile.format,
httpsPath: nestedFile.https_path,
s3Path: nestedFile.s3_path,
},
}))
.sort((a, b) => a.node.format.localeCompare(b.node.format)),
},
annotation: {
annotationMethod: file.annotation.annotation_method,
annotationPublication: file.annotation.annotation_publication,
annotationSoftware: file.annotation.annotation_software,
confidencePrecision: file.annotation.confidence_precision,
confidenceRecall: file.annotation.confidence_recall,
depositionDate: file.annotation.deposition_date,
groundTruthStatus: file.annotation.ground_truth_status,
groundTruthUsed: file.annotation.ground_truth_used,
id: file.annotation.id,
isCuratorRecommended: file.annotation.is_curator_recommended,
lastModifiedDate: file.annotation.last_modified_date!,
methodLinks: file.annotation.method_links as string,
methodType: file.annotation.method_type as Annotation_Method_Type_Enum,
objectCount: file.annotation.object_count,
objectDescription: file.annotation.object_description,
objectId: file.annotation.object_id,
objectName: file.annotation.object_name,
objectState: file.annotation.object_state,
releaseDate: file.annotation.release_date,
authors: {
edges: file.annotation.authors.map((author) => ({
node: {
primaryAuthorStatus: author.primary_author_status,
correspondingAuthorStatus: author.corresponding_author_status,
name: author.name,
email: author.email,
orcid: author.orcid,
},
})),
},
authorsAggregate: {
aggregate: [
{
count: file.annotation.authors_aggregate.aggregate?.count,
},
],
},
deposition: {
id: file.annotation.deposition!.id ?? 0,
title: file.annotation.deposition!.title,
},
},
})),
tomograms: v1.tomograms.map((tomogram) => ({
ctfCorrected: tomogram.ctf_corrected,
fiducialAlignmentStatus:
Expand Down
174 changes: 172 additions & 2 deletions frontend/packages/data-portal/app/graphql/getRunByIdV2.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ import {
} from '@apollo/client'

import { gql } from 'app/__generated_v2__'
import { GetRunByIdV2Query } from 'app/__generated_v2__/graphql'
import {
Annotation_File_Shape_Type_Enum,
Annotation_Method_Type_Enum,
AnnotationShapeWhereClause,
GetRunByIdV2Query,
} from 'app/__generated_v2__/graphql'
import { MAX_PER_PAGE } from 'app/constants/pagination'
import { FilterState, getFilterState } from 'app/hooks/useFilter'

const GET_RUN_BY_ID_QUERY_V2 = gql(`
query GetRunByIdV2(
$id: Int
$limit: Int
$annotationShapesOffset: Int
$annotationShapesFilter: AnnotationShapeWhereClause
) {
runs(where: { id: { _eq: $id } }) {
id
Expand Down Expand Up @@ -174,7 +184,82 @@ const GET_RUN_BY_ID_QUERY_V2 = gql(`
}

# Annotations table
# TODO(bchu)
annotationShapes(
where: $annotationShapesFilter
orderBy: [
{
annotation: {
groundTruthStatus: desc
}
},
{
annotation: {
depositionDate: desc
}
},
{
annotation: {
id: desc
}
}
]
limitOffset: {
limit: $limit
offset: $annotationShapesOffset
}
) {
shapeType
annotationFiles {
edges {
node {
format
httpsPath
s3Path
}
}
}
annotation {
annotationMethod
annotationPublication
annotationSoftware
confidencePrecision
confidenceRecall
depositionDate
groundTruthStatus
groundTruthUsed
id
isCuratorRecommended
lastModifiedDate
methodLinks
methodType
objectCount
objectDescription
objectId
objectName
objectState
releaseDate
authors(orderBy: { authorListOrder: asc }) {
edges {
node {
primaryAuthorStatus
correspondingAuthorStatus
name
email
orcid
}
}
}
authorsAggregate {
aggregate {
count
}
}
deposition {
id
title
}
}
}

# Tomograms table + download selector
tomograms(where: { run: { id: { _eq: $id } } }) {
Expand Down Expand Up @@ -234,14 +319,99 @@ const GET_RUN_BY_ID_QUERY_V2 = gql(`
}
`)

function getAnnotationShapesFilter(
runId: number,
filterState: FilterState,
): AnnotationShapeWhereClause {
const where: AnnotationShapeWhereClause = {
annotation: {
run: {
id: {
_eq: runId,
},
},
},
}

// Deposition filter
const depositionId = +(filterState.ids.deposition ?? Number.NaN)
if (!Number.isNaN(depositionId) && depositionId > 0) {
where.annotation!.deposition = {
id: {
_eq: depositionId,
},
}
}

// Annotation shape filter
const {
annotation: { objectShapeTypes },
} = filterState
if (objectShapeTypes.length > 0) {
where.shapeType = {
_in: objectShapeTypes as Annotation_File_Shape_Type_Enum[],
}
}

// Author filters
const { name, orcid } = filterState.author
if (name || orcid) {
where.annotation!.authors = {}
}
if (name) {
where.annotation!.authors!.name = {
_ilike: `%${name}%`,
}
}
if (orcid) {
where.annotation!.authors!.orcid = {
_ilike: `%${orcid}%`,
}
}

// Annotation filters
const { objectNames, annotationSoftwares, methodTypes, objectId } =
filterState.annotation
if (objectNames.length > 0) {
where.annotation!.objectName = {
_in: objectNames,
}
}
if (objectId) {
where.annotation!.objectId = {
_ilike: `%${objectId.replace(':', '_')}`,
}
}
if (methodTypes.length > 0) {
where.annotation!.methodType = {
_in: methodTypes as Annotation_Method_Type_Enum[],
}
}
if (annotationSoftwares.length > 0) {
where.annotation!.annotationSoftware = {
_in: annotationSoftwares,
}
}

return where
}

export async function getRunByIdV2(
client: ApolloClient<NormalizedCacheObject>,
id: number,
annotationsPage: number,
params: URLSearchParams = new URLSearchParams(),
): Promise<ApolloQueryResult<GetRunByIdV2Query>> {
return client.query({
query: GET_RUN_BY_ID_QUERY_V2,
variables: {
id,
limit: MAX_PER_PAGE,
annotationShapesOffset: (annotationsPage - 1) * MAX_PER_PAGE,
annotationShapesFilter: getAnnotationShapesFilter(
id,
getFilterState(params),
),
},
})
}
2 changes: 1 addition & 1 deletion frontend/packages/data-portal/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const SCHEMA_URL =

const SCHEMA_URL_V2 =
process.env.API_URL_V2 ||
'https://graphql.cryoetdataportal.czscience.com/graphql'
'https://graphql.cryoet.staging.si.czi.technology/graphql'

const config: CodegenConfig = {
generates: {
Expand Down
Loading