scout-browser 4.80__py3-none-any.whl → 4.82.1__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 (62) hide show
  1. scout/__version__.py +1 -1
  2. scout/adapter/mongo/disease_terms.py +5 -2
  3. scout/adapter/mongo/query.py +23 -11
  4. scout/adapter/mongo/variant.py +2 -2
  5. scout/build/managed_variant.py +12 -1
  6. scout/build/variant/genotype.py +2 -0
  7. scout/build/variant/variant.py +5 -0
  8. scout/constants/__init__.py +1 -0
  9. scout/constants/clinvar.py +1 -1
  10. scout/constants/indexes.py +10 -0
  11. scout/constants/query_terms.py +3 -1
  12. scout/models/variant/variant.py +1 -0
  13. scout/parse/variant/frequency.py +56 -54
  14. scout/parse/variant/genotype.py +89 -15
  15. scout/parse/variant/transcript.py +17 -9
  16. scout/parse/variant/variant.py +12 -0
  17. scout/server/app.py +6 -3
  18. scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
  19. scout/server/blueprints/cases/controllers.py +2 -57
  20. scout/server/blueprints/cases/templates/cases/case_report.html +212 -190
  21. scout/server/blueprints/cases/templates/cases/chanjo2_form.html +47 -0
  22. scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +4 -4
  23. scout/server/blueprints/cases/templates/cases/gene_panel.html +17 -23
  24. scout/server/blueprints/cases/templates/cases/utils.html +3 -1
  25. scout/server/blueprints/cases/views.py +0 -22
  26. scout/server/blueprints/clinvar/controllers.py +3 -3
  27. scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +29 -2
  28. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +36 -18
  29. scout/server/blueprints/clinvar/views.py +13 -1
  30. scout/server/blueprints/diagnoses/controllers.py +2 -0
  31. scout/server/blueprints/institutes/controllers.py +76 -38
  32. scout/server/blueprints/institutes/templates/overview/cases.html +54 -42
  33. scout/server/blueprints/managed_variants/templates/managed_variants/managed_variants.html +1 -1
  34. scout/server/blueprints/managed_variants/views.py +2 -4
  35. scout/server/blueprints/panels/templates/panels/panel.html +8 -7
  36. scout/server/blueprints/panels/templates/panels/panel_pdf_case_hits.html +2 -2
  37. scout/server/blueprints/panels/templates/panels/panel_pdf_simple.html +3 -3
  38. scout/server/blueprints/panels/views.py +2 -11
  39. scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +3 -2
  40. scout/server/blueprints/variant/controllers.py +3 -2
  41. scout/server/blueprints/variant/templates/variant/components.html +1 -1
  42. scout/server/blueprints/variant/templates/variant/utils.html +3 -1
  43. scout/server/blueprints/variant/templates/variant/variant.html +20 -15
  44. scout/server/blueprints/variant/templates/variant/variant_details.html +78 -26
  45. scout/server/blueprints/variant/utils.py +9 -13
  46. scout/server/blueprints/variants/controllers.py +32 -3
  47. scout/server/blueprints/variants/forms.py +15 -1
  48. scout/server/blueprints/variants/templates/variants/components.html +55 -0
  49. scout/server/blueprints/variants/templates/variants/fusion-variants.html +3 -50
  50. scout/server/blueprints/variants/templates/variants/str-variants.html +8 -5
  51. scout/server/blueprints/variants/templates/variants/utils.html +57 -31
  52. scout/server/blueprints/variants/templates/variants/variants.html +1 -1
  53. scout/server/blueprints/variants/utils.py +7 -10
  54. scout/server/extensions/clinvar_extension.py +10 -2
  55. scout/server/templates/report_base.html +3 -3
  56. scout/server/templates/utils.html +2 -2
  57. {scout_browser-4.80.dist-info → scout_browser-4.82.1.dist-info}/METADATA +6 -5
  58. {scout_browser-4.80.dist-info → scout_browser-4.82.1.dist-info}/RECORD +62 -61
  59. {scout_browser-4.80.dist-info → scout_browser-4.82.1.dist-info}/LICENSE +0 -0
  60. {scout_browser-4.80.dist-info → scout_browser-4.82.1.dist-info}/WHEEL +0 -0
  61. {scout_browser-4.80.dist-info → scout_browser-4.82.1.dist-info}/entry_points.txt +0 -0
  62. {scout_browser-4.80.dist-info → scout_browser-4.82.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,47 @@
1
+ {% macro chanjo2_report_form(institute_obj, case_obj, panel_name, report_type, hgnc_ids, link_text) %}
2
+
3
+ {% set form_name = "chanjo2_"+panel_name+"_"+report_type %}
4
+ {% set form_action = config.CHANJO2_URL+"/"+report_type %}
5
+ {% set build = "GRCh38" if "38" in case_obj.get("genome_build", "37") else "GRCh37" %}
6
+ {% set default_level = institute_obj.coverage_cutoff %}
7
+ {% set interval_type = "genes" %} <!-- wgs analysis as default -->
8
+ {% set samples = [] %}
9
+ {% set analysis_types = [] %}
10
+
11
+ <!-- set sample dictionaries -->
12
+
13
+ {% for ind in case_obj.individuals %}
14
+ {% if ind.d4_file %}
15
+ {% set sample = {
16
+ "name" : ind.display_name,
17
+ "coverage_file_path" : ind.d4_file,
18
+ "case_name" : case_obj.display_name,
19
+ "analysis_date" : case_obj.analysis_date.strftime("%FT%T.%f")[:-3] ~ "Z" }
20
+ %}
21
+ {% do samples.append( sample ) %}
22
+ {% do analysis_types.append( ind.analysis_type ) %}
23
+ {% endif %}
24
+ {% endfor %}
25
+
26
+ {% if "wes" in analysis_types %}
27
+ {% set interval_type = "exons" %}
28
+ {% elif "wts" in analysis_types %}
29
+ {% set interval_type = "transcripts" %}
30
+ {% endif %}
31
+
32
+ <form name="{{form_name}}" method="post" action="{{form_action}}" target="_blank" rel="noopener">
33
+ <input type="hidden" id="build" name="build" value="{{build}}">
34
+ <input type="hidden" id="default_level" name="default_level" value="{{default_level}}">
35
+ <input type="hidden" id="interval_type" name="interval_type" value="{{interval_type}}">
36
+ <input type="hidden" id="panel_name" name="panel_name" value="{{panel_name}}">
37
+ <input type="hidden" id="case_display_name" name="case_display_name" value="{{case_obj.display_name}}">
38
+ <input type="hidden" id="hgnc_gene_ids" name="hgnc_gene_ids" value={{hgnc_ids}}>
39
+ <input type="hidden" id="samples" name="samples" value="{{samples|safe}}">
40
+ {% if panel_name == "HPO Panel" %}
41
+ <button type="submit" class="btn btn-secondary btn-sm text-white"> Coverage {{report_type}} (Chanjo2) </button>
42
+ {% else %}
43
+ <a class="link-primary" onclick="this.closest('form').submit();return false;">{{link_text}}</a>
44
+ {% endif %}
45
+ </form>
46
+
47
+ {% endmacro %}
@@ -1,3 +1,5 @@
1
+ {% from "cases/chanjo2_form.html" import chanjo2_report_form %}
2
+
1
3
  {% macro action_bar(institute, case, causatives, collaborators, current_user, report_types, has_rna_tracks) %}
2
4
  <!-- Collapsible Sidebar, Based on https://www.codeply.com/go/LFd2SEMECH -->
3
5
  <div id="sidebar-container" class="sidebar-expanded d-none d-md-block"><!-- d-* hiddens the Sidebar in smaller devices. Its itens can be kept on the Navbar 'Menu' -->
@@ -84,13 +86,11 @@
84
86
  </div>
85
87
 
86
88
  <!-- If connected to a chanjo or chanjo2 instance, display coverage report -->
87
- {% if case.chanjo2_coverage %}
89
+ {% if case.chanjo2_coverage and case.default_genes %}
88
90
  <div href="#" class="bg-dark list-group-item text-white">
89
91
  <div class="d-flex flex-row flex-fill bd-highlight">
90
92
  <span class="menu-collapsed">Coverage (chanjo2)</span>
91
- <a href="{{ url_for('cases.chanjo2_coverage_report', institute_id=institute._id, case_name=case.display_name, panel_name=case.panel_names|join(', '), report_type='report') }}" target="_blank" rel="noopener">
92
- <span class="fa fa-link"></span>
93
- </a>
93
+ {{ chanjo2_report_form(institute, case, case.panel_names|join(', '), 'report', case.default_genes|join(','), "<span class='fa fa-link'></span>"|safe ) }} <!--chanjo2 report-->
94
94
  </div>
95
95
  </div>
96
96
  {% endif %}
@@ -1,3 +1,5 @@
1
+ {% from "cases/chanjo2_form.html" import chanjo2_report_form %}
2
+
1
3
  {% macro genepanels_table(case, institute) %}
2
4
  <div class="card panel-default">
3
5
  <div class="panel-heading">Gene panels</div>
@@ -130,8 +132,8 @@
130
132
  </div>
131
133
  </div>
132
134
 
133
- <div class="row mt-2"> <!-- Show variants in dynamic gene list -->
134
- <div class="row">
135
+ <div>
136
+ <div class="row"> <!-- Show variants in dynamic gene list -->
135
137
  <form action="{{ url_for('cases.update_clinical_filter_hpo', institute_id=institute._id, case_name=case.display_name)+'#hpo_clinical_filter' }}" method="POST">
136
138
  <div class="form-check form-switch">
137
139
  <input type="checkbox" class="form-check-input" id="hpo_clinical_filter" onChange="this.form.submit()" name="hpo_clinical_filter"{% if case.hpo_clinical_filter %}checked{% endif %}>
@@ -199,41 +201,33 @@
199
201
  <div class="row mt-1">
200
202
  <div class="btn-group btn-group-sm">
201
203
  {% if case.chanjo_coverage %}
202
- <button class="btn btn-secondary btn-sm text-white" onClick="document.getElementById('hpo-report-form').submit();">Coverage report</button>
203
- <a href="#section" onClick="document.getElementById('hpo-report-form').submit();" />
204
- <form id="hpo-report-form" action="{{ url_for('report.report', sample_id=case.individuals|map(attribute='individual_id')|list, panel_name="HPO panel", level=institute.coverage_cutoff) }}" method="POST" target="_blank">
204
+ <a class="btn btn-secondary btn-sm text-white" href="#section" onClick="document.getElementById('hpo-report-form').submit();" >Coverage report</a>
205
+ <form id="hpo-report-form" action="{{ url_for('report.report', sample_id=case.individuals|map(attribute='individual_id')|list, panel_name='HPO panel', level=institute.coverage_cutoff) }}" method="POST" target="_blank">
205
206
  <input type="hidden" name="gene_ids" value="{{ case.dynamic_gene_list|map(attribute='hgnc_id')|join(',') }}">
206
207
  </form>
207
208
 
208
- <button class="btn btn-secondary btn-sm text-white" onClick="document.getElementById('hpo-overview-form').submit();">Coverage overview</button>
209
- <a href="#section" onClick="document.getElementById('hpo-overview-form').submit();" />
210
- <form id="hpo-overview-form" action="{{ url_for('report.genes', sample_id=case.individuals|map(attribute='individual_id')|list, panel_name="HPO panel", level=institute.coverage_cutoff) }}" method="POST" target="_blank">
209
+ <a class="btn btn-secondary btn-sm text-white" href="#section" onClick="document.getElementById('hpo-overview-form').submit();" >Coverage overview</a>
210
+ <form id="hpo-overview-form" action="{{ url_for('report.genes', sample_id=case.individuals|map(attribute='individual_id')|list, panel_name='HPO panel', level=institute.coverage_cutoff) }}" method="POST" target="_blank">
211
211
  <input type="hidden" name="gene_ids" value="{{ case.dynamic_gene_list|map(attribute='hgnc_id')|join(',') }}">
212
212
  </form>
213
213
  {% endif %}
214
214
 
215
215
  {% if case.chanjo2_coverage %}
216
- <a class="btn btn-secondary btn-sm text-white"
217
- href="{{ url_for('cases.chanjo2_coverage_report', institute_id=institute._id,
218
- case_name=case.display_name, panel_name='HPO Panel', report_type='report') }}" target="_blank" rel="noopener">
219
- Coverage report (Chanjo2)
220
- </a>
221
-
222
- <a class="btn btn-secondary btn-sm text-white"
223
- href="{{ url_for('cases.chanjo2_coverage_report', institute_id=institute._id,
224
- case_name=case.display_name, panel_name='HPO Panel', report_type='overview') }}" target="_blank" rel="noopener">
225
- Coverage overview (Chanjo2)
226
- </a>
216
+ {{ chanjo2_report_form(institute, case, "HPO Panel", 'report', case.dynamic_gene_list|map(attribute='hgnc_id')|join(',')) }} <!--chanjo2 report-->
217
+ {{ chanjo2_report_form(institute, case, "HPO Panel", 'overview', case.dynamic_gene_list|map(attribute='hgnc_id')|join(',')) }} <!--chanjo2 genes overview -->
227
218
  {% endif %}
228
219
 
229
220
  </div>
230
221
  </div>
231
222
  {% endif %} <!-- End of if case.chanjo_coverage or case.chanjo2_coverage -->
232
- </div> <!-- End of Show variants in dynamic gene list -->
233
223
 
234
- <div class="btn-group d-flex justify-content-center mt-1"> <!-- Download genes in HPO gene panel -->
235
- <a class="btn btn-sm btn-primary text-white" href="{{ url_for('cases.download_hpo_genes', institute_id=institute._id, case_name=case.display_name, category="clinical") }}" download><span class="fa fa-download text-white" aria-hidden="true"></span>&nbsp;&nbsp;Clinical HPO gene panel</a>
236
- <a class="btn btn-sm btn-primary text-white" href="{{ url_for('cases.download_hpo_genes', institute_id=institute._id, case_name=case.display_name, category="research") }}" download><span class="fa fa-download text-white" aria-hidden="true"></span>&nbsp;&nbsp;Research HPO gene panel</a>
224
+ <div class="row mt-1">
225
+ <div class="btn-group btn-group-sm"> <!-- Download genes in HPO gene panel -->
226
+ <a class="btn btn-sm btn-primary text-white" href="{{ url_for('cases.download_hpo_genes', institute_id=institute._id, case_name=case.display_name, category="clinical") }}" download><span class="fa fa-download text-white" aria-hidden="true"></span>&nbsp;&nbsp;Clinical HPO gene panel</a>
227
+ <a class="btn btn-sm btn-primary text-white" href="{{ url_for('cases.download_hpo_genes', institute_id=institute._id, case_name=case.display_name, category="research") }}" download><span class="fa fa-download text-white" aria-hidden="true"></span>&nbsp;&nbsp;Research HPO gene panel</a>
228
+ </div>
229
+ </div>
230
+
237
231
  </div>
238
232
 
239
233
  {% endif %}
@@ -268,7 +268,7 @@
268
268
 
269
269
  <!-- show transcript info only if it's canonical, disease-associated or primary (print max 5 primary transcripts) -->
270
270
  {% if transcript.refseq_identifiers and n_primary_txs.count <= 5 or transcript.is_canonical or transcript.is_disease_associated or transcript.is_primary %}
271
- <li>{{transcript.transcript_id}}, RefSeq:[{{ transcript.refseq_identifiers|join(", ") or "-" }}], {{ (transcript.coding_sequence_name or '')|truncate(20, True) }}, {{ (transcript.protein_sequence_name or '')|url_decode|truncate(20, True) }}
271
+ <li class="mb-1">{{transcript.transcript_id}}, RefSeq:[{{ transcript.refseq_identifiers|join(", ") or "-" }}], {{ (transcript.coding_sequence_name or '')|truncate(20, True) }}, {{ (transcript.protein_sequence_name or '')|url_decode|truncate(20, True) }}
272
272
  {% if transcript.is_canonical %}
273
273
  <span class="badge rounded-pill bg-info text-white" title="canonical">C</span>
274
274
  {% endif %}
@@ -602,6 +602,8 @@
602
602
  <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">
603
603
  {% if case.clinvar_variants and variant._id in case.clinvar_variants.keys() %}
604
604
  (included in ClinVar submission)
605
+ {% elif variant.category in ('cancer', 'cancer_sv') %}
606
+ <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>
605
607
  {% else %}
606
608
  <button type="submit" name="var_id" value="{{variant._id}}" class="btn btn-secondary btn-sm" style="float: right;">Add to ClinVar submission</button>
607
609
  {% endif %}
@@ -313,28 +313,6 @@ def pdf_case_report(institute_id, case_name):
313
313
  )
314
314
 
315
315
 
316
- @cases_bp.route("/<institute_id>/<case_name>/chanjo2_report/<report_type>", methods=["GET"])
317
- def chanjo2_coverage_report(institute_id: str, case_name: str, report_type: str):
318
- """Return the HTML coverage report or coverage overview created by chanjo2."""
319
-
320
- REPORT_TYPES = ["report", "overview"]
321
- if report_type not in REPORT_TYPES:
322
- flash(f"Wrong value for report_type parameter. Accepted values: {REPORT_TYPES}", "warning")
323
- return redirect(request.referrer)
324
-
325
- institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
326
- report_html_content: str = controllers.chanjo2_coverage_report_contents(
327
- institute_obj=institute_obj,
328
- case_obj=case_obj,
329
- panel_name=request.args.get("panel_name"),
330
- panel_id=request.args.get("panel_id"),
331
- report_type=report_type,
332
- )
333
- if report_html_content is None:
334
- return redirect(request.referrer)
335
- return report_html_content
336
-
337
-
338
316
  @cases_bp.route("/<institute_id>/<case_name>/mt_report", methods=["GET"])
339
317
  def mt_report(institute_id, case_name):
340
318
  institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
@@ -107,7 +107,7 @@ def _get_snv_var_form(variant_obj, case_obj):
107
107
  var_form.variations_ids.data = var_ids.split(";")[0]
108
108
  var_form.chromosome.data = variant_obj.get("chromosome")
109
109
  var_form.start.data = variant_obj.get("position")
110
- var_form.stop.data = variant_obj.get("position")
110
+ var_form.stop.data = variant_obj.get("end")
111
111
  return var_form
112
112
 
113
113
 
@@ -276,7 +276,7 @@ def parse_variant_form_fields(form):
276
276
  clinvar_var["variations_ids"] = form["dbsnp_id"]
277
277
 
278
278
  if clinvar_var.get("ref_seq") and clinvar_var.get("hgvs"):
279
- # Variant is described by RefSeq and HGVS already, remove redundanti fields from submission
279
+ # Variant is described by RefSeq and HGVS already, remove redundant fields from submission
280
280
  for item in ["chromosome", "start", "stop", "ref", "alt"]:
281
281
  clinvar_var.pop(item)
282
282
 
@@ -437,7 +437,7 @@ def send_api_submission(institute_id, submission_id, key):
437
437
 
438
438
  clinvar_id = store.get_clinvar_id(
439
439
  submission_id
440
- ) # This is the official ID associated with this submission in Clinvar ((ex: SUB999999)
440
+ ) # This is the official ID associated with this submission in Clinvar (ex: SUB999999)
441
441
 
442
442
  if clinvar_id: # Check if submission object has already an associated ClinVar ID
443
443
  conversion_res["submissionName"] = clinvar_id
@@ -95,10 +95,16 @@
95
95
  </form>
96
96
  {% if subm_obj.status == 'open' and show_submit %}
97
97
  <form id="useApi_{{subm_obj._id}}" action="{{ url_for('clinvar.clinvar_update_submission', institute_id=institute._id, submission=subm_obj._id) }}" method="POST">
98
- {{ submit_modal(subm_obj) }}
98
+ {{ submit_modal() }}
99
99
  <td style="width: 20%"><button type="button" class="btn btn-sm btn-success form-control" name="update_submission" value="api_submit" data-bs-toggle="modal" data-bs-target="#submitModal">Submit to ClinVar</button></td>
100
100
  </form>
101
101
  {% endif %}
102
+ {% if subm_obj.status == 'submitted' %} <!--Submission status query -->
103
+ <form id="statusApi_{{subm_obj._id}}" action="{{ url_for('clinvar.clinvar_submission_status', submission_id=subm_obj.clinvar_subm_id) }}" method="POST">
104
+ {{ status_modal() }}
105
+ <td style="width: 20%"><button type="button" class="btn btn-sm btn-secondary form-control" name="status_enquiry" value="api_status" data-bs-toggle="modal" data-bs-target="#statusModal">Submission status enquiry</button></td>
106
+ </form>
107
+ {% endif %}
102
108
  </tr>
103
109
  </table>
104
110
  </div>
@@ -240,7 +246,28 @@
240
246
  </div>
241
247
  {% endmacro %}
242
248
 
243
- {% macro submit_modal(subm_obj) %}
249
+ {% macro status_modal() %}
250
+ <div class="modal fade" id="statusModal" tabindex="-1">
251
+ <div class="modal-dialog">
252
+ <div class="modal-content">
253
+ <div class="modal-header">
254
+ <h5 class="modal-title">Submission status</h5>
255
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
256
+ </div>
257
+ <div class="modal-body">
258
+ <label for="apiKey">Submitter's <a href="https://www.ncbi.nlm.nih.gov/clinvar/docs/api_http/" target="_blank" rel="noopener">API key</a></label>
259
+ <input type="password" class="form-control" name="apiKey" id="apiKey" placeholder="64 alphanumeric characters" value="{{institute.clinvar_key or ''}}" required>
260
+ </div>
261
+ <div class="modal-footer">
262
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
263
+ <button type="submit" name="show_status" value="status" class="btn btn-primary">Submission status enquiry</button>
264
+ </div>
265
+ </div>
266
+ </div>
267
+ </div>
268
+ {% endmacro %}
269
+
270
+ {% macro submit_modal() %}
244
271
  <div class="modal fade" id="submitModal" tabindex="-1">
245
272
  <div class="modal-dialog">
246
273
  <div class="modal-content">
@@ -34,7 +34,7 @@
34
34
  </ul>
35
35
  <!-- fieldsets -->
36
36
  <fieldset data-step="1">
37
- <legend>ClinVar variant submission form</legend>
37
+ <legend class="text-dark">ClinVar variant submission form</legend>
38
38
  <p class="text-dark">
39
39
  The form present on this page mirrors the fields on the ClinVar submission spreadsheets.<br>
40
40
  After collecting the required fields, this variant gets saved in an open ClinVar submission object.<br>
@@ -44,7 +44,7 @@
44
44
  </fieldset>
45
45
 
46
46
  <fieldset data-step="2">
47
- <legend>Assertion criteria <span class="text-danger" data-bs-toggle='tooltip' title="The new ClinVar spreadsheet templates (versions L1.7 and 4.3) no longer include the following columns: Assertion method and Assertion method citation.
47
+ <legend class="text-dark">Assertion criteria <span class="text-danger" data-bs-toggle='tooltip' title="The new ClinVar spreadsheet templates (versions L1.7 and 4.3) no longer include the following columns: Assertion method and Assertion method citation.
48
48
  New files for assertion criteria will be uploaded on your landing page in the ClinVar Submission Portal, independent of a submission. For this reason Scout will collect this information but will not export it as columns in the submission Variant file."><strong>?</strong></span></legend>
49
49
 
50
50
  <!-- hidden fields -->
@@ -73,7 +73,7 @@
73
73
  <br><br>
74
74
  <div class="d-flex flex-column flex-lg-row justify-content-center align-items-center">
75
75
  {{variant_data.var_form.assertion_method_cit_db.label(class="fw-bold text-dark my-2 mx-lg-2")}}
76
- {{variant_data.var_form.assertion_method_cit_db(class="bg-white my-2 mx-lg-2")}}
76
+ {{variant_data.var_form.assertion_method_cit_db(class="btn-secondary my-2 mx-lg-2")}}
77
77
  <div class="my-2 mx-lg-2">{{variant_data.var_form.assertion_method_cit_id.label(class="fw-bold text-dark")}}
78
78
  <span class="text-danger" id="assertion_method_cit_id_tooltip" data-bs-toggle='tooltip' title="The Assertion method citation is now supplied as a database identifier and an id number in the corresponding format">
79
79
  <strong>?</strong>
@@ -85,7 +85,7 @@
85
85
  <input type="button" name="next" class="next action-button" value="Next"/>
86
86
  </fieldset>
87
87
  <fieldset data-step="3">
88
- <legend>Variant Details</legend>
88
+ <legend class="text-dark">Variant Details</legend>
89
89
  {{variant_data.var_form.gene_symbol.label(class="fw-bold, text-dark")}} <span class="text-danger" data-bs-toggle='tooltip' title="Gene symbol should be provided only to indicate the gene-disease relationship supporting the variant interpretation. Gene symbol is not expected for SVs, except to make a statement that a specific gene within the variant has a relationship to the interpreted condition."><strong>?</strong></span>
90
90
  {{variant_data.var_form.gene_symbol(class="bg-white")}}
91
91
  <br>
@@ -114,7 +114,7 @@
114
114
  {% for item_row in variant_data.var_form.tx_hgvs | batch(3) %}
115
115
  <tr>
116
116
  {% for item in item_row %}
117
- <td style="width: 3%; text-align: right; vertical-align: baseline;">{{item()}}</td>
117
+ <td style="width: 3%; text-align: right; vertical-align: baseline;">{{item}}</td>
118
118
 
119
119
  {% if "(validated)" in item.label.text %}
120
120
  {% set ns.validated = true %}
@@ -124,7 +124,7 @@
124
124
  {% set ns.label = item.label.text | replace(" (validated)", "") %}
125
125
 
126
126
  <td style="width: 30%; text-align: left; ">
127
- <p
127
+ <p class="text-dark"
128
128
  {% if ns.label|length > 25 %}
129
129
  data-bs-toggle="tooltip" title="{{ns.label}}">{{ ns.label|truncate(25,true,'..') }}
130
130
  {% else %}
@@ -196,7 +196,7 @@
196
196
  </fieldset>
197
197
 
198
198
  <fieldset data-step="4">
199
- <legend>Inheritance model</legend>
199
+ <legend class="text-dark">Inheritance model</legend>
200
200
  <h3 class="fs-subtitle">The mode of inheritance specific to the variant-disease pair, not generally for the disease</h3>
201
201
  {{variant_data.var_form.inheritance_mode.label(class="fw-bold, text-dark")}}
202
202
  {{variant_data.var_form.inheritance_mode(class="form-control, btn-secondary")}}
@@ -206,7 +206,7 @@
206
206
  </fieldset>
207
207
 
208
208
  <fieldset data-step="5">
209
- <legend>Germline Classification</legend>
209
+ <legend class="text-dark">Germline Classification</legend>
210
210
  <h3 class="fs-subtitle">Represents a variant-level classification for a disease, rather than an interpretation of the clinical significance of a variant for a specific patient.</h3>
211
211
 
212
212
  <div class="row">
@@ -231,23 +231,19 @@
231
231
  </fieldset>
232
232
 
233
233
  <fieldset data-step="6">
234
- <legend>Associated Conditions</legend>
234
+ <legend class="text-dark">Associated Conditions</legend>
235
235
  <h3 class="fs-subtitle">The condition for which the variant is interpreted. Examples available at <a href="https://www.ncbi.nlm.nih.gov/clinvar/docs/spreadsheet/#condition" target="_blank" rel="noopener">ClinVar</a></h3>
236
236
 
237
237
  <div class="row">
238
238
  <div class="col-6" id="clinvar_condition_container">
239
239
  {{variant_data.var_form.condition_type.label(class="fw-bold, text-dark")}}<span class="text-danger" data-bs-toggle='tooltip' title="Required field."><strong>*</strong></span>
240
+
240
241
  <select class="form-control, btn-secondary" name="condition_type" id="condition_type">
241
242
  {% for dbtype, _ in variant_data.var_form.condition_type.choices %}
242
- {% if dbtype == "OMIM" and variant_data.var_form.omim_terms.choices %}
243
- <option value="{{dbtype}}" selected>{{dbtype}}</option>
244
- {% elif dbtype == "HP" and variant_data.var_form.hpo_terms.choices %}
245
- <option value="{{dbtype}}" selected>{{dbtype}}</option>
246
- {% else %}
247
- <option value="{{dbtype}}">{{dbtype}}</option>
248
- {% endif %}
243
+ <option value="{{dbtype}}">{{dbtype}}</option>
249
244
  {% endfor %}
250
245
  </select>
246
+
251
247
  <br><br>
252
248
  {{variant_data.var_form.conditions.label(class="fw-bold, text-dark")}} <span class="text-danger" data-bs-toggle='tooltip' title="Required field. Include data from ONE DATABASE TYPE ONLY (i.e. only OMIM terms, only HPO terms etc). If multiple conditions are submitted for a variant, this indicates that the variant was interpreted for the combination of conditions in the same individual(s). i.e. this variant causes both condition A and condition B in the same individual. This scenario is most common for a new disease or syndrome that does not yet have a name and is described by several clinical features. If you want to indicate that the variant has been interpreted for more than one condition, please submit these as separate records."><strong>*?</strong></span>
253
249
  <select class="select2" id="condition_tags" name="conditions" multiple="true" style="width:100%;">
@@ -313,7 +309,7 @@
313
309
  </fieldset>
314
310
 
315
311
  <fieldset data-step="7">
316
- <legend>Observation Data</legend>
312
+ <legend class="text-dark">Observation Data</legend>
317
313
  <h3 class="fs-subtitle">Information provided by filling in these fields will be used to create the CaseData.csv file. Observations from at least one individuals are required.</h3>
318
314
 
319
315
  <ul class="list-group">
@@ -372,6 +368,7 @@ var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
372
368
 
373
369
  // Validates fields for conditions associated to a variant
374
370
  function validateConditions(){
371
+
375
372
  if ($("#condition_tags option:selected").length == 0) {
376
373
  alert("Please provide at least one condition ID");
377
374
  return false;
@@ -387,7 +384,16 @@ function validateConditions(){
387
384
  return false;
388
385
  }
389
386
 
390
- return true
387
+ // Make sure that provided conditions are numbers, except when condition type is MeSH
388
+ var selectedConditionsValues = $('#condition_tags').val();
389
+ for (let i = 0; i < selectedConditionsValues.length; i++) {
390
+ if (isNaN(selectedConditionsValues[i]) && $('#condition_type').val() != 'MeSH'){
391
+ alert(`Condition ID "${selectedConditionsValues[i]}" has an invalid format.`);
392
+ return false;
393
+ }
394
+ }
395
+
396
+ return true;
391
397
  }
392
398
 
393
399
  var current_fs, next_fs, previous_fs; //fieldsets
@@ -526,6 +532,18 @@ $('#condition_tags').select2({
526
532
  allowClear: true,
527
533
  });
528
534
 
535
+ // Set selected condition type on page load
536
+ window.onload = function() {
537
+ const selectedCondId = document.getElementById('condition_type');
538
+ {% if variant_data.var_form.omim_terms.choices %}
539
+ selectedCondId.options.selectedIndex = 4;
540
+ {% elif variant_data.var_form.orpha_terms.choices %}
541
+ selectedCondId.options.selectedIndex = 5;
542
+ {% elif variant_data.var_form.hpo_terms.choices %}
543
+ selectedCondId.options.selectedIndex = 0;
544
+ {% endif %}
545
+ };
546
+
529
547
  // reset and modify conditions field's placeholder when condition ID type changes
530
548
  $(function(){
531
549
  $("#condition_type").change(function(){
@@ -8,7 +8,7 @@ from flask import Blueprint, flash, redirect, render_template, request, send_fil
8
8
  from flask_login import current_user
9
9
 
10
10
  from scout.constants.clinvar import CASEDATA_HEADER, CLINVAR_HEADER, GERMLINE_CLASSIF_TERMS
11
- from scout.server.extensions import store
11
+ from scout.server.extensions import clinvar_api, store
12
12
  from scout.server.utils import institute_and_case
13
13
 
14
14
  from . import controllers
@@ -24,6 +24,18 @@ clinvar_bp = Blueprint(
24
24
  )
25
25
 
26
26
 
27
+ @clinvar_bp.route("/clinvar/status-enquiry/<submission_id>", methods=["POST"])
28
+ def clinvar_submission_status(submission_id):
29
+ """Sends a request to ClinVar to retrieve and display the status of a submission."""
30
+
31
+ # flash a message with current submission status for a ClinVar submission
32
+ clinvar_api.show_submission_status(
33
+ submission_id=submission_id, api_key=request.form.get("apiKey")
34
+ )
35
+
36
+ return redirect(request.referrer)
37
+
38
+
27
39
  @clinvar_bp.route("/<institute_id>/<case_name>/clinvar/add_variant", methods=["POST"])
28
40
  def clinvar_add_variant(institute_id, case_name):
29
41
  """Create a ClinVar submission document in database for one or more variants from a case."""
@@ -36,6 +36,8 @@ def disease_terms(store: MongoAdapter, query: str, source: str) -> dict:
36
36
 
37
37
  for gene in gene_ids:
38
38
  gene_caption = store.hgnc_gene_caption(hgnc_identifier=gene)
39
+ if not gene_caption:
40
+ continue
39
41
  gene_ids_symbols.append(
40
42
  {"hgnc_id": gene_caption["hgnc_id"], "hgnc_symbol": gene_caption["hgnc_symbol"]}
41
43
  )
@@ -469,7 +469,12 @@ def cases(store, request, institute_id):
469
469
 
470
470
  data["status_ncases"] = store.nr_cases_by_status(institute_id=institute_id)
471
471
  data["nr_cases"] = sum(data["status_ncases"].values())
472
- data["sanger_unevaluated"] = get_sanger_unevaluated(store, institute_id, current_user.email)
472
+
473
+ sanger_ordered_not_validated: Tuple[Dict[str, list]] = get_sanger_unevaluated(
474
+ store, institute_id, current_user.email
475
+ )
476
+ data["sanger_unevaluated"]: Dict[str, list] = sanger_ordered_not_validated[0]
477
+ data["sanger_validated_by_others"]: Dict[str, list] = sanger_ordered_not_validated[1]
473
478
 
474
479
  # local function to add info to case obj
475
480
  def populate_case_obj(case_obj):
@@ -559,27 +564,76 @@ def populate_case_filter_form(params):
559
564
  return form
560
565
 
561
566
 
562
- def get_sanger_unevaluated(store, institute_id, user_id):
563
- """Get all variants for an institute having Sanger validations ordered but still not evaluated
567
+ def _get_unevaluated_variants_for_case(
568
+ case_obj: dict, var_ids_list: List[str], sanger_validated_by_user_by_case: Dict[str, List[str]]
569
+ ) -> Tuple[Dict[str, list]]:
570
+ """Returns the variants with Sanger ordered by a user that need validation or are validated by another user."""
564
571
 
565
- Args:
566
- store(scout.adapter.MongoAdapter)
567
- institute_id(str)
572
+ case_display_name: str = case_obj["display_name"]
573
+ case_id: str = case_obj["_id"]
574
+
575
+ def _variant_has_sanger_ordered(variant_obj: dict) -> bool:
576
+ """Returns True if the sanger_ordered status of a variant is True, else False."""
577
+
578
+ if (
579
+ variant_obj is None
580
+ or variant_obj.get("sanger_ordered") is None
581
+ or variant_obj.get("sanger_ordered") is False
582
+ ):
583
+ return False
584
+ return True
585
+
586
+ unevaluated_by_case = {case_display_name: []}
587
+ evaluated_by_others_by_case = {case_display_name: []}
588
+ for var_id in var_ids_list:
589
+ # For each variant with sanger validation ordered
590
+ variant_obj = store.variant(document_id=var_id, case_id=case_id)
591
+
592
+ # Double check that Sanger was ordered (and not canceled) for the variant
593
+ if _variant_has_sanger_ordered(variant_obj) is False:
594
+ continue
595
+
596
+ validation = variant_obj.get("validation", "not_evaluated")
597
+
598
+ # Check that the variant is not evaluated
599
+ if validation in ["True positive", "False positive"]:
600
+ if var_id in sanger_validated_by_user_by_case.get(
601
+ case_id, []
602
+ ): # User had validated this variant
603
+ continue
604
+
605
+ # Another user has validated the variant
606
+ evaluated_by_others_by_case[case_display_name].append(variant_obj["_id"])
607
+ else:
608
+ unevaluated_by_case[case_display_name].append(variant_obj["_id"])
609
+
610
+ return unevaluated_by_case, evaluated_by_others_by_case
611
+
612
+
613
+ def get_sanger_unevaluated(
614
+ store: MongoAdapter, institute_id: str, user_id: str
615
+ ) -> Tuple[List[Dict[str, list]]]:
616
+ """Return all variant with Sanger sequencing ordered by a user with validation missing or validated by another user.
568
617
 
569
618
  Returns:
570
- unevaluated: a list that looks like this: [ {'case1': [varID_1, varID_2, .., varID_n]}, {'case2' : [varID_1, varID_2, .., varID_n]} ],
619
+ unevaluated: a list that looks like this: [ {'case1': [varID_1, varID_2, .., varID_n]}, .. ],
571
620
  where the keys are case_ids and the values are lists of variants with Sanger ordered but not yet validated
621
+ evaluated_by_others: a list that looks like the one above where varID_1, varID_2 etc are variants validated by some other user
572
622
 
573
623
  """
624
+ sanger_ordered_by_user_by_case: Dict[str, List[str]] = {
625
+ case_variants["_id"]: case_variants["vars"]
626
+ for case_variants in store.sanger_ordered(institute_id=institute_id, user_id=user_id)
627
+ }
628
+ sanger_validated_by_user_by_case: Dict[str, List[str]] = {
629
+ case_variants["_id"]: case_variants["vars"]
630
+ for case_variants in store.validated(institute_id=institute_id, user_id=user_id)
631
+ }
574
632
 
575
- # Retrieve a list of ids for variants with Sanger ordered grouped by case from the 'event' collection
576
- # This way is much faster than querying over all variants in all cases of an institute
577
- sanger_ordered_by_case = store.sanger_ordered(institute_id, user_id)
578
633
  unevaluated = []
634
+ evaluated_by_others = []
579
635
 
580
- # for each object where key==case and value==[variant_id with Sanger ordered]
581
- for item in sanger_ordered_by_case:
582
- case_id = item["_id"]
636
+ for case_id, var_ids_list in sanger_ordered_by_user_by_case.items():
583
637
  # Get the case to collect display name
584
638
  CASE_SANGER_UNEVALUATED_PROJECTION = {"display_name": 1}
585
639
  case_obj = store.case(case_id=case_id, projection=CASE_SANGER_UNEVALUATED_PROJECTION)
@@ -588,36 +642,20 @@ def get_sanger_unevaluated(store, institute_id, user_id):
588
642
  continue
589
643
 
590
644
  case_display_name = case_obj.get("display_name")
591
-
592
- # List of variant document ids
593
- varid_list = item["vars"]
594
-
595
- unevaluated_by_case = {case_display_name: []}
596
- for var_id in varid_list:
597
- # For each variant with sanger validation ordered
598
- variant_obj = store.variant(document_id=var_id, case_id=case_id)
599
-
600
- # Double check that Sanger was ordered (and not canceled) for the variant
601
- if (
602
- variant_obj is None
603
- or variant_obj.get("sanger_ordered") is None
604
- or variant_obj.get("sanger_ordered") is False
605
- ):
606
- continue
607
-
608
- validation = variant_obj.get("validation", "not_evaluated")
609
-
610
- # Check that the variant is not evaluated
611
- if validation in ["True positive", "False positive"]:
612
- continue
613
-
614
- unevaluated_by_case[case_display_name].append(variant_obj["_id"])
645
+ unevaluated_by_case, evaluated_by_others_by_case = _get_unevaluated_variants_for_case(
646
+ case_obj=case_obj,
647
+ var_ids_list=var_ids_list,
648
+ sanger_validated_by_user_by_case=sanger_validated_by_user_by_case,
649
+ )
615
650
 
616
651
  # If for a case there is at least one Sanger validation to evaluate add the object to the unevaluated objects list
617
652
  if len(unevaluated_by_case[case_display_name]) > 0:
618
653
  unevaluated.append(unevaluated_by_case)
619
654
 
620
- return unevaluated
655
+ if len(evaluated_by_others_by_case[case_display_name]) > 0:
656
+ evaluated_by_others.append(evaluated_by_others_by_case)
657
+
658
+ return unevaluated, evaluated_by_others
621
659
 
622
660
 
623
661
  def export_gene_variants(