Skip to content

Commit 0473eb4

Browse files
committed
Gene-specific chart finalized
1 parent 130c98c commit 0473eb4

File tree

4 files changed

+192
-8
lines changed

4 files changed

+192
-8
lines changed

src/pages/studyView/StudyViewPageStore.ts

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ import {
186186
transformSampleDataToSelectedSampleClinicalData,
187187
updateCustomIntervalFilter,
188188
invokeGenomicDataCount,
189+
invokeGenomicDataCountIncludeSampleid,
190+
groupSamplesByMutationStatus,
189191
invokeMutationDataCount,
190192
invokeNamespaceDataCount,
191193
invokeGenericAssayDataCount,
@@ -1635,6 +1637,79 @@ export class StudyViewPageStore
16351637
});
16361638
}
16371639

1640+
private createGeneSpecificComparisonSession(
1641+
chartMeta: ChartMeta,
1642+
clinicalAttributeValues: ClinicalDataCountSummary[],
1643+
statusCallback: (phase: LoadingPhase) => void
1644+
): Promise<string> {
1645+
statusCallback(LoadingPhase.DOWNLOADING_GROUPS);
1646+
1647+
return new Promise<string>(resolve => {
1648+
onMobxPromise([this.selectedSamples], async () => {
1649+
const chartInfo = this._geneSpecificChartMap.get(
1650+
chartMeta.uniqueKey
1651+
);
1652+
if (!chartInfo || !this.hasFilteredSamples) return;
1653+
1654+
const data: any[] = await invokeGenomicDataCountIncludeSampleid(
1655+
chartInfo,
1656+
this.filters,
1657+
true
1658+
);
1659+
1660+
const lcValueToColor = _.keyBy(clinicalAttributeValues, d =>
1661+
d.value.toLowerCase()
1662+
);
1663+
1664+
/**
1665+
* Step 1:
1666+
* Normalize counts into { group, sampleId } pairs
1667+
*/
1668+
const groupedSamples = groupSamplesByMutationStatus(data);
1669+
1670+
/**
1671+
* Step 2:
1672+
* Build SessionGroupData objects for each group, using the sample ids from step 1
1673+
*/
1674+
const groups: SessionGroupData[] = _.chain(
1675+
clinicalAttributeValues
1676+
)
1677+
.map(attrVal => {
1678+
const sampleIds = groupedSamples[attrVal.value];
1679+
if (!sampleIds?.length) return null;
1680+
1681+
return getGroupParameters(
1682+
attrVal.value,
1683+
sampleIds.map(id => ({
1684+
sampleId: id,
1685+
studyId: this.studyIds[0],
1686+
})),
1687+
this.studyIds,
1688+
lcValueToColor[attrVal.value.toLowerCase()]?.color
1689+
);
1690+
})
1691+
.compact()
1692+
.slice(
1693+
0,
1694+
doesChartHaveComparisonGroupsLimit(chartMeta)
1695+
? MAX_GROUPS_IN_SESSION
1696+
: undefined
1697+
)
1698+
.value();
1699+
1700+
statusCallback(LoadingPhase.CREATING_SESSION);
1701+
1702+
const { id } = await comparisonClient.addComparisonSession({
1703+
groups,
1704+
clinicalAttributeName: chartMeta.displayName,
1705+
origin: this.studyIds,
1706+
});
1707+
1708+
resolve(id);
1709+
});
1710+
});
1711+
}
1712+
16381713
private createCnaGeneComparisonSession(
16391714
chartMeta: ChartMeta,
16401715
hugoGeneSymbols: string[],
@@ -2069,13 +2144,9 @@ export class StudyViewPageStore
20692144
chartMeta.uniqueKey
20702145
);
20712146
if (isGeneSpecificChart) {
2072-
const chartInfo = this._geneSpecificChartMap.get(
2073-
chartMeta.uniqueKey
2074-
)!;
2075-
2076-
comparisonId = await this.createCnaGeneComparisonSession(
2147+
comparisonId = await this.createGeneSpecificComparisonSession(
20772148
chartMeta,
2078-
[chartInfo.hugoGeneSymbol],
2149+
params.clinicalAttributeValues!,
20792150
statusCallback
20802151
);
20812152
} else {

src/pages/studyView/StudyViewUtils.spec.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import {
6666
updateCustomIntervalFilter,
6767
updateGeneQuery,
6868
updateSavedUserPreferenceChartIds,
69+
groupSamplesByMutationStatus,
6970
} from 'pages/studyView/StudyViewUtils';
7071
import {
7172
CancerStudy,
@@ -3656,6 +3657,69 @@ describe('StudyViewUtils', () => {
36563657
});
36573658
});
36583659

3660+
describe('groupSamplesByMutationStatus', () => {
3661+
const data = [
3662+
{
3663+
counts: [
3664+
{
3665+
value: 'NOT_PROFILED',
3666+
sampleIds: ['ignore_me_001'],
3667+
},
3668+
],
3669+
},
3670+
{
3671+
counts: [
3672+
{
3673+
value: 'Missense_Mutation',
3674+
sampleIds: ['study_ABC_123', 'study_DEF_456'],
3675+
},
3676+
],
3677+
},
3678+
{
3679+
counts: [
3680+
{
3681+
value: 'NOT_MUTATED',
3682+
sampleIds: ['study_GHI_789'],
3683+
},
3684+
],
3685+
},
3686+
] as any;
3687+
3688+
it('groups and trims sample ids by mutation status correctly', () => {
3689+
const result = groupSamplesByMutationStatus(data);
3690+
assert.deepEqual(result, {
3691+
MUTATED: ['123', '456'],
3692+
NOT_MUTATED: ['789'],
3693+
});
3694+
});
3695+
3696+
it('handles missing or empty counts safely', () => {
3697+
const emptyData = [{}, { counts: [] }];
3698+
expect(groupSamplesByMutationStatus(emptyData)).toEqual({});
3699+
});
3700+
3701+
it('deduplicates sample IDs across mutation entries', () => {
3702+
const duplicateData = [
3703+
{
3704+
counts: [
3705+
{
3706+
value: 'Missense_Mutation',
3707+
sampleIds: ['study_X_001', 'study_X_002'],
3708+
},
3709+
{
3710+
value: 'Nonsense_Mutation',
3711+
sampleIds: ['study_X_001'],
3712+
},
3713+
],
3714+
},
3715+
];
3716+
3717+
expect(groupSamplesByMutationStatus(duplicateData)).toEqual({
3718+
MUTATED: ['001', '002'],
3719+
});
3720+
});
3721+
});
3722+
36593723
describe('getFilteredStudiesWithSamples', () => {
36603724
const samples: Sample[] = [
36613725
{

src/pages/studyView/StudyViewUtils.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ import { MultiSelectionTableRow } from './table/MultiSelectionTable';
125125
import Survival from 'pages/groupComparison/Survival';
126126
import { StructVarMultiSelectionTableRow } from './table/StructuralVariantMultiSelectionTable';
127127
import { ObservableMap } from 'mobx';
128+
import { boolean } from 'yargs';
128129

129130
// Cannot use ClinicalDataTypeEnum here for the strong type. The model in the type is not strongly typed
130131
export enum ClinicalDataTypeEnum {
@@ -4252,6 +4253,55 @@ export async function invokeGenericAssayDataCount(
42524253
return undefined;
42534254
}
42544255

4256+
export async function invokeGenomicDataCountIncludeSampleid(
4257+
chartInfo: GenomicChart,
4258+
filters: StudyViewFilter,
4259+
includeSampleIds: boolean
4260+
) {
4261+
let result = [];
4262+
let params = {
4263+
genomicDataCountFilter: {
4264+
genomicDataFilters: [
4265+
{
4266+
hugoGeneSymbol: chartInfo.hugoGeneSymbol,
4267+
profileType: chartInfo.profileType,
4268+
} as GenomicDataFilter,
4269+
],
4270+
studyViewFilter: filters,
4271+
},
4272+
$queryParameters: {
4273+
projection: 'DETAILED',
4274+
includeSampleIds,
4275+
},
4276+
} as any;
4277+
4278+
result = await getInternalClient().fetchMutationDataCountsUsingPOST(params);
4279+
4280+
return result;
4281+
}
4282+
4283+
export function groupSamplesByMutationStatus(data: any[]) {
4284+
const SKIP_VALUES = new Set(['NOT_PROFILED']);
4285+
const NON_MUTATION_VALUES = new Set(['NOT_MUTATED']);
4286+
4287+
return _.chain(data)
4288+
.flatMap(d => d.counts ?? [])
4289+
.filter(c => !SKIP_VALUES.has(c.value) && c.sampleIds.length > 0)
4290+
.flatMap(c => {
4291+
const group = NON_MUTATION_VALUES.has(c.value)
4292+
? c.value
4293+
: 'MUTATED';
4294+
4295+
return c.sampleIds.map((rawId: string) => ({
4296+
group,
4297+
sampleId: rawId.substring(rawId.lastIndexOf('_') + 1),
4298+
}));
4299+
})
4300+
.groupBy('group')
4301+
.mapValues(items => _.uniq(items.map(i => i.sampleId)))
4302+
.value();
4303+
}
4304+
42554305
export async function invokeGenomicDataCount(
42564306
chartInfo: GenomicChart,
42574307
filters: StudyViewFilter

src/pages/studyView/charts/ChartContainer.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,7 @@ export class ChartContainer extends React.Component<IChartContainerProps, {}> {
406406
(this.props.promise.result.treatments ??
407407
this.props.promise.result.patientTreatments ??
408408
this.props.promise.result)!.length > 1 &&
409-
COMPARISON_CHART_TYPES.indexOf(this.props.chartType) > -1 &&
410-
!this.props.chartMeta.mutationOptionType
409+
COMPARISON_CHART_TYPES.indexOf(this.props.chartType) > -1
411410
);
412411
}
413412

0 commit comments

Comments
 (0)