From 6c83a00c24ed606cc6a93d80811e37fc3727f58b Mon Sep 17 00:00:00 2001 From: Manuel Holtgrewe Date: Wed, 13 Sep 2023 14:40:13 +0200 Subject: [PATCH] feat: display ClinVar and GTEx values for each gene (#672, #68) (#1129) --- varfish/vueapp/src/varfish/api/annonars.ts | 45 ++++ .../src/varfish/components/VegaPlot.vue | 13 +- .../vueapp/src/components/VariantDetails.vue | 1 + .../src/components/VariantDetails/Clinvar.vue | 47 ++--- .../VariantDetails/ClinvarFreqPlot.vue | 137 +++++++++++++ .../src/components/VariantDetails/Gene.vue | 154 +++++++++++++- .../VariantDetails/GtexGenePlot.vue | 192 ++++++++++++++++++ variants/vueapp/src/stores/variantDetails.ts | 8 + 8 files changed, 560 insertions(+), 37 deletions(-) create mode 100644 variants/vueapp/src/components/VariantDetails/ClinvarFreqPlot.vue create mode 100644 variants/vueapp/src/components/VariantDetails/GtexGenePlot.vue diff --git a/varfish/vueapp/src/varfish/api/annonars.ts b/varfish/vueapp/src/varfish/api/annonars.ts index 0421b487b..0fba7da21 100644 --- a/varfish/vueapp/src/varfish/api/annonars.ts +++ b/varfish/vueapp/src/varfish/api/annonars.ts @@ -59,6 +59,51 @@ export class AnnonarsApiClient { return result } + /** + * Retrieve clinvar-gene information via annonars REST API. + * + * @param hgncIds Array of HGNC IDs to use, e.g., `["HGNC:26467"]`. + * @param chunkSize How many IDs to send in one request. + * @returns Promise with an array of gene information objects. + */ + async retrieveGeneClinvarInfos( + hgncIds: Array, + chunkSize?: number, + ): Promise> { + const hgncIdChunks = chunks(hgncIds, chunkSize ?? this.defaultChunkSize) + + const promises = hgncIdChunks.map((chunk) => { + const url = `${this.baseUrl}/genes/clinvar?hgnc_id=${chunk.join(',')}` + + const headers = { + Accept: 'application/json', + 'Content-Type': 'application/json', + } + if (this.csrfToken) { + headers['X-CSRFToken'] = this.csrfToken + } + + return fetch(url, { + method: 'GET', + credentials: 'same-origin', + headers, + }) + }) + + const responses = await Promise.all(promises) + const results = await Promise.all( + responses.map((response) => response.json()), + ) + + const result = [] + results.forEach((chunk) => { + for (const value of Object.values(chunk.genes)) { + result.push(value) + } + }) + return result + } + /** * Retrieve variant information via annonars REST API. */ diff --git a/varfish/vueapp/src/varfish/components/VegaPlot.vue b/varfish/vueapp/src/varfish/components/VegaPlot.vue index 4ada4fb1f..9e1be07b4 100644 --- a/varfish/vueapp/src/varfish/components/VegaPlot.vue +++ b/varfish/vueapp/src/varfish/components/VegaPlot.vue @@ -20,6 +20,7 @@ const props = defineProps({ }, encoding: Object, params: Object, + layer: Object, width: { type: [Number, String], default: 300, @@ -29,7 +30,7 @@ const props = defineProps({ default: 300, }, mark: { - type: Object, + type: [Boolean, Object], default: { type: 'bar' }, }, renderer: { @@ -46,7 +47,7 @@ const vegaViewRef = ref(null) /** The vega specification. */ const vegaLiteSpec = computed(() => { - return { + const res = { $schema: 'https://vega.github.io/schema/vega-lite/v5.json', width: props.width, height: props.height, @@ -56,10 +57,16 @@ const vegaLiteSpec = computed(() => { values: props.dataValues, name: props.dataName, }, - mark: props.mark, encoding: props.encoding, transform: props.transform, } + if (props.mark !== undefined && props.mark !== null && props.mark !== false) { + res.mark = props.mark + } + if (props.layer !== undefined && props.layer !== null) { + res.layer = props.layer + } + return res }) /** Make component reactive to props.data changes. */ diff --git a/variants/vueapp/src/components/VariantDetails.vue b/variants/vueapp/src/components/VariantDetails.vue index ab8922693..2c672ac9b 100644 --- a/variants/vueapp/src/components/VariantDetails.vue +++ b/variants/vueapp/src/components/VariantDetails.vue @@ -188,6 +188,7 @@ onMounted(() => { variantDetailsStore?.varAnnos?.clinvar) -const interpretations = [ - 'N/A', - 'Benign', - 'Likely benign', - 'Uncertain signifiance', - 'Likely pathogenic', - 'Pathogenic', +const clinicalSignificanceLabel = [ + 'pathogenic', // 0 + 'likely pathogenic', // 1 + 'uncertain signifiance', // 2 + 'likely benign', // 3 + 'benign', // 4 + 'other', ] -const reviewStatus = (goldStars: number): string => { - const res = [] - for (let i = 1; i <= 5; i++) { - res.push() - } - return res.join('') -} +const reviewStatusLabel = [ + 'no assertion provided', // 0 + 'no assertion criteria provided', // 1 + 'criteria provided, conflicting interpretations', // 2 + 'criteria provided, single submitter', // 3 + 'criteria provided, multiple submitters, no conflicts', // 4 + 'reviewed by expert panel', // 5 + 'practice guideline', // 6 +] + +const reviewStatusStars = [0, 0, 0, 1, 2, 3, 4]