Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
gromdimon committed Sep 13, 2023
1 parent 225aff7 commit 88496ee
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 123 deletions.
46 changes: 44 additions & 2 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from fastapi.staticfiles import StaticFiles
from starlette.background import BackgroundTask
from starlette.requests import Request
from starlette.responses import FileResponse, RedirectResponse, Response, StreamingResponse
from starlette.responses import FileResponse, JSONResponse, Response, StreamingResponse

# Load environment
env = os.environ
Expand All @@ -34,7 +34,38 @@
if os.path.exists(VERSION_FILE): # pragma: no cover
with open(VERSION_FILE) as f:
REEV_VERSION = f.read().strip() or None

#: Template for ACMG rating
ACMG_RATING: dict = {
"pvs1": False,
"ps1": False,
"ps2": False,
"ps3": False,
"ps4": False,
"pm1": False,
"pm2": False,
"pm3": False,
"pm4": False,
"pm5": False,
"pm6": False,
"pp1": False,
"pp2": False,
"pp3": False,
"pp4": False,
"pp5": False,
"ba1": False,
"bs1": False,
"bs2": False,
"bs3": False,
"bs4": False,
"bp1": False,
"bp2": False,
"bp3": False,
"bp4": False,
"bp5": False,
"bp6": False,
"bp7": False,
"class_override": None,
}

app = FastAPI()

Expand Down Expand Up @@ -125,6 +156,17 @@ async def variantvalidator(request: Request, path: str):
)


# Register app for retrieving ACMG classification.
@app.get("/acmg/{path:path}")
async def acmg(request: Request, path: str):
"""Implement searching for ACMG classification."""
query_params = request.query_params
print(query_params)
acmg_rating = ACMG_RATING.copy()
acmg_rating["class_override"] = query_params.get("class_override", None)
return JSONResponse(acmg_rating)


# Register route for favicon.
@app.get("/favicon.ico")
async def favicon():
Expand Down
136 changes: 39 additions & 97 deletions frontend/src/components/VariantDetails/AcmgRating.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,45 @@
import { computed, onMounted, ref, watch } from 'vue'
import isEqual from 'lodash.isequal'
import { copy } from '@/api/utils'
import { StoreState } from '@/stores/misc'
import { useVariantAcmgRatingStore } from '@/stores/variantAcmgRating'
import { useVariantInfoStore } from '@/stores/variantInfo'
const props = defineProps({
smallVariant: Object
})
const acmgRatingStore = useVariantAcmgRatingStore()
const variantInfoStore = useVariantInfoStore()
const emptyAcmgRatingTemplate: any = {
pvs1: 0,
ps1: 0,
ps2: 0,
ps3: 0,
ps4: 0,
pm1: 0,
pm2: 0,
pm3: 0,
pm4: 0,
pm5: 0,
pm6: 0,
pp1: 0,
pp2: 0,
pp3: 0,
pp4: 0,
pp5: 0,
ba1: 0,
bs1: 0,
bs2: 0,
bs3: 0,
bs4: 0,
bp1: 0,
bp2: 0,
bp3: 0,
bp4: 0,
bp5: 0,
bp6: 0,
bp7: 0,
class_auto: 3,
pvs1: false,
ps1: false,
ps2: false,
ps3: false,
ps4: false,
pm1: false,
pm2: false,
pm3: false,
pm4: false,
pm5: false,
pm6: false,
pp1: false,
pp2: false,
pp3: false,
pp4: false,
pp5: false,
ba1: false,
bs1: false,
bs2: false,
bs3: false,
bs4: false,
bp1: false,
bp2: false,
bp3: false,
bp4: false,
bp5: false,
bp6: false,
bp7: false,
// class_auto: 3,
class_override: null
}
Expand Down Expand Up @@ -84,50 +81,13 @@ const resetAcmgRating = () => {
acmgRatingToSubmit.value.bp5 = acmgRatingStore.acmgRating.bp5
acmgRatingToSubmit.value.bp6 = acmgRatingStore.acmgRating.bp6
acmgRatingToSubmit.value.bp7 = acmgRatingStore.acmgRating.bp7
acmgRatingToSubmit.value.class_auto = acmgRatingStore.acmgRating.class_auto
// acmgRatingToSubmit.value.class_auto = acmgRatingStore.acmgRating.class_auto
acmgRatingToSubmit.value.class_override = acmgRatingStore.acmgRating.class_override
} else {
unsetAcmgRating()
}
}
const acmgRatingSubmitted = computed(() => {
if (!acmgRatingStore.acmgRating) {
return false
}
return (
acmgRatingToSubmit.value.pvs1 === acmgRatingStore.acmgRating.pvs1 &&
acmgRatingToSubmit.value.ps1 === acmgRatingStore.acmgRating.ps1 &&
acmgRatingToSubmit.value.ps2 === acmgRatingStore.acmgRating.ps2 &&
acmgRatingToSubmit.value.ps3 === acmgRatingStore.acmgRating.ps3 &&
acmgRatingToSubmit.value.ps4 === acmgRatingStore.acmgRating.ps4 &&
acmgRatingToSubmit.value.pm1 === acmgRatingStore.acmgRating.pm1 &&
acmgRatingToSubmit.value.pm2 === acmgRatingStore.acmgRating.pm2 &&
acmgRatingToSubmit.value.pm3 === acmgRatingStore.acmgRating.pm3 &&
acmgRatingToSubmit.value.pm4 === acmgRatingStore.acmgRating.pm4 &&
acmgRatingToSubmit.value.pm5 === acmgRatingStore.acmgRating.pm5 &&
acmgRatingToSubmit.value.pm6 === acmgRatingStore.acmgRating.pm6 &&
acmgRatingToSubmit.value.pp1 === acmgRatingStore.acmgRating.pp1 &&
acmgRatingToSubmit.value.pp2 === acmgRatingStore.acmgRating.pp2 &&
acmgRatingToSubmit.value.pp3 === acmgRatingStore.acmgRating.pp3 &&
acmgRatingToSubmit.value.pp4 === acmgRatingStore.acmgRating.pp4 &&
acmgRatingToSubmit.value.pp5 === acmgRatingStore.acmgRating.pp5 &&
acmgRatingToSubmit.value.ba1 === acmgRatingStore.acmgRating.ba1 &&
acmgRatingToSubmit.value.bs1 === acmgRatingStore.acmgRating.bs1 &&
acmgRatingToSubmit.value.bs2 === acmgRatingStore.acmgRating.bs2 &&
acmgRatingToSubmit.value.bs3 === acmgRatingStore.acmgRating.bs3 &&
acmgRatingToSubmit.value.bs4 === acmgRatingStore.acmgRating.bs4 &&
acmgRatingToSubmit.value.bp1 === acmgRatingStore.acmgRating.bp1 &&
acmgRatingToSubmit.value.bp2 === acmgRatingStore.acmgRating.bp2 &&
acmgRatingToSubmit.value.bp3 === acmgRatingStore.acmgRating.bp3 &&
acmgRatingToSubmit.value.bp4 === acmgRatingStore.acmgRating.bp4 &&
acmgRatingToSubmit.value.bp5 === acmgRatingStore.acmgRating.bp5 &&
acmgRatingToSubmit.value.bp6 === acmgRatingStore.acmgRating.bp6 &&
acmgRatingToSubmit.value.bp7 === acmgRatingStore.acmgRating.bp7 &&
acmgRatingToSubmit.value.class_override === acmgRatingStore.acmgRating.class_override
)
})
const updateAcmgClass = (acmgClass: number, isConflicting: boolean) => {
acmgRatingToSubmit.value.class_auto = acmgClass
acmgRatingConflicting.value = isConflicting
Expand Down Expand Up @@ -210,19 +170,12 @@ const convertEmptyToNull = (classOverride: any) => {
}
const onSubmitAcmgRating = async () => {
const acmgRatingToSubmitEmpty = isEqual(acmgRatingToSubmit, emptyAcmgRatingTemplate)
if (acmgRatingStore.acmgRating && acmgRatingToSubmitEmpty) {
// IS not empty but SHOULD be empty, so delete the ACMG rating
await acmgRatingStore.deleteAcmgRating()
} else if (!acmgRatingStore.acmgRating && acmgRatingToSubmitEmpty) {
if (isEqual(acmgRatingToSubmit, emptyAcmgRatingTemplate)) {
// IS empty and SHOULD be empty, so no update needed
acmgRatingToSubmit.value = copy(emptyAcmgRatingTemplate)
} else if (acmgRatingStore.acmgRating && !acmgRatingToSubmitEmpty) {
// IS not empty and SHOULD not be empty, so update the ACMG rating
await acmgRatingStore.updateAcmgRating(acmgRatingToSubmit.value)
} else if (!acmgRatingStore.acmgRating && !acmgRatingToSubmitEmpty) {
// IS empty but SHOULD not be empty, so create the ACMG rating
await acmgRatingStore.createAcmgRating(variantInfoStore.smallVariant, acmgRatingToSubmit.value)
return
} else {
// Submit ACMG rating to ClinVar
await acmgRatingStore.submitAcmgRating(props.smallVariant, acmgRatingToSubmit.value)
}
}
Expand Down Expand Up @@ -478,11 +431,10 @@ const acmgCriteriaInfo = {
</script>

<template>
{{ acmgRatingToSubmit }}
<v-row>
<v-col cols="12" md="4">
<div>
<h3>Pathogenic:</h3>
<h3><strong>Pathogenic:</strong></h3>
</div>
<div v-for="(criteriaType, criteriaKey) in acmgCriteriaInfo.pathogenic" :key="criteriaKey">
<div>
Expand All @@ -507,7 +459,7 @@ const acmgCriteriaInfo = {
</v-col>
<v-col cols="12" md="4">
<div>
<h3>Benign:</h3>
<h3><strong>Benign:</strong></h3>
</div>
<div v-for="(criteriaType, criteriaKey) in acmgCriteriaInfo.benign" :key="criteriaKey">
<div>
Expand All @@ -516,7 +468,6 @@ const acmgCriteriaInfo = {
</strong>
</div>
<div v-for="(criteria, criteriaKey) in criteriaType.criteria" :key="criteriaKey">
{{ criteria.id }} {{ acmgRatingToSubmit[criteria.id] }}
<div>
<div :title="criteria.description">
<v-switch
Expand Down Expand Up @@ -580,17 +531,8 @@ const acmgCriteriaInfo = {
<div class="button-group">
<v-btn @click="unsetAcmgRating()"> Clear </v-btn>
<v-btn @click="resetAcmgRating()"> Reset </v-btn>
<v-btn @click="onSubmitAcmgRating()">
<div v-if="acmgRatingConflicting">
<v-icon>mdi-circle-outline</v-icon>
</div>
<div v-else-if="acmgRatingSubmitted">
<v-icon>mdi-star-check-outline</v-icon>
</div>
<div else>
<v-icon>mdi-star-check</v-icon>
</div>
Submit
<v-btn prepend-icon="mdi-star-check" @click="onSubmitAcmgRating()">
Submit to ClinVar
</v-btn>
</div>
<div v-if="acmgRatingConflicting">
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/stores/geneInfo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/** Store for gene details.
/**
* Store for gene details.
*
* This includes the data retrieved from the APIs.
*/
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/stores/genesList.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/** Store for gene details.
/**
* Store for gene details.
*
* This includes the data retrieved from the APIs.
*/
Expand Down
56 changes: 35 additions & 21 deletions frontend/src/stores/variantAcmgRating.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,73 @@
/**
* Pinia store for handling per-variant ACMG rating.
*
* ## Store Dependencies
*
* - `caseDetailsStore`
* Store for handling per-variant ACMG rating.
*/
import { defineStore } from 'pinia'
import { ref } from 'vue'

import { StoreState } from '@/stores/misc'
import { API_BASE_PREFIX } from '@/api/common'

const API_BASE_URL = API_BASE_PREFIX

/** Alias definition of SmallVariant type; to be defined later. */
type SmallVariant = any
/** Alias definition of AcmgRating type; to be defined later. */
type AcmgRating = any

export const useVariantAcmgRatingStore = defineStore('variantAcmgRating', () => {
/** The current store state. */
const storeState = ref<StoreState>(StoreState.Initial)

/** The small variant that acmgRating are handled for. */
const smallVariant = ref<SmallVariant | null>(null)

/* The current store state. */
const storeState = ref<StoreState>(StoreState.Initial)

/** The small variants ACMG rating as fetched from API. */
const acmgRating = ref<AcmgRating | null>(null)

function clearData() {
storeState.value = StoreState.Initial
acmgRating.value = null
smallVariant.value = null
}

const createAcmgRating = async (smallVariant: SmallVariant, acmgRating: Object) => {
console.log('createAcmgRating', smallVariant, acmgRating)
}
const retrieveAcmgRating = async (smallVar: SmallVariant) => {
// Do not re-load data if the small variant is the same
if (smallVar === smallVariant.value) {
return
}

const retrieveAcmgRating = async (smallVariant: SmallVariant) => {
console.log('retrieveAcmgRating', smallVariant)
}
// Clear against artifact
clearData()

const updateAcmgRating = async (acmgRating: Object) => {
console.log('updateAcmgRating', acmgRating)
// Load data via API
storeState.value = StoreState.Loading
try {
const response = await fetch(`${API_BASE_URL}acmg/?small-var=${smallVar}`, {
method: 'GET'
})
// const body = await response.json()
acmgRating.value = response.json()
smallVariant.value = smallVar
storeState.value = StoreState.Active
} catch (e) {
console.error('There was an error loading the ACMG data.', e)
clearData()
storeState.value = StoreState.Error
}
}

const deleteAcmgRating = async () => {
console.log('deleteAcmgRating')
const submitAcmgRating = async (smallVar: SmallVariant, payload: Object) => {
// TODO: Implement the API call to submit the ACMG rating to ClinVar
smallVariant.value = smallVar
acmgRating.value = payload
}

return {
smallVariant,
storeState,
acmgRating,
clearData,
createAcmgRating,
retrieveAcmgRating,
updateAcmgRating,
deleteAcmgRating
submitAcmgRating
}
})
3 changes: 2 additions & 1 deletion frontend/src/stores/variantInfo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/** Store for variant details.
/**
* Store for variant details.
*
* This includes the data retrieved from the APIs.
*/
Expand Down

0 comments on commit 88496ee

Please sign in to comment.