Skip to content

Commit

Permalink
Unified Datasets (#3464)
Browse files Browse the repository at this point in the history
* Add changelog

* NickAkhmetov/CAT-677 Unified Prov Graph (#3458)

Co-authored-by: John Conroy <[email protected]>
Co-authored-by: Tabassum Kakar <[email protected]>
Co-authored-by: tkakar <[email protected]>
Co-authored-by: Austen Money <[email protected]>
fix table tabs (#3466)
Fix incorrect URL structure in gene search results (#3467)

* John conroy/update table of contents CAT-755 (#3473)

* Convert table of contents to typescript

* Convert route to ts and add boundaries

* Fix styling so top works

* Add icons map

* Fix responsiveness

* Handle sub links

* Update marker color

* Style table of contents

* Add icons to table of contents

* Add processed datasets for table of contents

* Add loading state

* Adjust padding per Tiffany feedback

* Update style per Tiffany's feedback

* Open toc accordions by default

* Add visualization to processed data sections in toc

* Add processed sections and refactor

* Update changelog

* Update organ page to use layout

* Switch to derived data per tiffany's feedback

* Use height props

* Pull utils/hooks/types into files

* Add a use callback for toc click handler

* Pull find active index into hook

* Consolidate styles

* Use system props

---------

Co-authored-by: John Conroy <[email protected]>

* NickAkhmetov/CAT-776 Dataset Relationships diagram (#3481)

* John conroy/summary bar (#3501)

* Remove placeholder for undefined items

* Create entity header action buttons

* Add workspaces button

* Add copy button

* Always show entity header

* Add basic view select chips

* Fix opacity transition

* Animation progress

* Stash

* Add check icon to view select

* Add datasets relationship graph to summary bar

* Delete unused gene components

* Update summary components

* Different heights for summary and diagram

* Show entity header content when expanded

* Use position sticky

* Fix divider

* Remove version select

* Add citation and consortium to summary for datasets

* Clamp description in expanded summary

* Only show view buttons on large desktops

* Show mapped status

* Dedupe flask data and entity store assay metadata

* Add icons to summary

* Split out cases into components

* Add icons

* Add paper for relationship diagram

* Show summary view for more entities

* Integrate publication summary

* Fix publication summary

* Fix undefined

* Fix tests

* Fix offsets

* Remove fit view

* Fix comments from review

* Fix data-testid propogation

---------

Co-authored-by: John Conroy <[email protected]>

* NickAkhmetov/CAT-777 Processed Data section (#3495)

* make sure sample metadata doesn't get lost

* hide empty dataset relationships box on dataset pages without processed descendants

---------

Co-authored-by: John Conroy <[email protected]>
Co-authored-by: Nikolay Akhmetov <[email protected]>
  • Loading branch information
3 people authored Aug 21, 2024
1 parent 80dd4af commit 25b9fea
Show file tree
Hide file tree
Showing 262 changed files with 7,429 additions and 2,858 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG-cat-831.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Extend provenance table logic to handle missing entities.
- Fix handling of large search requests.
- Request less data for provenance table tiles.
1 change: 1 addition & 0 deletions CHANGELOG-dataset-relationships.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add dataset relationships diagram.
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.
2 changes: 2 additions & 0 deletions CHANGELOG-unified-datasets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Update raw dataset detail pages to include processed dataset information.
- Update table of contents design.
2 changes: 1 addition & 1 deletion context/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const config: StorybookConfig = {
'@chromatic-com/storybook',
'@storybook/addon-webpack5-compiler-swc',
],

staticDirs: ['../app/static/assets', '../app/static/storybook-public'],
webpackFinal: async (config) => {
// exclude svgs from the default file loader
config.module?.rules?.forEach((rule) => {
Expand Down
33 changes: 20 additions & 13 deletions context/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import React from 'react';

import Providers from '../app/static/js/components/Providers';
import { initialize, mswDecorator } from 'msw-storybook-addon';
import { initialize, mswLoader } from 'msw-storybook-addon';
import { enableMapSet } from 'immer';


import '@fontsource-variable/inter/files/inter-latin-standard-normal.woff2';

enableMapSet();

const allowConditions = [(url) => String(url).endsWith('thumbnail.jpg')];
initialize({
onUnhandledRequest: ({ method, url }) => {
if (!allowConditions.some((conditionFn) => conditionFn(url))) {
console.error(`Unhandled ${method} request to ${url}.
This exception has been only logged in the console, however, it's strongly recommended to resolve this error as you don't want unmocked data in Storybook stories.
If you wish to mock an error response, please refer to this guide: https://mswjs.io/docs/recipes/mocking-error-responses
`);
serviceWorker: {
options: {
updateViaCache: 'none'
}
},
}
});
export const loaders = [mswLoader]

export const parameters = {
controls: {
Expand All @@ -29,15 +28,23 @@ export const parameters = {
},
};

export const mockEndpoints = { assetsEndpoint: 'https://assets.hubmapconsortium.org' };
export const mockEndpoints = { assetsEndpoint: 'https://assets.hubmapconsortium.org', softAssayEndpoint: '/soft-assay-endpoint' };
export const mockGroupsToken = '';

export const decorators = [
(Story) => (
<Providers endpoints={mockEndpoints} groupsToken={mockGroupsToken} isAuthenticated={false} userEmail={'undefined'} workspacesToken={'undefined'} isWorkspacesUser={false} isHubmapUser={false} flaskData={undefined}>
<Providers endpoints={mockEndpoints} groupsToken={mockGroupsToken} isAuthenticated={false} userEmail={'undefined'} workspacesToken={'undefined'} isWorkspacesUser={false} isHubmapUser={false} flaskData={{}}>
<Story />
</Providers>
),
mswDecorator,
];
export const tags = ['autodocs'];


const preview = {
parameters,
loaders,
decorators,
}

export default preview
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
6 changes: 4 additions & 2 deletions context/app/static/js/components/Contexts.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { useContext, createContext } from 'js/helpers/context';
import { Entity } from './types';
import { AllEntities } from './types';

// TODO: Continue populating these types as we find more of the uses of the flask data and app contexts

export interface FlaskDataContextType {
redirected_from: string;
entity: Entity; // Update to handle different entities.
entity: AllEntities; // Update to handle different entities.
[key: string]: unknown;
title: string; // preview page title
vis_lifted_uuid?: string;
redirected?: boolean;
}

export const FlaskDataContext = createContext<FlaskDataContextType>('FlaskDataContext');
Expand All @@ -27,6 +28,7 @@ interface AppContextType {
assetsEndpoint: string;
entityEndpoint: string;
elasticsearchEndpoint: string;
softAssayEndpoint: string;
groupsToken: string;
workspacesToken: string;
workspacesEndpoint: string;
Expand Down
39 changes: 25 additions & 14 deletions context/app/static/js/components/Providers.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import CssBaseline from '@mui/material/CssBaseline';
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';

const swrConfig = {
revalidateOnFocus: false,
Expand Down Expand Up @@ -55,22 +58,30 @@ export default function Providers({
[flaskData],
);

const flaskDataWithDefaults = useMemo(() => ({ entity: {}, ...flaskData }), [flaskData]);

const { springs } = useEntityHeaderSprings();

return (
<SWRConfig value={swrConfig}>
<GlobalFonts />
<MuiThemeProvider theme={theme}>
<SCThemeProvider theme={theme}>
<AppContext.Provider value={appContext}>
<FlaskDataContext.Provider value={flaskData}>
<ProtocolAPIContext.Provider value={protocolsContext}>
<CssBaseline />
<GlobalStyles />
{children}
</ProtocolAPIContext.Provider>
</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>
);
}
25 changes: 0 additions & 25 deletions context/app/static/js/components/Routes/Route/Route.jsx

This file was deleted.

44 changes: 44 additions & 0 deletions context/app/static/js/components/Routes/Route/Route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { PropsWithChildren, Suspense } from 'react';
import Stack from '@mui/material/Stack';
import Box from '@mui/material/Box';
import { ContainerProps } from '@mui/material/Container';

import { useIsDesktop } from 'js/hooks/media-queries';
import RouteLoader from '../RouteLoader';
import { StyledContainer } from './style';

export const leftRouteBoundaryID = 'left-route-boundary';
export const rightRouteBoundaryID = 'right-route-boundary';

function RouteBoundary({
id,
showBoundary,
}: {
id: typeof leftRouteBoundaryID | typeof rightRouteBoundaryID;
showBoundary: boolean;
}) {
return <Box id={id} flex="1 0" padding={2} display={!showBoundary ? 'none' : 'block'} />;
}

function Route({ children, disableWidthConstraint = false }: PropsWithChildren<{ disableWidthConstraint: boolean }>) {
const constrainWidthProps: Partial<ContainerProps> = disableWidthConstraint
? { maxWidth: false, disableGutters: true }
: { maxWidth: 'lg' };

const isDesktop = useIsDesktop();
const shouldShowBoundaries = !disableWidthConstraint && isDesktop;

return (
<Suspense fallback={<RouteLoader />}>
<Stack direction="row" width="100%">
<RouteBoundary id={leftRouteBoundaryID} showBoundary={shouldShowBoundaries} />
<StyledContainer {...constrainWidthProps} component="div">
{children}
</StyledContainer>
<RouteBoundary id={rightRouteBoundaryID} showBoundary={shouldShowBoundaries} />
</Stack>
</Suspense>
);
}

export default Route;
16 changes: 0 additions & 16 deletions context/app/static/js/components/Routes/Route/style.js

This file was deleted.

17 changes: 17 additions & 0 deletions context/app/static/js/components/Routes/Route/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { styled } from '@mui/material/styles';
import Container from '@mui/material/Container';

const StyledContainer = styled(Container)(({ theme }) => ({
marginTop: theme.spacing(2),
backgroundColor: theme.palette.background.default,
flexGrow: 1,
flexShrink: 1,
flexDirection: 'column',
display: 'flex',
})) as typeof Container;

// max width for a lg container
const routeContainerMaxWidth = 1232;
const routeContainerPadding = 32;

export { StyledContainer, routeContainerMaxWidth, routeContainerPadding };
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
Loading

0 comments on commit 25b9fea

Please sign in to comment.