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

NickAkhmetov/CAT-777 Processed Data section #3495

Merged
merged 52 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
5bab2ee
set up processed data section, pull analysis details out of prov, add…
NickAkhmetov Jul 31, 2024
dea3cb2
text styling adjustments to match designs
NickAkhmetov Jul 31, 2024
4d88066
add helper panel, add publication/creation date logic, add tooltips t…
NickAkhmetov Aug 1, 2024
ee78c1e
sort the processed data section
NickAkhmetov Aug 1, 2024
7503f55
match table of contents and helper panel behavior
NickAkhmetov Aug 1, 2024
e0519ca
only expand published datasets by default, reduce prop drilling via c…
NickAkhmetov Aug 1, 2024
c7273ed
switch to 3 line clamp in helper panel
NickAkhmetov Aug 2, 2024
e9cebb6
pull in image pyramids into processed data, remove base visualization…
NickAkhmetov Aug 2, 2024
a213e72
display dataset relationships for vislifted datasets' pyramids
NickAkhmetov Aug 2, 2024
9dfab45
fix node links
NickAkhmetov Aug 2, 2024
4ce7bc2
error toast on malformed hash
NickAkhmetov Aug 5, 2024
ca986f2
workspace menu functionalities
NickAkhmetov Aug 5, 2024
ea4289c
no need to collapse internal subsections
NickAkhmetov Aug 5, 2024
d4f9097
enable changing processed datasets' versions
NickAkhmetov Aug 7, 2024
f706d97
fixes for image pyramid case
NickAkhmetov Aug 7, 2024
96b5424
remove version select from dataset summary spec
NickAkhmetov Aug 7, 2024
043659c
Add tracking and changelog
NickAkhmetov Aug 7, 2024
2ea5fcf
Add old version alert to processed dataset
NickAkhmetov Aug 8, 2024
8a41ab2
improve section href logic and consistency
NickAkhmetov Aug 8, 2024
1a93ad2
fix wording on old version alert, fix convertProvDataToNodesAndEdges …
NickAkhmetov Aug 8, 2024
8cc2eca
bulk data transfer section
NickAkhmetov Aug 8, 2024
6c1622f
collections section
NickAkhmetov Aug 9, 2024
0bbab47
metadata section updates
NickAkhmetov Aug 12, 2024
d495020
Merge branch 'unified-datasets' into nickakhmetov/processed-datasets
NickAkhmetov Aug 12, 2024
eba2c3b
fixes from first round of review
NickAkhmetov Aug 13, 2024
1d67892
improve bulk data transfer section styling, move contributors to attr…
NickAkhmetov Aug 13, 2024
47052f7
improve lazy-loaded hash handling
NickAkhmetov Aug 13, 2024
5af67df
set up redirects to primary dataset page
NickAkhmetov Aug 13, 2024
1337b06
add organ icons to top, style metadata tabs with proper icon size/color
NickAkhmetov Aug 13, 2024
a99287e
add json button, make copy ID icon consistent, remove unnecessary ext…
NickAkhmetov Aug 13, 2024
4b6166c
regular dataset label
NickAkhmetov Aug 13, 2024
d2cbbc3
improve text alignment for dataset relationship diagram and rest of s…
NickAkhmetov Aug 13, 2024
21a6a59
set up redirect toast
NickAkhmetov Aug 13, 2024
8344df3
add collapsible detail sections and section icons
NickAkhmetov Aug 14, 2024
96c6ebb
Combine `DetailPageSection` and `SectionHeader` into `CollapsibleDet…
NickAkhmetov Aug 14, 2024
6f8667f
fix specs
NickAkhmetov Aug 15, 2024
4987df4
add icons to other detail pages, simplify attribution
NickAkhmetov Aug 15, 2024
9f9c156
add icons to other detail pages, convert/clean up organ page, hide ac…
NickAkhmetov Aug 15, 2024
91e0fd7
fix missing cdn url errors
NickAkhmetov Aug 16, 2024
81efb7f
mock collapsible detail page section out
NickAkhmetov Aug 16, 2024
fb74d02
add explanatory comment to collections section context
NickAkhmetov Aug 19, 2024
1cbc62a
reuse lineclamp component instead of duplicating it
NickAkhmetov Aug 19, 2024
ad04b6d
fix barrel imports
NickAkhmetov Aug 19, 2024
c71383f
add specs
NickAkhmetov Aug 19, 2024
9008a74
add explanatory comment for publication bulk data transfer section
NickAkhmetov Aug 19, 2024
84dc88b
use vitessce hints instead of assayname
NickAkhmetov Aug 19, 2024
180d146
deduplicate dag prov types
NickAkhmetov Aug 19, 2024
3170a72
hide helper panel if no dataset is currently visible, add animations
NickAkhmetov Aug 19, 2024
70c5c45
type lint fix
NickAkhmetov Aug 19, 2024
38f29b3
CAT-831 Data loading improvements for provenance
NickAkhmetov Aug 20, 2024
2300131
fix test mock
NickAkhmetov Aug 20, 2024
8813366
adjust styles for error tile to match
NickAkhmetov Aug 20, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG-processed-datasets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added processed datasets section to display all visualizations for a given raw dataset.
15 changes: 5 additions & 10 deletions context/app/routes_browse.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,6 @@ def details(type, uuid):
**get_default_flask_data(),
'entity': entity,
}
marker = request.args.get('marker')

if type == 'dataset':
conf_cells_uuid = client.get_vitessce_conf_cells_and_lifted_uuid(entity, marker=marker)
flask_data.update({
'vitessce_conf': conf_cells_uuid.vitessce_conf.conf,
'has_notebook': conf_cells_uuid.vitessce_conf.cells is not None,
'vis_lifted_uuid': conf_cells_uuid.vis_lifted_uuid
})

if type == 'publication':
publication_ancillary_data = client.get_publication_ancillary_json(entity)
Expand Down Expand Up @@ -93,7 +84,11 @@ def details_vitessce(type, uuid):
abort(404)
client = get_client()
entity = client.get_entity(uuid)
vitessce_conf = client.get_vitessce_conf_cells_and_lifted_uuid(entity).vitessce_conf
parent_uuid = request.args.get('parent') or None
marker = request.args.get('marker') or None
parent = client.get_entity(parent_uuid) if parent_uuid else None
vitessce_conf = client.get_vitessce_conf_cells_and_lifted_uuid(
entity, marker=marker, parent=parent).vitessce_conf
# Returns a JSON null if there is no visualization.
response = jsonify(vitessce_conf.conf)
response.headers.add("Access-Control-Allow-Origin", "*")
Expand Down
9 changes: 1 addition & 8 deletions context/app/static/js/components/Routes/Routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ function Routes({ flaskData }) {
markdown,
errorCode,
list_uuid,
has_notebook,
vis_lifted_uuid,
entities,
organs,
organs_count,
Expand Down Expand Up @@ -86,12 +84,7 @@ function Routes({ flaskData }) {
if (urlPath.startsWith('/browse/dataset/') || urlPath.startsWith('/browse/support/')) {
return (
<Route>
<Dataset
assayMetadata={entity}
vitData={vitessce_conf}
hasNotebook={has_notebook}
visLiftedUUID={vis_lifted_uuid}
/>
<Dataset assayMetadata={entity} />
</Route>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React from 'react';
import { render, screen } from 'test-utils/functions';

import ProvAnalysisDetails from './ProvAnalysisDetails';
import { DagProvenanceType } from 'js/components/types';
import AnalysisDetails from './AnalysisDetails';

test('should display ingest and cwl lists', () => {
const dagListData = [
{ origin: 'https://github.com/fake1/fake1.git', hash: 'aaaaaaa' },
{ origin: 'https://github.com/fake2/fake2.git', hash: 'bbbbbbb' },
{ origin: 'https://github.com/fake3/fake3.git', hash: 'ccccccc', name: 'fake3.cwl' },
];
render(<ProvAnalysisDetails dagListData={dagListData} />);
render(<AnalysisDetails dagListData={dagListData} />);

expect(screen.getByText('Ingest Pipelines')).toBeInTheDocument();
expect(screen.getByText('CWL Pipelines')).toBeInTheDocument();
Expand All @@ -19,8 +20,8 @@ test('should display ingest and cwl lists', () => {
});

test('should not display pipelines when pipelines do not exist', () => {
const dagListData = [];
render(<ProvAnalysisDetails dagListData={dagListData} />);
const dagListData: DagProvenanceType[] = [];
render(<AnalysisDetails dagListData={dagListData} />);

expect(screen.queryByText('Ingest Pipelines')).not.toBeInTheDocument();
expect(screen.queryByText('CWL Pipelines')).not.toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';

import { DagProvenanceType } from 'js/components/types';
import AnalysisDetailsList from './AnalysisDetailsList';

interface AnalysisDetails {
dagListData: DagProvenanceType[];
}

function AnalysisDetails({ dagListData }: AnalysisDetails) {
const ingestPipelines = dagListData.filter((pipeline) => !('name' in pipeline));
const cwlPipelines = dagListData.filter((pipeline) => 'name' in pipeline);

return (
<div>
{ingestPipelines.length > 0 && (
<AnalysisDetailsList
pipelines={ingestPipelines}
pipelineType="Ingest"
tooltip="Supplementary links for the data ingestion pipelines for this dataset"
/>
)}
{cwlPipelines.length > 0 && (
<AnalysisDetailsList
pipelines={cwlPipelines}
pipelineType="CWL"
tooltip="Supplementary links for the CWL (Common Workflow Language) pipelines for this dataset"
/>
)}
</div>
);
}

export default AnalysisDetails;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { render, screen } from 'test-utils/functions';

import ProvAnalysisDetailsLink from './ProvAnalysisDetailsLink';
import ProvAnalysisDetailsLink from './AnalysisDetailsLink';

test('should display ingest pipeline link', () => {
const fakePipeline = { origin: 'https://github.com/fake/fake.git', hash: 'aabbccd' };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
import React from 'react';
import PropTypes from 'prop-types';

import { CwlIcon, FlexOutboundLink, PrimaryTextDivider, StyledListItem } from './style';

function ProvAnalysisDetailsLink({ data }) {
interface CWLPipelineLink {
hash: string;
name: string;
origin: string;
}

interface IngestPipelineLink {
hash: string;
origin: string;
}

interface ProvAnalysisDetailsLinkProps {
data: CWLPipelineLink | IngestPipelineLink;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move this into the Dataset type?


function ProvAnalysisDetailsLink({ data }: ProvAnalysisDetailsLinkProps) {
const trimmedOrigin = data.origin.replace(/\.git$/, '');
const githubUrl =
'name' in data ? `${trimmedOrigin}/blob/${data.hash}/${data.name}` : `${trimmedOrigin}/tree/${data.hash}`;
Expand All @@ -26,12 +40,4 @@ function ProvAnalysisDetailsLink({ data }) {
);
}

ProvAnalysisDetailsLink.propTypes = {
data: PropTypes.shape({
hash: PropTypes.string,
name: PropTypes.string,
origin: PropTypes.string,
}).isRequired,
};

export default ProvAnalysisDetailsLink;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import List from '@mui/material/List';
import Typography from '@mui/material/Typography';

import InfoTooltipIcon from 'js/shared-styles/icons/TooltipIcon';
import ProvAnalysisDetailsLink from './AnalysisDetailsLink';

interface ProvAnalysisDetailsListProps {
pipelines: {
hash: string;
name?: string;
origin: string;
}[];
pipelineType: string;
tooltip?: string;
}

function ProvAnalysisDetailsList({ pipelines, pipelineType, tooltip }: ProvAnalysisDetailsListProps) {
return (
<>
<Typography variant="subtitle2">
{`${pipelineType} Pipelines`} <InfoTooltipIcon iconTooltipText={tooltip} />
</Typography>
<List data-testid={pipelineType}>
{pipelines.map((item) => (
<ProvAnalysisDetailsLink
data={item}
key={`provenance-analysis-details-${pipelineType.toLowerCase()}-pipeline-${item.hash}`}
/>
))}
</List>
</>
);
}

export default ProvAnalysisDetailsList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import AnalysisDetails from './AnalysisDetails';

export default AnalysisDetails;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { styled } from '@mui/material/styles';
import LaunchRoundedIcon from '@mui/icons-material/LaunchRounded';
import Divider from '@mui/material/Divider';

import OutboundLink from 'js/shared-styles/Links/OutboundLink';

const CwlIcon = styled(LaunchRoundedIcon)(({ theme }) => ({
marginLeft: theme.spacing(0.5),
fontSize: '1rem',
alignSelf: 'center',
}));

const FlexOutboundLink = styled(OutboundLink)({
display: 'flex',
});

const PrimaryTextDivider = styled(Divider)(({ theme }) => ({
marginLeft: theme.spacing(0.5),
marginRight: theme.spacing(0.5),
height: '15px',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this height val be 1rem?

backgroundColor: theme.palette.text.primary,
alignSelf: 'center',
}));

const StyledListItem = styled('li')({
display: 'flex',
});

export { CwlIcon, FlexOutboundLink, PrimaryTextDivider, StyledListItem };
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,20 @@ import React from 'react';

import SectionHeader from 'js/shared-styles/sections/SectionHeader';
import { DetailPageSection } from 'js/components/detailPage/style';
import { useFlaskDataContext } from 'js/components/Contexts';
import { FilesContextProvider } from 'js/components/detailPage/files/FilesContext';
import BulkDataTransferPanels from './BulkDataTransferPanels';
import { StyledContainer } from './style';
import { SectionDescription } from '../ProcessedData/ProcessedDataset/SectionDescription';

function BulkDataTransfer() {
const {
entity: { entity_type },
} = useFlaskDataContext();

return (
<FilesContextProvider>
<DetailPageSection id="bulk-data-transfer" data-testid="bulk-data-transfer">
<SectionHeader
iconTooltipText={`Information about how to bulk download all files related to this ${entity_type?.toLowerCase()}.`}
>
Bulk Data Transfer
</SectionHeader>
<SectionHeader>Bulk Data Transfer</SectionHeader>
<SectionDescription>
This section explains how to download data in bulk from raw and processed datasets. Processed datasets have
separate download directories in Globus or dbGaP, distinct from the raw dataset.
</SectionDescription>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This text is longer and might be nice to have as a const.

<StyledContainer>
<BulkDataTransferPanels />
</StyledContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React from 'react';

import SectionHeader from 'js/shared-styles/sections/SectionHeader';
import PanelList from 'js/shared-styles/panels/PanelList';
import { useFlaskDataContext } from 'js/components/Contexts';
import { DetailPageSection } from 'js/components/detailPage/style';
import { buildCollectionsPanelsProps } from 'js/pages/Collections/utils';
import { CollectionHit } from 'js/pages/Collections/types';
import { SectionDescription } from '../ProcessedData/ProcessedDataset/SectionDescription';

interface CollectionsSectionProps {
collectionsData: CollectionHit[];
Expand All @@ -14,15 +14,13 @@ interface CollectionsSectionProps {
function CollectionsSection({ collectionsData }: CollectionsSectionProps) {
const panelsProps = buildCollectionsPanelsProps(collectionsData);

const {
entity: { entity_type },
} = useFlaskDataContext();

return (
<DetailPageSection id="collections">
<SectionHeader iconTooltipText={`List of collections that contain this ${entity_type?.toLowerCase()}.`}>
Collections
</SectionHeader>
<SectionHeader>Collections</SectionHeader>
<SectionDescription>
Collections may contain references to either raw or processed datasets. If a processed dataset is not included
in any collection, there will be no corresponding tabs in the table below.
</SectionDescription>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here ^

<PanelList panelsProps={panelsProps} />
</DetailPageSection>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,14 @@ async function fetchPipelineInfo({ url, datasets, groupsToken }: PipelineInfoReq
if (datasets.length !== 1) {
return Promise.resolve('');
}
return (await fetchSoftAssay({ url, dataset: datasets[0], groupsToken }))['pipeline-shorthand'];
const result = await fetchSoftAssay({ url, dataset: datasets[0], groupsToken });

// Handle image pyramids separately since their pipeline-shorthand is blank
if (result.assaytype === 'image_pyramid') {
return 'Image Pyramid Generation';
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a difference between this statement and checking for is_pyramid in the vitessce-hints?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't, so I've changed this condition to match what we use in portal-visualization.


return result['pipeline-shorthand'];
}

export function usePipelineInfo(datasets: string[]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import Typography from '@mui/material/Typography';
import { AccountTreeRounded, ExtensionRounded, SvgIconComponent } from '@mui/icons-material';
import Skeleton from '@mui/material/Skeleton';
import { Box } from '@mui/system';
import { datasetSectionId } from 'js/pages/Dataset/utils';
import StatusIcon from '../StatusIcon';
import { usePipelineInfo } from './hooks';
import { useProcessedDatasetDetails } from '../ProcessedData/ProcessedDataset/hooks';

interface CommonNodeInfo extends Record<string, unknown> {
name: string;
Expand All @@ -21,6 +23,7 @@ interface PipelineNodeInfo extends CommonNodeInfo {

interface DatasetNodeInfo extends CommonNodeInfo {
datasetType?: string;
uuid: string;
status: string;
}

Expand Down Expand Up @@ -122,13 +125,15 @@ function PipelineNode({
type ProcessedDatasetNodeProps = Node<DatasetNodeInfo, 'processedDataset'>;

function ProcessedDatasetNode({ data }: NodeProps<ProcessedDatasetNodeProps>) {
const { datasetDetails, isLoading } = useProcessedDatasetDetails(data.uuid);
return (
<NodeTemplate
rounded
target
href={`#${data.name}-section`}
href={datasetDetails ? `#${datasetSectionId(datasetDetails, 'section')}` : undefined}
icon={nodeIcons.processedDataset}
bgColor={nodeColors.processedDataset}
isLoading={isLoading}
{...data}
>
{data.datasetType}
Expand All @@ -139,13 +144,15 @@ function ProcessedDatasetNode({ data }: NodeProps<ProcessedDatasetNodeProps>) {
type ComponentDatasetNodeProps = Node<DatasetNodeInfo, 'componentDataset'>;

function ComponentDatasetNode({ data }: NodeProps<ComponentDatasetNodeProps>) {
const { datasetDetails, isLoading } = useProcessedDatasetDetails(data.uuid);
return (
<NodeTemplate
rounded
target
icon={nodeIcons.componentDataset}
href={`#${data.name}-section`}
href={datasetDetails ? `#${datasetSectionId(datasetDetails, 'section')}` : undefined}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be

href={datasetDetails && `#${datasetSectionId(datasetDetails, 'section')}`}`

?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to explicitly pass undefined in props when I can to be safe in case the false boolean value that the expression false && "string" gets toString()ed to "false" down the render chain. With that said, since this is repeated throughout this file, I'll make a helper function for this logic instead.

bgColor={nodeColors.componentDataset}
isLoading={isLoading}
{...data}
>
{data.datasetType}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ export const nodes = [
name: 'HBM749.SMWP.555',
status: 'Published',
datasetType: 'CODEX',
uuid: '6df2f796ad72307d04dc94d688b725c5',
},
},
{
Expand Down Expand Up @@ -500,6 +501,7 @@ export const nodes = [
name: 'HBM878.HRPG.642',
status: 'QA',
datasetType: 'CODEX [Cytokit + SPRM]',
uuid: 'd8f851efb54f84d7ee0952e10d4c449e',
},
},
{
Expand All @@ -509,6 +511,7 @@ export const nodes = [
name: 'HBM398.SWKV.256',
status: 'Published',
datasetType: 'CODEX [Cytokit + SPRM]',
uuid: 'ff77fcae7f6d9b5b7b8741c282677eef',
},
},
];
Expand Down
Loading
Loading