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 40 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.
53 changes: 42 additions & 11 deletions context/app/routes_browse.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import json
from urllib.parse import urlparse
from urllib.parse import urlparse, quote

from flask import (
render_template, jsonify,
Expand Down Expand Up @@ -47,22 +47,49 @@ def details(type, uuid):
client = get_client()
entity = client.get_entity(uuid)
actual_type = entity['entity_type'].lower()

# Redirect to primary dataset if this is
# - a support entity (e.g. an image pyramid)
# - a processed or component dataset
is_support = actual_type == 'support'
is_processed = entity.get('processing') != 'raw' and actual_type == 'dataset'
is_component = entity.get('is_component', False) is True
if (is_support or is_processed or is_component):
supported_entity = client.get_entities(
'datasets',
query_override={
"bool": {
"must": {
"terms": {
"descendant_ids": [uuid]
}
}
}
},
non_metadata_fields=['hubmap_id', 'uuid']
)

pipeline_anchor = entity.get('pipeline', entity.get('hubmap_id')).replace(' ', '')
anchor = quote(f'section-{pipeline_anchor}-{entity.get("status")}').lower()

if len(supported_entity) > 0:
return redirect(
url_for('routes_browse.details',
type='dataset',
uuid=supported_entity[0]['uuid'],
_anchor=anchor,
redirected=True))

if type != actual_type:
return redirect(url_for('routes_browse.details', type=actual_type, uuid=uuid))

redirected = request.args.get('redirected') == 'True'

flask_data = {
**get_default_flask_data(),
'entity': entity,
'redirected': redirected,
}
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 +120,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
1 change: 1 addition & 0 deletions context/app/static/js/components/Contexts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface FlaskDataContextType {
[key: string]: unknown;
title: string; // preview page title
vis_lifted_uuid?: string;
redirected?: boolean;
}

export const FlaskDataContext = createContext<FlaskDataContextType>('FlaskDataContext');
Expand Down
35 changes: 19 additions & 16 deletions context/app/static/js/components/Providers.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { FlaskDataContext, AppContext } from 'js/components/Contexts';
import GlobalStyles from 'js/components/globalStyles';
import { ProtocolAPIContext } from 'js/components/detailPage/Protocol/ProtocolAPIContext';
import { EntityStoreProvider } from 'js/stores/useEntityStore';
import { InitialHashContextProvider } from 'js/hooks/useInitialHash';
import theme from '../theme';
import GlobalFonts from '../fonts';
import { useEntityHeaderSprings } from './detailPage/entityHeader/EntityHeader/hooks';
Expand Down Expand Up @@ -63,22 +64,24 @@ export default function Providers({

return (
<SWRConfig value={swrConfig}>
<GlobalFonts />
<MuiThemeProvider theme={theme}>
<SCThemeProvider theme={theme}>
<AppContext.Provider value={appContext}>
<FlaskDataContext.Provider value={flaskDataWithDefaults}>
<EntityStoreProvider springs={springs} assayMetadata={flaskData?.entity ?? {}}>
<ProtocolAPIContext.Provider value={protocolsContext}>
<CssBaseline />
<GlobalStyles />
{children}
</ProtocolAPIContext.Provider>
</EntityStoreProvider>
</FlaskDataContext.Provider>
</AppContext.Provider>
</SCThemeProvider>
</MuiThemeProvider>
<InitialHashContextProvider>
<GlobalFonts />
<MuiThemeProvider theme={theme}>
<SCThemeProvider theme={theme}>
<AppContext.Provider value={appContext}>
<FlaskDataContext.Provider value={flaskDataWithDefaults}>
<EntityStoreProvider springs={springs} assayMetadata={flaskData?.entity ?? {}}>
<ProtocolAPIContext.Provider value={protocolsContext}>
<CssBaseline />
<GlobalStyles />
{children}
</ProtocolAPIContext.Provider>
</EntityStoreProvider>
</FlaskDataContext.Provider>
</AppContext.Provider>
</SCThemeProvider>
</MuiThemeProvider>
</InitialHashContextProvider>
</SWRConfig>
);
}
10 changes: 2 additions & 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 Expand Up @@ -341,6 +334,7 @@ Routes.propTypes = {
redirected_from: PropTypes.string,
cell_type: PropTypes.string,
globusGroups: PropTypes.object,
redirected: PropTypes.bool,
}),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@ import TableCell from '@mui/material/TableCell';
import TableRow from '@mui/material/TableRow';
import { capitalize } from '@mui/material/utils';

import SectionHeader from 'js/shared-styles/sections/SectionHeader';
import Description from 'js/shared-styles/sections/Description';
import { useTabs } from 'js/shared-styles/tabs';
import { Tab, Tabs, TabPanel } from 'js/shared-styles/tables/TableTabs';
import { StyledTableContainer } from 'js/shared-styles/tables';
import { InternalLink } from 'js/shared-styles/Links';
import { CellTypeBiomarkerInfo } from 'js/hooks/useUBKG';

import DetailPageSection from '../detailPage/DetailPageSection';
import { CollapsibleDetailPageSection } from '../detailPage/DetailPageSection';
import { useCellTypeBiomarkers } from './hooks';

const tableKeys = ['genes', 'proteins'] as const;
Expand Down Expand Up @@ -80,8 +79,7 @@ export default function CellTypesBiomarkersTable() {
const { openTabIndex, handleTabChange } = useTabs();

return (
<DetailPageSection id="biomarkers">
<SectionHeader>Biomarkers</SectionHeader>
<CollapsibleDetailPageSection id="biomarkers" title="Biomarkers">
<Stack direction="column" spacing={2}>
<Description>
This is a list of identified biomarkers that are validated from the listed source. Explore other sources in
Expand Down Expand Up @@ -118,6 +116,6 @@ export default function CellTypesBiomarkersTable() {
))}
</div>
</Stack>
</DetailPageSection>
</CollapsibleDetailPageSection>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import SectionHeader from 'js/shared-styles/sections/SectionHeader';
import React, { Fragment } from 'react';
import Description from 'js/shared-styles/sections/Description';
import { useTabs } from 'js/shared-styles/tabs';
Expand All @@ -13,16 +12,15 @@ import { format } from 'date-fns/format';
import { InternalLink } from 'js/shared-styles/Links';
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import DetailPageSection from '../detailPage/DetailPageSection';
import { CollapsibleDetailPageSection } from '../detailPage/DetailPageSection';
import { useCellTypeDetails } from './hooks';

export default function CellTypesEntitiesTables() {
const { datasets, samples, organs } = useCellTypeDetails();

const { openTabIndex, handleTabChange } = useTabs();
return (
<DetailPageSection id="organs">
<SectionHeader>Organs</SectionHeader>
<CollapsibleDetailPageSection title="Organs" id="organs">
<Description>
This is the list of organs and its associated data that is dependent on the data available within HuBMAP. To
filter the list of data in the table below by organ, select organ(s) from the list below. Multiple organs can be
Expand Down Expand Up @@ -71,6 +69,6 @@ export default function CellTypesEntitiesTables() {
))}
</StyledTableContainer>
</TabPanel>
</DetailPageSection>
</CollapsibleDetailPageSection>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import Box from '@mui/material/Box';
import Skeleton from '@mui/material/Skeleton';
import { LegendItem, LegendLabel, LegendOrdinal } from '@visx/legend';

import SectionHeader from 'js/shared-styles/sections/SectionHeader';
import Description from 'js/shared-styles/sections/Description';

import VerticalStackedBarChart from 'js/shared-styles/charts/VerticalStackedBarChart';
import { useBandScale, useLogScale, useOrdinalScale } from 'js/shared-styles/charts/hooks';
import DetailPageSection from 'js/components/detailPage/DetailPageSection';
import { CollapsibleDetailPageSection } from 'js/components/detailPage/DetailPageSection';
import { TooltipData } from 'js/shared-styles/charts/types';
import { CellTypeOrgan } from 'js/hooks/useCrossModalityApi';
import { useCellTypeDetails, useCellTypeName } from './hooks';
Expand Down Expand Up @@ -158,10 +157,9 @@ export default function CellTypesVisualization() {
const { organs = [] } = useCellTypeDetails();

return (
<DetailPageSection id="distribution-across-organs">
<SectionHeader>Distribution Across Organs</SectionHeader>
<CollapsibleDetailPageSection id="distribution-across-organs" title="Distribution Across Organs">
<Description>Cell counts in this visualization are dependent on the data available within HuBMAP.</Description>
<CellTypeOrgansGraph organs={organs} />
</DetailPageSection>
</CollapsibleDetailPageSection>
);
}
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;
Loading
Loading