Skip to content
Open
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
8 changes: 8 additions & 0 deletions frontend/packages/data-portal/app/constants/pagination.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
export const MAX_PER_PAGE = 20

/**
* When displaying a group of accordions, we page them out by 10s.
* Inside of the accordion, once the lowest level of an accordion is
* expanded, we page the content inside of it by 5s.
*/
export const MAX_PER_ACCORDION_GROUP = 10
export const MAX_PER_FULLY_OPEN_ACCORDION = 5

/**
* Max number of annotated objects to show for dataset.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
* Pulls all datasets associated with a deposition.
* Three ways for a dataset to be associated:
* 1) Direct association, the dataset says it belong to deposition.
* 2) The dataset contains a tomogram that belongs to deposition.
* 3) The dataset contains an annotation that belongs to deposition.
* ^^^ Note: For #3, we actually do counts off shape so more useful elsewhere
*
* It's possible for any combo of these 3 to be true simultaneously.
* HOWEVER, for the expand depositions work, we don't actually care about #1.
* We only expose datasets through context of user viewing annotations or tomograms,
* so if a dataset only is associated because of #1, it's not used in current UX
* (although I think it would be very weird for a dataset to be owned by a deposition
* but have no tomograms or annotations in that dataset also owned).
*
* Additionally, we might care about tracking which datasets are associated due to
* #2 or #3. For instance, to show the "Group by" of datasets (and runs), you wouldn't
* want to have a line dedicated to a dataset that only has tomograms for the deposition
* if you're currently viewing the annotations tab.
*
* For the queries in this file, the use of the `count` is not immediately used for
* purposes of collecting all the datasets based on #2 or #3, it's mostly there because
* we can't run `groupBy` queries without a `count` in place. However, it does have the
* benefit of being useful for displaying count info on the "Group by" accordion rows
* (ex: Dataset ABC has ?? runs | ## annotations), we can use the results from this to
* pull the count of annotations / tomograms for a given dataset. (And can sum up against
* it for getting the count on an organism, since an organism is really just a list of datasets.)
* Note: For #3, we actually provide the counts off the # AnnotationShapes, not just
* the annotations since it's the former that is how we present # annotations in UX.
*
* We are not trying to limit the returned results at all since we need to know _all_ the
* datasets at once to present it to the user. This should be safe because the number of datasets
* is fairly limited and the amount of data we pull per dataset is pretty small.
* If this eventually becomes an issue (say, CryoET has lots more datasets in the future and
* some wacky deposition has 1 thing in every single one of them), we'll have to rethink stuff.
*/
import {
ApolloClient,
ApolloQueryResult,
NormalizedCacheObject,
} from '@apollo/client'

import { gql } from 'app/__generated_v2__'
import { GetDatasetsForDepositionViaAnnotationShapesQuery, GetDatasetsForDepositionViaTomogramsQuery } from 'app/__generated_v2__/graphql'

const GET_DATASETS_FOR_DEPOSITION_VIA_TOMOGRAMS = gql(`
query getDatasetsForDepositionViaTomograms(
$depositionId: Int!
) {
tomogramsAggregate(where: {depositionId: {_eq: $depositionId}}) {
aggregate {
count(columns: id)
groupBy {
run {
dataset {
id
title
organismName
organismTaxid
}
}
}
}
}
}
`)

// Strictly speaking, we can get the dataset info just as easily off annotations.
// But instead by pulling this off the AnnotationShapes, the count is accurate
// for how we display # annotations to the user (# shapes, not parent annotations)
const GET_DATASETS_FOR_DEPOSITION_VIA_ANNOTATION_SHAPES = gql(`
query getDatasetsForDepositionViaAnnotationShapes(
$depositionId: Int!
) {
annotationShapesAggregate(
where: {
annotation: {
depositionId: {_eq: $depositionId}
}
}
) {
aggregate {
count(columns: id)
groupBy {
annotation {
run {
dataset {
id
title
organismName
organismTaxid
}
}
}
}
}
}
}
`)

export async function getDatasetsForDepositionViaTomograms({
client,
depositionId,
}: {
client: ApolloClient<NormalizedCacheObject>
depositionId: number
}): Promise<ApolloQueryResult<GetDatasetsForDepositionViaTomogramsQuery>> {
return client.query({
query: GET_DATASETS_FOR_DEPOSITION_VIA_TOMOGRAMS,
variables: {
depositionId,
}
})
}

export async function getDatasetsForDepositionViaAnnotationShapes({
client,
depositionId,
}: {
client: ApolloClient<NormalizedCacheObject>
depositionId: number
}): Promise<ApolloQueryResult<GetDatasetsForDepositionViaAnnotationShapesQuery>> {
return client.query({
query: GET_DATASETS_FOR_DEPOSITION_VIA_ANNOTATION_SHAPES,
variables: {
depositionId,
}
})
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
/**
* - Created new version of GET_DEPOSITION_ANNOTATIONS that handles sorting and dataset filtering
* I believe the original GET_DEPOSITION_ANNOTATIONS query will probably still be necessary
* since having two queries is still the easiest way to handle a version without and a version
* with dataset filtering. However, the original version very likely needs to bring in the
* `orderBy` clause of the newer version so it sorts the same way as the existing annotation
* tables and matches the design. (I just pulled the sorting logic from GET_RUN_BY_ID_QUERY_V2)
* since that's the approach we're trying to match, pretty sure it works here!
* - Note the `pageSize` arg for the function to do client query on above
* This is necessary because we serve the same kind of data whether we filter based on user
* directly filtering for a dataset or doing a "Group by" on organism, BUT the page size of
* those two cases is different (20 for the former, 5 for the latter)
* - Possible we could optimize the annotationFiles out of existence, pull the path from
* the Annotation path (either s3MetadataPath or httpsMetadataPath, same diff for the
* number we want out of the path), but I don't know if different shapes have different
* files or they all belong to the same file somehow, needs more data exploration.
* - I didn't have time to write the equivalent version of this for tomograms, but I think it will
* be extremely similar. Sorry!
*/
import type {
ApolloClient,
ApolloQueryResult,
NormalizedCacheObject,
} from '@apollo/client'

import { gql } from 'app/__generated_v2__'
import type { GetDepositionAnnotationsQuery } from 'app/__generated_v2__/graphql'
import type { GetDepositionAnnotationsForDatasetsQuery, GetDepositionAnnotationsQuery } from 'app/__generated_v2__/graphql'
import { MAX_PER_PAGE } from 'app/constants/pagination'

const GET_DEPOSITION_ANNOTATIONS = gql(`
Expand Down Expand Up @@ -60,6 +79,82 @@ const GET_DEPOSITION_ANNOTATIONS = gql(`
}
`)

// I expect there's some way to pass a `null` sort of datasetIds filter that instead
// will not perform a filter on dataset ids, but I didn't have time to explore how, so
// // for now we just have two queries.
const GET_DEPOSITION_ANNOTATIONS_FOR_DATASETS = gql(`
query GetDepositionAnnotationsForDatasets(
$depositionId: Int!,
$datasetIds: [Int!]!,
$limit: Int!,
$offset: Int!,
) {
annotationShapes(
where: {
annotation: {
depositionId: {
_eq: $depositionId
},
run: {
datasetId:{
_in: $datasetIds
},
},
},
},
limitOffset: {
limit: $limit,
offset: $offset,
},
orderBy: [
{
annotation: {
groundTruthStatus: desc
}
},
{
annotation: {
depositionDate: desc
}
},
{
annotation: {
id: desc
}
}
]
) {
id
shapeType

annotation {
groundTruthStatus
id
methodType
objectName

run {
id
name

dataset {
id
title
}
}
}

annotationFiles(first: 1) {
edges {
node {
s3Path
}
}
}
}
}
`)

export async function getDepositionAnnotations({
client,
id,
Expand All @@ -78,3 +173,27 @@ export async function getDepositionAnnotations({
},
})
}

export async function getDepositionAnnotationsForDatasets({
client,
depositionId,
datasetIds,
pageSize = MAX_PER_PAGE,
page,
}: {
client: ApolloClient<NormalizedCacheObject>
depositionId: number
datasetIds: number[]
pageSize?: number
page: number
}): Promise<ApolloQueryResult<GetDepositionAnnotationsForDatasetsQuery>> {
return client.query({
query: GET_DEPOSITION_ANNOTATIONS_FOR_DATASETS,
variables: {
depositionId,
datasetIds,
limit: pageSize,
offset: (page - 1) * pageSize,
},
})
}
Loading
Loading