Skip to content

Commit

Permalink
feat: genome browser
Browse files Browse the repository at this point in the history
  • Loading branch information
gromdimon committed Sep 26, 2023
1 parent 17d83c0 commit b4ef261
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 9 deletions.
4 changes: 4 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
BACKEND_PREFIX_MEHARI = env.get("REEV_BACKEND_PREFIX_MEHARI", "http://mehari:8080")
#: Prefix for the backend of viguno service
BACKEND_PREFIX_VIGUNO = env.get("REEV_BACKEND_PREFIX_VIGUNO", "http://viguno:8080")
#: Prefix for the backend of nginx service
BACKEND_PREFIX_NGINX = env.get("REEV_BACKEND_PREFIX_NGINX", "http://nginx:8080")
#: Path to REEV version file.
VERSION_FILE = env.get("REEV_VERSION_FILE", "/VERSION")
#: The REEV version from the file (``None`` if to load dynamically from git)
Expand Down Expand Up @@ -97,6 +99,8 @@ async def reverse_proxy(request: Request) -> Response:
backend_url = BACKEND_PREFIX_MEHARI + url.path.replace("/proxy/mehari", "")
elif url.path.startswith("/proxy/viguno"):
backend_url = BACKEND_PREFIX_VIGUNO + url.path.replace("/proxy/viguno", "")
elif url.path.startswith("/proxy/nginx"):
backend_url = BACKEND_PREFIX_NGINX + url.path.replace("/proxy/nginx", "")

if backend_url:
backend_url = backend_url + (f"?{url.query}" if url.query else "")
Expand Down
6 changes: 6 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"dependencies": {
"@mdi/font": "^7.2.96",
"@reactgular/chunks": "^1.0.1",
"igv": "^2.15.11",
"pinia": "^2.1.6",
"resize-observer-polyfill": "^1.5.1",
"vega": "^5.25.0",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/api/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ export const API_BASE_PREFIX_ANNONARS =

export const API_BASE_PREFIX_MEHARI =
import.meta.env.MODE == 'development' ? '//localhost:8080/proxy/mehari' : '/proxy/mehari'

export const API_BASE_PREFIX_NGINX =
import.meta.env.MODE == 'development' ? '//localhost:8080/proxy/nginx' : '/proxy/nginx'
159 changes: 159 additions & 0 deletions frontend/src/components/GenomeBrowser.tracks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { API_BASE_PREFIX_NGINX } from '@/api/common'

const API_BASE_URL = `${API_BASE_PREFIX_NGINX}/`

const visibilityWindow = 10000000

const hescTadTrack = {
name: 'hESC TADs',
sourceType: 'annotation',
format: 'bed',
visibilityWindow,
url: `${API_BASE_URL}grch37/hesc.bed`,
color: 'gray'
}

const curatedMmsTrack = {
name: 'Curated MMS',
sourceType: 'annotation',
format: 'bed',
visibilityWindow,
url: `${API_BASE_URL}grch37/patho-mms.bed`,
color: 'red'
}

// const clinvarTrack = {
// name: 'ClinVar SVs',
// sourceType: 'custom',
// visibilityWindow,
// height: 100,
// displayMode: 'SQUISHED',
// source: {
// url: `/svs/worker/clinvar/grch37/?min_pathogenicity=likely-pathogenic&chromosome=$CHR&begin=$START&end=$END`,
// method: 'GET',
// contentType: 'application/json',
// mappings: {
// chr: 'chromosome',
// start: 'begin',
// },
// queryable: true,
// },
// format: 'annotation',
// colorBy: 'pathogenicity',
// colorTable: {
// pathogenic: 'red',
// 'likely-pathogenic': 'orange',
// uncertain: 'blue',
// 'likely-benign': 'gray',
// benign: 'light gray',
// },
// }

const duplicationTrack = {
name: 'UCSC Segmental Duplications',
sourceType: 'annotation',
format: 'bed',
visibilityWindow,
url: `${API_BASE_URL}grch37/genomicSuperDups.bed.gz`,
indexURL: `${API_BASE_URL}grch37/genomicSuperDups.bed.gz.tbi`,
color: 'black'
}

const repeatsTrack = {
name: 'UCSC Repeat Masker',
sourceType: 'annotation',
format: 'bed',
visibilityWindow,
url: `${API_BASE_URL}grch37/rmsk.bed.gz`,
indexURL: `${API_BASE_URL}grch37/rmsk.bed.gz.tbi`,
color: 'black'
}

const altTrack = {
name: 'UCSC Alt Loci Track',
sourceType: 'annotation',
format: 'bed',
visibilityWindow,
url: `${API_BASE_URL}grch37/altSeqLiftOverPsl.bed.gz`,
indexURL: `${API_BASE_URL}grch37/altSeqLiftOverPsl.bed.gz.tbi`,
color: 'black'
}

const fixTrack = {
name: 'UCSC Fix Track',
sourceType: 'annotation',
format: 'bed',
visibilityWindow,
url: `${API_BASE_URL}grch37/fixSeqLiftOverPsl.bed.gz`,
indexURL: `${API_BASE_URL}grch37/fixSeqLiftOverPsl.bed.gz.tbi`,
color: 'black'
}

const bgDbTracks = [
// {
// title: 'In-House SVs',
// token: 'inhouse',
// },
{
title: 'gnomad-SV',
token: 'gnomad'
},
{
title: 'DGV SVs',
token: 'dgv'
},
{
title: 'DGV GS SVs',
token: 'dgv-gs'
},
{
title: 'ExAC CNVs',
token: 'exac'
}
].map(({ title, token }) => {
return {
name: title,
sourceType: 'annotation',
format: 'bed',
visibilityWindow,
displayMode: 'SQUISHED',
url: `${API_BASE_URL}grch37/${token}.bed.gz`,
indexURL: `${API_BASE_URL}grch37/${token}.bed.gz.tbi`,
color: 'black'
}
})
export const publicTracks = [
duplicationTrack,
repeatsTrack,
altTrack,
fixTrack,
hescTadTrack,
curatedMmsTrack
// clinvarTrack,
].concat(bgDbTracks)

export const genCaseTrack = (caseUuid: string) => ({
order: -1,
height: '200',
name: 'Case SVs',
sourceType: 'custom',
visibilityWindow,
source: {
url: `/svs/ajax/fetch-variants/${caseUuid}/?chromosome=$CHR&start=$START&end=$END`,
// url: `https://varfish.bihealth.org/svs/ajax/fetch-variants/7a538a72-f8fb-4d71-a08b-6045f1e983dc/?chromosome=chr15&start=16989370.8&end=26828561.7`,
method: 'GET',
contentType: 'application/json',
mappings: {
chr: 'chromosome'
},
queryable: true
},
format: 'annotation',
colorBy: 'sv_type',
colorTable: {
DEL: 'red',
DUP: 'green',
INV: 'blue',
'*': 'black'
}
})
104 changes: 104 additions & 0 deletions frontend/src/components/GenomeBrowser.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import igv from 'igv'
// import { genCaseTrack, publicTracks } from '@/components/GenomeBrowser.tracks'
/** Alias for Genome Browser type. */
type GenomeBrowser = any
// Define the props.
const props = defineProps({
// Case UUID
caseUuid: String,
// Genome build, e.g., "hg19" or "b37"
genome: String,
// Locus to go to, e.g., "chr1:1,900,000-2,000,000"
locus: String
})
// The <div> to show the browser in.
const genomeBrowserDivRef = ref(null)
// Set on IGV browser creation.
const igvBrowser = ref(null)
const translateGenome = (value: any) => {
if (value === 'GRCh37') {
return 'hg19'
} else if (value === 'GRCh38') {
return 'b38'
} else {
return value
}
}
// Conditionally add case tracks.
// const addCaseTracks = (browser: GenomeBrowser) => {
// if (props.caseUuid) {
// console.log(genCaseTrack(props.caseUuid))
// browser.loadTrack(genCaseTrack(props.caseUuid))
// }
// }
// Add all tracks.
// const addTracks = (browser: any) => {
// addCaseTracks(browser)
// for (const track of publicTracks) {
// browser.loadTrack(track)
// }
// }
// Watch changes to the genome (requires full reload).
watch(
() => props.genome,
() => {
igvBrowser.value?.loadGenome(translateGenome(props.genome)).then((browser: GenomeBrowser) => {
browser.search(props.locus)
})
// .then((browser: GenomeBrowser) => {
// addTracks(browser)
// })
}
)
// Watch changes to the case (requires track reload).
watch(
() => props.caseUuid,
() => {
if (igvBrowser.value) {
igvBrowser.value.removeTrackByName('Case SVs')
// addCaseTracks(igvBrowser.value)
}
}
)
// Watch changes to the locus (jumping is enough).
watch(
() => props.locus,
() => {
if (igvBrowser.value) {
igvBrowser.value.search(props.locus)
}
}
)
// Construct igv.js browser when mounted.
onMounted(() => {
igv
.createBrowser(genomeBrowserDivRef.value, {
genome: translateGenome(props.genome),
locus: props.locus
})
.then((browser: GenomeBrowser) => {
igvBrowser.value = browser
// addTracks(browser)
if (props.locus) {
igvBrowser.value?.search(props.locus)
}
})
})
</script>

<template>
<div ref="genomeBrowserDivRef"></div>
</template>
4 changes: 2 additions & 2 deletions frontend/src/components/SvDetails/SvDetailsGenotypeCall.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const allKeys: ComputedRef<any[]> = computed(() => {
<tr>
<th>Sample</th>

<template v-for="(_, sample) in currentSvRecord.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 currentSvRecord.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
15 changes: 8 additions & 7 deletions frontend/src/views/SvDetailView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import HeaderDetailPage from '@/components/HeaderDetailPage.vue'
import SvDetailsGenes from '@/components/SvDetails/SvGenes.vue'
import SvDetailsClinvar from '@/components/SvDetails/SvDetailsClinvar.vue'
import SvDetailsGenotypeCall from '@/components/SvDetails/SvDetailsGenotypeCall.vue'
import GenomeBrowser from '@/components/GenomeBrowser.vue'
import { type SvRecord } from '@/stores/svInfo'
export interface Props {
Expand Down Expand Up @@ -38,9 +39,9 @@ const loadDataToStore = async () => {
}
// Pretty display of coordinates.
const svLocus = (record: SvRecord): string | null => {
const svLocus = (record: SvRecord): string | undefined => {
if (!record) {
return null
return undefined
}
let locus: string
Expand Down Expand Up @@ -134,11 +135,11 @@ const genomeReleaseRef = ref(props.genomeRelease)
<div id="genome-browser" class="sv-item">
<h2>Genome Browser</h2>
<v-divider />
<!-- <GenomeBrowser
:case-uuid="svInfoStore.caseUuid"
:genome="genomeRelease"
:locus="svLocus(svInfoStore.currentCNVRecord)"
/> -->
<GenomeBrowser
:case-uuid="'case-uuid'"
:genome="genomeRelease === 'grch37' ? 'hg19' : 'b38'"
:locus="svLocus(svInfoStore.currentSvRecord)"
/>
</div>
</div>

Expand Down

0 comments on commit b4ef261

Please sign in to comment.