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

Add a separate publication tab to the explore page #744

Merged
merged 2 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions data/processSynapseJSON.log
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ ncc: Version 0.28.6
ncc: Compiling file index.js
ncc: Using [email protected] (local user-provided)
40kB sourcemap-register.js
11086kB index.js
11087kB index.js
1428kB index.js.map
11126kB [33746ms] - ncc 0.28.6
11127kB [37938ms] - ncc 0.28.6
Missing ParentBiospecimenID: {
Component: 'ScRNA-seqLevel3',
Filename: 'single_cell_RNAseq_level_3_ped_glioma/HTAN_pHGG_161_New_Reg1_snRNA/barcodes.tsv.gz',
Expand Down
4 changes: 2 additions & 2 deletions data/publications_manifest_all.json
Original file line number Diff line number Diff line change
Expand Up @@ -13633,7 +13633,7 @@
"Location of Publication": "biorXiv",
"Publication Abstract": "Homeostasis of normal tissues and the emergence of diseases such as cancer are controlled by changes in the proportions and states of diverse cell types, cell-cell interactions, and acellular components of the tissue microenvironment1. Spatial omics using highly multiplexed tissue profiling2 makes it possible to study these processes in situ, usually on thin, 4-5 micron thick sections (the standard histopathology format)3. Microscopy-based tissue imaging is commonly performed at a resolution sufficient to determine cell types but not detect the subtle morphological features associated with cytoskeletal reorganisation, juxtracrine signalling, or membrane trafficking4. Here we introduce a 3D imaging approach using existing instruments and reagents that is able to characterize a wide variety of organelles and structures at sub-micron scale while simultaneously quantifying millimetre-scale spatial features. We perform high-resolution 54-plex cyclic immunofluorescence (CyCIF) imaging3 on sections of primary human melanoma thick enough (30-40 microns) to fully encompass two or more layers of intact cells. In pre-invasive melanoma in situ5, 3D imaging of entire cell volumes showed that transformed melanocytic cells are plastic in state and participate in tightly localised niches of interferon signalling near sites of initial invasion into the underlying dermis. Below this layer, immune cells engaged in an unexpectedly diverse array of membrane-membrane interactions as well as looser \u201cneighbourhood\u201d associations6. These data provide new insight into the transitions occurring during early tumour formation and demonstrate the potential for phenotyping tissues at a level of detail previously restricted to cultured cells and organoids.",
"License": "CC-BY-NC-ND-4.0",
"PMID": "",
"PMID": "https://pubmed.ncbi.nlm.nih.gov/38014052/",
"Publication contains HTAN ID": "Yes",
"Data Type": "CyCIF",
"Tool": "Yes",
Expand Down Expand Up @@ -43726,7 +43726,7 @@
"Location of Publication": "biorXiv",
"Publication Abstract": "Neuroblastoma is a pediatric cancer arising from the developing sympathoadrenal lineage with complex inter- and intra-tumoral heterogeneity. To chart this complexity, we generated a comprehensive cell atlas of 55 neuroblastoma patient tumors, collected from two pediatric cancer institutions, spanning a range of clinical, genetic, and histologic features. Our atlas combines single-cell/nucleus RNA-seq (sc/scRNA-seq), bulk RNA-seq, whole exome sequencing, DNA methylation profiling, spatial transcriptomics, and two spatial proteomic methods. Sc/snRNA-seq revealed three malignant cell states with features of sympathoadrenal lineage development. All of the neuroblastomas had malignant cells that resembled sympathoblasts and the more differentiated adrenergic cells. A subset of tumors had malignant cells in a mesenchymal cell state with molecular features of Schwann cell precursors. DNA methylation profiles defined four groupings of patients, which differ in the degree of malignant cell heterogeneity and clinical outcomes. Using spatial proteomics, we found that neuroblastomas are spatially compartmentalized, with malignant tumor cells sequestered away from immune cells. Finally, we identify spatially restricted signaling patterns in immune cells from spatial transcriptomics. To facilitate the visualization and analysis of our atlas as a resource for further research in neuroblastoma, single cell, and spatial-omics, all data are shared through the Human Tumor Atlas Network Data Commons at www.humantumoratlas.org.",
"License": "CC-BY-NC-ND-4.0",
"PMID": "",
"PMID": "https://pubmed.ncbi.nlm.nih.gov/38260392/",
"Publication contains HTAN ID": "Yes",
"Data Type": "scRNA-seq,Bulk RNA-seq,Bulk DNA,MIBI",
"Tool": "Yes",
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export async function fetchData(): Promise<LoadDataResult> {
const processedSynURL =
process.env.NODE_ENV === 'development'
? '/processed_syn_data.json'
: `${getCloudBaseUrl()}/processed_syn_data_20250103_1351.json`;
: `${getCloudBaseUrl()}/processed_syn_data_20250122_1537.json`;
return fetchSynData(processedSynURL);
}

Expand Down
19 changes: 18 additions & 1 deletion packages/data-portal-commons/src/lib/publicationHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,24 @@ export function getPublicationTitle(
publicationSummary?: PublicationSummary,
publicationManifest?: PublicationManifest
): string | undefined {
return publicationSummary?.title || publicationManifest?.Title;
const title = publicationSummary?.title || publicationManifest?.Title;
// remove trailing dot (if any)
return title?.trim().replace(/\.$/, '');
}

export function getPublicationDate(
publicationSummary?: PublicationSummary,
publicationManifest?: PublicationManifest
) {
let date: string | undefined;

if (publicationSummary) {
date = publicationSummary.pubdate;
} else if (publicationManifest) {
date = publicationManifest.YearofPublication?.toString();
}

return date;
}

export function getPublicationSupportingLinks(
Expand Down
42 changes: 36 additions & 6 deletions packages/data-portal-explore/src/components/Explore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
HTANToGenericAttributeMap,
LoadDataResult,
PublicationManifest,
PublicationSummary,
} from '@htan/data-portal-commons';
import { AttributeNames } from '@htan/data-portal-utils';
import {
Expand All @@ -53,7 +54,8 @@ export interface IExploreState {
filters: { [key: string]: string[] };
schemaDataById?: { [schemaDataId: string]: DataSchemaData };
atlases: Atlas[];
publicationsByUid: { [uid: string]: PublicationManifest };
publicationManifestByUid: { [uid: string]: PublicationManifest };
publicationSummaryByPubMedID?: { [pubMedId: string]: PublicationSummary };
atlasData?: any;
}

Expand Down Expand Up @@ -84,7 +86,8 @@ export class Explore extends React.Component<IExploreProps, IExploreState> {
files: [],
filters: {},
atlases: [],
publicationsByUid: {},
publicationManifestByUid: {},
publicationSummaryByPubMedID: {},
schemaDataById: {},
};

Expand Down Expand Up @@ -162,7 +165,9 @@ export class Explore extends React.Component<IExploreProps, IExploreState> {
this.setState({
files: fillInEntities(data),
atlases: data.atlases,
publicationsByUid: data.publicationManifestByUid,
publicationManifestByUid: data.publicationManifestByUid,
publicationSummaryByPubMedID:
data.publicationSummaryByPubMedID,
});
});

Expand All @@ -188,6 +193,11 @@ export class Explore extends React.Component<IExploreProps, IExploreState> {
);
}

@computed
get samples() {
return getFilteredSamples(this.state.files, this.cases, false);
}

@computed
get filteredSamples() {
return getFilteredSamples(
Expand All @@ -206,6 +216,11 @@ export class Explore extends React.Component<IExploreProps, IExploreState> {
);
}

@computed
get cases() {
return getFilteredCases(this.state.files, {}, true);
}

@computed
get filteredCases() {
return getFilteredCases(
Expand All @@ -224,6 +239,15 @@ export class Explore extends React.Component<IExploreProps, IExploreState> {
);
}

@computed get filteredPublications() {
return _(this.filteredCases)
.flatMap((c) => c.publicationIds)
.compact()
.uniq()
.map((id) => this.state.publicationManifestByUid[id])
.value();
}

@computed get atlasMap() {
return _.keyBy(this.state.atlases, (a) => a.htan_id);
}
Expand Down Expand Up @@ -349,8 +373,8 @@ export class Explore extends React.Component<IExploreProps, IExploreState> {
selectedSynapseAtlases={this.selectedAtlases}
allSynapseAtlases={this.allAtlases}
onSelectAtlas={this.onSelectAtlas}
samples={this.filteredSamples}
cases={this.filteredCases}
samples={this.samples}
cases={this.cases}
filteredCasesByNonAtlasFilters={
this.filteredCasesByNonAtlasFilters
}
Expand All @@ -369,7 +393,13 @@ export class Explore extends React.Component<IExploreProps, IExploreState> {
toggleShowAllCases={this.toggleShowAllCases}
cloudBaseUrl={this.props.cloudBaseUrl}
getAtlasMetaData={this.props.getAtlasMetaData}
publicationsByUid={this.state.publicationsByUid}
publicationManifestByUid={
this.state.publicationManifestByUid
}
publicationSummaryByPubMedID={
this.state.publicationSummaryByPubMedID
}
filteredPublications={this.filteredPublications}
genericAttributeMap={HTANToGenericAttributeMap} // TODO needs to be configurable, different mappings for each portal
/>
</div>
Expand Down
85 changes: 60 additions & 25 deletions packages/data-portal-explore/src/components/ExploreTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Entity,
getNormalizedOrgan,
PublicationManifest,
PublicationSummary,
} from '@htan/data-portal-commons';
import { DataSchemaData } from '@htan/data-portal-schema';

Expand All @@ -19,6 +20,7 @@ import { BiospecimenTable } from './BiospecimenTable';
import { CaseTable } from './CaseTable';
import { DEFAULT_EXPLORE_PLOT_OPTIONS, ExplorePlot } from './ExplorePlot';
import { FileTable } from './FileTable';
import { PublicationTable } from './PublicationTable';
import { ExploreTab } from '../lib/types';

import styles from './exploreTabs.module.scss';
Expand Down Expand Up @@ -53,7 +55,9 @@ interface IExploreTabsProps {

genericAttributeMap?: { [attr: string]: GenericAttributeNames };
getAtlasMetaData: () => AtlasMetaData;
publicationsByUid: { [uid: string]: PublicationManifest };
publicationManifestByUid: { [uid: string]: PublicationManifest };
publicationSummaryByPubMedID?: { [pubMedId: string]: PublicationSummary };
filteredPublications: PublicationManifest[];
}

const metricTypes = [
Expand Down Expand Up @@ -146,6 +150,18 @@ export const ExploreTabs: React.FunctionComponent<IExploreTabsProps> = observer(
Atlases
</a>
</li>
<li className="nav-item">
<a
onClick={() => setTab(ExploreTab.PUBLICATION)}
className={`nav-link ${
activeTab === ExploreTab.PUBLICATION
? 'active'
: ''
}`}
>
Publications
</a>
</li>
<li className="nav-item">
<a
onClick={() => setTab(ExploreTab.CASES)}
Expand Down Expand Up @@ -182,23 +198,19 @@ export const ExploreTabs: React.FunctionComponent<IExploreTabsProps> = observer(
Files
</a>
</li>
{
<li className="nav-item">
<a
onClick={() => setTab(ExploreTab.PLOTS)}
className={`nav-link ${
activeTab === ExploreTab.PLOTS
? 'active'
: ''
}`}
>
Plots{' '}
<span style={{ color: 'orange' }}>
Beta!
</span>
</a>
</li>
}
<li className="nav-item">
<a
onClick={() => setTab(ExploreTab.PLOTS)}
className={`nav-link ${
activeTab === ExploreTab.PLOTS
? 'active'
: ''
}`}
>
Plots{' '}
<span style={{ color: 'orange' }}>Beta!</span>
</a>
</li>
</ul>
</div>

Expand All @@ -213,8 +225,8 @@ export const ExploreTabs: React.FunctionComponent<IExploreTabsProps> = observer(
groupsByPropertyFiltered={
props.groupsByPropertyFiltered
}
patientCount={props.cases.length}
publicationsByUid={props.publicationsByUid}
patientCount={props.filteredCases.length}
publicationsByUid={props.publicationManifestByUid}
/>
</div>
)}
Expand All @@ -235,10 +247,10 @@ export const ExploreTabs: React.FunctionComponent<IExploreTabsProps> = observer(
</label>*/}
<BiospecimenTable
synapseAtlases={props.filteredSynapseAtlases}
samples={props.samples}
samples={props.filteredSamples}
schemaDataById={props.schemaDataById}
genericAttributeMap={props.genericAttributeMap}
publicationsByUid={props.publicationsByUid}
publicationsByUid={props.publicationManifestByUid}
/>
</div>
)}
Expand All @@ -259,10 +271,31 @@ export const ExploreTabs: React.FunctionComponent<IExploreTabsProps> = observer(
</label>*/}
<CaseTable
synapseAtlases={props.filteredSynapseAtlases}
cases={props.cases}
cases={props.filteredCases}
schemaDataById={props.schemaDataById}
genericAttributeMap={props.genericAttributeMap}
publicationsByUid={props.publicationsByUid}
publicationsByUid={props.publicationManifestByUid}
/>
</div>
)}

{activeTab === ExploreTab.PUBLICATION && (
<div
className={`tab-content ${styles.publicationTab} ${
activeTab !== ExploreTab.PUBLICATION ? 'd-none' : ''
}`}
>
<PublicationTable
publications={props.filteredPublications}
publicationSummaryByPubMedID={
props.publicationSummaryByPubMedID
}
participants={props.cases}
filteredParticipants={props.filteredCases}
biospecimens={props.samples}
filteredBiospecimens={props.filteredSamples}
files={props.files}
filteredFiles={props.filteredFiles}
/>
</div>
)}
Expand All @@ -275,7 +308,9 @@ export const ExploreTabs: React.FunctionComponent<IExploreTabsProps> = observer(
>
<AtlasTable
setTab={setTab}
publications={_.values(props.publicationsByUid)}
publications={_.values(
props.publicationManifestByUid
)}
getAtlasMetaData={props.getAtlasMetaData}
synapseAtlasData={props.allSynapseAtlases}
selectedAtlases={props.selectedSynapseAtlases}
Expand Down
Loading