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.
Files changed (87) hide show
  1. scout/adapter/mongo/case.py +26 -122
  2. scout/adapter/mongo/clinvar.py +98 -32
  3. scout/adapter/mongo/event.py +0 -47
  4. scout/adapter/mongo/hgnc.py +7 -2
  5. scout/adapter/mongo/omics_variant.py +8 -0
  6. scout/adapter/mongo/variant_loader.py +12 -4
  7. scout/build/variant/variant.py +1 -0
  8. scout/commands/load/variants.py +1 -1
  9. scout/commands/update/user.py +87 -49
  10. scout/constants/__init__.py +4 -0
  11. scout/constants/clinvar.py +10 -0
  12. scout/constants/igv_tracks.py +6 -2
  13. scout/constants/phenotype.py +1 -0
  14. scout/constants/variant_tags.py +18 -0
  15. scout/demo/NIST.trgt.stranger.vcf.gz +0 -0
  16. scout/demo/NIST.trgt.stranger.vcf.gz.tbi +0 -0
  17. scout/demo/__init__.py +1 -0
  18. scout/load/hpo.py +8 -2
  19. scout/models/clinvar.py +86 -0
  20. scout/parse/variant/coordinates.py +5 -1
  21. scout/parse/variant/gene.py +5 -9
  22. scout/parse/variant/genotype.py +66 -42
  23. scout/parse/variant/variant.py +2 -0
  24. scout/server/app.py +71 -2
  25. scout/server/blueprints/alignviewers/controllers.py +8 -6
  26. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +4 -0
  27. scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
  28. scout/server/blueprints/cases/controllers.py +57 -29
  29. scout/server/blueprints/cases/templates/cases/case_report.html +28 -90
  30. scout/server/blueprints/cases/templates/cases/matchmaker.html +1 -1
  31. scout/server/blueprints/cases/templates/cases/phenotype.html +1 -1
  32. scout/server/blueprints/cases/templates/cases/utils.html +34 -53
  33. scout/server/blueprints/cases/views.py +32 -33
  34. scout/server/blueprints/clinvar/controllers.py +235 -54
  35. scout/server/blueprints/clinvar/form.py +38 -1
  36. scout/server/blueprints/clinvar/static/form_style.css +8 -1
  37. scout/server/blueprints/clinvar/templates/clinvar/clinvar_onc_submissions.html +200 -0
  38. scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +3 -2
  39. scout/server/blueprints/clinvar/templates/clinvar/components.html +198 -0
  40. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_onc_variant.html +187 -0
  41. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +9 -348
  42. scout/server/blueprints/clinvar/templates/clinvar/scripts.html +193 -0
  43. scout/server/blueprints/clinvar/views.py +90 -13
  44. scout/server/blueprints/diagnoses/controllers.py +4 -8
  45. scout/server/blueprints/diagnoses/templates/diagnoses/diagnoses.html +1 -1
  46. scout/server/blueprints/diagnoses/templates/diagnoses/disease_term.html +1 -1
  47. scout/server/blueprints/diagnoses/views.py +2 -2
  48. scout/server/blueprints/institutes/controllers.py +148 -75
  49. scout/server/blueprints/institutes/forms.py +1 -0
  50. scout/server/blueprints/institutes/templates/overview/cases.html +1 -1
  51. scout/server/blueprints/institutes/templates/overview/gene_variants.html +15 -6
  52. scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +28 -2
  53. scout/server/blueprints/institutes/templates/overview/utils.html +1 -1
  54. scout/server/blueprints/institutes/views.py +17 -4
  55. scout/server/blueprints/login/controllers.py +2 -1
  56. scout/server/blueprints/login/views.py +5 -2
  57. scout/server/blueprints/mme/templates/mme/mme_submissions.html +2 -2
  58. scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +2 -2
  59. scout/server/blueprints/omics_variants/views.py +2 -2
  60. scout/server/blueprints/phenotypes/controllers.py +15 -2
  61. scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +1 -1
  62. scout/server/blueprints/variant/controllers.py +11 -12
  63. scout/server/blueprints/variant/templates/variant/cancer-variant.html +2 -1
  64. scout/server/blueprints/variant/templates/variant/components.html +0 -1
  65. scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -1
  66. scout/server/blueprints/variant/templates/variant/utils.html +1 -1
  67. scout/server/blueprints/variant/templates/variant/variant.html +2 -2
  68. scout/server/blueprints/variant/templates/variant/variant_details.html +100 -84
  69. scout/server/blueprints/variant/utils.py +25 -0
  70. scout/server/blueprints/variants/controllers.py +11 -42
  71. scout/server/blueprints/variants/templates/variants/cancer-variants.html +5 -3
  72. scout/server/blueprints/variants/templates/variants/str-variants.html +4 -1
  73. scout/server/blueprints/variants/templates/variants/sv-variants.html +3 -3
  74. scout/server/blueprints/variants/templates/variants/utils.html +4 -0
  75. scout/server/blueprints/variants/templates/variants/variants.html +4 -4
  76. scout/server/blueprints/variants/views.py +9 -8
  77. scout/server/config.py +3 -0
  78. scout/server/extensions/beacon_extension.py +7 -2
  79. scout/server/extensions/clinvar_extension.py +2 -2
  80. scout/server/templates/bootstrap_global.html +11 -1
  81. scout/server/templates/layout.html +6 -1
  82. scout/server/utils.py +24 -3
  83. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/METADATA +1 -1
  84. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/RECORD +87 -81
  85. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/WHEEL +0 -0
  86. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/entry_points.txt +0 -0
  87. {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 CaseDataForm, SNVariantForm, SVariantForm
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 = str(case_obj.get("genome_build", "37"))
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
- var_form = SNVariantForm()
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(variant_obj, case_obj):
148
- """Populate the Flaskform associated to a variant
149
-
150
- Args:
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
- elif variant_obj["category"] in ["sv", "cancer_sv"]:
164
- var_form = _get_sv_var_form(variant_obj, case_obj)
165
- 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)
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["multiple_condition_explanation"]
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
- """Converts submission objects (Variant and Casedata database documents) to a json submission using
383
- 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
+ """
384
398
 
385
- Args:
386
- 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
387
404
 
388
- Returns:
389
- A tuple: code(int), conversion_res(dict) - corresponding to response.status and response.__dict__ from preClinVar
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 None in [variant_data, obs_data]:
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 str(case_obj.get("genome_build")) else "GRCh38"
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.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)
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
- variant_obj: dict = store.variant(document_id=variant_data.get("local_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_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 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;