Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
gromdimon committed Sep 26, 2023
1 parent 59dab19 commit 17d83c0
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 62 deletions.
14 changes: 14 additions & 0 deletions frontend/src/api/annonars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,27 @@ export class AnnonarsClient {
this.csrfToken = csrfToken ?? null
}

/**
* Fetch gene information via annonars REST API.
*
* @param hgncId HGNC ID, e.g., `"HGNC:26467"`.
*/
async fetchGeneInfo(hgncId: string): Promise<any> {
const response = await fetch(`${this.apiBaseUrl}genes/info?hgnc_id=${hgncId}`, {
method: 'GET'
})
return await response.json()
}

/**
* Fetch variant information via annonars and mehari REST APIs.
*
* @param genomeRelease GRCh37 or GRCh38.
* @param chromosome Chromosome, e.g., `"chr1"`.
* @param pos Position of the variant.
* @param reference Reference nucleotide, e.g., `"A"`.
* @param alternative Alternative nucleotide, e.g., `"G"`.
*/
async fetchVariantInfo(
genomeRelease: string,
chromosome: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { useCNVInfoStore } from '@/stores/cnvInfo'
import { useSvInfoStore } from '@/stores/svInfo'
const cnvInfoStore = useCNVInfoStore()
const svInfoStore = useSvInfoStore()
const vcvUrl = (vcv: string): string => {
const stripped = vcv.replace(/^VCV0+/, '')
Expand All @@ -11,14 +11,11 @@ const vcvUrl = (vcv: string): string => {

<template>
<div>
<template v-if="cnvInfoStore.currentCNVRecord?.payload?.clinvar_ovl_vcvs?.length">
<template v-if="svInfoStore.currentSvRecord?.payload?.clinvar_ovl_vcvs?.length">
<p>The following overlapping SVs are flagged as (likely) pathogenic in ClinVar.</p>

<ul>
<li
v-for="vcv in cnvInfoStore.currentCNVRecord?.payload?.clinvar_ovl_vcvs ?? []"
:key="vcv"
>
<li v-for="vcv in svInfoStore.currentSvRecord?.payload?.clinvar_ovl_vcvs ?? []" :key="vcv">
<a :href="vcvUrl(vcv)">
{{ vcv }}
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { computed, type ComputedRef } from 'vue'
import { displayName } from '@/lib/utils'
const props = defineProps({
currentCNVRecord: Object
currentSvRecord: Object
})
const GT_FIELDS = Object.freeze({
Expand All @@ -27,12 +27,12 @@ const GT_FIELDS = Object.freeze({
const identity = (x: any) => x
const allKeys: ComputedRef<any[]> = computed(() => {
if (!props.currentCNVRecord?.payload?.call_info) {
if (!props.currentSvRecord?.payload?.call_info) {
return []
}
let tmp: any = []
for (let call_info of Object.values(props.currentCNVRecord.payload.call_info) as any) {
for (let call_info of Object.values(props.currentSvRecord.payload.call_info) as any) {
tmp = tmp.concat(
Object.entries(call_info)
.filter(([, value]) => value !== null)
Expand All @@ -47,12 +47,12 @@ const allKeys: ComputedRef<any[]> = computed(() => {

<template>
<div class="card">
<table class="table table-striped table-hover" v-if="currentCNVRecord">
<table class="table table-striped table-hover" v-if="currentSvRecord">
<thead>
<tr>
<th>Sample</th>

<template v-for="(_, sample) in currentCNVRecord.payload.call_info" :key="sample">
<template v-for="(_, sample) in currentSvRecord.payload.call_info" :key="sample">
<th>
{{ displayName(sample as any) }}
</th>
Expand All @@ -62,7 +62,7 @@ const allKeys: ComputedRef<any[]> = computed(() => {
<tbody>
<tr v-for="key in allKeys" :key="key">
<th>{{ GT_FIELDS[key as keyof typeof GT_FIELDS]?.label ?? key }}</th>
<td v-for="genotype in currentCNVRecord.payload.call_info" :key="genotype">
<td v-for="genotype in currentSvRecord.payload.call_info" :key="genotype">
<div v-if="key === 'matched_gt_criteria'">
{{ (GT_FIELDS.matched_gt_criteria?.fmt)(genotype[key]) }}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { useCNVInfoStore } from '@/stores/cnvInfo'
import { useSvInfoStore } from '@/stores/svInfo'
import { computed, type ComputedRef, type Ref, ref } from 'vue'
import { roundIt } from '@/lib/utils'
Expand Down Expand Up @@ -88,7 +88,7 @@ const headers: Header[] = [
]
/** The SV details store. */
const cnvInfoStore = useCNVInfoStore()
const svInfoStore = useSvInfoStore()
/** Helper type for `resultsInfos`. */
type ResultsInfo = {
Expand All @@ -102,7 +102,7 @@ type ResultsInfo = {
*/
const resultsInfos: ComputedRef<Map<string, ResultsInfo>> = computed(() => {
const result = new Map()
for (const txEffect of cnvInfoStore.currentCNVRecord?.payload?.tx_effects ?? []) {
for (const txEffect of svInfoStore.currentSvRecord?.payload?.tx_effects ?? []) {
const value: ResultsInfo = {
isDiseaseGene: txEffect.gene.is_disease_gene,
txEffect: txEffect.transcript_effects[0]
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export const infoFromQuery = (query: string): any => {
*
* @param query Incoming query string
*/
export const infoFromCNVQuery = (query: string): any => {
export const infoFromSvQuery = (query: string): any => {
const [sv_type, chromosome, start, end] = query.split(':')
return {
sv_type: sv_type,
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import VariantDetailView from '@/views/VariantDetailView.vue'
import GenesListView from '@/views/GenesListView.vue'
import ACMGCriteriaDocs from '@/views/ACMGCriteriaDocs.vue'
import PathNotFound from '@/views/PathNotFound.vue'
import CNVDetailView from '@/views/CNVDetailView.vue'
import SvDetailView from '@/views/SvDetailView.vue'

const routes = [
{
Expand Down Expand Up @@ -46,7 +46,7 @@ const routes = [
{
path: '/cnv/:searchTerm/:genomeRelease',
name: 'cnv',
component: CNVDetailView,
component: SvDetailView,
props: (route: any) => {
return { searchTerm: route.params.searchTerm, genomeRelease: route.params.genomeRelease }
}
Expand Down
27 changes: 14 additions & 13 deletions frontend/src/stores/cnvInfo.ts → frontend/src/stores/svInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,35 @@ import { ref } from 'vue'

import { AnnonarsClient } from '@/api/annonars'
import { StoreState } from '@/stores/misc'
import { infoFromCNVQuery } from '@/lib/utils'
import { infoFromSvQuery } from '@/lib/utils'

/** `SvRecord` is a type alias for easier future interface definition. */
export type CNVRecord = any | null
export type SvRecord = any | null
/** `GeneInfo` is a type alias for easier future interface definition. */
type GeneInfo = any | null

export const useCNVInfoStore = defineStore('cnvInfo', () => {
export const useSvInfoStore = defineStore('svInfo', () => {
/** The current store state. */
const storeState = ref<StoreState>(StoreState.Initial)

/** The search query for the CNV. */
const cnvTerm = ref<string | null>(null)
/** The search query for the SV. */
const svTerm = ref<string | null>(null)

/** The current CNV record. */
const currentCNVRecord = ref<CNVRecord>(null)
/** The current SV record. */
const currentSvRecord = ref<SvRecord>(null)

/** Infos on the variants of the record. */
const genesInfos = ref<GeneInfo[]>([])

function clearData() {
storeState.value = StoreState.Initial
currentCNVRecord.value = null
currentSvRecord.value = null
genesInfos.value = []
}

const loadData = async (variantQuery: string, genomeRelease: string) => {
// Prevent fetching twice.
if (variantQuery === cnvTerm.value) {
if (variantQuery === svTerm.value) {
return
}

Expand All @@ -47,7 +47,7 @@ export const useCNVInfoStore = defineStore('cnvInfo', () => {
// Load data via API
storeState.value = StoreState.Loading
try {
const { sv_type, chromosome, start, end } = infoFromCNVQuery(variantQuery)
const { sv_type, chromosome, start, end } = infoFromSvQuery(variantQuery)
// Fetch new details
const annonarsClient = new AnnonarsClient()
const hgncIds = []
Expand All @@ -63,24 +63,25 @@ export const useCNVInfoStore = defineStore('cnvInfo', () => {
genesInfos.value = await annonarsClient.fetchGeneInfos(hgncIds)
}

currentCNVRecord.value = {
currentSvRecord.value = {
sv_type: sv_type,
chromosome: chromosome,
start: start,
end: end,
release: genomeRelease
}
storeState.value = StoreState.Active
svTerm.value = variantQuery
} catch (e) {
console.error('There was an error loading the CNV data.', e)
console.error('There was an error loading the SV data.', e)
storeState.value = StoreState.Error
}
}

return {
storeState,
genesInfos,
currentCNVRecord,
currentSvRecord,
clearData,
loadData
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
<script setup lang="ts">
/**
* Detailed display of CNV information.
*/
import { watch, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useCNVInfoStore } from '@/stores/cnvInfo'
import { useSvInfoStore } from '@/stores/svInfo'
import { StoreState } from '@/stores/misc'
import HeaderDetailPage from '@/components/HeaderDetailPage.vue'
import { type CNVRecord } from '@/stores/cnvInfo'
import CNVDetailsGenes from '@/components/CNVDetails/CNVGenes.vue'
import CNVDetailsClinvar from '@/components/CNVDetails/CNVDetailsClinvar.vue'
import CNVDetailsGenotypeCall from '@/components/CNVDetails/CNVDetailsGenotypeCall.vue'
import SvDetailsGenes from '@/components/SvDetails/SvGenes.vue'
import SvDetailsClinvar from '@/components/SvDetails/SvDetailsClinvar.vue'
import SvDetailsGenotypeCall from '@/components/SvDetails/SvDetailsGenotypeCall.vue'
import { type SvRecord } from '@/stores/svInfo'
export interface Props {
searchTerm?: string
Expand All @@ -28,7 +24,7 @@ const props = withDefaults(defineProps<Props>(), {
const router = useRouter()
const route = useRoute()
const cnvInfoStore = useCNVInfoStore()
const svInfoStore = useSvInfoStore()
const scrollToSection = async () => {
const sectionId = route.hash.slice(1)
Expand All @@ -37,12 +33,12 @@ const scrollToSection = async () => {
}
const loadDataToStore = async () => {
await cnvInfoStore.loadData(props.searchTerm, props.genomeRelease)
await svInfoStore.loadData(props.searchTerm, props.genomeRelease)
await scrollToSection()
}
// Pretty display of coordinates.
const cnvLocus = (record: CNVRecord): string | null => {
const svLocus = (record: SvRecord): string | null => {
if (!record) {
return null
}
Expand Down Expand Up @@ -72,10 +68,10 @@ watch(() => route.hash, scrollToSection)
// If variantInfoStore.storeState is StoreState.Error then redirect to the
// home page.
watch(
() => cnvInfoStore.storeState,
() => svInfoStore.storeState,
(storeState) => {
if (storeState == StoreState.Error) {
cnvInfoStore.clearData()
svInfoStore.clearData()
router.push({ name: 'home' })
}
}
Expand All @@ -97,7 +93,7 @@ const genomeReleaseRef = ref(props.genomeRelease)
<template>
<HeaderDetailPage v-model:search-term="searchTermRef" v-model:genome-release="genomeReleaseRef" />
<v-navigation-drawer location="right" class="overflow-auto">
<div v-if="cnvInfoStore.storeState == StoreState.Active" class="variant-info">
<div v-if="svInfoStore.storeState == StoreState.Active" class="variant-info">
<v-list density="compact" nav>
<v-list-item
v-for="section in SECTIONS"
Expand All @@ -112,43 +108,43 @@ const genomeReleaseRef = ref(props.genomeRelease)
</v-navigation-drawer>
<v-layout>
<v-main style="min-height: 300px">
<div v-if="cnvInfoStore.storeState == StoreState.Active" class="cnv-info">
<div id="gene" class="cnv-item">
<div v-if="svInfoStore.storeState == StoreState.Active" class="sv-info">
<div id="gene" class="sv-item">
<h2>Genes</h2>
<v-divider />
<CNVDetailsGenes :genes-infos="cnvInfoStore.genesInfos" />
<SvDetailsGenes :genes-infos="svInfoStore.genesInfos" />
</div>
<div id="clinvar" class="cnv-item">
<div id="clinvar" class="sv-item">
<h2>ClinVar</h2>
<v-divider />
<CNVDetailsClinvar />
<SvDetailsClinvar />
</div>
<div id="call-details" class="cnv-item">
<div id="call-details" class="sv-item">
<h2>Genotype Call</h2>
<v-divider />
Precise coordinates:
<code> {{ cnvLocus(cnvInfoStore.currentCNVRecord) }} </code>
<CNVDetailsGenotypeCall :current-cnv-record="cnvInfoStore.currentCNVRecord" />
<code> {{ svLocus(svInfoStore.currentSvRecord) }} </code>
<SvDetailsGenotypeCall :current-sv-record="svInfoStore.currentSvRecord" />
</div>
<div id="acmg" class="cnv-item">
<div id="acmg" class="sv-item">
<h2>ACMG</h2>
<v-divider />
<!-- <CNVAcmgRating /> -->
</div>
<div id="genome-browser" class="cnv-item">
<div id="genome-browser" class="sv-item">
<h2>Genome Browser</h2>
<v-divider />
<!-- <GenomeBrowser
:case-uuid="cnvInfoStore.caseUuid"
:case-uuid="svInfoStore.caseUuid"
:genome="genomeRelease"
:locus="cnvLocus(cnvInfoStore.currentCNVRecord)"
:locus="svLocus(svInfoStore.currentCNVRecord)"
/> -->
</div>
</div>

<div v-else>
<div class="d-flex align-center justify-center" style="min-height: 300px">
<h1>Loading CNV information</h1>
<h1>Loading SV information</h1>
<v-progress-circular indeterminate></v-progress-circular>
</div>
</div>
Expand All @@ -157,12 +153,12 @@ const genomeReleaseRef = ref(props.genomeRelease)
</template>

<style scoped>
.cnv-info {
.sv-info {
width: 95%;
margin: 20px;
}
.cnv-item {
.sv-item {
margin-bottom: 20px;
border: 2px solid rgb(229, 85, 64);
border-radius: 10px;
Expand Down

0 comments on commit 17d83c0

Please sign in to comment.