scout-browser 4.102.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.
Files changed (59) hide show
  1. scout/adapter/mongo/case.py +26 -122
  2. scout/adapter/mongo/clinvar.py +91 -25
  3. scout/adapter/mongo/event.py +0 -47
  4. scout/adapter/mongo/variant_loader.py +7 -3
  5. scout/build/variant/variant.py +1 -0
  6. scout/commands/load/variants.py +1 -1
  7. scout/commands/update/user.py +87 -49
  8. scout/constants/__init__.py +3 -0
  9. scout/constants/clinvar.py +10 -0
  10. scout/constants/variant_tags.py +18 -0
  11. scout/demo/NIST.trgt.stranger.vcf.gz +0 -0
  12. scout/demo/NIST.trgt.stranger.vcf.gz.tbi +0 -0
  13. scout/demo/__init__.py +1 -0
  14. scout/models/clinvar.py +86 -0
  15. scout/parse/variant/coordinates.py +5 -1
  16. scout/parse/variant/gene.py +5 -9
  17. scout/parse/variant/genotype.py +66 -42
  18. scout/parse/variant/variant.py +2 -0
  19. scout/server/app.py +71 -2
  20. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +2 -0
  21. scout/server/blueprints/cases/controllers.py +1 -1
  22. scout/server/blueprints/cases/templates/cases/case_report.html +19 -2
  23. scout/server/blueprints/cases/templates/cases/utils.html +8 -29
  24. scout/server/blueprints/clinvar/controllers.py +233 -53
  25. scout/server/blueprints/clinvar/form.py +38 -1
  26. scout/server/blueprints/clinvar/static/form_style.css +8 -1
  27. scout/server/blueprints/clinvar/templates/clinvar/clinvar_onc_submissions.html +200 -0
  28. scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +3 -2
  29. scout/server/blueprints/clinvar/templates/clinvar/components.html +198 -0
  30. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_onc_variant.html +187 -0
  31. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +9 -348
  32. scout/server/blueprints/clinvar/templates/clinvar/scripts.html +193 -0
  33. scout/server/blueprints/clinvar/views.py +90 -13
  34. scout/server/blueprints/institutes/controllers.py +44 -5
  35. scout/server/blueprints/institutes/forms.py +1 -0
  36. scout/server/blueprints/institutes/templates/overview/gene_variants.html +15 -6
  37. scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +28 -2
  38. scout/server/blueprints/institutes/templates/overview/utils.html +1 -1
  39. scout/server/blueprints/institutes/views.py +17 -4
  40. scout/server/blueprints/mme/templates/mme/mme_submissions.html +2 -2
  41. scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +2 -2
  42. scout/server/blueprints/variant/controllers.py +1 -1
  43. scout/server/blueprints/variant/templates/variant/cancer-variant.html +2 -1
  44. scout/server/blueprints/variant/templates/variant/components.html +0 -1
  45. scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -1
  46. scout/server/blueprints/variant/templates/variant/variant.html +2 -2
  47. scout/server/blueprints/variant/templates/variant/variant_details.html +32 -24
  48. scout/server/blueprints/variants/templates/variants/cancer-variants.html +5 -3
  49. scout/server/blueprints/variants/templates/variants/str-variants.html +4 -1
  50. scout/server/blueprints/variants/templates/variants/sv-variants.html +3 -3
  51. scout/server/blueprints/variants/templates/variants/utils.html +4 -0
  52. scout/server/blueprints/variants/templates/variants/variants.html +4 -4
  53. scout/server/extensions/clinvar_extension.py +2 -2
  54. scout/server/templates/layout.html +1 -1
  55. {scout_browser-4.102.0.dist-info → scout_browser-4.103.0.dist-info}/METADATA +1 -1
  56. {scout_browser-4.102.0.dist-info → scout_browser-4.103.0.dist-info}/RECORD +59 -53
  57. {scout_browser-4.102.0.dist-info → scout_browser-4.103.0.dist-info}/WHEEL +0 -0
  58. {scout_browser-4.102.0.dist-info → scout_browser-4.103.0.dist-info}/entry_points.txt +0 -0
  59. {scout_browser-4.102.0.dist-info → scout_browser-4.103.0.dist-info}/licenses/LICENSE +0 -0
@@ -617,42 +617,21 @@
617
617
  </li>
618
618
  {% endfor %}
619
619
  </ul>
620
- {{ clinvar_vars_summary(suspects, case, institute) }}
621
620
  </div>
622
621
  </div>
623
622
  {% endmacro %}
624
623
 
625
624
  {% macro clinvar_var_status(variant, case, institute) %}
626
- {% if variant._id and variant.category != 'str' %}
625
+ {% if case.clinvar_variants and variant._id in case.clinvar_variants.keys() %}
626
+ (included in a ClinVar submission)
627
+ {% elif variant._id and variant.category in ["snv", "sv"] %}
627
628
  <form id="clinvar_submit" class="d-flex justify-content-center" action="{{ url_for('clinvar.clinvar_add_variant', institute_id=institute._id, case_name=case.display_name) }}" method="POST">
628
- {% if case.clinvar_variants and variant._id in case.clinvar_variants.keys() %}
629
- (included in ClinVar submission)
630
- {% elif variant.category in ('cancer', 'cancer_sv') %}
631
- <button data-bs-toggle='tooltip' data-bs-placement="bottom" title="Submission of somatic variants from Scout is temporarily suspended. We are working to harmonize submissions with changes introduced by ClinVar for this variant category." disabled >Add to ClinVar submission</button>
632
- {% elif variant.category in ('snv', 'sv') %}
633
- <button type="submit" name="var_id" value="{{variant._id}}" class="btn btn-secondary btn-sm" style="float: right;">Add to ClinVar submission</button>
634
- {% endif %}
629
+ <button type="submit" name="var_id" value="{{variant._id}}" class="btn btn-secondary btn-sm" style="float: right;">Add to ClinVar submission</button>
630
+ </form>
631
+ {% elif variant.category in ('cancer') %}
632
+ <form id="clinvar_onc_submit" class="d-flex justify-content-center" action="{{ url_for('clinvar.clinvar_add_onc_variant', institute_id=institute._id, case_name=case.display_name) }}" method="POST">
633
+ <button type="submit" name="var_id" value="{{variant._id}}" class="btn btn-secondary btn-sm" style="float: right;">Add to ClinVar submission</button>
635
634
  </form>
636
- {% endif %}
637
- {% endmacro %}
638
-
639
- {% macro clinvar_vars_summary(suspects, case, institute) %}
640
- {% if case.clinvar_variants %}
641
- {% if case.clinvar_variants_not_in_suspects | length >0 %}
642
- <div class="d-flex align-content-center flex-wrap">
643
- <div class="col-auto d-flex align-items-center wrap">
644
- <h6 class="m-2">&nbsp;Variants present in a ClinVar submission, no longer pinned:</h6>
645
- </div>
646
- {% for entry in case.clinvar_variants_not_in_suspects %}
647
- <div class="m-2">{{ pretty_link_variant(entry, case) }}</div>
648
- {% endfor %}
649
- </div>
650
- {% endif %}
651
- <div class="card-footer d-flex justify-content-center pb-0" style="background-color:inherit;">
652
- <a href="{{url_for('clinvar.clinvar_submissions', institute_id=institute._id)}}"
653
- class="btn btn-secondary btn-sm text-white mx-3" target="_blank" rel="noopener noreferrer">View ClinVar
654
- submissions <i class="fas fa-arrow-circle-right"></i></a>
655
- </div>
656
635
  {% endif %}
657
636
  {% endmacro %}
658
637
 
@@ -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,14 +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
22
- from scout.server.utils import get_case_genome_build
24
+ from scout.server.utils import get_case_genome_build, safe_redirect_back
23
25
  from scout.utils.hgvs import validate_hgvs
24
26
  from scout.utils.scout_requests import fetch_refseq_version
25
27
 
26
- from .form import CaseDataForm, SNVariantForm, SVariantForm
28
+ from .form import (
29
+ CancerSNVariantForm,
30
+ CaseDataForm,
31
+ SNVariantForm,
32
+ SVariantForm,
33
+ )
27
34
 
28
35
  LOG = logging.getLogger(__name__)
29
36
 
@@ -76,6 +83,7 @@ def _set_var_form_common_fields(var_form, variant_obj, case_obj):
76
83
  var_form.case_id.data = case_obj["_id"]
77
84
  var_form.local_id.data = variant_obj["_id"]
78
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"
79
87
  var_form.chromosome.data = variant_obj.get("chromosome")
80
88
  var_form.ref.data = variant_obj.get("reference")
81
89
  var_form.alt.data = variant_obj.get("alternative")
@@ -101,7 +109,7 @@ def _set_var_form_common_fields(var_form, variant_obj, case_obj):
101
109
  ]
102
110
 
103
111
 
104
- def _get_snv_var_form(variant_obj, case_obj):
112
+ def _get_snv_var_form(variant_obj: dict, case_obj: dict):
105
113
  """Sets up values for a SNV variant form
106
114
  Args:
107
115
  variant_obj(dict) scout.models.Variant
@@ -110,7 +118,10 @@ def _get_snv_var_form(variant_obj, case_obj):
110
118
  Returns:
111
119
  var_form(scout.server.blueprints.clinvar.form.SNVariantForm)
112
120
  """
113
- var_form = SNVariantForm()
121
+ if case_obj.get("track") == "cancer":
122
+ var_form = CancerSNVariantForm()
123
+ else:
124
+ var_form = SNVariantForm()
114
125
  _set_var_form_common_fields(var_form, variant_obj, case_obj)
115
126
  var_form.tx_hgvs.choices = _get_var_tx_hgvs(case_obj, variant_obj)
116
127
  var_ids = variant_obj.get("dbsnp_id") or ""
@@ -145,25 +156,18 @@ def _get_sv_var_form(variant_obj, case_obj):
145
156
  return var_form
146
157
 
147
158
 
148
- def _populate_variant_form(variant_obj, case_obj):
149
- """Populate the Flaskform associated to a variant
150
-
151
- Args:
152
- variant_obj(dict) scout.models.Variant
153
- case_obj(dict) scout.models.Case
154
-
155
- Return:
156
- var_form(scout.server.blueprints.clinvar.form.SVariantForm or
157
- scout.server.blueprints.clinvar.form.SNVariantForm )
158
-
159
- """
160
- if variant_obj["category"] in ["snv", "cancer"]:
161
- var_form = _get_snv_var_form(variant_obj, case_obj)
162
- 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."""
163
163
 
164
- elif variant_obj["category"] in ["sv", "cancer_sv"]:
165
- var_form = _get_sv_var_form(variant_obj, case_obj)
166
- var_form.category.data = "sv"
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)
167
171
 
168
172
  return var_form
169
173
 
@@ -188,11 +192,14 @@ def _populate_case_data_form(variant_obj, case_obj):
188
192
  ind_form.include_ind.data = affected
189
193
  ind_form.individual_id.data = ind.get("display_name")
190
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
+ )
191
198
  cdata_form_list.append(ind_form)
192
199
  return cdata_form_list
193
200
 
194
201
 
195
- def _variant_classification(var_obj):
202
+ def _variant_classification(var_obj: dict):
196
203
  """Set a 'classified_as' key/header_value in the variant object, to be displayed on
197
204
  form page with the aim of aiding setting the mandatory 'clinsig' form field
198
205
 
@@ -204,6 +211,8 @@ def _variant_classification(var_obj):
204
211
  """
205
212
  if "acmg_classification" in var_obj:
206
213
  return ACMG_MAP[var_obj["acmg_classification"]]
214
+ elif "ccv_classification" in var_obj:
215
+ return CCV_MAP[var_obj["ccv_classification"]]
207
216
  elif "manual_rank" in var_obj:
208
217
  return MANUAL_RANK_OPTIONS[var_obj["manual_rank"]]["name"]
209
218
 
@@ -256,7 +265,9 @@ def _set_conditions(clinvar_var: dict, form: ImmutableMultiDict):
256
265
  [f"{condition_prefix}{condition_id}" for condition_id in form.getlist("conditions")]
257
266
  )
258
267
  if bool(form.get("multiple_condition_explanation")):
259
- clinvar_var["explanation_for_multiple_conditions"] = form["multiple_condition_explanation"]
268
+ clinvar_var["explanation_for_multiple_conditions"] = form.get(
269
+ "multiple_condition_explanation"
270
+ )
260
271
 
261
272
 
262
273
  def parse_variant_form_fields(form):
@@ -379,20 +390,22 @@ def update_clinvar_submission_status(request_obj, institute_id, submission_id):
379
390
  send_api_submission(institute_id, submission_id, submitter_key)
380
391
 
381
392
 
382
- def json_api_submission(submission_id):
383
- """Converts submission objects (Variant and Casedata database documents) to a json submission using
384
- the PreClinVar service
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
+ """
385
398
 
386
- Args:
387
- submission_id(str): the database id of a clinvar submission
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
388
404
 
389
- Returns:
390
- A tuple: code(int), conversion_res(dict) - corresponding to response.status and response.__dict__ from preClinVar
391
- """
392
- variant_data = store.clinvar_objs(submission_id, "variant_data")
393
- 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")
394
407
 
395
- if None in [variant_data, obs_data]:
408
+ if not variant_data or not obs_data:
396
409
  return (400, "Submission must contain both Variant and CaseData info")
397
410
 
398
411
  # Retrieve eventual assertion criteria for the submission
@@ -569,6 +582,24 @@ def _clinvar_submission_header(submission_objs, csv_type):
569
582
  return custom_header
570
583
 
571
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
+
572
603
  def add_variant_to_submission(institute_obj: dict, case_obj: dict, form: ImmutableMultiDict):
573
604
  """It is invoked by the 'clinvar_save' endpoint. Adds one variant with eventual CaseData observations to an open (or new) ClinVar submission."""
574
605
 
@@ -576,8 +607,8 @@ def add_variant_to_submission(institute_obj: dict, case_obj: dict, form: Immutab
576
607
  casedata_list: List[dict] = parse_casedata_form_fields(form)
577
608
  institute_id = institute_obj["_id"]
578
609
 
579
- # retrieve or create an open ClinVar submission:
580
- subm: dict = store.get_open_clinvar_submission(institute_id, current_user._id)
610
+ # retrieve or create an open germline ClinVar submission:
611
+ subm: dict = store.get_open_germline_clinvar_submission(institute_id, current_user._id)
581
612
  # save submission objects in submission:
582
613
  result: Optional[dict] = store.add_to_submission(subm["_id"], (variant_data, casedata_list))
583
614
  if result:
@@ -585,20 +616,11 @@ def add_variant_to_submission(institute_obj: dict, case_obj: dict, form: Immutab
585
616
  "An open ClinVar submission was updated correctly with submitted data",
586
617
  "success",
587
618
  )
619
+
588
620
  # Create user-related events
589
- variant_obj: dict = store.variant(document_id=variant_data.get("local_id"))
590
- user_obj: dict = store.user(user_id=current_user._id)
591
- for category in ["case", "variant"]:
592
- store.create_event(
593
- institute=institute_obj,
594
- case=case_obj,
595
- user=user_obj,
596
- link=f"/{institute_id}/{case_obj['display_name']}/{variant_obj['_id']}",
597
- category=category,
598
- verb="clinvar_add",
599
- variant=variant_obj,
600
- subject=variant_obj["display_name"],
601
- )
621
+ add_clinvar_events(
622
+ institute_obj=institute_obj, case_obj=case_obj, variant_id=form.get("linking_id")
623
+ )
602
624
 
603
625
 
604
626
  def remove_item_from_submission(submission: str, object_type: str, subm_variant_id: str):
@@ -632,3 +654,161 @@ def remove_item_from_submission(submission: str, object_type: str, subm_variant_
632
654
  variant=variant_obj,
633
655
  subject=variant_obj["display_name"],
634
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 individual")
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;