Skip to content

Commit

Permalink
single run annotation metadata (#155)
Browse files Browse the repository at this point in the history
#111

- Refactors code to use
[remix-typedjson](https://github.com/kiliman/remix-typedjson) for better
typing when using loader data
- Add state for active annotation data
- Implements new components for the annotation drawer

## Demo

<img width="274" alt="image"
src="https://github.com/chanzuckerberg/cryoet-data-portal/assets/2176050/473115bc-fbd6-45ae-b995-0cd25a2b7518">
  • Loading branch information
codemonkey800 authored Nov 15, 2023
1 parent 0747e76 commit ed7b04e
Show file tree
Hide file tree
Showing 31 changed files with 385 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useLoaderData, useLocation, useNavigate } from '@remix-run/react'
import { useLocation, useNavigate } from '@remix-run/react'
import { useMemo } from 'react'
import { useTypedLoaderData } from 'remix-typedjson'

import { GetToolbarDataQuery } from 'app/__generated__/graphql'
import { TabData, Tabs } from 'app/components/Tabs'
Expand All @@ -17,7 +18,7 @@ export function BrowseDataTabs() {
? BrowseDataTab.Datasets
: BrowseDataTab.Runs

const data = useLoaderData<GetToolbarDataQuery>()
const data = useTypedLoaderData<GetToolbarDataQuery>()
const datasetCount = data.datasets_aggregate.aggregate?.count ?? 0
const runCount = data.runs_aggregate.aggregate?.count ?? 0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import {
REGEX_MAP,
URL_MAP,
} from 'app/constants/external-dbs'
import { TableDataValue } from 'app/types/table'
import { cns } from 'app/utils/cns'

interface DatabaseEntryProps {
entry: string
entry: TableDataValue
inline?: boolean
}

Expand All @@ -18,7 +19,7 @@ export function DatabaseEntry(props: DatabaseEntryProps) {
let id: string = ''

for (const [dbt, pattern] of REGEX_MAP) {
const match = pattern.exec(entry)
const match = pattern.exec(String(entry))
if (match !== null) {
dbtype = dbt
// eslint-disable-next-line prefer-destructuring
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function DatasetHeader() {
uppercase: true,
},
]}
onMoreInfoClick={drawer.toggle}
onMoreInfoClick={() => drawer.setActiveDrawerId('dataset-metadata')}
releaseDate={dataset.release_date}
title={dataset.title}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ export function DatasetMetadataDrawer() {
const { dataset } = useDatasetById()

return (
<MetadataDrawer title={dataset.title} label={i18n.datasetDetails}>
<MetadataDrawer
drawerId="dataset-metadata"
title={dataset.title}
label={i18n.datasetDetails}
>
<DatasetMetadataTable dataset={dataset} />
<SampleAndExperimentConditionsTable dataset={dataset} />
<DatasetTiltSeriesTable />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { Dataset_Funding } from 'app/__generated__/graphql'
import { AccordionMetadataTable } from 'app/components/AccordionMetadataTable'
import { DatabaseEntry } from 'app/components/DatabaseEntry'
import { Link } from 'app/components/Link'
import { getTableData } from 'app/components/utils'
import { DOI_ID } from 'app/constants/external-dbs'
import { i18n } from 'app/i18n'
import { getTableData } from 'app/utils/table'

import { AuthorInfo, DatasetAuthors } from './DatasetAuthors'
import { DatasetType } from './type'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AccordionMetadataTable } from 'app/components/AccordionMetadataTable'
import { getTableData } from 'app/components/utils'
import { i18n } from 'app/i18n'
import { getTableData } from 'app/utils/table'

import { DatasetType } from './type'

Expand Down
44 changes: 32 additions & 12 deletions frontend/packages/data-portal/app/components/MetadataDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { ButtonIcon } from '@czi-sds/components'
import { useSearchParams } from '@remix-run/react'
import { ReactNode, useEffect, useRef } from 'react'
import { ReactNode, useCallback, useEffect, useRef } from 'react'

import { Demo } from 'app/components/Demo'
import { Drawer } from 'app/components/Drawer'
import { TabData, Tabs } from 'app/components/Tabs'
import { i18n } from 'app/i18n'
import { useDrawer } from 'app/state/drawer'
import { DrawerId, useDrawer } from 'app/state/drawer'
import { cns } from 'app/utils/cns'

enum MetadataTab {
Expand All @@ -28,13 +28,22 @@ const TAB_OPTIONS: TabData<MetadataTab>[] = [
const ACTIVE_TAB_PARAM = 'tab'

interface MetaDataDrawerProps {
title: string
label: string
children: ReactNode
disabled?: boolean
drawerId: DrawerId
label: string
onClose?(): void
title: string
}

export function MetadataDrawer(props: MetaDataDrawerProps) {
const { title, label, children } = props
export function MetadataDrawer({
children,
disabled,
drawerId,
label,
onClose,
title,
}: MetaDataDrawerProps) {
const drawer = useDrawer()

const [searchParams, setSearchParams] = useSearchParams()
Expand All @@ -44,25 +53,36 @@ export function MetadataDrawer(props: MetaDataDrawerProps) {
const initialLoadRef = useRef(true)
if (initialLoadRef.current && searchParams.has(ACTIVE_TAB_PARAM)) {
initialLoadRef.current = false
drawer.setOpen(true)
drawer.setActiveDrawerId(drawerId)
}

useEffect(() => {
if (drawer.open && searchParams.get(ACTIVE_TAB_PARAM) !== activeTab) {
if (
drawer.activeDrawerId &&
searchParams.get(ACTIVE_TAB_PARAM) !== activeTab
) {
setSearchParams((params) => {
params.set(ACTIVE_TAB_PARAM, activeTab)
return params
})
} else if (!drawer.open) {
} else if (!drawer.activeDrawerId) {
setSearchParams((params) => {
params.delete(ACTIVE_TAB_PARAM)
return params
})
}
}, [activeTab, drawer.open, searchParams, setSearchParams])
}, [activeTab, drawer.activeDrawerId, searchParams, setSearchParams])

const handleClose = useCallback(() => {
drawer.setActiveDrawerId(null)
onClose?.()
}, [drawer, onClose])

return (
<Drawer open={drawer.open} onClose={() => drawer.setOpen(false)}>
<Drawer
open={drawer.activeDrawerId === drawerId && !disabled}
onClose={handleClose}
>
<div className="flex flex-col flex-auto">
<header className="flex items-start justify-between px-sds-xl pt-sds-xl pb-sds-xxl">
<div className="flex flex-col gap-sds-s">
Expand All @@ -76,7 +96,7 @@ export function MetadataDrawer(props: MetaDataDrawerProps) {
</div>

<ButtonIcon
onClick={() => drawer.setOpen(false)}
onClick={handleClose}
sdsIcon="xMark"
sdsIconProps={{
color: 'gray',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { AccordionMetadataTable } from 'app/components/AccordionMetadataTable'
import { i18n } from 'app/i18n'
import { useAnnotation } from 'app/state/annotation'

export function AnnotationConfidenceTable() {
const { activeAnnotation: annotation } = useAnnotation()

if (!annotation) {
return null
}

const isGroundTruth = annotation.ground_truth_status

return (
<AccordionMetadataTable
id="annotation-confidence"
header={i18n.annotationConfidence}
data={[
{
label: i18n.groundTruthStatus,
values: [isGroundTruth ? i18n.true : i18n.false],
},
{
label: i18n.curatorRecommended,
values: ['TBD'],
},
{
label: i18n.groundTruthUsed,
values: [
isGroundTruth
? i18n.notApplicable
: annotation.ground_truth_used ?? '--',
],
},
{
label: i18n.precision,
values: [
isGroundTruth
? i18n.notApplicable
: annotation.confidence_precision ?? '--',
],
},
{
label: i18n.recall,
values: [
isGroundTruth
? i18n.notApplicable
: annotation.confidence_recall ?? '--',
],
},
]}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { MetadataDrawer } from 'app/components/MetadataDrawer'
import { i18n } from 'app/i18n'
import { useAnnotation } from 'app/state/annotation'
import { getAnnotationTitle } from 'app/utils/annotation'

import { AnnotationConfidenceTable } from './AnnotationConfidenceTable'
import { AnnotationObjectTable } from './AnnotationObjectTable'
import { AnnotationOverviewTable } from './AnnotationOveriewTable'

export function AnnotationDrawer() {
const { activeAnnotation, setActiveAnnotation } = useAnnotation()

return (
<MetadataDrawer
disabled={!activeAnnotation}
drawerId="annotation-metadata"
label={i18n.annotationDetails}
title={getAnnotationTitle(activeAnnotation)}
onClose={() => setActiveAnnotation(null)}
>
<AnnotationOverviewTable />
<AnnotationObjectTable />
<AnnotationConfidenceTable />
</MetadataDrawer>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { AccordionMetadataTable } from 'app/components/AccordionMetadataTable'
import { i18n } from 'app/i18n'
import { useAnnotation } from 'app/state/annotation'

export function AnnotationObjectTable() {
const { activeAnnotation: annotation } = useAnnotation()

if (!annotation) {
return null
}

return (
<AccordionMetadataTable
id="annotation-object"
header={i18n.annotationObject}
data={[
{
label: i18n.objectName,
values: [annotation.object_name],
},
{
label: i18n.goId,
values: ['TBD'],
},
{
label: i18n.objectShapeType,
values: [annotation.shape_type],
},
{
label: i18n.objectState,
values: [annotation.object_state ?? '--'],
},
{
label: i18n.objectDescription,
values: [annotation.object_description ?? '--'],
},
]}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { AccordionMetadataTable } from 'app/components/AccordionMetadataTable'
import { i18n } from 'app/i18n'
import { useAnnotation } from 'app/state/annotation'

export function AnnotationOverviewTable() {
const { activeAnnotation: annotation } = useAnnotation()

if (!annotation) {
return null
}

return (
<AccordionMetadataTable
id="annotation-overview"
header={i18n.annotationOverview}
data={[
{
label: i18n.depositionDate,
values: [annotation.deposition_date],
},
{
label: i18n.releaseDateBlank,
values: [annotation.release_date],
},
{
label: i18n.lastModifiedBlank,
values: [annotation.last_modified_date ?? '--'],
},
{
label: i18n.authorsMaybePlural,
// TODO render author emails
// TODO render bold primary author
values: annotation.authors.map((author) => author.name),
},
{
label: i18n.lastModifiedBlank,
values: [annotation.last_modified_date ?? '--'],
},
{
label: i18n.affiliationNames,
values: Array.from(
new Set(
annotation.author_affiliations
.flatMap((author) => author.affiliation_name)
.filter((value): value is string => !!value),
),
),
},
{
label: i18n.publications,
values: [annotation.annotation_publication ?? '--'],
},
{
label: i18n.annotationMethod,
values: [annotation.annotation_method],
},
{
label: i18n.annotationSoftware,
values: [annotation.annotation_software ?? '--'],
},
]}
/>
)
}
Loading

0 comments on commit ed7b04e

Please sign in to comment.