From 41fb142c79e26f5b85da14f640da52f2500ab850 Mon Sep 17 00:00:00 2001 From: aditigopalan Date: Tue, 19 Jul 2022 09:33:57 -0400 Subject: [PATCH 1/5] Add reVUE revised effect for one KIT variant This uses some of the old "curious cases" infrastructure but replaces it by directly using the VUE data from the revue-website repo. This still needs to be fully refactored to lose all references to "curious cases". --- src/component/variantPage/BasicInfo.tsx | 56 +++++++++--- .../variantPage/BiologicalFunction.tsx | 17 +--- .../variantPage/FunctionalGroups.tsx | 4 +- .../biologicalFunction/CuriousCase.tsx | 52 ++++------- src/data/VUEs.json | 86 +++++++++++++++++++ src/page/Variant.tsx | 5 +- src/page/VariantStore.ts | 28 +++--- 7 files changed, 170 insertions(+), 78 deletions(-) create mode 100644 src/data/VUEs.json diff --git a/src/component/variantPage/BasicInfo.tsx b/src/component/variantPage/BasicInfo.tsx index 7c3c5683..232b0ebc 100644 --- a/src/component/variantPage/BasicInfo.tsx +++ b/src/component/variantPage/BasicInfo.tsx @@ -20,6 +20,7 @@ import basicInfo from './BasicInfo.module.scss'; import { Link } from 'react-router-dom'; import { annotationQueryFields } from '../../config/configDefaults'; import Toggle from '../Toggle'; +import { VUE } from './biologicalFunction/CuriousCase'; interface IBasicInfoProps { annotation: VariantAnnotationSummary | undefined; @@ -31,6 +32,7 @@ interface IBasicInfoProps { isCanonicalTranscriptSelected?: boolean | undefined; allValidTranscripts: string[]; onTranscriptSelect(transcriptId: string): void; + vue: VUE | undefined; } type MutationTypeFormat = { @@ -141,7 +143,8 @@ export default class BasicInfo extends React.Component { if (this.props.annotation) { let renderData: BasicInfoData[] | null = this.getDataFromTranscriptConsequenceSummary( - selectedTranscript || canonicalTranscript + selectedTranscript || canonicalTranscript, + this.props.variant ); if (renderData === null) { return null; @@ -204,7 +207,8 @@ export default class BasicInfo extends React.Component { } public getDataFromTranscriptConsequenceSummary( - transcript: TranscriptConsequenceSummary | undefined + transcript: TranscriptConsequenceSummary | undefined, + variant: string ): BasicInfoData[] | null { // no canonical transcript, return null if (transcript === undefined) { @@ -235,18 +239,42 @@ export default class BasicInfo extends React.Component { key: 'tsg', category: 'tsg', }); - // protein change - parsedData.push({ - value: transcript.hgvspShort, - key: 'hgvsShort', - category: 'default', - }); - // variant classification - parsedData.push({ - value: transcript.variantClassification, - key: 'variantClassification', - category: getMutationTypeClassName(transcript), - }); + + // Check if variant is a VUE + let revisedProteinEffectRecord; + if (this.props.vue?.revisedProteinEffects) { + revisedProteinEffectRecord = this.props.vue.revisedProteinEffects.find((x) => x.variant === variant) + } + if (revisedProteinEffectRecord?.revisedProteinEffect) { + parsedData.push( + { + value: 'VUE', + key: 'RevueAnnotation', + category: getMutationTypeClassName(transcript), + }, + ); + parsedData.push( + { + value: revisedProteinEffectRecord.revisedProteinEffect, + key: 'hgvsShort', + category: 'default', + } + + ) + parsedData.push({ + value: revisedProteinEffectRecord.variantClassification, + key: 'variantClassification', + category: getMutationTypeClassName(transcript), + }); + } else { + // variant classification + parsedData.push({ + value: transcript.variantClassification, + key: 'variantClassification', + category: getMutationTypeClassName(transcript), + }); + } + // variant type parsedData.push({ value: this.props.annotation!.variantType, diff --git a/src/component/variantPage/BiologicalFunction.tsx b/src/component/variantPage/BiologicalFunction.tsx index 99eb1d3b..e3136c33 100644 --- a/src/component/variantPage/BiologicalFunction.tsx +++ b/src/component/variantPage/BiologicalFunction.tsx @@ -7,23 +7,18 @@ import { IndicatorQueryResp } from 'oncokb-ts-api-client'; import Separator from '../Separator'; import Oncokb from './biologicalFunction/Oncokb'; import ClinvarInterpretation from './biologicalFunction/ClinvarInterpretation'; -import CuriousCase from './biologicalFunction/CuriousCase'; -import { CuriousCases } from 'genome-nexus-ts-api-client/dist/generated/GenomeNexusAPIInternal'; +import CuriousCase, { VUE } from './biologicalFunction/CuriousCase'; interface IBiologicalFunctionProps { oncokb: IndicatorQueryResp | undefined; isCanonicalTranscriptSelected: boolean; clinvar?: Clinvar; - curiousCases?: CuriousCases; + curiousCases?: VUE; } @observer class BiologicalFunction extends React.Component { public render() { - // only show curious case when URL has "curious" - const showCuriousCase = - window.location.search.split('curious').length > 1 ? true : false; - return ( <> { this.props.isCanonicalTranscriptSelected } /> - {showCuriousCase && ( - <> - - - - )} + + ); } diff --git a/src/component/variantPage/FunctionalGroups.tsx b/src/component/variantPage/FunctionalGroups.tsx index 58f40bd9..f1ad8487 100644 --- a/src/component/variantPage/FunctionalGroups.tsx +++ b/src/component/variantPage/FunctionalGroups.tsx @@ -16,7 +16,7 @@ import functionalGroupsStyle from './functionalGroups.module.scss'; import ClinicalImplication from './ClinicalImplication'; import { RemoteData } from 'cbioportal-utils'; import PrevalenceInCancer from './PrevalenceInCancer'; -import { CuriousCases } from 'genome-nexus-ts-api-client/dist/generated/GenomeNexusAPIInternal'; +import { VUE } from './biologicalFunction/CuriousCase'; interface IFunctionalGroupsProps { annotationInternal?: VariantAnnotationSummary; @@ -28,8 +28,8 @@ interface IFunctionalGroupsProps { indexAnnotationsByGenomicLocationPromise: RemoteData<{ [genomicLocation: string]: VariantAnnotation; }>; - curiousCases?: CuriousCases; genomeBuild?: string; + curiousCases?: VUE; } @observer diff --git a/src/component/variantPage/biologicalFunction/CuriousCase.tsx b/src/component/variantPage/biologicalFunction/CuriousCase.tsx index aa8b167a..54649d9c 100644 --- a/src/component/variantPage/biologicalFunction/CuriousCase.tsx +++ b/src/component/variantPage/biologicalFunction/CuriousCase.tsx @@ -1,43 +1,31 @@ import { DefaultTooltip } from 'cbioportal-frontend-commons'; -import { CuriousCases } from 'genome-nexus-ts-api-client/dist/generated/GenomeNexusAPIInternal'; import { observer } from 'mobx-react'; import * as React from 'react'; import functionalGroupsStyle from '../functionalGroups.module.scss'; +export declare type VUE = { + 'comment': string; + 'context': string; + 'referenceText': string; + 'pubmedIds': Array; + 'revisedProteinEffects': Array<{ + 'revisedProteinEffect': string; + 'variantClassification': string; + 'variant': string; + }> +}; + interface ICuriousCaseProps { - curiousCases?: CuriousCases; + curiousCases?: VUE; } -const TooltipLinks: React.FunctionComponent<{ pubmedIds: number[] }> = ( - props -) => { - let tooltipLinks: JSX.Element[] = []; - props.pubmedIds.forEach((id) => { - tooltipLinks.push( - - {id} - {`; `} - - ); - }); - return <>{tooltipLinks}; -}; - const CuriousCaseContent: React.FunctionComponent<{ - curiousCases?: CuriousCases; + curiousCases?: VUE; }> = (props) => { return props.curiousCases ? ( - {props.curiousCases.comment} {`. Pubmed ids: `} - {props.curiousCases.pubmedIds ? ( - - ) : ( - 'NA' - )} + {props.curiousCases.comment}{' '} + ({props.curiousCases.referenceText}) ) : ( NA @@ -52,13 +40,9 @@ export default class CuriousCase extends React.Component {
Curated list of splice variants.
} + overlay={
Repository of Variants with Unexpected Effects
} > - - Curious Case - + reVUE diff --git a/src/data/VUEs.json b/src/data/VUEs.json new file mode 100644 index 00000000..c9c79f62 --- /dev/null +++ b/src/data/VUEs.json @@ -0,0 +1,86 @@ +[ + { + "hugoGeneSymbol": "APC", + "genomicLocation": "5:g.112151184A>G", + "defaultEffect": "synonymous", + "comment": "Introduces splice acceptor site causing a frameshift", + "pubmedIds": [29316426], + "context": "Recurrent alteration in CRC", + "referenceText": "Yaeger et al, 2018" + }, + { + "hugoGeneSymbol": "BAP1", + "genomicLocation": "3:g.52439306A>G", + "defaultEffect": "synonymous", + "comment": "Leads to exon 11 skipping", + "context": "Recurrent and prognostic in kidney cancer", + "pubmedIds": [33681728], + "referenceText": "Niersch et al, 2021" + }, + { + "genomicLocation": "17:g.7579883G>A", + "defaultEffect": "synonymous", + "comment": "Affects mRNA structure, reduces HDM2 binding", + "context": "Recurrent in many cancer types", + "pubmedIds": [31671760], + "hugoGeneSymbol": "TP53", + "referenceText": "Swiatkowska et al, 2019" + }, + { + "genomicLocation": "12:g.25380277G>T", + "defaultEffect": "missense", + "comment": "Introduces a splice donor site if Q61K is not co-occurring with G60G", + "context": "Recurrent in many cancer types", + "pubmedIds": [35236983], + "hugoGeneSymbol": "KRAS", + "referenceText": "Kobayashi et al, 2022" + }, + { + "hugoGeneSymbol": "CTNNB1", + "genomicLocation": "Deletions flanking exon 2-3 (chr3:41265579_41266277del)", + "defaultEffect": "splice", + "comment": "Complete exon 3 skip or inframe deletion in exon 3", + "pubmedIds": [29316426], + "context": "Recurrent alteration in CRC", + "referenceText": "Yaeger et al, 2018" + }, + { + "hugoGeneSymbol": "KIT", + "genomicLocation": "Deletions flanking intron 10 - exon 11 boundary (4:55593576_55593606del)", + "defaultEffect": "splice", + "comment": "Changes splice donor site, cause inframe deletion in exon 11", + "context": "Actionable in GIST", + "pubmedIds": [15507676], + "referenceText": "Corless et al, 2004", + "revisedProteinEffects": [ + {"variant":"4:g.55593576_55593606del", "transcriptId": "ENST00000288135", "revisedProteinEffect": "p.549_557del", "variantClassification":"In frame deletion"} + ] + }, + { + "hugoGeneSymbol": "MET", + "genomicLocation": "Indels and substitutions flanking exon 14 (7:116411926_116412083del)", + "defaultEffect": "splice", + "comment": "Skipping of exon 14", + "context": "Actionable in NSCLC", + "pubmedIds": [27343443], + "referenceText": "Schrock et al, 2014" + }, + { + "hugoGeneSymbol": "PIK3R1", + "genomicLocation": "Indels and substitutions flanking exon 11 (5:67589663G>A)", + "defaultEffect": "splice", + "comment": "Skipping of exon 11", + "context": "Recurrent in breast and uterine cancer", + "pubmedIds": [25488983], + "referenceText": "Lucas et al, 2014" + }, + { + "hugoGeneSymbol": "FLT3", + "genomicLocation": "Indels around tyrosine kinase (13:28608214_28608215insTT...)", + "defaultEffect": "splice", + "comment": "Internal Tandem Duplication", + "context": "Recurrent alteration in AML", + "pubmedIds": [25488983], + "referenceText": "Zhang et al, 2020" + } +] diff --git a/src/page/Variant.tsx b/src/page/Variant.tsx index 0f535f95..072e43d0 100644 --- a/src/page/Variant.tsx +++ b/src/page/Variant.tsx @@ -391,6 +391,7 @@ class Variant extends React.Component { transcriptId ) } + vue={this.props.store.vue.result} /> @@ -470,12 +471,10 @@ class Variant extends React.Component { ]: VariantAnnotation; }> } - curiousCases={ - this.props.store.curiousCases.result - } genomeBuild={ this.props.store.genomeBuild.result } + curiousCases={this.props.store.vue.result} /> diff --git a/src/page/VariantStore.ts b/src/page/VariantStore.ts index e52456db..8209f74a 100644 --- a/src/page/VariantStore.ts +++ b/src/page/VariantStore.ts @@ -22,14 +22,15 @@ import { import { annotationQueryFields } from '../config/configDefaults'; import { getTranscriptConsequenceSummary } from '../util/AnnotationSummaryUtil'; import { getDataFetcher } from '../util/ApiUtils'; -import genomeNexusInternalClient from '../util/genomeNexusClientInternalInstance'; +//import genomeNexusInternalClient from '../util/genomeNexusClientInternalInstance'; import genomeNexusClient from '../util/genomeNexusClientInstance'; import oncoKbClient from '../util/oncokbClientInstance'; import { - variantToGenomicLocationString, + //variantToGenomicLocationString, variantToMutation, } from '../util/variantUtils'; import { MainStore } from './MainStore'; +import { default as VUEs } from './../data/VUEs.json'; export interface VariantStoreConfig { variant: string; @@ -198,18 +199,21 @@ export class VariantStore { onError: () => {}, }); - readonly curiousCases = remoteData({ + readonly vue = remoteData({ await: () => [this.annotation], - invoke: async () => { - return genomeNexusInternalClient.fetchCuriousCasesGET({ - genomicLocation: encodeURIComponent( - variantToGenomicLocationString(this.annotationSummary) - ), - }); - }, - onError: (err: Error) => { - // fail silently + invoke: async() => { + if (this.annotation.result?.annotation_summary) { + const transcriptSummary = this.annotationSummary?.transcriptConsequenceSummary; + // Check if variant is a VUE + const vue = VUEs.find((x) => x.hugoGeneSymbol === transcriptSummary?.hugoGeneSymbol); + if (vue?.revisedProteinEffects && vue.revisedProteinEffects.find((x) => x.variant === this.variant)) { + return vue; + } else { + return undefined; + } + } }, + onError: () => {}, }); @computed From c504b435b04f7c550fb558b0658a8fcdc7ba12f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=94=A7=20Ino=20de=20Bruijn=20=F0=9F=A7=AC?= Date: Sun, 30 Oct 2022 12:51:11 -0400 Subject: [PATCH 2/5] Fix protein change in lollipop --- src/component/variantPage/BasicInfo.tsx | 32 ++++----- .../variantPage/BiologicalFunction.tsx | 6 +- .../variantPage/FunctionalGroups.tsx | 6 +- .../biologicalFunction/CuriousCase.tsx | 52 -------------- .../variantPage/biologicalFunction/ReVUE.tsx | 70 +++++++++++++++++++ src/data/VUEs.json | 7 +- src/page/Variant.tsx | 9 ++- src/page/VariantStore.ts | 35 ++++++++-- src/util/variantUtils.ts | 6 +- 9 files changed, 138 insertions(+), 85 deletions(-) delete mode 100644 src/component/variantPage/biologicalFunction/CuriousCase.tsx create mode 100644 src/component/variantPage/biologicalFunction/ReVUE.tsx diff --git a/src/component/variantPage/BasicInfo.tsx b/src/component/variantPage/BasicInfo.tsx index 232b0ebc..731d1478 100644 --- a/src/component/variantPage/BasicInfo.tsx +++ b/src/component/variantPage/BasicInfo.tsx @@ -20,7 +20,7 @@ import basicInfo from './BasicInfo.module.scss'; import { Link } from 'react-router-dom'; import { annotationQueryFields } from '../../config/configDefaults'; import Toggle from '../Toggle'; -import { VUE } from './biologicalFunction/CuriousCase'; +import { VUE } from './biologicalFunction/ReVUE'; interface IBasicInfoProps { annotation: VariantAnnotationSummary | undefined; @@ -243,24 +243,22 @@ export default class BasicInfo extends React.Component { // Check if variant is a VUE let revisedProteinEffectRecord; if (this.props.vue?.revisedProteinEffects) { - revisedProteinEffectRecord = this.props.vue.revisedProteinEffects.find((x) => x.variant === variant) + revisedProteinEffectRecord = + this.props.vue.revisedProteinEffects.find( + (x) => x.variant === variant + ); } if (revisedProteinEffectRecord?.revisedProteinEffect) { - parsedData.push( - { - value: 'VUE', - key: 'RevueAnnotation', - category: getMutationTypeClassName(transcript), - }, - ); - parsedData.push( - { - value: revisedProteinEffectRecord.revisedProteinEffect, - key: 'hgvsShort', - category: 'default', - } - - ) + parsedData.push({ + value: 'VUE', + key: 'RevueAnnotation', + category: getMutationTypeClassName(transcript), + }); + parsedData.push({ + value: revisedProteinEffectRecord.revisedProteinEffect, + key: 'hgvsShort', + category: 'default', + }); parsedData.push({ value: revisedProteinEffectRecord.variantClassification, key: 'variantClassification', diff --git a/src/component/variantPage/BiologicalFunction.tsx b/src/component/variantPage/BiologicalFunction.tsx index e3136c33..eb77d21f 100644 --- a/src/component/variantPage/BiologicalFunction.tsx +++ b/src/component/variantPage/BiologicalFunction.tsx @@ -7,13 +7,13 @@ import { IndicatorQueryResp } from 'oncokb-ts-api-client'; import Separator from '../Separator'; import Oncokb from './biologicalFunction/Oncokb'; import ClinvarInterpretation from './biologicalFunction/ClinvarInterpretation'; -import CuriousCase, { VUE } from './biologicalFunction/CuriousCase'; +import ReVUE, { VUE } from './biologicalFunction/ReVUE'; interface IBiologicalFunctionProps { oncokb: IndicatorQueryResp | undefined; isCanonicalTranscriptSelected: boolean; clinvar?: Clinvar; - curiousCases?: VUE; + vue?: VUE; } @observer @@ -35,7 +35,7 @@ class BiologicalFunction extends React.Component { } /> - + ); } diff --git a/src/component/variantPage/FunctionalGroups.tsx b/src/component/variantPage/FunctionalGroups.tsx index f1ad8487..8f470f9f 100644 --- a/src/component/variantPage/FunctionalGroups.tsx +++ b/src/component/variantPage/FunctionalGroups.tsx @@ -16,7 +16,7 @@ import functionalGroupsStyle from './functionalGroups.module.scss'; import ClinicalImplication from './ClinicalImplication'; import { RemoteData } from 'cbioportal-utils'; import PrevalenceInCancer from './PrevalenceInCancer'; -import { VUE } from './biologicalFunction/CuriousCase'; +import { VUE } from './biologicalFunction/ReVUE'; interface IFunctionalGroupsProps { annotationInternal?: VariantAnnotationSummary; @@ -29,7 +29,7 @@ interface IFunctionalGroupsProps { [genomicLocation: string]: VariantAnnotation; }>; genomeBuild?: string; - curiousCases?: VUE; + vue?: VUE; } @observer @@ -67,7 +67,7 @@ class FunctionalGroups extends React.Component { this.props.isCanonicalTranscriptSelected } clinvar={this.clinvar} - curiousCases={this.props.curiousCases} + vue={this.props.vue} /> diff --git a/src/component/variantPage/biologicalFunction/CuriousCase.tsx b/src/component/variantPage/biologicalFunction/CuriousCase.tsx deleted file mode 100644 index 54649d9c..00000000 --- a/src/component/variantPage/biologicalFunction/CuriousCase.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { DefaultTooltip } from 'cbioportal-frontend-commons'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import functionalGroupsStyle from '../functionalGroups.module.scss'; - -export declare type VUE = { - 'comment': string; - 'context': string; - 'referenceText': string; - 'pubmedIds': Array; - 'revisedProteinEffects': Array<{ - 'revisedProteinEffect': string; - 'variantClassification': string; - 'variant': string; - }> -}; - -interface ICuriousCaseProps { - curiousCases?: VUE; -} - -const CuriousCaseContent: React.FunctionComponent<{ - curiousCases?: VUE; -}> = (props) => { - return props.curiousCases ? ( - - {props.curiousCases.comment}{' '} - ({props.curiousCases.referenceText}) - - ) : ( - NA - ); -}; - -@observer -export default class CuriousCase extends React.Component { - public render() { - return ( -
-
- Repository of Variants with Unexpected Effects
} - > - reVUE - -
- - - ); - } -} diff --git a/src/component/variantPage/biologicalFunction/ReVUE.tsx b/src/component/variantPage/biologicalFunction/ReVUE.tsx new file mode 100644 index 00000000..cff8bebb --- /dev/null +++ b/src/component/variantPage/biologicalFunction/ReVUE.tsx @@ -0,0 +1,70 @@ +import { DefaultTooltip } from 'cbioportal-frontend-commons'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import functionalGroupsStyle from '../functionalGroups.module.scss'; + +export declare type RevisedProteinEffectRecord = { + revisedProteinEffect: string; + variantClassification: string; + variant: string; +}; + +export declare type VUE = { + comment: string; + context: string; + referenceText: string; + pubmedIds: Array; + revisedProteinEffects: Array; +}; + +interface IReVUEProps { + vue?: VUE; +} + +const ReVUEContent: React.FunctionComponent<{ + vue?: VUE; +}> = (props) => { + return props.vue ? ( + + {props.vue.comment}{' '} + + ({props.vue.referenceText}) + + + ) : ( + NA + ); +}; + +@observer +export default class ReVUE extends React.Component { + public render() { + return ( +
+
+ + Repository for Variants with Unexpected Effects +
+ } + > + + reVUE + + +
+ + + ); + } +} diff --git a/src/data/VUEs.json b/src/data/VUEs.json index c9c79f62..a3767fe3 100644 --- a/src/data/VUEs.json +++ b/src/data/VUEs.json @@ -53,7 +53,12 @@ "pubmedIds": [15507676], "referenceText": "Corless et al, 2004", "revisedProteinEffects": [ - {"variant":"4:g.55593576_55593606del", "transcriptId": "ENST00000288135", "revisedProteinEffect": "p.549_557del", "variantClassification":"In frame deletion"} + { + "variant": "4:g.55593576_55593606del", + "transcriptId": "ENST00000288135", + "revisedProteinEffect": "p.549_557del", + "variantClassification": "In_Frame_Del" + } ] }, { diff --git a/src/page/Variant.tsx b/src/page/Variant.tsx index 072e43d0..22e74772 100644 --- a/src/page/Variant.tsx +++ b/src/page/Variant.tsx @@ -148,6 +148,7 @@ class Variant extends React.Component { private getMutationMapper() { const mutation = variantToMutation( this.props.store.annotationSummary, + this.props.store.revisedProteinEffect, this.props.store.selectedTranscript ); if ( @@ -185,7 +186,7 @@ class Variant extends React.Component { [TrackName.Exon]: 'visible', [TrackName.UniprotTopology]: 'visible', }} - hugoSymbol={mutation[0].gene.hugoGeneSymbol} + hugoSymbol={mutation[0]?.gene?.hugoGeneSymbol} entrezGeneId={Number( getTranscriptConsequenceSummary( this.props.store.annotationSummary, @@ -364,7 +365,9 @@ class Variant extends React.Component { mutation={ variantToMutation( this.props.store - .annotationSummary + .annotationSummary, + this.props.store + .revisedProteinEffect )[0] } variant={this.props.variant} @@ -474,7 +477,7 @@ class Variant extends React.Component { genomeBuild={ this.props.store.genomeBuild.result } - curiousCases={this.props.store.vue.result} + vue={this.props.store.vue.result} /> diff --git a/src/page/VariantStore.ts b/src/page/VariantStore.ts index 8209f74a..5ddae209 100644 --- a/src/page/VariantStore.ts +++ b/src/page/VariantStore.ts @@ -177,7 +177,10 @@ export class VariantStore { await: () => [this.civicGenes], invoke: async () => { if (this.civicGenes.result) { - const mutations = variantToMutation(this.annotationSummary); + const mutations = variantToMutation( + this.annotationSummary, + this.revisedProteinEffect + ); mutations.forEach((mutation) => { // fetchCivicVariants cannot handle protein change values starting with 'p.' @@ -201,12 +204,20 @@ export class VariantStore { readonly vue = remoteData({ await: () => [this.annotation], - invoke: async() => { + invoke: async () => { if (this.annotation.result?.annotation_summary) { - const transcriptSummary = this.annotationSummary?.transcriptConsequenceSummary; + const transcriptSummary = + this.annotationSummary?.transcriptConsequenceSummary; // Check if variant is a VUE - const vue = VUEs.find((x) => x.hugoGeneSymbol === transcriptSummary?.hugoGeneSymbol); - if (vue?.revisedProteinEffects && vue.revisedProteinEffects.find((x) => x.variant === this.variant)) { + const vue = VUEs.find( + (x) => + x.hugoGeneSymbol === transcriptSummary?.hugoGeneSymbol + ); + if ( + vue?.revisedProteinEffects?.find( + (x) => x.variant === this.variant + ) + ) { return vue; } else { return undefined; @@ -216,6 +227,19 @@ export class VariantStore { onError: () => {}, }); + @computed + get revisedProteinEffect() { + return this.vue.result?.revisedProteinEffects?.find( + (x) => + x.variant === this.variant && + (this.selectedTranscript === x.transcriptId || + // selectedTranscript is empty string if canonical is shown + (!this.selectedTranscript && + this.annotationSummary?.canonicalTranscriptId === + x.transcriptId)) + ); + } + @computed get annotationSummary() { return this.annotation.result @@ -227,6 +251,7 @@ export class VariantStore { get getMutationMapperStore() { const mutation = variantToMutation( this.annotationSummary, + this.revisedProteinEffect, this.selectedTranscript ); if ( diff --git a/src/util/variantUtils.ts b/src/util/variantUtils.ts index 5a53b4e2..7efc9594 100644 --- a/src/util/variantUtils.ts +++ b/src/util/variantUtils.ts @@ -3,10 +3,12 @@ import { Mutation, } from 'cbioportal-utils'; import { VariantAnnotationSummary } from 'genome-nexus-ts-api-client'; +import { RevisedProteinEffectRecord } from '../component/variantPage/biologicalFunction/ReVUE'; import { getTranscriptConsequenceSummary } from './AnnotationSummaryUtil'; export function variantToMutation( data: VariantAnnotationSummary | undefined, + revisedProteinEffectRecord?: RevisedProteinEffectRecord | undefined, transcript?: string ): Mutation[] { let mutations = []; @@ -26,7 +28,9 @@ export function variantToMutation( endPosition: data.genomicLocation.end, referenceAllele: data.genomicLocation.referenceAllele, variantAllele: data.genomicLocation.variantAllele, - proteinChange: transcriptConsequence.hgvspShort, + proteinChange: + revisedProteinEffectRecord?.revisedProteinEffect || + transcriptConsequence.hgvspShort, proteinPosStart: transcriptConsequence.proteinPosition?.start ? transcriptConsequence.proteinPosition.start : getProteinPosStart(transcriptConsequence.hgvspShort), From d426c068490e33eb9ac51b562449e4ce97985e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=94=A7=20Ino=20de=20Bruijn=20=F0=9F=A7=AC?= Date: Sun, 30 Oct 2022 15:31:54 -0400 Subject: [PATCH 3/5] Style pills more --- .../variantPage/BasicInfo.module.scss | 9 ++ src/component/variantPage/BasicInfo.tsx | 111 ++++++++++++++---- .../variantPage/biologicalFunction/ReVUE.tsx | 2 +- src/page/Variant.tsx | 4 + src/util/variantUtils.ts | 36 ++++-- 5 files changed, 128 insertions(+), 34 deletions(-) diff --git a/src/component/variantPage/BasicInfo.module.scss b/src/component/variantPage/BasicInfo.module.scss index a2e4284f..38394c7d 100644 --- a/src/component/variantPage/BasicInfo.module.scss +++ b/src/component/variantPage/BasicInfo.module.scss @@ -76,6 +76,15 @@ align-items: center; } +.vue-wrapper { + border: 1px dotted transparent; + border-radius: 5; + + &:hover { + border: 1px dotted #8e7cc3; + } +} + .missense-mutation { color: #008000; font-weight: bold; diff --git a/src/component/variantPage/BasicInfo.tsx b/src/component/variantPage/BasicInfo.tsx index 731d1478..9d610c94 100644 --- a/src/component/variantPage/BasicInfo.tsx +++ b/src/component/variantPage/BasicInfo.tsx @@ -20,7 +20,8 @@ import basicInfo from './BasicInfo.module.scss'; import { Link } from 'react-router-dom'; import { annotationQueryFields } from '../../config/configDefaults'; import Toggle from '../Toggle'; -import { VUE } from './biologicalFunction/ReVUE'; +import { RevisedProteinEffectRecord, VUE } from './biologicalFunction/ReVUE'; +import { ReVUEContent } from './biologicalFunction/ReVUE'; interface IBasicInfoProps { annotation: VariantAnnotationSummary | undefined; @@ -33,6 +34,7 @@ interface IBasicInfoProps { allValidTranscripts: string[]; onTranscriptSelect(transcriptId: string): void; vue: VUE | undefined; + revisedProteinEffectRecord: RevisedProteinEffectRecord | undefined; } type MutationTypeFormat = { @@ -152,7 +154,14 @@ export default class BasicInfo extends React.Component { if (renderData) { renderData = renderData.filter((data) => data.value != null); // remove null fields } - let basicInfoList = _.map(renderData, (data) => { + let basicInfoBeforeVUE = _.map(renderData.slice(0, 2), (data) => { + return this.generateBasicInfoPills( + data.value, + data.key, + data.category + ); + }); + let basicInfoAfterVUE = _.map(renderData.slice(2), (data) => { return this.generateBasicInfoPills( data.value, data.key, @@ -163,7 +172,14 @@ export default class BasicInfo extends React.Component { return (
- {basicInfoList} + {basicInfoBeforeVUE} + {this.props.revisedProteinEffectRecord && + this.props.vue && + this.generateBasicInfoReVUE( + this.props.vue, + this.props.revisedProteinEffectRecord + )} + {basicInfoAfterVUE} {this.jsonButton()} {haveTranscriptTable && this.transcriptsButton(this.showAllTranscripts)} @@ -241,30 +257,13 @@ export default class BasicInfo extends React.Component { }); // Check if variant is a VUE - let revisedProteinEffectRecord; - if (this.props.vue?.revisedProteinEffects) { - revisedProteinEffectRecord = - this.props.vue.revisedProteinEffects.find( - (x) => x.variant === variant - ); - } - if (revisedProteinEffectRecord?.revisedProteinEffect) { - parsedData.push({ - value: 'VUE', - key: 'RevueAnnotation', - category: getMutationTypeClassName(transcript), - }); + if (!this.props.revisedProteinEffectRecord) { + // protein change parsedData.push({ - value: revisedProteinEffectRecord.revisedProteinEffect, + value: transcript.hgvspShort, key: 'hgvsShort', category: 'default', }); - parsedData.push({ - value: revisedProteinEffectRecord.variantClassification, - key: 'variantClassification', - category: getMutationTypeClassName(transcript), - }); - } else { // variant classification parsedData.push({ value: transcript.variantClassification, @@ -368,6 +367,72 @@ export default class BasicInfo extends React.Component { ); } + public generateBasicInfoReVUE( + vue: VUE, + revisedProteinEffectRecord: RevisedProteinEffectRecord + ) { + return ( + + | Source:{' '} + + reVUE + + + } + > + + + reVUE logo + + VUE + + + {revisedProteinEffectRecord.revisedProteinEffect} + + + {revisedProteinEffectRecord.variantClassification} + + + + + ); + } + public generateBasicInfoPills( value: string | null, key: string, diff --git a/src/component/variantPage/biologicalFunction/ReVUE.tsx b/src/component/variantPage/biologicalFunction/ReVUE.tsx index cff8bebb..ae4e047b 100644 --- a/src/component/variantPage/biologicalFunction/ReVUE.tsx +++ b/src/component/variantPage/biologicalFunction/ReVUE.tsx @@ -21,7 +21,7 @@ interface IReVUEProps { vue?: VUE; } -const ReVUEContent: React.FunctionComponent<{ +export const ReVUEContent: React.FunctionComponent<{ vue?: VUE; }> = (props) => { return props.vue ? ( diff --git a/src/page/Variant.tsx b/src/page/Variant.tsx index 22e74772..7286b237 100644 --- a/src/page/Variant.tsx +++ b/src/page/Variant.tsx @@ -395,6 +395,10 @@ class Variant extends React.Component { ) } vue={this.props.store.vue.result} + revisedProteinEffectRecord={ + this.props.store + .revisedProteinEffect + } /> diff --git a/src/util/variantUtils.ts b/src/util/variantUtils.ts index 7efc9594..2cf33da2 100644 --- a/src/util/variantUtils.ts +++ b/src/util/variantUtils.ts @@ -18,6 +18,28 @@ export function variantToMutation( transcript ); if (data && transcriptConsequence) { + let proteinChange; + let proteinPosStart; + let proteinPosEnd; + let mutationType; + + if (revisedProteinEffectRecord?.revisedProteinEffect) { + proteinChange = revisedProteinEffectRecord.revisedProteinEffect; + proteinPosStart = getProteinPosStart(proteinChange); + proteinPosEnd = + getProteinPositionFromProteinChange(proteinChange)?.end; + mutationType = revisedProteinEffectRecord.variantClassification; + } else { + proteinChange = transcriptConsequence.hgvspShort; + proteinPosStart = transcriptConsequence.proteinPosition?.start + ? transcriptConsequence.proteinPosition.start + : getProteinPosStart(transcriptConsequence.hgvspShort); + proteinPosEnd = transcriptConsequence.proteinPosition + ? transcriptConsequence.proteinPosition.end + : undefined; + mutationType = transcriptConsequence.variantClassification; + } + mutation = { gene: { hugoGeneSymbol: transcriptConsequence.hugoGeneSymbol, @@ -28,16 +50,10 @@ export function variantToMutation( endPosition: data.genomicLocation.end, referenceAllele: data.genomicLocation.referenceAllele, variantAllele: data.genomicLocation.variantAllele, - proteinChange: - revisedProteinEffectRecord?.revisedProteinEffect || - transcriptConsequence.hgvspShort, - proteinPosStart: transcriptConsequence.proteinPosition?.start - ? transcriptConsequence.proteinPosition.start - : getProteinPosStart(transcriptConsequence.hgvspShort), - proteinPosEnd: transcriptConsequence.proteinPosition - ? transcriptConsequence.proteinPosition.end - : undefined, - mutationType: transcriptConsequence.variantClassification, + proteinChange, + proteinPosStart, + proteinPosEnd, + mutationType, }; mutations.push(mutation); } From fb3aa9ab3df98688d57e395f92e7a115473e6fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=94=A7=20Ino=20de=20Bruijn=20=F0=9F=A7=AC?= Date: Mon, 30 Jan 2023 18:00:07 -0500 Subject: [PATCH 4/5] Add more VUEs --- src/data/VUEs.json | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/data/VUEs.json b/src/data/VUEs.json index a3767fe3..5e9ff895 100644 --- a/src/data/VUEs.json +++ b/src/data/VUEs.json @@ -6,7 +6,15 @@ "comment": "Introduces splice acceptor site causing a frameshift", "pubmedIds": [29316426], "context": "Recurrent alteration in CRC", - "referenceText": "Yaeger et al, 2018" + "referenceText": "Yaeger et al, 2018", + "revisedProteinEffects": [ + { + "variant": "5:g.112151184A>G", + "transcriptId": "ENST00000257430", + "revisedProteinEffect": "p.G279Ffs*10", + "variantClassification": "Frame_Shift_Ins" + } + ] }, { "hugoGeneSymbol": "BAP1", @@ -15,7 +23,15 @@ "comment": "Leads to exon 11 skipping", "context": "Recurrent and prognostic in kidney cancer", "pubmedIds": [33681728], - "referenceText": "Niersch et al, 2021" + "referenceText": "Niersch et al, 2021", + "revisedProteinEffects": [ + { + "variant": "3:g.52439306A>G", + "transcriptId": "ENST00000460680", + "revisedProteinEffect": "p.D311Gfs*23", + "variantClassification": "Frame_Shift_Del" + } + ] }, { "genomicLocation": "17:g.7579883G>A", @@ -24,7 +40,15 @@ "context": "Recurrent in many cancer types", "pubmedIds": [31671760], "hugoGeneSymbol": "TP53", - "referenceText": "Swiatkowska et al, 2019" + "referenceText": "Swiatkowska et al, 2019", + "revisedProteinEffects": [ + { + "variant": "17:g.7579883G>A", + "transcriptId": "ENST00000269305", + "revisedProteinEffect": "p.V10=", + "variantClassification": "Silent" + } + ] }, { "genomicLocation": "12:g.25380277G>T", @@ -33,7 +57,15 @@ "context": "Recurrent in many cancer types", "pubmedIds": [35236983], "hugoGeneSymbol": "KRAS", - "referenceText": "Kobayashi et al, 2022" + "referenceText": "Kobayashi et al, 2022", + "revisedProteinEffects": [ + { + "variant": "12:g.25380277G>T", + "transcriptId": "ENST00000256078", + "revisedProteinEffect": "p.X61", + "variantClassification": "splice_donor_variant" + } + ] }, { "hugoGeneSymbol": "CTNNB1", From 5e13855bae81723ad8bbf958fd9d624ca6b48353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=94=A7=20Ino=20de=20Bruijn=20=F0=9F=A7=AC?= Date: Thu, 2 Feb 2023 14:30:44 -0500 Subject: [PATCH 5/5] Add some more revue examples --- src/data/VUEs.json | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/data/VUEs.json b/src/data/VUEs.json index 5e9ff895..ea583561 100644 --- a/src/data/VUEs.json +++ b/src/data/VUEs.json @@ -74,7 +74,15 @@ "comment": "Complete exon 3 skip or inframe deletion in exon 3", "pubmedIds": [29316426], "context": "Recurrent alteration in CRC", - "referenceText": "Yaeger et al, 2018" + "referenceText": "Yaeger et al, 2018", + "revisedProteinEffects": [ + { + "variant": "4:g.55593576_55593606del", + "transcriptId": "ENST00000349496", + "revisedProteinEffect": "p.E9_D81del", + "variantClassification": "In_Frame_Del" + } + ] }, { "hugoGeneSymbol": "KIT", @@ -100,7 +108,15 @@ "comment": "Skipping of exon 14", "context": "Actionable in NSCLC", "pubmedIds": [27343443], - "referenceText": "Schrock et al, 2014" + "referenceText": "Schrock et al, 2014", + "revisedProteinEffects": [ + { + "variant": "13:g.28608217_28608218insCCAAACTCTAAATTTTCTCTTGGAAACTCCCATTTGAGATCATATTCATATTCTCTG", + "transcriptId": "ENST00000241453", + "revisedProteinEffect": "p.E598_F612dup", + "variantClassification": "In_Frame_Ins" + } + ] }, { "hugoGeneSymbol": "PIK3R1", @@ -109,7 +125,15 @@ "comment": "Skipping of exon 11", "context": "Recurrent in breast and uterine cancer", "pubmedIds": [25488983], - "referenceText": "Lucas et al, 2014" + "referenceText": "Lucas et al, 2014", + "revisedProteinEffects": [ + { + "variant": "5:g.67589663G>C", + "transcriptId": "ENST00000521381", + "revisedProteinEffect": "p.D434_E476del", + "variantClassification": "In_Frame_Del" + } + ] }, { "hugoGeneSymbol": "FLT3", @@ -117,7 +141,15 @@ "defaultEffect": "splice", "comment": "Internal Tandem Duplication", "context": "Recurrent alteration in AML", - "pubmedIds": [25488983], - "referenceText": "Zhang et al, 2020" + "pubmedIds": [31285539], + "referenceText": "Zhang et al, 2020", + "revisedProteinEffects": [ + { + "variant": "13:g.28608217_28608218insCCAAACTCTAAATTTTCTCTTGGAAACTCCCATTTGAGATCATATTCATATTCTCTG", + "transcriptId": "ENST00000241453", + "revisedProteinEffect": "p.E598_F612dup", + "variantClassification": "In_Frame_Ins" + } + ] } ]