Skip to content

Commit

Permalink
feat: refurbishing gene details page (#229)
Browse files Browse the repository at this point in the history
  • Loading branch information
holtgrewe committed Nov 28, 2023
1 parent ce8ab52 commit 8db14f8
Show file tree
Hide file tree
Showing 28 changed files with 3,079 additions and 1,852 deletions.
4 changes: 4 additions & 0 deletions frontend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@ ci: \
.PHONY: serve
serve:
MODE=development npm run dev

.PHONY: serve-public
serve-public:
MODE=development npm run dev-public
2,542 changes: 1,507 additions & 1,035 deletions frontend/package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"scripts": {
"dev": "vite --port 8081",
"dev-public": "vite --host 0.0.0.0 --port 8081",
"build": "run-p type-check build-only",
"preview": "vite preview",
"test:unit": "vitest --coverage",
Expand All @@ -19,13 +20,14 @@
"igv": "^2.15.11",
"lodash": "^4.17.21",
"pinia": "^2.1.6",
"title-case": "^4.1.2",
"vega": "^5.25.0",
"vega-embed": "^6.22.2",
"vue": "^3.3.4",
"vue-matomo": "^4.2.0",
"vue-router": "^4.2.5",
"vue3-easy-data-table": "^1.5.47",
"vuetify": "^3.3.19"
"vuetify": "^3.4.0"
},
"devDependencies": {
"@pinia/testing": "^0.1.3",
Expand Down
43 changes: 43 additions & 0 deletions frontend/src/api/viguno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ import { API_INTERNAL_BASE_PREFIX_VIGUNO } from '@/api/common'

const API_BASE_URL = `${API_INTERNAL_BASE_PREFIX_VIGUNO}/`

/** HPO term as returned by the API */
export interface HpoTerm {
/** Term ID with `HP:` prefix. */
term_id: string
/** Human-readable term name. */
name: string
}

/** Gene as returned by the API. */
export interface Gene {
gene_ncbi_id: number
gene_symbol: string
hgnc_id: string
hpo_terms?: HpoTerm[]
}

export class VigunoClient {
private apiBaseUrl: string
private csrfToken: string | null
Expand Down Expand Up @@ -66,4 +82,31 @@ export class VigunoClient {

return await response.json()
}

/**
* Retrieves HPO terms associated with a gene.
*
* @param hgncId
* @returns List of HPO terms associated with the gene.
*/
async fetchHpoTermsForHgncId(hgncId: string): Promise<HpoTerm[]> {
const url = `${this.apiBaseUrl}hpo/genes?gene_id=${hgncId}&hpo_terms=true`
const response = await fetch(url, {
method: 'GET'
})

if (!response.ok) {
const errorBody = await response.json()
throw new Error(errorBody.msg || response.statusText)
}

const res = await response.json()
if (!res.result?.length || res.result.length === 0) {
return []
}
return (res.result[0].hpo_terms ?? []).map((term: HpoTerm) => ({
term_id: term.term_id,
name: term.name
}))
}
}
73 changes: 73 additions & 0 deletions frontend/src/components/BookmarkListItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useBookmarksStore } from '@/stores/bookmarks'
import { StoreState } from '@/stores/misc'
// Import the BookmarksClient
export interface Props {
type: string
id: string
}
const props = withDefaults(defineProps<Props>(), {
type: '',
id: ''
})
const bookmarksStore = useBookmarksStore()
const isBookmarked = ref(false)
const loadDataToStore = async () => {
await bookmarksStore.loadBookmarks()
}
// Fetch existing bookmark for the given type and id on mount
onMounted(async () => {
loadDataToStore()
bookmarksStore
.fetchBookmark(props.type, props.id)
?.then((bookmark) => {
if (bookmark) {
isBookmarked.value = true
} else {
isBookmarked.value = false
}
})
.catch((e) => {
isBookmarked.value = false
console.error(e)
})
})
// Function to toggle bookmark
const toggleBookmark = async () => {
if (isBookmarked.value) {
await bookmarksStore.deleteBookmark(props.type, props.id)
} else {
await bookmarksStore.createBookmark(props.type, props.id)
}
const retrievedBookmark = await bookmarksStore.fetchBookmark(props.type, props.id)
if (retrievedBookmark) {
isBookmarked.value = true
} else {
isBookmarked.value = false
}
}
</script>

<template>
<template v-if="bookmarksStore.storeState === StoreState.Error">
<v-list-item class="text-caption"> Bookmarks available after login </v-list-item>
</template>
<template v-else class="mb-2">
<v-list-item
:prepend-icon="isBookmarked ? 'mdi-bookmark' : 'mdi-bookmark-outline'"
@click="toggleBookmark()"
>
Bookmark
</v-list-item>
</template>
</template>
136 changes: 70 additions & 66 deletions frontend/src/components/GeneDetails/AlternativeIdentifiers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,78 +7,82 @@ const props = withDefaults(defineProps<Props>(), {
hgnc: null
})
</script>

<template>
<v-card>
<v-card-title>Alternative Identifiers</v-card-title>
<v-divider></v-divider>
<v-card-text>
<div>
<strong> ENSEMBL: </strong>
<a
:href="`https://www.ensembl.org/Homo_sapiens/Gene/Summary?g=${props.hgnc?.ensemblGeneId}`"
target="_blank"
>
<div>
<div class="text-subtitle-1">Alternative Identifiers</div>

<div>
<strong> ENSEMBL: </strong>
<a
:href="`https://www.ensembl.org/Homo_sapiens/Gene/Summary?g=${props.hgnc?.ensemblGeneId}`"
target="_blank"
>
<v-icon>mdi-launch</v-icon>
{{ props.hgnc?.ensemblGeneId }}
</a>
</div>
<div>
<strong> HGNC: </strong>
<a
:href="`https://www.genenames.org/data/gene-symbol-report/#!/hgnc_id/${props.hgnc?.hgncId}`"
target="_blank"
>
<v-icon>mdi-launch</v-icon>
{{ props.hgnc?.hgncId }}
</a>
</div>
<div v-if="props.hgnc?.mgdId?.length">
<strong>MGI: </strong>
<template v-for="(mgd_id, index) in props.hgnc.mgdId" :key="mgd_id">
<template v-if="index > 0">, </template>
<a :href="`https://www.informatics.jax.org/marker/${mgd_id}`" target="_blank">
<v-icon>mdi-launch</v-icon>
{{ props.hgnc?.ensemblGeneId }}
{{ mgd_id }}
</a>
</div>
<div>
<strong> HGNC: </strong>
</template>
</div>
<span v-else> No MGI </span>
<div v-if="props.hgnc?.pubmedId?.length">
<strong>Primary PMID: </strong>
<template v-for="(pmid, index) in props.hgnc.pubmedId" :key="pmid">
<template v-if="index > 0">, </template>
<a :href="`https://pubmed.ncbi.nlm.nih.gov/${pmid}/`" target="_blank">
<v-icon>mdi-launch</v-icon>
{{ pmid }}
</a>
</template>
</div>
<div v-else>No primary PMID</div>
<div v-if="props.hgnc?.refseqAccession?.length">
<strong> RefSeq: </strong>
<template v-for="(accession, index) in props.hgnc.refseqAccession" :key="index">
<template v-if="index > 0">, </template>
<a
:href="`https://www.genenames.org/data/gene-symbol-report/#!/hgnc_id/${props.hgnc?.hgncId}`"
:href="`https://www.ncbi.nlm.nih.gov/nuccore/?term=${accession}+AND+srcdb_refseq[PROP]`"
target="_blank"
>
<v-icon>mdi-launch</v-icon>
{{ props.hgnc?.hgncId }}
{{ accession }}
</a>
</template>
</div>
<div v-else>No RefSeq</div>
<div v-if="props.hgnc?.uniprotIds?.length">
<strong> UniProt: </strong>
<template v-for="(uniprotid, index) in props.hgnc.uniprotIds" :key="index">
<template v-if="index > 0">, </template>
<a :href="`https://www.uniprot.org/uniprotkb/${uniprotid}/entry`" target="_blank">
<v-icon>mdi-launch</v-icon>
{{ uniprotid }}
</a>
</div>
<div v-if="props.hgnc?.mgdId?.length">
<strong>MGI: </strong>
<template v-for="(mgd_id, index) in props.hgnc.mgdId" :key="mgd_id">
<template v-if="index > 0">, </template>
<a :href="`https://www.informatics.jax.org/marker/${mgd_id}`" target="_blank">
<v-icon>mdi-launch</v-icon>
{{ mgd_id }}
</a>
</template>
</div>
<span v-else> No MGI </span>
<div v-if="props.hgnc?.pubmedId?.length">
<strong>Primary PMID: </strong>
<template v-for="(pmid, index) in props.hgnc.pubmedId" :key="pmid">
<template v-if="index > 0">, </template>
<a :href="`https://pubmed.ncbi.nlm.nih.gov/${pmid}/`" target="_blank">
<v-icon>mdi-launch</v-icon>
{{ pmid }}
</a>
</template>
</div>
<div v-else>No primary PMID</div>
<div v-if="props.hgnc?.refseqAccession?.length">
<strong> RefSeq: </strong>
<template v-for="(accession, index) in props.hgnc.refseqAccession" :key="index">
<template v-if="index > 0">, </template>
<a
:href="`https://www.ncbi.nlm.nih.gov/nuccore/?term=${accession}+AND+srcdb_refseq[PROP]`"
target="_blank"
>
<v-icon>mdi-launch</v-icon>
{{ accession }}
</a>
</template>
</div>
<div v-else>No RefSeq</div>
<div v-if="props.hgnc?.uniprotIds?.length">
<strong> UniProt: </strong>
<template v-for="(uniprotid, index) in props.hgnc.uniprotIds" :key="index">
<template v-if="index > 0">, </template>
<a :href="`https://www.uniprot.org/uniprotkb/${uniprotid}/entry`" target="_blank">
<v-icon>mdi-launch</v-icon>
{{ uniprotid }}
</a>
</template>
</div>
<div v-else>No UniProt</div>
</v-card-text>
</v-card>
</template>
</div>
<div v-else>No UniProt</div>
</div>
</template>
Loading

0 comments on commit 8db14f8

Please sign in to comment.