Skip to content

Commit

Permalink
Several clinvar submission improvements (#286).
Browse files Browse the repository at this point in the history
  • Loading branch information
holtgrewe committed Jan 31, 2022
1 parent 5edf707 commit a82d07e
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 97 deletions.
2 changes: 2 additions & 0 deletions HISTORY.rst
Expand Up @@ -39,6 +39,7 @@ End-User Summary
- Improving performance of case listing (#304)
- Adding shortcut buttons to phenotype annotation (#289)
- Fixing issue with multiple added variants (#283)
- Implementing several usability improvements for clinvar submission editor (#286).

Full Change List
================
Expand Down Expand Up @@ -79,6 +80,7 @@ Full Change List
- Improving performance of case listing (#304)
- Adding shortcut buttons to phenotype annotation (#289)
- Fixing issue with multiple added variants (#283)
- Implementing several usability improvements for clinvar submission editor (#286).

-------
v0.23.9
Expand Down
235 changes: 167 additions & 68 deletions varfish/vueapp/src/components/SubmissionList.vue
Expand Up @@ -20,9 +20,10 @@
@click="onListItemClicked(item.sodar_uuid)"
>
{{ getSubmissionLabel(item) }}
<span v-if="getSubmissionIndividualsCount(item) > 0" class="small">({{ getSubmissionIndividualsLabel(item) }})</span>
<i v-if="item._isInvalid" class="iconify text-warning" data-icon="bi:exclamation-circle"></i>
<div class="pull-right">
<i class="fa fa-chevron-right"></i>
<span class="iconify" data-icon="fa-solid:chevron-right"></span>
</div>
</b-list-group-item>
</draggable>
Expand All @@ -41,68 +42,89 @@

<b-modal id="modal-add-submission" size="lg" scrollable title="Add Submission to Submission List" hide-footer>
<p>
Create a new submission by selecting one of the variants below or
<b-button size="sm" variant="primary" @click="onCreateEmptySubmissionClicked()">
<i class="iconify" data-icon="fa-solid:fa-asterisk"></i>
create empty.
</b-button>
Create new submissions by selecting from the variants below and clicking <b-badge variant="primary"><i class="iconify" data-icon="fa-solid:plus"></i></b-badge>.
If you select no variant, a blank submission will be created.
</p>
<ul class="list-group mb-3">
<li
class="list-group-item list-group-item-action list-group-item-dark"
>
<b>Filters:</b>

<span
:class="{ 'cursor-pointer ml-2 badge badge-light': !modalIncludeAll, 'cursor-pointer ml-2 badge badge-success': modalIncludeAll }"
@click="toggleData('modalIncludeAll')"
>
all: <i :class="{ 'fa fa-check-circle': modalIncludeAll, 'fa fa-times-circle': !modalIncludeAll }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeComments, 'cursor-pointer ml-1 badge badge-success': modalIncludeComments }"
@click="toggleData('modalIncludeComments')"
>
comments <i :class="{ 'fa fa-check-circle': modalIncludeComments, 'fa fa-times-circle': !modalIncludeComments }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeCandidates, 'cursor-pointer ml-1 badge badge-success': modalIncludeCandidates }"
@click="toggleData('modalIncludeCandidates')"
>
candidates <i :class="{ 'fa fa-check-circle': modalIncludeCandidates, 'fa fa-times-circle': !modalIncludeCandidates }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeFinalCausatives, 'cursor-pointer ml-1 badge badge-success': modalIncludeFinalCausatives }"
@click="toggleData('modalIncludeFinalCausatives')"
>
causative <i :class="{ 'fa fa-check-circle': modalIncludeFinalCausatives, 'fa fa-times-circle': !modalIncludeFinalCausatives }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeAcmg3, 'cursor-pointer ml-1 badge badge-success': modalIncludeAcmg3 }"
@click="toggleData('modalIncludeAcmg3')"
>
VUCS3 <i :class="{ 'fa fa-check-circle': modalIncludeAcmg3, 'fa fa-times-circle': !modalIncludeAcmg3 }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeAcmg4, 'cursor-pointer ml-1 badge badge-success': modalIncludeAcmg4 }"
@click="toggleData('modalIncludeAcmg4')"
>
LP4 <i :class="{ 'fa fa-check-circle': modalIncludeAcmg4, 'fa fa-times-circle': !modalIncludeAcmg4 }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeAcmg5, 'cursor-pointer ml-1 badge badge-success': modalIncludeAcmg5 }"
@click="toggleData('modalIncludeAcmg5')"
>
P5 <i :class="{ 'fa fa-check-circle': modalIncludeAcmg5, 'fa fa-times-circle': !modalIncludeAcmg5 }"></i>
</span>
<div class="form-inline">
<div class="form-group">
<span
:class="{ 'cursor-pointer badge badge-light': !modalIncludeAll, 'cursor-pointer ml-2 badge badge-success': modalIncludeAll }"
@click="toggleData('modalIncludeAll')"
>
all: <i :class="{ 'fa fa-check-circle': modalIncludeAll, 'fa fa-times-circle': !modalIncludeAll }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeComments, 'cursor-pointer ml-1 badge badge-success': modalIncludeComments }"
@click="toggleData('modalIncludeComments')"
>
<i class="iconify" data-icon="fa-solid:comment"></i>&nbsp;&nbsp;
<i :class="{ 'fa fa-check-circle': modalIncludeComments, 'fa fa-times-circle': !modalIncludeComments }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeCandidates, 'cursor-pointer ml-1 badge badge-success': modalIncludeCandidates }"
@click="toggleData('modalIncludeCandidates')"
>
<i class="iconify" data-icon="fa-solid:heart"></i>&nbsp;&nbsp;
<i :class="{ 'fa fa-check-circle': modalIncludeCandidates, 'fa fa-times-circle': !modalIncludeCandidates }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeFinalCausatives, 'cursor-pointer ml-1 badge badge-success': modalIncludeFinalCausatives }"
@click="toggleData('modalIncludeFinalCausatives')"
>
<i class="iconify" data-icon="fa-solid:flag-checkered"></i>&nbsp;&nbsp;
<i :class="{ 'fa fa-check-circle': modalIncludeFinalCausatives, 'fa fa-times-circle': !modalIncludeFinalCausatives }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeAcmg3, 'cursor-pointer ml-1 badge badge-success': modalIncludeAcmg3 }"
@click="toggleData('modalIncludeAcmg3')"
>
VUCS3 <i :class="{ 'fa fa-check-circle': modalIncludeAcmg3, 'fa fa-times-circle': !modalIncludeAcmg3 }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeAcmg4, 'cursor-pointer ml-1 badge badge-success': modalIncludeAcmg4 }"
@click="toggleData('modalIncludeAcmg4')"
>
LP4 <i :class="{ 'fa fa-check-circle': modalIncludeAcmg4, 'fa fa-times-circle': !modalIncludeAcmg4 }"></i>
</span>
<span
:class="{ 'cursor-pointer ml-1 badge badge-light': !modalIncludeAcmg5, 'cursor-pointer ml-1 badge badge-success': modalIncludeAcmg5 }"
@click="toggleData('modalIncludeAcmg5')"
>
P5 <i :class="{ 'fa fa-check-circle': modalIncludeAcmg5, 'fa fa-times-circle': !modalIncludeAcmg5 }"></i>
</span>
</div>
<div class="form-group ml-3">
<b-form-input v-model="individualFilter" placeholder="Sample ID" size="sm"></b-form-input>
</div>
<div class="form-group ml-3">
<b-form-checkbox v-model="onlyAddAffected">
affecteds only
</b-form-checkbox>
</div>
<div class="form-group ml-3">
<b-button @click="onCreateSubmissionClicked()" size="sm" variant="primary">
<i class="iconify" data-icon="fa-solid:plus"></i>
</b-button>
</div>
</div>
</li>
<li
class="list-group-item list-group-item-action"
:class="{
'list-group-item x-not-active': !isVariantSelected(item),
'list-group-item x-active': isVariantSelected(item),
}"
v-for="item in modalUserAnnotations"
:key="item.sodar_uuid"
@click="onCreateSubmissionClicked(item)"
@click="onVariantClicked(item)"
@mousedown.prevent=""
>
<h5>
<i class="iconify x-active-show text-primary" data-icon="bi:check-square-fill"></i>
<i class="iconify x-active-hide text-muted" data-icon="bi:square"></i>
{{ getVariantLabel(item) }}
<small v-if="getVariantExtraLabel(item)">
{{ getVariantExtraLabel(item) }}
Expand All @@ -116,7 +138,8 @@
}"
:title="`${item.comments.length} user comments`"
>
<i class="fa fa-comment-o"></i> {{ item.comments.length }}
<i class="iconify" data-icon="fa-regular:comment"></i>
{{ item.comments.length }}
</span>
|
<span
Expand Down Expand Up @@ -154,7 +177,7 @@
class="list-group-item list-group-item-action text-muted font-italic text-center"
v-if="modalUserAnnotations.length === 0"
>
There is no user annotation for variants in this project.
There is no matching user annotation for variants in this project.
</li>
</ul>
</b-modal>
Expand All @@ -165,7 +188,7 @@
import { mapActions, mapState } from 'vuex'
import draggable from 'vuedraggable'
import SubmissionEditor from './SubmissionEditor'
import { getSubmissionLabel, validConfirmed } from '@/helpers'
import { isDiseaseTerm, getSubmissionLabel, validConfirmed, removeItemAll, HPO_INHERITANCE_MODE, HPO_AGE_OF_ONSET } from '@/helpers'
export default {
components: { draggable, SubmissionEditor },
Expand All @@ -177,12 +200,16 @@ export default {
modalIncludeFinalCausatives: true,
modalIncludeAcmg3: false,
modalIncludeAcmg4: true,
modalIncludeAcmg5: true
modalIncludeAcmg5: true,
individualFilter: '',
onlyAddAffected: true,
selectedSmallVariants: []
}
},
computed: {
...mapState({
individuals: state => state.clinvarExport.individuals,
submissionIndividuals: state => state.clinvarExport.submissionIndividuals,
submissions: state => state.clinvarExport.submissions,
currentSubmissionSet: state => state.clinvarExport.currentSubmissionSet,
currentSubmission: state => state.clinvarExport.currentSubmission,
Expand Down Expand Up @@ -216,6 +243,11 @@ export default {
return { ...smallVar, flags, rating, comments }
})
.filter(smallVar => {
if (this.individualFilter) {
if (!smallVar.caseNames.some(s => s.includes(this.individualFilter))) {
return false
}
}
if (this.modalIncludeAll) {
return true
} else if (this.modalIncludeComments && smallVar.comments.length > 0) {
Expand Down Expand Up @@ -252,6 +284,25 @@ export default {
getSubmissionLabel,
validConfirmed,
getSubmissionIndividualsCount (item) {
return item.submission_individuals.length
},
getSubmissionIndividualsLabel (item) {
let names = item.submission_individuals.map(
uuid => this.individuals[this.submissionIndividuals[uuid].individual].name.replace(/-N.-DNA.-....$/, '')
)
if (names.length > 2) {
names = names.slice(0, 2) + ['...']
}
return names.join(', ')
},
isVariantSelected (item) {
const variantDesc = this.getVariantDesc(item)
return this.selectedSmallVariants.includes(variantDesc)
},
getVariantDesc (item) {
return `${item.release}-${item.chromosome}-${item.start}-${item.reference}-${item.alternative}`
},
getVariantLabel (item) {
return `${item.refseq_gene_symbol}:${item.refseq_hgvs_p || '<none>'}`
},
Expand Down Expand Up @@ -279,19 +330,34 @@ export default {
this.selectCurrentSubmission(item)
})
},
onCreateEmptySubmissionClicked () {
this.createSubmissionInCurrentSubmissionSet({
smallVariant: null,
submission: this.getEmptySubmissionData(),
individualUuids: []
})
this.$bvModal.hide('modal-add-submission')
onVariantClicked (smallVariant) {
const variantDesc = this.getVariantDesc(smallVariant)
if (this.selectedSmallVariants.includes(variantDesc)) {
removeItemAll(this.selectedSmallVariants, variantDesc)
} else {
this.selectedSmallVariants.push(variantDesc)
}
},
/**
* Clicked on an existing small variant with user annotation.
*/
onCreateSubmissionClicked (smallVariant) {
this.createSubmissionInCurrentSubmissionSet(this.getSubmissionData(smallVariant))
onCreateSubmissionClicked () {
if (!this.selectedSmallVariants.length) {
this.createSubmissionInCurrentSubmissionSet({
smallVariant: null,
submission: this.getEmptySubmissionData(),
individualUuids: []
})
} else {
for (let i = 0; i < this.modalUserAnnotations.length; ++i) {
const smallVariant = this.modalUserAnnotations[i]
const variantDesc = this.getVariantDesc(smallVariant)
if (this.selectedSmallVariants.includes(variantDesc)) {
this.createSubmissionInCurrentSubmissionSet(this.getSubmissionData(smallVariant))
}
}
this.selectedSmallVariants = []
}
this.$bvModal.hide('modal-add-submission')
},
onAddSubmissionClicked () {
Expand All @@ -305,10 +371,14 @@ export default {
*/
getSubmissionData (smallVariant) {
// Get individuals that carry the variants.
const affectedNames = Object.values(this.individuals)
.filter(indiv => indiv.affected === 'yes')
.map(indiv => indiv.name)
const carrierNames = Object.entries(smallVariant.genotype)
.filter(kv => {
const name = kv[0]
const value = kv[1]
return value.gt && value.gt.includes('1')
return value.gt && value.gt.includes('1') && (!this.onlyAddAffected || affectedNames.includes(name))
})
.map(kv => kv[0])
const individualUuids = Object.entries(this.individuals)
Expand All @@ -327,15 +397,36 @@ export default {
const variantGene = [smallVariant.refseq_gene_symbol]
const variantHgvs = [smallVariant.refseq_hgvs_p || 'p.?']
let ageOfOnset = ''
let inheritance = ''
const diseases = []
for (const individualUuid of individualUuids) {
const individual = this.individuals[individualUuid]
if (individual.phenotype_terms) {
// eslint-disable-next-line camelcase
for (let { term_id, term_name } of individual.phenotype_terms) {
// eslint-disable-next-line camelcase
term_name = term_name.split(';')[0].trim()
inheritance = inheritance || HPO_INHERITANCE_MODE.get(term_id) || ''
ageOfOnset = ageOfOnset || HPO_AGE_OF_ONSET.get(term_id) || ''
// eslint-disable-next-line camelcase
if (isDiseaseTerm(term_id) && !diseases.some(x => x.term_id === term_id)) {
diseases.push({ term_id, term_name })
}
}
}
}
const submission = {
record_status: 'novel',
release_status: 'public',
significance_status: 'criteria provided, single submitter',
significance_description: significanceDescription,
significance_last_evaluation: (new Date()).toISOString().substr(0, 10),
assertion_method: Object.values(this.assertionMethods)[0].sodar_uuid,
age_of_onset: '',
inheritance: '',
age_of_onset: ageOfOnset,
diseases: diseases,
inheritance: inheritance,
variant_type: 'Variation',
variant_assembly: smallVariant.release,
variant_chromosome: smallVariant.chromosome,
Expand Down Expand Up @@ -398,4 +489,12 @@ export default {
.cursor-pointer {
cursor: pointer;
}
.x-not-active .x-active-show {
display: none;
}
.x-active .x-active-hide {
display: none;
}
</style>

0 comments on commit a82d07e

Please sign in to comment.