scout-browser 4.101.0__py3-none-any.whl → 4.103.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scout/adapter/mongo/case.py +26 -122
- scout/adapter/mongo/clinvar.py +98 -32
- scout/adapter/mongo/event.py +0 -47
- scout/adapter/mongo/hgnc.py +7 -2
- scout/adapter/mongo/omics_variant.py +8 -0
- scout/adapter/mongo/variant_loader.py +12 -4
- scout/build/variant/variant.py +1 -0
- scout/commands/load/variants.py +1 -1
- scout/commands/update/user.py +87 -49
- scout/constants/__init__.py +4 -0
- scout/constants/clinvar.py +10 -0
- scout/constants/igv_tracks.py +6 -2
- scout/constants/phenotype.py +1 -0
- scout/constants/variant_tags.py +18 -0
- scout/demo/NIST.trgt.stranger.vcf.gz +0 -0
- scout/demo/NIST.trgt.stranger.vcf.gz.tbi +0 -0
- scout/demo/__init__.py +1 -0
- scout/load/hpo.py +8 -2
- scout/models/clinvar.py +86 -0
- scout/parse/variant/coordinates.py +5 -1
- scout/parse/variant/gene.py +5 -9
- scout/parse/variant/genotype.py +66 -42
- scout/parse/variant/variant.py +2 -0
- scout/server/app.py +71 -2
- scout/server/blueprints/alignviewers/controllers.py +8 -6
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +4 -0
- scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
- scout/server/blueprints/cases/controllers.py +57 -29
- scout/server/blueprints/cases/templates/cases/case_report.html +28 -90
- scout/server/blueprints/cases/templates/cases/matchmaker.html +1 -1
- scout/server/blueprints/cases/templates/cases/phenotype.html +1 -1
- scout/server/blueprints/cases/templates/cases/utils.html +34 -53
- scout/server/blueprints/cases/views.py +32 -33
- scout/server/blueprints/clinvar/controllers.py +235 -54
- scout/server/blueprints/clinvar/form.py +38 -1
- scout/server/blueprints/clinvar/static/form_style.css +8 -1
- scout/server/blueprints/clinvar/templates/clinvar/clinvar_onc_submissions.html +200 -0
- scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +3 -2
- scout/server/blueprints/clinvar/templates/clinvar/components.html +198 -0
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_onc_variant.html +187 -0
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +9 -348
- scout/server/blueprints/clinvar/templates/clinvar/scripts.html +193 -0
- scout/server/blueprints/clinvar/views.py +90 -13
- scout/server/blueprints/diagnoses/controllers.py +4 -8
- scout/server/blueprints/diagnoses/templates/diagnoses/diagnoses.html +1 -1
- scout/server/blueprints/diagnoses/templates/diagnoses/disease_term.html +1 -1
- scout/server/blueprints/diagnoses/views.py +2 -2
- scout/server/blueprints/institutes/controllers.py +148 -75
- scout/server/blueprints/institutes/forms.py +1 -0
- scout/server/blueprints/institutes/templates/overview/cases.html +1 -1
- scout/server/blueprints/institutes/templates/overview/gene_variants.html +15 -6
- scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +28 -2
- scout/server/blueprints/institutes/templates/overview/utils.html +1 -1
- scout/server/blueprints/institutes/views.py +17 -4
- scout/server/blueprints/login/controllers.py +2 -1
- scout/server/blueprints/login/views.py +5 -2
- scout/server/blueprints/mme/templates/mme/mme_submissions.html +2 -2
- scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +2 -2
- scout/server/blueprints/omics_variants/views.py +2 -2
- scout/server/blueprints/phenotypes/controllers.py +15 -2
- scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +1 -1
- scout/server/blueprints/variant/controllers.py +11 -12
- scout/server/blueprints/variant/templates/variant/cancer-variant.html +2 -1
- scout/server/blueprints/variant/templates/variant/components.html +0 -1
- scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -1
- scout/server/blueprints/variant/templates/variant/utils.html +1 -1
- scout/server/blueprints/variant/templates/variant/variant.html +2 -2
- scout/server/blueprints/variant/templates/variant/variant_details.html +100 -84
- scout/server/blueprints/variant/utils.py +25 -0
- scout/server/blueprints/variants/controllers.py +11 -42
- scout/server/blueprints/variants/templates/variants/cancer-variants.html +5 -3
- scout/server/blueprints/variants/templates/variants/str-variants.html +4 -1
- scout/server/blueprints/variants/templates/variants/sv-variants.html +3 -3
- scout/server/blueprints/variants/templates/variants/utils.html +4 -0
- scout/server/blueprints/variants/templates/variants/variants.html +4 -4
- scout/server/blueprints/variants/views.py +9 -8
- scout/server/config.py +3 -0
- scout/server/extensions/beacon_extension.py +7 -2
- scout/server/extensions/clinvar_extension.py +2 -2
- scout/server/templates/bootstrap_global.html +11 -1
- scout/server/templates/layout.html +6 -1
- scout/server/utils.py +24 -3
- {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/METADATA +1 -1
- {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/RECORD +87 -81
- {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/WHEEL +0 -0
- {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/licenses/LICENSE +0 -0
@@ -2,13 +2,15 @@ import csv
|
|
2
2
|
import logging
|
3
3
|
from datetime import datetime
|
4
4
|
from tempfile import NamedTemporaryFile
|
5
|
-
from typing import List, Optional, Tuple
|
5
|
+
from typing import List, Optional, Tuple, Union
|
6
6
|
|
7
|
-
from flask import flash
|
7
|
+
from flask import flash, request
|
8
8
|
from flask_login import current_user
|
9
|
+
from pydantic_core._pydantic_core import ValidationError
|
9
10
|
from werkzeug.datastructures import ImmutableMultiDict
|
10
11
|
|
11
12
|
from scout.constants.acmg import ACMG_MAP
|
13
|
+
from scout.constants.ccv import CCV_MAP
|
12
14
|
from scout.constants.clinvar import (
|
13
15
|
CASEDATA_HEADER,
|
14
16
|
CLINVAR_HEADER,
|
@@ -16,13 +18,19 @@ from scout.constants.clinvar import (
|
|
16
18
|
SCOUT_CLINVAR_SV_TYPES_MAP,
|
17
19
|
)
|
18
20
|
from scout.constants.variant_tags import MANUAL_RANK_OPTIONS
|
19
|
-
from scout.models.clinvar import clinvar_variant
|
21
|
+
from scout.models.clinvar import OncogenicitySubmissionItem, clinvar_variant
|
20
22
|
from scout.server.blueprints.variant.utils import add_gene_info
|
21
23
|
from scout.server.extensions import clinvar_api, store
|
24
|
+
from scout.server.utils import get_case_genome_build, safe_redirect_back
|
22
25
|
from scout.utils.hgvs import validate_hgvs
|
23
26
|
from scout.utils.scout_requests import fetch_refseq_version
|
24
27
|
|
25
|
-
from .form import
|
28
|
+
from .form import (
|
29
|
+
CancerSNVariantForm,
|
30
|
+
CaseDataForm,
|
31
|
+
SNVariantForm,
|
32
|
+
SVariantForm,
|
33
|
+
)
|
26
34
|
|
27
35
|
LOG = logging.getLogger(__name__)
|
28
36
|
|
@@ -30,7 +38,7 @@ LOG = logging.getLogger(__name__)
|
|
30
38
|
def _get_var_tx_hgvs(case_obj: dict, variant_obj: dict) -> List[Tuple[str, str]]:
|
31
39
|
"""Retrieve all transcripts / HGVS for a given variant."""
|
32
40
|
|
33
|
-
build =
|
41
|
+
build = get_case_genome_build(case_obj)
|
34
42
|
tx_hgvs_list = [("", "Do not specify")]
|
35
43
|
case_has_build_37 = "37" in case_obj.get("genome_build", "37")
|
36
44
|
|
@@ -75,6 +83,7 @@ def _set_var_form_common_fields(var_form, variant_obj, case_obj):
|
|
75
83
|
var_form.case_id.data = case_obj["_id"]
|
76
84
|
var_form.local_id.data = variant_obj["_id"]
|
77
85
|
var_form.linking_id.data = variant_obj["_id"]
|
86
|
+
var_form.assembly.data = "GRCh38" if get_case_genome_build(case_obj) == "38" else "GRCh37"
|
78
87
|
var_form.chromosome.data = variant_obj.get("chromosome")
|
79
88
|
var_form.ref.data = variant_obj.get("reference")
|
80
89
|
var_form.alt.data = variant_obj.get("alternative")
|
@@ -100,7 +109,7 @@ def _set_var_form_common_fields(var_form, variant_obj, case_obj):
|
|
100
109
|
]
|
101
110
|
|
102
111
|
|
103
|
-
def _get_snv_var_form(variant_obj, case_obj):
|
112
|
+
def _get_snv_var_form(variant_obj: dict, case_obj: dict):
|
104
113
|
"""Sets up values for a SNV variant form
|
105
114
|
Args:
|
106
115
|
variant_obj(dict) scout.models.Variant
|
@@ -109,7 +118,10 @@ def _get_snv_var_form(variant_obj, case_obj):
|
|
109
118
|
Returns:
|
110
119
|
var_form(scout.server.blueprints.clinvar.form.SNVariantForm)
|
111
120
|
"""
|
112
|
-
|
121
|
+
if case_obj.get("track") == "cancer":
|
122
|
+
var_form = CancerSNVariantForm()
|
123
|
+
else:
|
124
|
+
var_form = SNVariantForm()
|
113
125
|
_set_var_form_common_fields(var_form, variant_obj, case_obj)
|
114
126
|
var_form.tx_hgvs.choices = _get_var_tx_hgvs(case_obj, variant_obj)
|
115
127
|
var_ids = variant_obj.get("dbsnp_id") or ""
|
@@ -144,25 +156,18 @@ def _get_sv_var_form(variant_obj, case_obj):
|
|
144
156
|
return var_form
|
145
157
|
|
146
158
|
|
147
|
-
def _populate_variant_form(
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
variant_obj(dict) scout.models.Variant
|
152
|
-
case_obj(dict) scout.models.Case
|
153
|
-
|
154
|
-
Return:
|
155
|
-
var_form(scout.server.blueprints.clinvar.form.SVariantForm or
|
156
|
-
scout.server.blueprints.clinvar.form.SNVariantForm )
|
157
|
-
|
158
|
-
"""
|
159
|
-
if variant_obj["category"] in ["snv", "cancer"]:
|
160
|
-
var_form = _get_snv_var_form(variant_obj, case_obj)
|
161
|
-
var_form.category.data = "snv"
|
159
|
+
def _populate_variant_form(
|
160
|
+
variant_obj: dict, case_obj: dict
|
161
|
+
) -> Union[SNVariantForm, SVariantForm, CancerSNVariantForm]:
|
162
|
+
"""Populate the Flaskform associated to a variant. This form will be used in the multistep ClinVar submission form."""
|
162
163
|
|
163
|
-
|
164
|
-
|
165
|
-
|
164
|
+
form_getters = {
|
165
|
+
"snv": _get_snv_var_form,
|
166
|
+
"sv": _get_sv_var_form,
|
167
|
+
"cancer": _get_snv_var_form,
|
168
|
+
}
|
169
|
+
category = variant_obj["category"]
|
170
|
+
var_form = form_getters[category](variant_obj, case_obj)
|
166
171
|
|
167
172
|
return var_form
|
168
173
|
|
@@ -187,11 +192,14 @@ def _populate_case_data_form(variant_obj, case_obj):
|
|
187
192
|
ind_form.include_ind.data = affected
|
188
193
|
ind_form.individual_id.data = ind.get("display_name")
|
189
194
|
ind_form.linking_id.data = variant_obj["_id"]
|
195
|
+
ind_form.allele_of_origin.data = (
|
196
|
+
"somatic" if case_obj.get("track") == "cancer" else "germline"
|
197
|
+
)
|
190
198
|
cdata_form_list.append(ind_form)
|
191
199
|
return cdata_form_list
|
192
200
|
|
193
201
|
|
194
|
-
def _variant_classification(var_obj):
|
202
|
+
def _variant_classification(var_obj: dict):
|
195
203
|
"""Set a 'classified_as' key/header_value in the variant object, to be displayed on
|
196
204
|
form page with the aim of aiding setting the mandatory 'clinsig' form field
|
197
205
|
|
@@ -203,6 +211,8 @@ def _variant_classification(var_obj):
|
|
203
211
|
"""
|
204
212
|
if "acmg_classification" in var_obj:
|
205
213
|
return ACMG_MAP[var_obj["acmg_classification"]]
|
214
|
+
elif "ccv_classification" in var_obj:
|
215
|
+
return CCV_MAP[var_obj["ccv_classification"]]
|
206
216
|
elif "manual_rank" in var_obj:
|
207
217
|
return MANUAL_RANK_OPTIONS[var_obj["manual_rank"]]["name"]
|
208
218
|
|
@@ -255,7 +265,9 @@ def _set_conditions(clinvar_var: dict, form: ImmutableMultiDict):
|
|
255
265
|
[f"{condition_prefix}{condition_id}" for condition_id in form.getlist("conditions")]
|
256
266
|
)
|
257
267
|
if bool(form.get("multiple_condition_explanation")):
|
258
|
-
clinvar_var["explanation_for_multiple_conditions"] = form
|
268
|
+
clinvar_var["explanation_for_multiple_conditions"] = form.get(
|
269
|
+
"multiple_condition_explanation"
|
270
|
+
)
|
259
271
|
|
260
272
|
|
261
273
|
def parse_variant_form_fields(form):
|
@@ -378,20 +390,22 @@ def update_clinvar_submission_status(request_obj, institute_id, submission_id):
|
|
378
390
|
send_api_submission(institute_id, submission_id, submitter_key)
|
379
391
|
|
380
392
|
|
381
|
-
def json_api_submission(submission_id):
|
382
|
-
"""
|
383
|
-
|
393
|
+
def json_api_submission(submission_id: str) -> Tuple[int, dict]:
|
394
|
+
"""Returns an integer and a json submission object as a dict. If the submission is of type "oncogenocity" there is no need for conversion and can be used as is.
|
395
|
+
Germline submission objects (Variant and Casedata database documents) are converted to a json submission using
|
396
|
+
the PreClinVar service.
|
397
|
+
"""
|
384
398
|
|
385
|
-
|
386
|
-
submission_id
|
399
|
+
json_submission: dict = store.get_onc_submission_json(
|
400
|
+
submission=submission_id
|
401
|
+
) # Oncogenocity submissions are already saved in the desired format
|
402
|
+
if json_submission:
|
403
|
+
return 200, json_submission
|
387
404
|
|
388
|
-
|
389
|
-
|
390
|
-
"""
|
391
|
-
variant_data = store.clinvar_objs(submission_id, "variant_data")
|
392
|
-
obs_data = store.clinvar_objs(submission_id, "case_data")
|
405
|
+
variant_data: list = store.clinvar_objs(submission_id, "variant_data")
|
406
|
+
obs_data: list = store.clinvar_objs(submission_id, "case_data")
|
393
407
|
|
394
|
-
if
|
408
|
+
if not variant_data or not obs_data:
|
395
409
|
return (400, "Submission must contain both Variant and CaseData info")
|
396
410
|
|
397
411
|
# Retrieve eventual assertion criteria for the submission
|
@@ -399,7 +413,7 @@ def json_api_submission(submission_id):
|
|
399
413
|
|
400
414
|
# Retrieve genome build for the case submitted
|
401
415
|
case_obj = store.case(case_id=variant_data[0].get("case_id")) or {"genome_build": 37}
|
402
|
-
extra_params["assembly"] = "GRCh37" if "37" in
|
416
|
+
extra_params["assembly"] = "GRCh37" if "37" in get_case_genome_build(case_obj) else "GRCh38"
|
403
417
|
|
404
418
|
def _write_file(afile, header, lines): # Write temp CSV file
|
405
419
|
writes = csv.writer(afile, delimiter=",", quoting=csv.QUOTE_ALL)
|
@@ -568,6 +582,24 @@ def _clinvar_submission_header(submission_objs, csv_type):
|
|
568
582
|
return custom_header
|
569
583
|
|
570
584
|
|
585
|
+
def add_clinvar_events(institute_obj: dict, case_obj: dict, variant_id: str):
|
586
|
+
"""Create case and variant-related events when a variants gets added to a ClinVar submission object."""
|
587
|
+
|
588
|
+
variant_obj: dict = store.variant(document_id=variant_id)
|
589
|
+
user_obj: dict = store.user(user_id=current_user._id)
|
590
|
+
for category in ["case", "variant"]:
|
591
|
+
store.create_event(
|
592
|
+
institute=institute_obj,
|
593
|
+
case=case_obj,
|
594
|
+
user=user_obj,
|
595
|
+
link=f"/{institute_obj['_id']}/{case_obj['display_name']}/{variant_obj['_id']}",
|
596
|
+
category=category,
|
597
|
+
verb="clinvar_add",
|
598
|
+
variant=variant_obj,
|
599
|
+
subject=variant_obj["display_name"],
|
600
|
+
)
|
601
|
+
|
602
|
+
|
571
603
|
def add_variant_to_submission(institute_obj: dict, case_obj: dict, form: ImmutableMultiDict):
|
572
604
|
"""It is invoked by the 'clinvar_save' endpoint. Adds one variant with eventual CaseData observations to an open (or new) ClinVar submission."""
|
573
605
|
|
@@ -575,8 +607,8 @@ def add_variant_to_submission(institute_obj: dict, case_obj: dict, form: Immutab
|
|
575
607
|
casedata_list: List[dict] = parse_casedata_form_fields(form)
|
576
608
|
institute_id = institute_obj["_id"]
|
577
609
|
|
578
|
-
# retrieve or create an open ClinVar submission:
|
579
|
-
subm: dict = store.
|
610
|
+
# retrieve or create an open germline ClinVar submission:
|
611
|
+
subm: dict = store.get_open_germline_clinvar_submission(institute_id, current_user._id)
|
580
612
|
# save submission objects in submission:
|
581
613
|
result: Optional[dict] = store.add_to_submission(subm["_id"], (variant_data, casedata_list))
|
582
614
|
if result:
|
@@ -584,20 +616,11 @@ def add_variant_to_submission(institute_obj: dict, case_obj: dict, form: Immutab
|
|
584
616
|
"An open ClinVar submission was updated correctly with submitted data",
|
585
617
|
"success",
|
586
618
|
)
|
619
|
+
|
587
620
|
# Create user-related events
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
store.create_event(
|
592
|
-
institute=institute_obj,
|
593
|
-
case=case_obj,
|
594
|
-
user=user_obj,
|
595
|
-
link=f"/{institute_id}/{case_obj['display_name']}/{variant_obj['_id']}",
|
596
|
-
category=category,
|
597
|
-
verb="clinvar_add",
|
598
|
-
variant=variant_obj,
|
599
|
-
subject=variant_obj["display_name"],
|
600
|
-
)
|
621
|
+
add_clinvar_events(
|
622
|
+
institute_obj=institute_obj, case_obj=case_obj, variant_id=form.get("linking_id")
|
623
|
+
)
|
601
624
|
|
602
625
|
|
603
626
|
def remove_item_from_submission(submission: str, object_type: str, subm_variant_id: str):
|
@@ -631,3 +654,161 @@ def remove_item_from_submission(submission: str, object_type: str, subm_variant_
|
|
631
654
|
variant=variant_obj,
|
632
655
|
subject=variant_obj["display_name"],
|
633
656
|
)
|
657
|
+
|
658
|
+
|
659
|
+
### ClinVar oncogenicity variants submissions controllers
|
660
|
+
|
661
|
+
|
662
|
+
def set_onc_clinvar_form(var_id: str, data: dict):
|
663
|
+
"""Adds form key/values to the form used in ClinVar to create a multistep submission page for an oncogenic variant."""
|
664
|
+
|
665
|
+
var_obj = store.variant(var_id)
|
666
|
+
if not var_obj:
|
667
|
+
return
|
668
|
+
|
669
|
+
var_obj["classification"] = _variant_classification(var_obj)
|
670
|
+
|
671
|
+
var_form = _populate_variant_form(var_obj, data["case"]) # variant-associated form
|
672
|
+
cdata_forms = _populate_case_data_form(var_obj, data["case"]) # CaseData form
|
673
|
+
variant_data = {
|
674
|
+
"var_id": var_id,
|
675
|
+
"var_obj": var_obj,
|
676
|
+
"var_form": var_form,
|
677
|
+
"cdata_forms": cdata_forms,
|
678
|
+
}
|
679
|
+
data["variant_data"] = variant_data
|
680
|
+
|
681
|
+
|
682
|
+
def _parse_onc_classification(onc_item: dict, form: ImmutableMultiDict):
|
683
|
+
"""Assigns an oncogenicity classification to a submission item based on the user form."""
|
684
|
+
onc_item["oncogenicityClassification"] = {
|
685
|
+
"oncogenicityClassificationDescription": form.get("onc_classification"),
|
686
|
+
"dateLastEvaluated": form.get("last_evaluated"),
|
687
|
+
}
|
688
|
+
if form.get("clinsig_comment"):
|
689
|
+
onc_item["oncogenicityClassification"]["comment"] = form.get("clinsig_comment")
|
690
|
+
|
691
|
+
|
692
|
+
def _parse_onc_assertion(onc_item: dict, form: ImmutableMultiDict):
|
693
|
+
"""Adds to an oncogenicity classification item the specifics of the user classification."""
|
694
|
+
|
695
|
+
if form.get("assertion_method_cit_id"):
|
696
|
+
onc_item["oncogenicityClassification"]["citation"] = [
|
697
|
+
{
|
698
|
+
"db": form.get("assertion_method_cit_db"),
|
699
|
+
"id": form.get("assertion_method_cit_id"),
|
700
|
+
}
|
701
|
+
]
|
702
|
+
|
703
|
+
|
704
|
+
def _parse_variant_set(onc_item: dict, form: ImmutableMultiDict):
|
705
|
+
"""Parse variant specifics from the ClinVar user form. It's an array but we support oonly one variant per oncogenic item."""
|
706
|
+
|
707
|
+
variant = {}
|
708
|
+
if form.get("tx_hgvs") not in [None, "Do not specify"]:
|
709
|
+
variant["hgvs"] = form["tx_hgvs"]
|
710
|
+
else: # Use coordinates
|
711
|
+
variant["chromosomeCoordinates"] = {
|
712
|
+
"assembly": form.get("assembly"),
|
713
|
+
"chromosome": "MT" if form.get("chromosome") == "M" else form.get("chromosome"),
|
714
|
+
"start": int(form.get("start")),
|
715
|
+
"stop": int(form.get("stop")),
|
716
|
+
"alternateAllele": form.get("alt"),
|
717
|
+
}
|
718
|
+
|
719
|
+
if form.get("gene_symbol"):
|
720
|
+
variant["gene"] = [{"symbol": form["gene_symbol"]}]
|
721
|
+
|
722
|
+
onc_item["variantSet"] = {"variant": [variant]}
|
723
|
+
|
724
|
+
|
725
|
+
def _parse_condition_set(onc_item: dict, form: ImmutableMultiDict):
|
726
|
+
"""Associate one or more phenotype conditions to a variant of a ClinVar submission."""
|
727
|
+
onc_item["conditionSet"] = {"condition": []}
|
728
|
+
selected_db = form.get("condition_type")
|
729
|
+
selected_conditions: List[str] = form.getlist("conditions")
|
730
|
+
for cond in selected_conditions:
|
731
|
+
onc_item["conditionSet"]["condition"].append({"db": selected_db, "id": cond})
|
732
|
+
if form.get("multiple_condition_explanation"):
|
733
|
+
onc_item["conditionSet"]["multipleConditionExplanation"] = form.get(
|
734
|
+
"multiple_condition_explanation"
|
735
|
+
)
|
736
|
+
|
737
|
+
|
738
|
+
def _parse_observations(onc_item: dict, form: ImmutableMultiDict):
|
739
|
+
"""Associates observations to a variant of a ClinVar submission."""
|
740
|
+
onc_item["observedIn"] = []
|
741
|
+
include_inds = form.getlist("include_ind")
|
742
|
+
ind_list = form.getlist("individual_id")
|
743
|
+
affected_status_list = form.getlist("affected_status")
|
744
|
+
allele_origin_list = form.getlist("allele_of_origin")
|
745
|
+
collection_method_list = form.getlist("collection_method")
|
746
|
+
somatic_fraction = form.getlist("somatic_allele_fraction")
|
747
|
+
somatic_in_normal = form.getlist("somatic_allele_in_normal")
|
748
|
+
|
749
|
+
for idx, ind_id in enumerate(ind_list):
|
750
|
+
if ind_id not in include_inds:
|
751
|
+
continue
|
752
|
+
|
753
|
+
obs = {
|
754
|
+
"alleleOrigin": allele_origin_list[idx],
|
755
|
+
"affectedStatus": affected_status_list[idx],
|
756
|
+
"collectionMethod": collection_method_list[idx],
|
757
|
+
"numberOfIndividuals": 1,
|
758
|
+
"presenceOfSomaticVariantInNormalTissue": somatic_in_normal[idx],
|
759
|
+
}
|
760
|
+
if somatic_fraction[idx]:
|
761
|
+
obs["somaticVariantAlleleFraction"] = int(somatic_fraction[idx])
|
762
|
+
|
763
|
+
onc_item["observedIn"].append(obs)
|
764
|
+
|
765
|
+
|
766
|
+
def parse_clinvar_onc_item(form: ImmutableMultiDict) -> dict:
|
767
|
+
"""Parse form fields for an oncogenic classication of a variant and converts it into the format extected by ClinVar (json dict)."""
|
768
|
+
onc_item = {"recordStatus": "novel"}
|
769
|
+
_parse_onc_classification(onc_item, form)
|
770
|
+
_parse_onc_assertion(onc_item, form)
|
771
|
+
_parse_variant_set(onc_item, form)
|
772
|
+
_parse_condition_set(onc_item, form)
|
773
|
+
_parse_observations(onc_item, form)
|
774
|
+
|
775
|
+
return onc_item
|
776
|
+
|
777
|
+
|
778
|
+
def add_onc_variant_to_submission(institute_obj: dict, case_obj: dict, form: ImmutableMultiDict):
|
779
|
+
"""Adds a somatic variant to a pre-existing open oncogeginicty seubmission. If the latter doesn't exists it creates it."""
|
780
|
+
|
781
|
+
onc_item: dict = parse_clinvar_onc_item(form) # The variant item to add to an open submission
|
782
|
+
|
783
|
+
# Add case specifics to the submission item
|
784
|
+
onc_item["institute_id"] = institute_obj["_id"]
|
785
|
+
onc_item["case_id"] = case_obj["_id"]
|
786
|
+
onc_item["case_name"] = case_obj.get("display_name", "_id")
|
787
|
+
onc_item["variant_id"] = form.get("linking_id")
|
788
|
+
|
789
|
+
# Validate oncogenicity item model
|
790
|
+
try:
|
791
|
+
OncogenicitySubmissionItem(**onc_item)
|
792
|
+
except ValidationError as ve:
|
793
|
+
LOG.error(ve)
|
794
|
+
flash(str(ve), "warning")
|
795
|
+
return
|
796
|
+
|
797
|
+
# retrieve or create an open ClinVar submission:
|
798
|
+
onc_subm: dict = store.get_open_onc_clinvar_submission(
|
799
|
+
institute_id=institute_obj["_id"], user_id=current_user._id
|
800
|
+
)
|
801
|
+
# Add variant to submission object
|
802
|
+
onc_subm.setdefault("oncogenicitySubmission", []).append(onc_item)
|
803
|
+
onc_subm["updated_at"] = datetime.now()
|
804
|
+
store.clinvar_submission_collection.find_one_and_replace({"_id": onc_subm["_id"]}, onc_subm)
|
805
|
+
|
806
|
+
# update case with submission info
|
807
|
+
add_clinvar_events(
|
808
|
+
institute_obj=institute_obj, case_obj=case_obj, variant_id=form.get("linking_id")
|
809
|
+
)
|
810
|
+
|
811
|
+
flash(
|
812
|
+
"An open ClinVar submission was updated correctly with the variant details.",
|
813
|
+
"success",
|
814
|
+
)
|
@@ -28,6 +28,12 @@ from scout.constants import (
|
|
28
28
|
GERMLINE_CLASSIF_TERMS,
|
29
29
|
MULTIPLE_CONDITION_EXPLANATION,
|
30
30
|
)
|
31
|
+
from scout.constants.clinvar import (
|
32
|
+
CITATION_DBS_API,
|
33
|
+
CONDITION_DBS_API,
|
34
|
+
ONCOGENIC_CLASSIF_TERMS,
|
35
|
+
PRESENCE_IN_NORMAL_TISSUE,
|
36
|
+
)
|
31
37
|
|
32
38
|
LOG = logging.getLogger(__name__)
|
33
39
|
|
@@ -47,6 +53,7 @@ class ClinVarVariantForm(FlaskForm):
|
|
47
53
|
linking_id = HiddenField()
|
48
54
|
ref = HiddenField()
|
49
55
|
alt = HiddenField()
|
56
|
+
assembly = HiddenField()
|
50
57
|
gene_symbol = StringField("Gene symbols, comma-separated")
|
51
58
|
inheritance_mode = SelectField(
|
52
59
|
"Inheritance model",
|
@@ -123,7 +130,7 @@ class CaseDataForm(FlaskForm):
|
|
123
130
|
Schema available here: https://github.com/Clinical-Genomics/preClinVar/blob/718905521590196dc84fd576bc43d9fac418b97a/preClinVar/resources/submission_schema.json#L288
|
124
131
|
"""
|
125
132
|
|
126
|
-
include_ind = BooleanField("Include
|
133
|
+
include_ind = BooleanField("Include")
|
127
134
|
individual_id = StringField("Individual ID")
|
128
135
|
linking_id = HiddenField()
|
129
136
|
affected_status = SelectField(
|
@@ -137,3 +144,33 @@ class CaseDataForm(FlaskForm):
|
|
137
144
|
default="clinical testing",
|
138
145
|
choices=[(item, item) for item in COLLECTION_METHOD],
|
139
146
|
)
|
147
|
+
somatic_allele_fraction = IntegerField("Somatic allele fraction")
|
148
|
+
somatic_allele_in_normal = SelectField( # Overriding default values
|
149
|
+
"Allele present in normal tissue",
|
150
|
+
default="absent",
|
151
|
+
choices=[(item, item) for item in PRESENCE_IN_NORMAL_TISSUE],
|
152
|
+
)
|
153
|
+
|
154
|
+
|
155
|
+
### Cancer variant - related forms
|
156
|
+
|
157
|
+
|
158
|
+
class CancerSNVariantForm(SNVariantForm):
|
159
|
+
"""Contains the form element to add a cancer variant to a ClinVar oncogenicity submission object."""
|
160
|
+
|
161
|
+
# Form step 1: Oncogenicity classification
|
162
|
+
record_status = HiddenField(default="novel")
|
163
|
+
onc_classification = SelectField(
|
164
|
+
"Oncogenicity classification",
|
165
|
+
choices=[(item, item) for item in ONCOGENIC_CLASSIF_TERMS],
|
166
|
+
)
|
167
|
+
assertion_method_cit_db = SelectField( # Overriding default values
|
168
|
+
"Citation DB",
|
169
|
+
default="clinical testing",
|
170
|
+
choices=[(item, item) for item in CITATION_DBS_API],
|
171
|
+
)
|
172
|
+
assertion_method_cit_id = StringField("Citation ID") # Overriding default values
|
173
|
+
|
174
|
+
condition_type = SelectField(
|
175
|
+
"Condition ID type", choices=[(item, item) for item in CONDITION_DBS_API]
|
176
|
+
)
|
@@ -126,12 +126,19 @@ padding: 0;
|
|
126
126
|
color: white;
|
127
127
|
text-transform: uppercase;
|
128
128
|
font-size: 12px;
|
129
|
-
width: 14.28%;
|
130
129
|
float: left;
|
131
130
|
position: relative;
|
132
131
|
letter-spacing: 1px;
|
133
132
|
}
|
134
133
|
|
134
|
+
#progressbar.steps-5 li {
|
135
|
+
width: 20%;
|
136
|
+
}
|
137
|
+
|
138
|
+
#progressbar.steps-7 li {
|
139
|
+
width: 14.28%;
|
140
|
+
}
|
141
|
+
|
135
142
|
#progressbar li:before {
|
136
143
|
content: counter(step);
|
137
144
|
counter-increment: step;
|