diff --git a/frontend/src/api/__tests__/dotty.spec.ts b/frontend/src/api/__tests__/dotty.spec.ts
index 10dcfde9..2fed9053 100644
--- a/frontend/src/api/__tests__/dotty.spec.ts
+++ b/frontend/src/api/__tests__/dotty.spec.ts
@@ -28,4 +28,20 @@ describe.concurrent('DottyClient', () => {
expect(result).toEqual(mockData)
})
+
+ it('should load transcripts successfully', async () => {
+ const mockData = {
+ transcripts: {
+ 'HGNC:1100': {
+ gene: 'info'
+ }
+ }
+ }
+ fetchMocker.mockResponseOnce(JSON.stringify(mockData), { status: 200 })
+
+ const client = new DottyClient()
+ const result = await client.fetchTranscripts('HGNC:1100', 'GRCh37')
+
+ expect(result).toEqual(mockData)
+ })
})
diff --git a/frontend/src/api/dotty.ts b/frontend/src/api/dotty.ts
index 3b7bf6ed..140fc29f 100644
--- a/frontend/src/api/dotty.ts
+++ b/frontend/src/api/dotty.ts
@@ -42,4 +42,19 @@ export class DottyClient {
return null
}
}
+
+ async fetchTranscripts(
+ hgnc_id: string,
+ assembly: 'GRCh37' | 'GRCh38' = 'GRCh38'
+ ): Promise {
+ const url = `${API_INTERNAL_BASE_PREFIX_DOTTY}/api/v1/find-transcripts?hgnc_id=${hgnc_id}&assembly=${assembly}`
+ const response = await fetch(url, {
+ method: 'GET'
+ })
+ if (response.status == 200) {
+ return await response.json()
+ } else {
+ return null
+ }
+ }
}
diff --git a/frontend/src/components/VariantDetails/VariationLandscape.vue b/frontend/src/components/VariationLandscape.vue
similarity index 82%
rename from frontend/src/components/VariantDetails/VariationLandscape.vue
rename to frontend/src/components/VariationLandscape.vue
index eedd1ab9..00ac69aa 100644
--- a/frontend/src/components/VariantDetails/VariationLandscape.vue
+++ b/frontend/src/components/VariationLandscape.vue
@@ -6,16 +6,19 @@ import VegaPlot from '@/components/VegaPlot.vue'
export interface Props {
/** Gene information from annonars. */
clinvar: any
+ /** Transctipts information. */
+ transcripts: any
/** The genome release. */
genomeRelease: string
- /** The gene symbol. */
- geneSymbol: string
+ /** The gene HGNC symbol. */
+ hgnc: string
}
const props = withDefaults(defineProps(), {
clinvar: null,
+ transcripts: null,
genomeRelease: 'grch37',
- geneSymbol: ''
+ hgnc: ''
})
const clinvarSignificanceMapping: Record = {
@@ -45,6 +48,54 @@ const convertClinvarSignificance = (input: number): number => {
}
}
+const minMax = computed(() => {
+ if (!props.clinvar) {
+ return []
+ }
+ let min = null
+ let max = null
+ for (const item of props.clinvar.variants ?? []) {
+ if (item.genome_release.toLowerCase() == props.genomeRelease) {
+ // Go through all item.variants and find the min and max pos.
+ for (const variant of item.variants) {
+ if (variant.pos < min || min == null) {
+ min = variant.pos
+ }
+ if (variant.pos > max || max == null) {
+ max = variant.pos
+ }
+ }
+ }
+ }
+ for (const exon of exons.value) {
+ if (exon.start < min || min == null) {
+ min = exon.start
+ }
+ if (exon.stop > max || max == null) {
+ max = exon.stop
+ }
+ }
+ return [min, max]
+})
+
+const exons = computed(() => {
+ if (!props.transcripts) {
+ return []
+ }
+ const exons = []
+ for (const transcript of props.transcripts.transcripts) {
+ for (const alignment of transcript.alignments) {
+ for (const exon of alignment.exons) {
+ exons.push({
+ start: exon.ref_start,
+ stop: exon.ref_end
+ })
+ }
+ }
+ }
+ return exons
+})
+
const vegaData = computed(() => {
if (!props.clinvar) {
return []
@@ -100,7 +151,7 @@ const vegaLayer = [
x: {
field: 'pos',
type: 'quantitative',
- scale: { domain: [41190000, 41282000] },
+ scale: { domain: minMax.value },
axis: { grid: false, zindex: 1000 },
title: null
},
@@ -204,7 +255,7 @@ const vegaLayer = [
x: {
field: 'pos',
type: 'quantitative',
- scale: { domain: [41190000, 41282000] },
+ scale: { domain: minMax.value },
axis: { grid: false },
title: null
},
@@ -246,13 +297,13 @@ const vegaLayer = [
},
{
description: 'gene - line',
- data: { values: [{ pos: 41196312 }, { pos: 41277381 }] },
+ data: { values: [{ pos: minMax.value[0] }, { pos: minMax.value[1] }] },
mark: { type: 'line', stroke: 'black', size: 1, opacity: 0.5 },
encoding: {
x: {
field: 'pos',
type: 'quantitative',
- scale: { domain: [41190000, 41282000] },
+ scale: { domain: minMax.value },
axis: { grid: false },
title: null
},
@@ -262,10 +313,7 @@ const vegaLayer = [
{
description: 'gene - exons',
data: {
- values: [
- { start: 41196312, stop: 41197819 },
- { start: 41199660, stop: 41199720 }
- ]
+ values: exons.value
},
mark: {
type: 'rect',
@@ -278,7 +326,7 @@ const vegaLayer = [
x: {
field: 'start',
type: 'quantitative',
- scale: { domain: [41190000, 41282000] },
+ scale: { domain: minMax.value },
axis: { grid: false },
title: null
},
@@ -292,7 +340,7 @@ const vegaLayer = [
- Slider: false
@@ -685,7 +645,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 1
Conflicting Evidence: Gain 1B, Gain 2C, Gain 2D, Gain 2E, Gain 2F, Gain 2I
- Slider: true
@@ -701,7 +660,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Default Score: 0
Max Score: 0
Conflicting Evidence: Gain 1B, Gain 2C, Gain 2D
- Slider: false
@@ -715,7 +673,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: -1
Max Score: 0
Conflicting Evidence: Gain 2A, Gain 2B
- Slider: true
@@ -730,7 +687,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: -1
Max Score: 0
Conflicting Evidence: Gain 2A, Gain 2B
- Slider: true
@@ -744,7 +700,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Default Score: 0
Max Score: 0
Conflicting Evidence: Gain 1B, Gain 2A
- Slider: false
@@ -759,7 +714,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: -1
Max Score: 0
Conflicting Evidence: Gain 2A
- Slider: true
@@ -772,7 +726,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Default Score: 0
Max Score: 0
Conflicting Evidence: Gain 1B
- Slider: false
@@ -783,7 +736,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Default Score: 0
Max Score: 0
Conflicting Evidence: Gain 1B, Gain 2I
- Slider: false
@@ -802,7 +754,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.9
Conflicting Evidence: Gain 1B, Gain 2H, Gain 2J, Gain 2K
- Slider: true
@@ -816,7 +767,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Default Score: 0
Max Score: 0
Conflicting Evidence: Gain 1B, Gain 2I
- Slider: false
@@ -830,7 +780,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.45
Conflicting Evidence: Gain 1B, Gain 2I
- Slider: true
@@ -844,7 +793,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Default Score: 0
Max Score: 0
Conflicting Evidence: Gain 1B
- Slider: false
@@ -858,7 +806,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Default Score: 0
Max Score: 0
Conflicting Evidence: Gain 3B, Gain 3C
- Slider: false
@@ -870,7 +817,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.45
Conflicting Evidence: Gain 1B, Gain 2I, Gain 3A, Gain 3C
- Slider: true
@@ -882,7 +828,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.9
Conflicting Evidence: Gain 1B, Gain 2I, Gain 3A, Gain 3B
- Slider: true
@@ -901,7 +846,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.9
Conflicting Evidence: Gain 4B, Gain 4C, Gain 4D
- Slider: true
@@ -922,7 +866,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.9
Conflicting Evidence: Gain 4A, Gain 4C, Gain 4D
- Slider: true
@@ -943,7 +886,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.9
Conflicting Evidence: Gain 4A, Gain 4B, Gain 4D
- Slider: true
@@ -963,7 +905,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: -0.3
Max Score: 0
Conflicting Evidence: Gain 4A, Gain 4B, Gain 4C
- Slider: true
@@ -980,7 +921,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.3
Conflicting Evidence: None
- Slider: true
@@ -994,7 +934,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.45
Conflicting Evidence: Gain 4G, Gain 4H
- Slider: true
@@ -1006,7 +945,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.3
Conflicting Evidence: Gain 4F, Gain 4H
- Slider: true
@@ -1018,7 +956,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.45
Conflicting Evidence: Gain 4F, Gain 4G
- Slider: true
@@ -1036,7 +973,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: -0.9
Max Score: 0
Conflicting Evidence: Gain 4K
- Slider: true
@@ -1050,7 +986,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Default Score: -0.3
Max Score: -0.9
Conflicting Evidence: Gain 4K
- Slider: true
@@ -1064,7 +999,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: -0.3
Max Score: 0
Conflicting Evidence: Gain 4I, Gain 4J
- Slider: true
@@ -1082,7 +1016,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.45
Conflicting Evidence: Gain 4M
- Slider: true
@@ -1097,7 +1030,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: 0
Max Score: 0.45
Conflicting Evidence: Gain 4L
- Slider: true
@@ -1112,7 +1044,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: -0.9
Max Score: 0
Conflicting Evidence: None
- Slider: true
@@ -1124,7 +1055,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Min Score: -0.9
Max Score: 0
Conflicting Evidence: None
- Slider: true
@@ -1142,7 +1072,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Conflicting Evidence: Gain 5B, Gain 5C, Gain 5D, Gain 5E, Gain 5F, Gain 5G, Gain 5H
- Slider: true
@@ -1159,7 +1088,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Conflicting Evidence: Gain 5A, Gain 5C, Gain 5D, Gain 5E, Gain 5F, Gain 5G, Gain 5H
- Slider: true
@@ -1176,7 +1104,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Conflicting Evidence: Gain 5A, Gain 5B, Gain 5D, Gain 5E, Gain 5F, Gain 5G, Gain 5H
- Slider: true
@@ -1196,7 +1123,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Conflicting Evidence: Gain 5A, Gain 5B, Gain 5C, Gain 5E, Gain 5F, Gain 5G, Gain 5H
- Slider: true
@@ -1212,7 +1138,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Conflicting Evidence: Gain 5A, Gain 5B, Gain 5C, Gain 5D, Gain 5F, Gain 5G, Gain 5H
- Slider: true
@@ -1225,7 +1150,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Conflicting Evidence: Gain 5A, Gain 5B, Gain 5C, Gain 5D, Gain 5E, Gain 5G, Gain 5H
- Slider: false
@@ -1243,7 +1167,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Conflicting Evidence: Gain 5A, Gain 5B, Gain 5C, Gain 5D, Gain 5E, Gain 5F, Gain 5H
- Slider: true
@@ -1261,7 +1184,6 @@ import HeaderDefault from '@/components/HeaderDefault.vue'
Conflicting Evidence: Gain 5A, Gain 5B, Gain 5C, Gain 5D, Gain 5E, Gain 5F, Gain 5G
- Slider: true
diff --git a/frontend/src/views/GeneDetailView.vue b/frontend/src/views/GeneDetailView.vue
index 857a8827..1564864f 100644
--- a/frontend/src/views/GeneDetailView.vue
+++ b/frontend/src/views/GeneDetailView.vue
@@ -5,6 +5,7 @@ import { useRoute, useRouter } from 'vue-router'
import ClinvarFreqPlot from '@/components/ClinVarFreqPlot.vue'
import GtexGenePlot from '@/components/GtexGenePlot.vue'
import HeaderDetailPage from '@/components/HeaderDetailPage.vue'
+import VariationLandscape from '@/components/VariationLandscape.vue'
import { roundIt } from '@/lib/utils'
import { useGeneInfoStore } from '@/stores/geneInfo'
import { StoreState } from '@/stores/misc'
@@ -33,7 +34,7 @@ const scrollToSection = async () => {
}
const loadDataToStore = async () => {
- await geneInfoStore.loadData(props.searchTerm)
+ await geneInfoStore.loadData(props.searchTerm, props.genomeRelease)
await scrollToSection()
}
@@ -59,6 +60,7 @@ watch(
const SECTIONS = [
{ id: 'hgnc', title: 'HGNC' },
+ { id: 'variation-landscape', title: 'Variation Landscape' },
{ id: 'constraints-scores', title: 'Constraints / Scores' },
{ id: 'ncbi-summary', title: 'NCBI Summary' },
{ id: 'alternative-identifiers', title: 'Alternative Identifiers' },
@@ -171,6 +173,17 @@ const genomeReleaseRef = ref(props.genomeRelease)
+
+
Gene-wide Variation landscape
+
+
+
+
Constraints/Scores
gnomAD
diff --git a/frontend/src/views/VariantDetailView.vue b/frontend/src/views/VariantDetailView.vue
index 74714e09..607fc04b 100644
--- a/frontend/src/views/VariantDetailView.vue
+++ b/frontend/src/views/VariantDetailView.vue
@@ -12,7 +12,6 @@ import VariantDetailsFreqs from '@/components/VariantDetails/VariantFreqs.vue'
import VariantDetailsGene from '@/components/VariantDetails/VariantGene.vue'
import VariantDetailsVariantTools from '@/components/VariantDetails/VariantTools.vue'
import VariantDetailsVariantValidator from '@/components/VariantDetails/VariantValidator.vue'
-import VariationLandscape from '@/components/VariantDetails/VariationLandscape.vue'
import { StoreState } from '@/stores/misc'
import { useVariantInfoStore } from '@/stores/variantInfo'
import { type SmallVariant } from '@/stores/variantInfo'
@@ -65,7 +64,6 @@ watch(
const SECTIONS = [
{ id: 'gene', title: 'Gene' },
- { id: 'variation-landscape', title: 'Variation Landscape' },
{ id: 'beacon-network', title: 'Beacon Network' },
{ id: 'clinvar', title: 'ClinVar' },
{ id: 'freqs', title: 'Population Frequencies' },
@@ -124,24 +122,6 @@ const genomeReleaseRef = ref(props.genomeRelease)
No gene information available
-
-
Gene-wide Variation landscape
-
-
-
-
-
Gene-wide Variation landscape
- No gene information available
-
-
Beacon Network
diff --git a/frontend/src/views/__tests__/VariantDetailView.spec.ts b/frontend/src/views/__tests__/VariantDetailView.spec.ts
index bbd87ed7..85d37937 100644
--- a/frontend/src/views/__tests__/VariantDetailView.spec.ts
+++ b/frontend/src/views/__tests__/VariantDetailView.spec.ts
@@ -16,7 +16,6 @@ import VariantFreqs from '@/components/VariantDetails/VariantFreqs.vue'
import VariantGene from '@/components/VariantDetails/VariantGene.vue'
import VariantTools from '@/components/VariantDetails/VariantTools.vue'
import VariantValidator from '@/components/VariantDetails/VariantValidator.vue'
-import VariationLandscape from '@/components/VariantDetails/VariationLandscape.vue'
import { AcmgCriteria, MultiSourceAcmgCriteriaState, Presence, StateSource } from '@/lib/acmgSeqVar'
import { setupMountedComponents } from '@/lib/test-utils'
import { StoreState } from '@/stores/misc'
@@ -137,7 +136,6 @@ describe('VariantDetailView', async () => {
const { wrapper } = makeWrapper()
const variantInfo = wrapper.findComponent(VariantGene)
- const variationLandscape = wrapper.findComponent(VariationLandscape)
const beaconNetwork = wrapper.findComponent(BeaconNetwork)
const clinVar = wrapper.findComponent(ClinVar)
const variantFreqs = wrapper.findComponent(VariantFreqs)
@@ -146,7 +144,6 @@ describe('VariantDetailView', async () => {
const variantValidator = wrapper.findComponent(VariantValidator)
const txCsq = wrapper.findComponent(TxCsq)
expect(variantInfo.exists()).toBe(true)
- expect(variationLandscape.exists()).toBe(true)
expect(beaconNetwork.exists()).toBe(true)
expect(clinVar.exists()).toBe(true)
expect(variantFreqs.exists()).toBe(true)