Skip to content

Commit

Permalink
Merge pull request #325 from ncihtan/319-atlas-count
Browse files Browse the repository at this point in the history
Show filtered case and sample counts in atlas table
  • Loading branch information
inodb authored Oct 1, 2021
2 parents e7bcf85 + 9b89d89 commit 287e198
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 38 deletions.
14 changes: 14 additions & 0 deletions components/ExploreTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@ import CaseTable from './CaseTable';
import FileTable from './FileTable';
import WPAtlasTable from './WPAtlasTable';
import { DataSchemaData } from '../lib/dataSchemaHelpers';
import {
ExploreSelectedFilter,
ISelectedFiltersByAttrName,
} from '../lib/types';

interface IExploreTabsProps {
router: NextRouter;
filteredFiles: Entity[];
nonAtlasSelectedFiltersByAttrName: ISelectedFiltersByAttrName;
samples: Entity[];
cases: Entity[];
filteredCasesByNonAtlasFilters: Entity[];
filteredSamplesByNonAtlasFilters: Entity[];
wpData: WPAtlas[];
schemaDataById?: { [schemaDataId: string]: DataSchemaData };
getGroupsByPropertyFiltered: any;
Expand Down Expand Up @@ -178,6 +185,13 @@ const ExploreTabs: React.FunctionComponent<IExploreTabsProps> = observer(
props.filteredSynapseAtlasesByNonAtlasFilters
}
onSelectAtlas={props.onSelectAtlas}
filteredCases={props.filteredCasesByNonAtlasFilters}
filteredBiospecimens={
props.filteredSamplesByNonAtlasFilters
}
selectedFiltersByAttrName={
props.nonAtlasSelectedFiltersByAttrName
}
/>
</div>
)}
Expand Down
51 changes: 48 additions & 3 deletions components/WPAtlasTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _ from 'lodash';
import { NextRouter } from 'next/router';
import React from 'react';
import { getDefaultDataTableStyle } from '../lib/dataTableHelpers';
import { Atlas, setTab } from '../lib/helpers';
import { Atlas, Entity, setTab } from '../lib/helpers';
import EnhancedDataTable from './EnhancedDataTable';
import { observer } from 'mobx-react';
import { action, computed, makeObservable, observable } from 'mobx';
Expand All @@ -12,13 +12,21 @@ import { faDownload } from '@fortawesome/free-solid-svg-icons';
import { ExploreTab } from './ExploreTabs';
import { Button, Modal } from 'react-bootstrap';
import getAtlasMetaData from '../lib/getAtlasMetaData';
import {
AttributeNames,
ExploreSelectedFilter,
ISelectedFiltersByAttrName,
} from '../lib/types';

interface IWPAtlasTableProps {
router: NextRouter;
synapseAtlasData: Atlas[];
selectedAtlases?: Atlas[];
filteredAtlases?: Atlas[];
onSelectAtlas?: (selected: Atlas[]) => void;
selectedFiltersByAttrName: ISelectedFiltersByAttrName;
filteredCases: Entity[];
filteredBiospecimens: Entity[];
}

const atlasMetadata = getAtlasMetaData();
Expand Down Expand Up @@ -131,6 +139,21 @@ export default class WPAtlasTable extends React.Component<IWPAtlasTableProps> {
);
}

@computed get filteredCasesByAtlas() {
return _.groupBy(this.props.filteredCases, (c: Entity) => c.atlasid);
}

@computed get filteredBiospecimensByAtlas() {
return _.groupBy(
this.props.filteredBiospecimens,
(c: Entity) => c.atlasid
);
}

@computed get shouldShowFilteredFractions() {
return !_.isEmpty(this.props.selectedFiltersByAttrName);
}

get columns() {
return [
{
Expand Down Expand Up @@ -193,15 +216,37 @@ export default class WPAtlasTable extends React.Component<IWPAtlasTableProps> {
grow: 0.5,
selector: 'num_cases',
cell: (atlas: Atlas) => (
<span className="ml-auto">{atlas.num_cases}</span>
<span className="ml-auto">
{this.shouldShowFilteredFractions
? `${
(
this.filteredCasesByAtlas[
atlas.htan_id
] || []
).length
}/`
: ''}
{atlas.num_cases}
</span>
),
sortable: true,
},
{
name: 'Biospecimens',
selector: 'num_biospecimens',
cell: (atlas: Atlas) => (
<span className="ml-auto">{atlas.num_biospecimens}</span>
<span className="ml-auto">
{this.shouldShowFilteredFractions
? `${
(
this.filteredBiospecimensByAtlas[
atlas.htan_id
] || []
).length
}/`
: ''}
{atlas.num_biospecimens}
</span>
),
sortable: true,
},
Expand Down
45 changes: 44 additions & 1 deletion lib/filterHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'lodash';

import { Entity } from './helpers';
import { Entity, filterObject } from './helpers';
import {
AttributeMap,
AttributeNames,
Expand Down Expand Up @@ -167,3 +167,46 @@ export function makeOptions(
};
});
}

export function getFilteredCases(
filteredFiles: Entity[],
selectedFiltersByAttrName: ISelectedFiltersByAttrName,
showAllCases: boolean
) {
const cases = _.chain(filteredFiles)
.flatMapDeep((f: Entity) => f.cases)
.uniqBy((f) => f.HTANParticipantID)
.value();

if (showAllCases) {
return cases;
} else {
const caseFilters = filterObject(
selectedFiltersByAttrName,
(filters, attrName) =>
!!AttributeMap[attrName as AttributeNames].caseFilter
);
return filterFiles(caseFilters, cases);
}
}

export function getFilteredSamples(
filteredFiles: Entity[],
filteredCases: Entity[],
showAllSamples: boolean
) {
const samples = _.chain(filteredFiles)
.flatMapDeep((file) => file.biospecimen)
.uniqBy((f) => f.HTANBiospecimenID)
.value();

if (showAllSamples) {
return samples;
} else {
const filteredCaseIds = _.keyBy(
filteredCases,
(c) => c.HTANParticipantID
);
return samples.filter((s) => s.HTANParentID in filteredCaseIds);
}
}
1 change: 1 addition & 0 deletions lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { WPAtlas } from '../types';
import {
ExploreOptionType,
ExploreSelectedFilter,
ISelectedFiltersByAttrName,
SynapseAtlas,
SynapseData,
SynapseSchema,
Expand Down
88 changes: 54 additions & 34 deletions pages/explore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { ScaleLoader } from 'react-spinners';
import { getAtlasList, WORDPRESS_BASE_URL } from '../ApiUtil';
import {
filterFiles,
getFilteredCases,
getFilteredSamples,
groupFilesByAttrNameAndValue,
} from '../lib/filterHelpers';
import {
Expand Down Expand Up @@ -201,50 +203,55 @@ class Search extends React.Component<

@computed
get filteredFiles() {
//const start = performance.now();
const ret = filterFiles(
this.selectedFiltersByAttrName,
this.state.files
);
//console.log('filtering cost', performance.now() - start);
return ret;
}

@computed
get filteredFilesByNonAtlasFilters() {
return filterFiles(
this.nonAtlasSelectedFiltersByAttrName,
this.state.files
);
}

@computed
get filteredSamples() {
const samples = _.chain(this.filteredFiles)
.flatMapDeep((file) => file.biospecimen)
.uniqBy((f) => f.HTANBiospecimenID)
.value();
return getFilteredSamples(
this.filteredFiles,
this.filteredCases,
this.showAllBiospecimens
);
}

if (this.showAllBiospecimens) {
return samples;
} else {
const filteredCaseIds = _.keyBy(
this.filteredCases,
(c) => c.HTANParticipantID
);
return samples.filter((s) => s.HTANParentID in filteredCaseIds);
}
@computed
get filteredSamplesByNonAtlasFilters() {
return getFilteredSamples(
this.filteredFilesByNonAtlasFilters,
this.filteredCasesByNonAtlasFilters,
this.showAllBiospecimens
);
}

@computed
get filteredCases() {
const cases = _.chain(this.filteredFiles)
.flatMapDeep((f: Entity) => f.cases)
.uniqBy((f) => f.HTANParticipantID)
.value();
return getFilteredCases(
this.filteredFiles,
this.selectedFiltersByAttrName,
this.showAllCases
);
}

if (this.showAllCases) {
return cases;
} else {
const caseFilters = filterObject(
this.selectedFiltersByAttrName,
(filters, attrName) =>
!!AttributeMap[attrName as AttributeNames].caseFilter
);
return filterFiles(caseFilters, cases);
}
@computed
get filteredCasesByNonAtlasFilters() {
return getFilteredCases(
this.filteredFilesByNonAtlasFilters,
this.nonAtlasSelectedFiltersByAttrName,
this.showAllCases
);
}

@computed get atlasMap() {
Expand Down Expand Up @@ -283,14 +290,18 @@ class Search extends React.Component<
}
}

@computed get nonAtlasSelectedFiltersByAttrName() {
return _.omit(this.selectedFiltersByAttrName, [
AttributeNames.AtlasName,
]);
}

@computed
get filteredAtlasesByNonAtlasFilters() {
const filtersExpectAtlasFilters = _.omit(
this.selectedFiltersByAttrName,
[AttributeNames.AtlasName]
);
const filtersExceptAtlasFilters = this
.nonAtlasSelectedFiltersByAttrName;

return _.chain(filterFiles(filtersExpectAtlasFilters, this.state.files))
return _.chain(filterFiles(filtersExceptAtlasFilters, this.state.files))
.map((f) => f.atlasid)
.uniq()
.map((id) => this.atlasMap[id])
Expand Down Expand Up @@ -360,6 +371,15 @@ class Search extends React.Component<
onSelectAtlas={this.onSelectAtlas}
samples={this.filteredSamples}
cases={this.filteredCases}
filteredCasesByNonAtlasFilters={
this.filteredCasesByNonAtlasFilters
}
filteredSamplesByNonAtlasFilters={
this.filteredSamplesByNonAtlasFilters
}
nonAtlasSelectedFiltersByAttrName={
this.nonAtlasSelectedFiltersByAttrName
}
wpData={this.props.wpAtlases}
getGroupsByPropertyFiltered={
this.getGroupsByPropertyFiltered
Expand Down

1 comment on commit 287e198

@vercel
Copy link

@vercel vercel bot commented on 287e198 Oct 1, 2021

Choose a reason for hiding this comment

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

Please sign in to comment.