Skip to content

Commit

Permalink
feat: Add Tomograms table (#988)
Browse files Browse the repository at this point in the history
closes #995 

Barebones initial table (UX not finalized yet, waiting until then to do
fit & finish). Metadata drawer is empty. Download modal does not target
the selected tomogram yet.

Query changes:
- Added an `is_canonical` check to the existing
`tomogram_voxel_spacings` field (which we can delete after launch) so
that if we need to turn off the flag, the UI will still only display the
canonical `tomogram`.
- Replaced `tomogram_stats` count with top-level `tomograms_aggregate`
count that doesn't require FE summing.

BE pieces not supported yet:
 - Distinguish between Portal Standard and Author Submitted tomograms.
 - Deposition date.
 - Alignment ID.


![image](https://github.com/user-attachments/assets/db706cf9-09d7-46bc-ae0a-b0f591c2d529)
  • Loading branch information
bchu1 authored Aug 9, 2024
1 parent 79f7247 commit 420acdd
Show file tree
Hide file tree
Showing 15 changed files with 321 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function PageHeader({
const { t } = useI18n()

return (
<div className="flex flex-auto justify-center">
<div className="flex flex-auto justify-center grow-0">
<header className="flex flex-col items-center w-full min-h-[48px]">
<div className="flex flex-col justify-start w-full pb-sds-xl">
<div
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Button, Icon } from '@czi-sds/components'
import { sum } from 'lodash-es'

import { Breadcrumbs } from 'app/components/Breadcrumbs'
import { I18n } from 'app/components/I18n'
Expand Down Expand Up @@ -42,7 +41,7 @@ function FileSummary({ data }: { data: FileSummaryData[] }) {

export function RunHeader() {
const multipleTomogramsEnabled = useFeatureFlag('multipleTomograms')
const { run, annotationFilesAggregates } = useRunById()
const { run, annotationFilesAggregates, tomogramsCount } = useRunById()
const { toggleDrawer } = useMetadataDrawer()
const { t } = useI18n()

Expand All @@ -56,11 +55,6 @@ export function RunHeader() {

const framesCount = run.tiltseries_aggregate.aggregate?.sum?.frames_count ?? 0
const tiltSeriesCount = run.tiltseries_aggregate.aggregate?.count ?? 0
const tomogramsCount = sum(
run.tomogram_stats.flatMap(
(stats) => stats.tomograms_aggregate.aggregate?.count ?? 0,
),
)
const annotationsCount = annotationFilesAggregates.totalCount

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useRunById } from 'app/hooks/useRunById'
import { i18n } from 'app/i18n'

import { RunTiltSeriesTable } from './RunTiltSeriesTable'
import { TomogramsTable } from './TomogramsTable'
import { TomogramsMetadataSection } from './TomogramsMetadataSection'

export function RunMetadataDrawer() {
const { run } = useRunById()
Expand All @@ -27,7 +27,7 @@ export function RunMetadataDrawer() {
initialOpen={false}
/>
<RunTiltSeriesTable />
<TomogramsTable />
<TomogramsMetadataSection />
</MetadataDrawer>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useAtom } from 'jotai'

import { useI18n } from 'app/hooks/useI18n'
import { MetadataDrawerId } from 'app/hooks/useMetadataDrawer'
import { metadataDrawerTomogramAtom } from 'app/state/metadataDrawerTomogram'

import { MetadataDrawer } from '../MetadataDrawer'

export function TomogramMetadataDrawer() {
const { t } = useI18n()
const [metadataDrawerTomogram] = useAtom(metadataDrawerTomogramAtom)

return (
<MetadataDrawer
title={metadataDrawerTomogram?.id.toString() ?? ''}
label={t('tomogramDetails')}
disabled={metadataDrawerTomogram === undefined}
drawerId={MetadataDrawerId.Tomogram}
>
<></>
</MetadataDrawer>
)
}
199 changes: 199 additions & 0 deletions frontend/packages/data-portal/app/components/Run/TomogramTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/* eslint-disable react/no-unstable-nested-components */

import { Button, Icon } from '@czi-sds/components'
import { ColumnDef, createColumnHelper } from '@tanstack/react-table'
import { useAtom } from 'jotai'
import { startCase } from 'lodash-es'
import { useCallback, useMemo } from 'react'

import { CellHeader, PageTable, TableCell } from 'app/components/Table'
import { TomogramTableWidths } from 'app/constants/table'
import { useDownloadModalQueryParamState } from 'app/hooks/useDownloadModalQueryParamState'
import { useI18n } from 'app/hooks/useI18n'
import {
MetadataDrawerId,
useMetadataDrawer,
} from 'app/hooks/useMetadataDrawer'
import { useRunById } from 'app/hooks/useRunById'
import {
metadataDrawerTomogramAtom,
Tomogram,
} from 'app/state/metadataDrawerTomogram'
import { getNeuroglancerUrl } from 'app/utils/url'

import { AuthorList } from '../AuthorList'
import { KeyPhoto } from '../KeyPhoto'

export function TomogramsTable() {
const { t } = useI18n()
const { tomograms } = useRunById()

const { toggleDrawer } = useMetadataDrawer()
const [, setMetadataDrawerTomogram] = useAtom(metadataDrawerTomogramAtom)

const { openTomogramDownloadModal } = useDownloadModalQueryParamState()

const openMetadataDrawer = useCallback(
(tomogram: Tomogram) => {
setMetadataDrawerTomogram(tomogram)
toggleDrawer(MetadataDrawerId.Tomogram)
},
[setMetadataDrawerTomogram, toggleDrawer],
)

const columns = useMemo(() => {
const columnHelper = createColumnHelper<Tomogram>()
return [
columnHelper.accessor('key_photo_url', {
header: () => <CellHeader width={TomogramTableWidths.photo} />,
cell: ({ row: { original } }) => (
<TableCell width={TomogramTableWidths.photo}>
<KeyPhoto
className="max-w-[134px]"
title={original.name}
src={original.key_photo_thumbnail_url ?? undefined}
/>
</TableCell>
),
}),
columnHelper.accessor('id', {
header: () => (
<CellHeader width={TomogramTableWidths.id}>
{t('tomogramId')}
</CellHeader>
),
cell: ({ row: { original } }) => (
<TableCell
className="flex flex-col gap-sds-xxxs !items-start"
width={TomogramTableWidths.id}
>
<div className="flex gap-sds-xs items-center">
<p className="text-sds-body-m leading-sds-body-m font-semibold text-ellipsis line-clamp-1 break-all">
{original.id}
</p>
</div>
<div className=" text-sds-gray-600 text-sds-body-xxs leading-sds-header-xxs">
<AuthorList authors={original.authors} compact />
</div>
</TableCell>
),
}),
// TODO(bchu): Switch to deposition_date when available.
columnHelper.accessor('name', {
id: 'deposition_date',
header: () => (
<CellHeader
className="whitespace-nowrap text-ellipsis"
width={TomogramTableWidths.depositionDate}
>
{t('depositionDate')}
</CellHeader>
),
cell: ({ getValue }) => (
<TableCell width={TomogramTableWidths.depositionDate}>
<div>{getValue()}</div>
</TableCell>
),
}),
// TODO(bchu): Switch to alignment_id when available.
columnHelper.accessor('name', {
header: () => (
<CellHeader width={TomogramTableWidths.alignment}>
{t('alignmentId')}
</CellHeader>
),
cell: ({ getValue }) => (
<TableCell width={TomogramTableWidths.alignment}>
<div>{getValue()}</div>
</TableCell>
),
}),
columnHelper.accessor('voxel_spacing', {
header: () => (
<CellHeader width={TomogramTableWidths.voxelSpacing}>
{t('voxelSpacing')}
</CellHeader>
),
cell: ({ getValue, row: { original } }) => (
<TableCell width={TomogramTableWidths.voxelSpacing}>
{t('unitAngstrom', { value: getValue() })}
<div className="text-sds-body-xxs leading-sds-body-xxs text-sds-gray-600">
({original.size_x}, {original.size_y}, {original.size_z})px
</div>
</TableCell>
),
}),
columnHelper.accessor('reconstruction_method', {
header: () => (
<CellHeader width={TomogramTableWidths.reconstructionMethod}>
{t('reconstructionMethod')}
</CellHeader>
),
cell: ({ getValue }) => (
<TableCell width={TomogramTableWidths.reconstructionMethod}>
<div>{getValue()}</div>
</TableCell>
),
}),
columnHelper.accessor('processing', {
header: () => (
<CellHeader width={TomogramTableWidths.postProcessing}>
{t('postProcessing')}
</CellHeader>
),
cell: ({ getValue }) => (
<TableCell width={TomogramTableWidths.postProcessing}>
<div>{startCase(getValue())}</div>
</TableCell>
),
}),
columnHelper.display({
id: 'tomogram-actions',
header: () => <CellHeader width={TomogramTableWidths.actions} />,
cell: ({ row: { original } }) => (
<TableCell width={TomogramTableWidths.actions}>
<div className="flex flex-col gap-sds-xs items-start">
{original.is_canonical &&
original.neuroglancer_config != null && (
<Button
sdsType="primary"
sdsStyle="rounded"
to={getNeuroglancerUrl(original.neuroglancer_config)}
startIcon={
<Icon sdsIcon="table" sdsSize="s" sdsType="button" />
}
>
{t('viewTomogram')}
</Button>
)}
<Button
sdsType="primary"
sdsStyle="minimal"
className="!justify-start !ml-sds-l"
onClick={() => openMetadataDrawer(original)}
startIcon={
<Icon sdsIcon="infoCircle" sdsSize="s" sdsType="button" />
}
>
<span>{t('info')}</span>
</Button>
<Button
sdsType="primary"
sdsStyle="minimal"
className="!justify-start !ml-sds-l"
onClick={openTomogramDownloadModal}
startIcon={
<Icon sdsIcon="download" sdsSize="s" sdsType="button" />
}
>
{t('download')}
</Button>
</div>
</TableCell>
),
}),
] as ColumnDef<Tomogram>[] // https://github.com/TanStack/table/issues/4382
}, [openMetadataDrawer, openTomogramDownloadModal, t])

return <PageTable data={tomograms} columns={columns} hoverType="none" />
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isFiducial } from 'app/utils/tomograms'

import { Matrix4x4 } from './Matrix4x4'

export function TomogramsTable() {
export function TomogramsMetadataSection() {
const { run } = useRunById()

const tomo = run.tomogram_voxel_spacings.at(0)?.tomograms.at(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Button, ButtonProps } from '@czi-sds/components'

import { useI18n } from 'app/hooks/useI18n'
import { EventPayloads, Events, usePlausible } from 'app/hooks/usePlausible'
import { getNeuroglancerUrl } from 'app/utils/url'

import { Link } from './Link'

Expand Down Expand Up @@ -45,9 +46,7 @@ export function ViewTomogramButton({
onMouseLeave={() => setIsHoveringOver?.(false)}
>
<Button
to={`https://neuroglancer-demo.appspot.com/#!${encodeURIComponent(
neuroglancerConfig,
)}`}
to={getNeuroglancerUrl(neuroglancerConfig)}
component={Link}
{...buttonProps}
>
Expand Down
12 changes: 12 additions & 0 deletions frontend/packages/data-portal/app/constants/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ export const AnnotationTableWidths = {
actions: { min: 120, max: 120 },
}

// TODO(bchu): Finalize these numbers.
export const TomogramTableWidths = {
photo: PHOTO_COLUMN_WIDTH,
id: { min: 250 },
depositionDate: { min: 91, max: 120 },
alignment: { min: 120, max: 130 },
voxelSpacing: { min: 120, max: 400 },
reconstructionMethod: { min: 120, max: 400 },
postProcessing: { min: 81, max: 120 },
actions: { min: 175, max: 120 },
}

export const RunTableWidths = {
photo: PHOTO_COLUMN_WIDTH,
name: { min: 400 },
Expand Down
Loading

0 comments on commit 420acdd

Please sign in to comment.