Skip to content

Commit

Permalink
Fix incorrect oncokb data and cancer type label used in mutations tab…
Browse files Browse the repository at this point in the history
…le (#4758)

* fix incorrect cancer type and oncokb levels in mutations table

---------

Co-authored-by: Bryan Lai <[email protected]>
  • Loading branch information
gblaih and Bryan Lai authored Nov 3, 2023
1 parent e85a23a commit eec4050
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 35 deletions.
9 changes: 9 additions & 0 deletions src/pages/groupComparison/GroupComparisonMutationMapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ interface IGroupComparisonMutationMapperProps extends IMutationMapperProps {
uniqueSampleKeyToTumorType: {
[uniqueSampleKey: string]: string;
};
uniqueSampleKeyToCancerType: {
[uniqueSampleKey: string]: string;
};
}

@observer
Expand Down Expand Up @@ -95,6 +98,9 @@ export default class GroupComparisonMutationMapper extends MutationMapper<
uniqueSampleKeyToTumorType={
this.props.uniqueSampleKeyToTumorType
}
uniqueSampleKeyToCancerType={
this.props.uniqueSampleKeyToCancerType
}
oncoKbCancerGenes={this.props.store.oncoKbCancerGenes}
pubMedCache={this.props.pubMedCache}
genomeNexusCache={this.props.genomeNexusCache}
Expand All @@ -112,6 +118,9 @@ export default class GroupComparisonMutationMapper extends MutationMapper<
this.props.store.indexedMyVariantInfoAnnotations
}
oncoKbData={this.props.store.oncoKbData}
oncoKbDataForCancerType={
this.props.store.oncoKbDataForCancerType
}
oncoKbDataForUnknownPrimary={
this.props.store.oncoKbDataForUnknownPrimary
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export default class GroupComparisonMutationMapperWrapper extends React.Componen
filterMutationsBySelectedTranscript: true,
uniqueSampleKeyToTumorType: this.props.store
.uniqueSampleKeyToTumorType.result,
uniqueSampleKeyToCancerType: this.props.store
.uniqueSampleKeyToCancerType.result,
}
);
return store;
Expand Down Expand Up @@ -228,6 +230,10 @@ export default class GroupComparisonMutationMapperWrapper extends React.Componen
this.props.store.uniqueSampleKeyToTumorType
.result!
}
uniqueSampleKeyToCancerType={
this.props.store.uniqueSampleKeyToCancerType
.result!
}
/>
</div>
);
Expand Down
72 changes: 52 additions & 20 deletions src/pages/groupComparison/GroupComparisonMutationTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,7 @@ export interface IGroupComparisonMutationTableProps
};
}

export function hasMultipleCancerTypes(
data: Mutation[],
resolveTumorType: (mutation: Mutation) => string
): boolean {
return !_.every(
data,
m => resolveTumorType(m) === resolveTumorType(data[0])
);
}
export type TumorTypeAttribute = 'CANCER_TYPE' | 'CANCER_TYPE_DETAILED';

export default class GroupComparisonMutationTable extends MutationTable<
IGroupComparisonMutationTableProps
Expand All @@ -92,6 +84,54 @@ export default class GroupComparisonMutationTable extends MutationTable<
super(props);
}

// determine what tumor type attribute is the same for data
protected getSameTumorTypeAttribute(
data: Mutation[]
): TumorTypeAttribute | undefined {
// first, return CANCER_TYPE_DETAILED if the same in all mutations
if (
_.every(
data,
m => this.resolveTumorType(m) === this.resolveTumorType(data[0])
)
) {
return 'CANCER_TYPE_DETAILED';
}
// if not, return CANCER_TYPE if the same in all mutations
if (
_.every(
data,
m =>
this.resolveCancerType(m) ===
this.resolveCancerType(data[0])
)
) {
return 'CANCER_TYPE';
}
}

// determine what function to use to resolve tumor type based on what tumor type attribute is the same for data
protected getResolveTumorType(data: Mutation[]) {
if (this.getSameTumorTypeAttribute(data) === 'CANCER_TYPE_DETAILED') {
return this.resolveTumorType;
}
if (this.getSameTumorTypeAttribute(data) === 'CANCER_TYPE') {
return this.resolveCancerType;
}
return () => 'Cancer of Unknown Primary';
}

// determine what oncokb data to use
protected getOncoKbData(data: Mutation[]) {
if (this.getSameTumorTypeAttribute(data) === 'CANCER_TYPE_DETAILED') {
return this.props.oncoKbData;
}
if (this.getSameTumorTypeAttribute(data) === 'CANCER_TYPE') {
return this.props.oncoKbDataForCancerType;
}
return this.props.oncoKbDataForUnknownPrimary;
}

protected generateColumns() {
super.generateColumns();

Expand Down Expand Up @@ -262,20 +302,14 @@ export default class GroupComparisonMutationTable extends MutationTable<
this._columns[MutationTableColumnType.ANNOTATION].render = (
d: Mutation[]
) => {
const multipleCancerTypes = hasMultipleCancerTypes(
d,
this.resolveTumorType
);
return (
<span id="mutation-annotation">
{AnnotationColumnFormatter.renderFunction(
d,
{
hotspotData: this.props.hotspotData,
myCancerGenomeData: this.props.myCancerGenomeData,
oncoKbData: multipleCancerTypes
? this.props.oncoKbDataForUnknownPrimary
: this.props.oncoKbData,
oncoKbData: this.getOncoKbData(d),
oncoKbCancerGenes: this.props.oncoKbCancerGenes,
usingPublicOncoKbInstance: this.props
.usingPublicOncoKbInstance,
Expand All @@ -297,11 +331,9 @@ export default class GroupComparisonMutationTable extends MutationTable<
userDisplayName: this.props.userDisplayName,
indexedVariantAnnotations: this.props
.indexedVariantAnnotations,
resolveTumorType: multipleCancerTypes
? () => 'Cancer of Unknown Primary'
: this.resolveTumorType,
resolveTumorType: this.getResolveTumorType(d),
},
multipleCancerTypes
!this.getSameTumorTypeAttribute(d)
)}
</span>
);
Expand Down
34 changes: 34 additions & 0 deletions src/shared/components/mutationMapper/MutationMapperStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export interface IMutationMapperStoreConfig {
uniqueSampleKeyToTumorType?: {
[uniqueSampleKey: string]: string;
};
uniqueSampleKeyToCancerType?: {
[uniqueSampleKey: string]: string;
};
}

export default class MutationMapperStore extends DefaultMutationMapperStore<
Expand Down Expand Up @@ -236,6 +239,15 @@ export default class MutationMapperStore extends DefaultMutationMapperStore<
: this.uniqueSampleKeyToTumorType[mutation.uniqueSampleKey];
}

@autobind
protected getDefaultCancerType(mutation: Mutation): string {
return this.mutationMapperStoreConfig.uniqueSampleKeyToCancerType
? this.mutationMapperStoreConfig.uniqueSampleKeyToCancerType[
mutation.uniqueSampleKey
]
: 'Unknown';
}

@autobind
protected getDefaultEntrezGeneId(mutation: Mutation): number {
return mutation.gene.entrezGeneId;
Expand Down Expand Up @@ -377,6 +389,28 @@ export default class MutationMapperStore extends DefaultMutationMapperStore<
return true;
}

readonly oncoKbDataForCancerType: MobxPromise<
IOncoKbData | Error
> = remoteData(
{
await: () => [this.mutationData, this.oncoKbAnnotatedGenes],
invoke: () => {
return this.config.enableOncoKb
? this.dataFetcher.fetchOncoKbData(
this.mutations,
this.oncoKbAnnotatedGenes.result!,
this.getDefaultCancerType,
this.getDefaultEntrezGeneId
)
: Promise.resolve(ONCOKB_DEFAULT_DATA);
},
onError: () => {
// fail silently, leave the error handling responsibility to the data consumer
},
},
ONCOKB_DEFAULT_DATA
);

readonly oncoKbDataForUnknownPrimary: MobxPromise<
IOncoKbData | Error
> = remoteData(
Expand Down
24 changes: 24 additions & 0 deletions src/shared/components/mutationTable/MutationTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ import { getDefaultExpectedAltCopiesColumnDefinition } from 'shared/components/m
export interface IMutationTableProps {
studyIdToStudy?: { [studyId: string]: CancerStudy };
uniqueSampleKeyToTumorType?: { [uniqueSampleKey: string]: string };
uniqueSampleKeyToCancerType?: { [uniqueSampleKey: string]: string };
molecularProfileIdToMolecularProfile?: {
[molecularProfileId: string]: MolecularProfile;
};
Expand Down Expand Up @@ -114,6 +115,7 @@ export interface IMutationTableProps {
>;
cosmicData?: ICosmicData;
oncoKbData?: RemoteData<IOncoKbData | Error | undefined>;
oncoKbDataForCancerType?: RemoteData<IOncoKbData | Error | undefined>;
oncoKbDataForUnknownPrimary?: RemoteData<IOncoKbData | Error | undefined>;
usingPublicOncoKbInstance: boolean;
mergeOncoKbIcons?: boolean;
Expand Down Expand Up @@ -350,6 +352,28 @@ export default class MutationTable<
return 'Unknown';
}

@autobind
protected resolveCancerType(mutation: Mutation) {
// first, try to get it from uniqueSampleKeyToCancerType map
if (this.props.uniqueSampleKeyToCancerType) {
return this.props.uniqueSampleKeyToCancerType[
mutation.uniqueSampleKey
];
}

// second, try the study cancer type
if (this.props.studyIdToStudy) {
const studyMetaData = this.props.studyIdToStudy[mutation.studyId];

if (studyMetaData.cancerTypeId !== 'mixed') {
return studyMetaData.cancerType.name;
}
}

// return Unknown, this should not happen...
return 'Unknown';
}

@action.bound
protected handleOncoKbIconModeToggle(mergeIcons: boolean) {
if (this.props.onOncoKbIconToggle) {
Expand Down
41 changes: 26 additions & 15 deletions src/shared/lib/StoreUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1122,25 +1122,36 @@ export function findMrnaRankMolecularProfileId(
export function generateUniqueSampleKeyToTumorTypeMap(
clinicalDataForSamples: MobxPromise<ClinicalData[]>,
studies?: MobxPromise<CancerStudy[]>,
samples?: MobxPromise<Sample[]>
samples?: MobxPromise<Sample[]>,
useCancerTypeAttribute?: boolean
): { [sampleId: string]: string } {
const map: { [sampleId: string]: string } = {};

if (clinicalDataForSamples.result) {
// first priority is CANCER_TYPE_DETAILED in clinical data
_.each(clinicalDataForSamples.result, (clinicalData: ClinicalData) => {
if (clinicalData.clinicalAttributeId === 'CANCER_TYPE_DETAILED') {
map[clinicalData.uniqueSampleKey] = clinicalData.value;
}
});

// // second priority is CANCER_TYPE in clinical data
// _.each(clinicalDataForSamples.result, (clinicalData: ClinicalData) => {
// // update map with CANCER_TYPE value only if it is not already updated
// if (clinicalData.clinicalAttributeId === "CANCER_TYPE" && map[clinicalData.uniqueSampleKey] === undefined) {
// map[clinicalData.uniqueSampleKey] = clinicalData.value;
// }
// });
// if useCancerTypeAttribute is true, use CANCER_TYPE in clinical data
// else, use CANCER_TYPE_DETAILED in clinical data
if (useCancerTypeAttribute) {
_.each(
clinicalDataForSamples.result,
(clinicalData: ClinicalData) => {
if (clinicalData.clinicalAttributeId === 'CANCER_TYPE') {
map[clinicalData.uniqueSampleKey] = clinicalData.value;
}
}
);
} else {
_.each(
clinicalDataForSamples.result,
(clinicalData: ClinicalData) => {
if (
clinicalData.clinicalAttributeId ===
'CANCER_TYPE_DETAILED'
) {
map[clinicalData.uniqueSampleKey] = clinicalData.value;
}
}
);
}
}

// last resort: fall back to the study cancer type
Expand Down
20 changes: 20 additions & 0 deletions src/shared/lib/comparison/AnalysisStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,26 @@ export default abstract class AnalysisStore {
},
});

readonly uniqueSampleKeyToCancerType = remoteData<{
[uniqueSampleKey: string]: string;
}>({
await: () => [
this.clinicalDataForSamples,
this.studiesForSamplesWithoutCancerTypeClinicalData,
this.samplesWithoutCancerTypeClinicalData,
],
invoke: () => {
return Promise.resolve(
generateUniqueSampleKeyToTumorTypeMap(
this.clinicalDataForSamples,
this.studiesForSamplesWithoutCancerTypeClinicalData,
this.samplesWithoutCancerTypeClinicalData,
true
)
);
},
});

readonly clinicalDataForSamples = remoteData<ClinicalData[]>(
{
await: () => [this.studies, this.samples],
Expand Down

0 comments on commit eec4050

Please sign in to comment.