scout-browser 4.94.1__py3-none-any.whl → 4.96.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scout/adapter/mongo/case.py +26 -25
- scout/adapter/mongo/hgnc.py +5 -1
- scout/adapter/mongo/managed_variant.py +4 -2
- scout/adapter/mongo/query.py +6 -4
- scout/adapter/mongo/variant.py +11 -6
- scout/build/case.py +3 -1
- scout/build/panel.py +1 -1
- scout/constants/acmg.py +25 -18
- scout/constants/gene_tags.py +22 -12
- scout/demo/643594.research.mei.vcf.gz +0 -0
- scout/demo/643594.research.mei.vcf.gz.tbi +0 -0
- scout/demo/cancer.load_config.yaml +1 -3
- scout/demo/rnafusion.load_config.yaml +1 -0
- scout/load/panelapp.py +8 -12
- scout/models/case/case_loading_models.py +3 -0
- scout/parse/case.py +1 -0
- scout/parse/omim.py +5 -6
- scout/parse/panelapp.py +16 -42
- scout/parse/variant/compound.py +20 -21
- scout/server/app.py +12 -0
- scout/server/blueprints/cases/controllers.py +38 -9
- scout/server/blueprints/cases/templates/cases/case_report.html +37 -4
- scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +12 -0
- scout/server/blueprints/clinvar/controllers.py +1 -0
- scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +10 -14
- scout/server/blueprints/clinvar/views.py +18 -31
- scout/server/blueprints/managed_variants/forms.py +17 -2
- scout/server/blueprints/managed_variants/templates/managed_variants/managed_variants.html +2 -2
- scout/server/blueprints/variant/templates/variant/cancer-variant.html +2 -2
- scout/server/blueprints/variant/templates/variant/components.html +27 -4
- scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -2
- scout/server/blueprints/variant/templates/variant/variant_details.html +1 -1
- scout/server/blueprints/variant/views.py +11 -5
- scout/server/blueprints/variants/forms.py +33 -5
- scout/server/blueprints/variants/templates/variants/cancer-variants.html +5 -4
- scout/server/blueprints/variants/templates/variants/str-variants.html +13 -9
- scout/server/extensions/clinvar_extension.py +56 -2
- scout/server/links.py +0 -14
- scout/utils/acmg.py +5 -5
- scout/utils/ccv.py +1 -9
- scout/utils/link.py +4 -3
- {scout_browser-4.94.1.dist-info → scout_browser-4.96.0.dist-info}/METADATA +1 -1
- {scout_browser-4.94.1.dist-info → scout_browser-4.96.0.dist-info}/RECORD +46 -46
- {scout_browser-4.94.1.dist-info → scout_browser-4.96.0.dist-info}/WHEEL +0 -0
- {scout_browser-4.94.1.dist-info → scout_browser-4.96.0.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.94.1.dist-info → scout_browser-4.96.0.dist-info}/licenses/LICENSE +0 -0
@@ -13,6 +13,7 @@ from flask_login import current_user
|
|
13
13
|
from requests.auth import HTTPBasicAuth
|
14
14
|
from xlsxwriter import Workbook
|
15
15
|
|
16
|
+
from scout import __version__
|
16
17
|
from scout.adapter import MongoAdapter
|
17
18
|
from scout.constants import (
|
18
19
|
CANCER_PHENOTYPE_MAP,
|
@@ -63,6 +64,7 @@ from scout.server.utils import (
|
|
63
64
|
institute_and_case,
|
64
65
|
)
|
65
66
|
from scout.utils.acmg import get_acmg_temperature
|
67
|
+
from scout.utils.ccv import get_ccv_temperature
|
66
68
|
|
67
69
|
LOG = logging.getLogger(__name__)
|
68
70
|
|
@@ -613,19 +615,43 @@ def check_outdated_gene_panel(panel_obj, latest_panel):
|
|
613
615
|
|
614
616
|
|
615
617
|
def add_bayesian_acmg_classification(variant_obj: dict):
|
616
|
-
"""Append info to display the ACMG VUS Bayesian score / temperature.
|
618
|
+
"""Append info to display the ACMG VUS Bayesian score / temperature.
|
619
|
+
Criteria have a term and a modifier field on the db document
|
620
|
+
that are joined together in a string to conform to a regular
|
621
|
+
ACMG term format. A set of such terms are passed on for evaluation
|
622
|
+
to the same function as the ACMG classification form uses.
|
623
|
+
"""
|
617
624
|
variant_acmg_classifications = list(
|
618
625
|
store.get_evaluations_case_specific(document_id=variant_obj["_id"])
|
619
626
|
)
|
620
627
|
if variant_acmg_classifications:
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
628
|
+
terms = set()
|
629
|
+
for criterium in variant_acmg_classifications[0].get("criteria", []):
|
630
|
+
term = criterium.get("term")
|
631
|
+
if criterium.get("modifier"):
|
632
|
+
term += f"_{criterium.get('modifier')}"
|
633
|
+
terms.add(term)
|
634
|
+
variant_obj["bayesian_acmg"] = get_acmg_temperature(terms)
|
635
|
+
|
636
|
+
|
637
|
+
def add_bayesian_ccv_classification(variant_obj: dict):
|
638
|
+
"""Append info to display the CCV VUS Bayesian score / temperature.
|
639
|
+
Criteria have a term and a modifier field on the db document
|
640
|
+
that are joined together in a string to conform to a regular
|
641
|
+
CCV term format. A set of such terms are passed on for evaluation
|
642
|
+
to the same function as the CCV classification form uses.
|
643
|
+
"""
|
644
|
+
variant_ccv_classifications = list(
|
645
|
+
store.get_ccv_evaluations_case_specific(document_id=variant_obj["_id"])
|
646
|
+
)
|
647
|
+
if variant_ccv_classifications:
|
648
|
+
terms = set()
|
649
|
+
for criterium in variant_ccv_classifications[0].get("ccv_criteria", []):
|
650
|
+
term = criterium.get("term")
|
651
|
+
if criterium.get("modifier"):
|
652
|
+
term += f"_{criterium.get('modifier')}"
|
653
|
+
terms.add(term)
|
654
|
+
variant_obj["bayesian_ccv"] = get_ccv_temperature(terms)
|
629
655
|
|
630
656
|
|
631
657
|
def case_report_variants(store: MongoAdapter, case_obj: dict, institute_obj: dict, data: dict):
|
@@ -642,6 +668,7 @@ def case_report_variants(store: MongoAdapter, case_obj: dict, institute_obj: dic
|
|
642
668
|
if case_key == "partial_causatives":
|
643
669
|
var_obj["phenotypes"] = case_obj["partial_causatives"][var_id]
|
644
670
|
add_bayesian_acmg_classification(var_obj)
|
671
|
+
add_bayesian_ccv_classification(var_obj)
|
645
672
|
evaluated_variants_by_type[eval_category].append(
|
646
673
|
_get_decorated_var(var_obj=var_obj, institute_obj=institute_obj, case_obj=case_obj)
|
647
674
|
)
|
@@ -683,6 +710,7 @@ def _append_evaluated_variant_by_type(
|
|
683
710
|
if variant_key in var_obj and var_obj[variant_key] is not None:
|
684
711
|
|
685
712
|
add_bayesian_acmg_classification(var_obj)
|
713
|
+
add_bayesian_ccv_classification(var_obj)
|
686
714
|
|
687
715
|
evaluated_variants_by_type[eval_category].append(
|
688
716
|
_get_decorated_var(var_obj=var_obj, institute_obj=institute_obj, case_obj=case_obj)
|
@@ -730,6 +758,7 @@ def case_report_content(store: MongoAdapter, institute_obj: dict, case_obj: dict
|
|
730
758
|
data["manual_rank_options"] = MANUAL_RANK_OPTIONS
|
731
759
|
data["genetic_models"] = dict(GENETIC_MODELS)
|
732
760
|
data["report_created_at"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
|
761
|
+
data["current_scout_version"] = __version__
|
733
762
|
|
734
763
|
case_report_variants(store, case_obj, institute_obj, data)
|
735
764
|
|
@@ -63,7 +63,7 @@
|
|
63
63
|
</nav>
|
64
64
|
|
65
65
|
<div id="container" class="container">
|
66
|
-
<h4>Scout case analysis report</h4> - created on: <strong>{{report_created_at}}</strong><br><br>
|
66
|
+
<h4>Scout case analysis report</h4> - created on: <strong>{{report_created_at}}</strong> using <strong>Scout v.{{current_scout_version}}</strong><br><br>
|
67
67
|
{{ phenotype_panel() }}
|
68
68
|
{{ gene_panels_panel()}}
|
69
69
|
{{ causatives_panel()}}
|
@@ -101,8 +101,11 @@
|
|
101
101
|
<td>
|
102
102
|
<table class="table table-sm">
|
103
103
|
<tr>
|
104
|
-
|
104
|
+
<td>Case created: <strong>{{case.created_at.strftime('%Y-%m-%d')}}</strong></td>
|
105
105
|
<td>Last updated: <strong>{{case.updated_at.strftime('%Y-%m-%d') if case.updated_at else "n.a." }}</strong></td>
|
106
|
+
{% if case.scout_load_version %}
|
107
|
+
<td> Loaded with <strong>Scout v.{{case.scout_load_version}}</strong></td>
|
108
|
+
{% endif %}
|
106
109
|
</tr>
|
107
110
|
</table>
|
108
111
|
<br>
|
@@ -715,7 +718,7 @@
|
|
715
718
|
{% endif %}
|
716
719
|
<td>
|
717
720
|
{% if variant.acmg_classification %}
|
718
|
-
<span class="badge rounded-pill bg-{{variant.acmg_classification['color'] if variant.acmg_classification['color'] else 'secondary'}}" title="{{variant.acmg_classification['
|
721
|
+
<span class="badge rounded-pill bg-{{variant.acmg_classification['color'] if variant.acmg_classification['color'] else 'secondary'}}" title="{{variant.acmg_classification['label']}}">{{variant.acmg_classification['label'] }}</span>
|
719
722
|
{% else %}
|
720
723
|
-
|
721
724
|
{% endif %}
|
@@ -730,21 +733,50 @@
|
|
730
733
|
{% if cancer %}
|
731
734
|
<td>
|
732
735
|
{% if variant.ccv_classification %}
|
733
|
-
<span class="badge rounded-pill bg-{{variant.ccv_classification['color'] if variant.ccv_classification['color'] else 'secondary'}}" title="{{variant.ccv_classification['
|
736
|
+
<span class="badge rounded-pill bg-{{variant.ccv_classification['color'] if variant.ccv_classification['color'] else 'secondary'}}" title="{{variant.ccv_classification['label']}}">{{variant.ccv_classification['label'] }}</span>
|
734
737
|
{% else %}
|
735
738
|
-
|
736
739
|
{% endif %}
|
740
|
+
{% if variant.bayesian_ccv %}
|
741
|
+
<span class="badge rounded-pill bg-{{variant.bayesian_ccv.temperature_class}}">Score {{variant.bayesian_ccv.points|string}} <span class='fa {{variant.bayesian_ccv.temperature_icon}}'></span>
|
742
|
+
{% if variant.bayesian_ccv.point_classification == "VUS" %}
|
743
|
+
{{variant.bayesian_ccv.temperature}}
|
744
|
+
{% endif %}
|
745
|
+
</span>
|
746
|
+
{% endif %}
|
737
747
|
</td>
|
738
748
|
{% endif %}
|
739
749
|
</tr>
|
740
750
|
</tbody>
|
741
751
|
</table>
|
752
|
+
<table id="severity-table" class="table table-sm" style="background-color: transparent">
|
753
|
+
<thead>
|
754
|
+
<tr>
|
755
|
+
<th>SIFT</th>
|
756
|
+
<th>REVEL score</th>
|
757
|
+
<th>REVEL rank score</th>
|
758
|
+
<th>Polyphen</th>
|
759
|
+
<th>SPIDEX</th>
|
760
|
+
<th>SpliceAI DS max</th>
|
761
|
+
</thead>
|
762
|
+
<tbody>
|
763
|
+
<tr>
|
764
|
+
<td>{{ variant.sift_predictions|join(', ') or '-'}}</td>
|
765
|
+
<td>{{ variant.revel or '-' }}</td>
|
766
|
+
<td>{{ variant.revel_score or '-' }}</td>
|
767
|
+
<td>{{ variant.polyphen_predictions|join(', ') or '-' }}</td>
|
768
|
+
<td>{{ variant.spidex|spidex_human if variant.spidex else none|spidex_human }}</td>
|
769
|
+
<td>{{ variant.spliceai_scores|join(', ') or '-' }}</td>
|
770
|
+
</tr>
|
771
|
+
</tbody>
|
772
|
+
</table>
|
742
773
|
<table id="panel-table" class="table table-sm" style="background-color: transparent">
|
743
774
|
<thead>
|
744
775
|
<tr>
|
745
776
|
<th>Affected gene(s)</th>
|
746
777
|
<th>Description</th>
|
747
778
|
<th>Region annotation</th>
|
779
|
+
<th>Consequence</th>
|
748
780
|
<th>Transcript - HGVS - Protein</th>
|
749
781
|
<th>Diagnoses [inheritance]</th>
|
750
782
|
</tr>
|
@@ -760,6 +792,7 @@
|
|
760
792
|
</td>
|
761
793
|
<td>{{gene.description|title}}</td>
|
762
794
|
<td>{{gene.region_annotation}}</td>
|
795
|
+
<td>{{gene.functional_annotation.replace("_"," ")|truncate(20, True)}}</td>
|
763
796
|
<td>
|
764
797
|
{{ variant_transcripts(gene) }}
|
765
798
|
</td>
|
@@ -15,6 +15,7 @@
|
|
15
15
|
{{ reports(institute,case, report_types) }}
|
16
16
|
{{ default_gene_panel(institute, case) }}
|
17
17
|
{{ analysis_date(case) }}
|
18
|
+
{{ software_version(case) }}
|
18
19
|
{{ genome_build(case, case_groups, has_rna_tracks) }}
|
19
20
|
{{ rank_model(case) }}
|
20
21
|
<li class="list-group-item bg-dark sidebar-separator menu-collapsed"></li>
|
@@ -190,6 +191,17 @@
|
|
190
191
|
{% endif %}
|
191
192
|
{% endmacro %}
|
192
193
|
|
194
|
+
{% macro software_version(case) %}
|
195
|
+
{% if case.scout_load_version%}
|
196
|
+
<div href="#" class="bg-dark list-group-item text-white" data-bs-toggle="tooltip" title="Scout version used for loading the case" data-bs-placement="right">
|
197
|
+
<div class="d-flex w-100 justify-content-start align-items-center">
|
198
|
+
<span class="fa fa-gear fa-fw me-3"></span>
|
199
|
+
<span class="menu-collapsed">v.{{ case.scout_load_version }}</span>
|
200
|
+
</div>
|
201
|
+
</div>
|
202
|
+
{% endif %}
|
203
|
+
{% endmacro %}
|
204
|
+
|
193
205
|
{% macro genome_build(case, case_groups, has_rna_tracks) %}
|
194
206
|
<div href="#" class="bg-dark list-group-item text-white">
|
195
207
|
<div class="d-flex w-100 justify-content-start align-items-center">
|
@@ -363,6 +363,7 @@ def update_clinvar_submission_status(request_obj, institute_id, submission_id):
|
|
363
363
|
f"Removed {deleted_objects} objects and {deleted_submissions} submission from database",
|
364
364
|
"info",
|
365
365
|
)
|
366
|
+
|
366
367
|
if update_status == "submit":
|
367
368
|
submitter_key = request_obj.form.get("apiKey")
|
368
369
|
send_api_submission(institute_id, submission_id, submitter_key)
|
@@ -58,13 +58,6 @@
|
|
58
58
|
<nav aria-label="breadcrumb">
|
59
59
|
<ol class="breadcrumb align-items-center">
|
60
60
|
<li class="breadcrumb-item">
|
61
|
-
<!-- Download the Variant data CSV file -->
|
62
|
-
<a href="{{ url_for('clinvar.clinvar_download_csv', submission=subm_obj._id, csv_type='variant_data', clinvar_id=subm_obj.clinvar_subm_id or 'None') }}" class="btn btn-primary btn-xs text-white" target="_blank" rel="noopener">
|
63
|
-
Download variants csv file
|
64
|
-
</a>
|
65
|
-
<a href=" {{ url_for('clinvar.clinvar_download_csv', submission=subm_obj._id, csv_type='case_data', clinvar_id=subm_obj.clinvar_subm_id or 'None') }}" class="btn btn-primary btn-xs text-white" target="_blank" rel="noopener">
|
66
|
-
Download casedata csv file
|
67
|
-
</a>
|
68
61
|
<a id="download_clinvar_json" href="{{ url_for('clinvar.clinvar_download_json', submission=subm_obj._id, clinvar_id=subm_obj.clinvar_subm_id or 'None') }}" class="btn btn-primary btn-xs text-white" target="_blank" rel="noopener">
|
69
62
|
Download submission json file
|
70
63
|
</a>
|
@@ -77,8 +70,7 @@
|
|
77
70
|
{% if subm_obj.status != 'submitted' %}
|
78
71
|
<li class="breadcrumb-item"><button type="submit" name="update_submission" value="submitted" class="btn btn-success btn-xs">Mark as submitted</button></li>
|
79
72
|
{% endif %}
|
80
|
-
|
81
|
-
<li class="breadcrumb-item"><button type="submit" name="update_submission" value="delete" class="btn btn-danger btn-xs">Delete submission</button></li>
|
73
|
+
<li class="breadcrumb-item"><button type="submit" name="update_submission" value="delete" class="btn btn-danger btn-xs">Delete submission from Scout</button></li>
|
82
74
|
</ol>
|
83
75
|
</nav>
|
84
76
|
</form>
|
@@ -101,9 +93,13 @@
|
|
101
93
|
{% endif %}
|
102
94
|
{% if subm_obj.status == 'submitted' %} <!--Submission status query -->
|
103
95
|
<form id="statusApi_{{subm_obj._id}}" action="{{ url_for('clinvar.clinvar_submission_status', submission_id=subm_obj.clinvar_subm_id) }}" method="POST">
|
104
|
-
{{
|
96
|
+
{{ action_modal("status", subm_obj._id) }}
|
105
97
|
<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_{{subm_obj._id}}">Submission status enquiry</button></td>
|
106
98
|
</form>
|
99
|
+
<form id="deleteApi_{{subm_obj._id}}" action="{{ url_for('clinvar.clinvar_submission_delete', submission_id=subm_obj.clinvar_subm_id) }}" method="POST">
|
100
|
+
{{ action_modal("remove", subm_obj._id) }}
|
101
|
+
<td style="width: 20%"><button type="button" class="btn btn-sm btn-danger form-control" name="_enquiry" value="api_status" data-bs-toggle="modal" data-bs-target="#removeModal_{{subm_obj._id}}">Delete submission from ClinVar</button></td>
|
102
|
+
</form>
|
107
103
|
{% endif %}
|
108
104
|
</tr>
|
109
105
|
</table>
|
@@ -246,12 +242,12 @@
|
|
246
242
|
</div>
|
247
243
|
{% endmacro %}
|
248
244
|
|
249
|
-
{% macro
|
250
|
-
<div class="modal fade" id="
|
245
|
+
{% macro action_modal(action_type, subm_id) %}
|
246
|
+
<div class="modal fade" id="{{action_type}}Modal_{{subm_id}}" tabindex="-1">
|
251
247
|
<div class="modal-dialog">
|
252
248
|
<div class="modal-content">
|
253
249
|
<div class="modal-header">
|
254
|
-
<h5 class="modal-title">Submission
|
250
|
+
<h5 class="modal-title">Submission {{action_type}}</h5>
|
255
251
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
256
252
|
</div>
|
257
253
|
<div class="modal-body">
|
@@ -260,7 +256,7 @@
|
|
260
256
|
</div>
|
261
257
|
<div class="modal-footer">
|
262
258
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
263
|
-
<button type="submit" name="
|
259
|
+
<button type="submit" name="{{action_type}}" value="{{action_type}}" class="btn btn-primary">Submission {{action_type}} enquiry</button>
|
264
260
|
</div>
|
265
261
|
</div>
|
266
262
|
</div>
|
@@ -1,8 +1,7 @@
|
|
1
|
-
import csv
|
2
1
|
import logging
|
3
2
|
from json import dumps
|
4
3
|
from tempfile import NamedTemporaryFile
|
5
|
-
from typing import List
|
4
|
+
from typing import List, Tuple
|
6
5
|
|
7
6
|
from flask import Blueprint, flash, redirect, render_template, request, send_file, url_for
|
8
7
|
from flask_login import current_user
|
@@ -29,10 +28,25 @@ def clinvar_submission_status(submission_id):
|
|
29
28
|
"""Sends a request to ClinVar to retrieve and display the status of a submission."""
|
30
29
|
|
31
30
|
# flash a message with current submission status for a ClinVar submission
|
32
|
-
|
33
|
-
submission_id=submission_id, api_key=request.form.get("apiKey")
|
31
|
+
flash(
|
32
|
+
f'Response from ClinVar: {clinvar_api.json_submission_status( submission_id=submission_id, api_key=request.form.get("apiKey"))}',
|
33
|
+
"primary",
|
34
34
|
)
|
35
|
+
return redirect(request.referrer)
|
36
|
+
|
37
|
+
|
38
|
+
@clinvar_bp.route("/clinvar/delete-enquiry/<submission_id>", methods=["POST"])
|
39
|
+
def clinvar_submission_delete(submission_id):
|
40
|
+
"""Sends a request to ClinVar to delete a successfully processed submission."""
|
35
41
|
|
42
|
+
# Retrieve the actual submission status:
|
43
|
+
delete_res: Tuple[int, dict] = clinvar_api.delete_clinvar_submission(
|
44
|
+
submission_id=submission_id, api_key=request.form.get("apiKey")
|
45
|
+
)
|
46
|
+
flash(
|
47
|
+
f"ClinVar response: { str(delete_res[1]) }",
|
48
|
+
"success" if delete_res[0] == 201 else "warning",
|
49
|
+
)
|
36
50
|
return redirect(request.referrer)
|
37
51
|
|
38
52
|
|
@@ -104,33 +118,6 @@ def clinvar_update_submission(institute_id, submission):
|
|
104
118
|
return redirect(request.referrer)
|
105
119
|
|
106
120
|
|
107
|
-
@clinvar_bp.route("/<submission>/download/csv/<csv_type>/<clinvar_id>", methods=["GET"])
|
108
|
-
def clinvar_download_csv(submission, csv_type, clinvar_id):
|
109
|
-
"""Download a csv (Variant file or CaseData file) for a clinVar submission"""
|
110
|
-
|
111
|
-
clinvar_file_data = controllers.clinvar_submission_file(submission, csv_type, clinvar_id)
|
112
|
-
|
113
|
-
if clinvar_file_data is None:
|
114
|
-
return redirect(request.referrer)
|
115
|
-
|
116
|
-
# Write temp CSV file and serve it in response
|
117
|
-
tmp_csv = NamedTemporaryFile(
|
118
|
-
mode="a+", prefix=clinvar_file_data[0].split(".")[0], suffix=".csv"
|
119
|
-
)
|
120
|
-
writes = csv.writer(tmp_csv, delimiter=",", quoting=csv.QUOTE_ALL)
|
121
|
-
writes.writerow(clinvar_file_data[1]) # Write header
|
122
|
-
writes.writerows(clinvar_file_data[2]) # Write lines
|
123
|
-
tmp_csv.flush()
|
124
|
-
tmp_csv.seek(0)
|
125
|
-
|
126
|
-
return send_file(
|
127
|
-
tmp_csv.name,
|
128
|
-
download_name=clinvar_file_data[0],
|
129
|
-
mimetype="text/csv",
|
130
|
-
as_attachment=True,
|
131
|
-
)
|
132
|
-
|
133
|
-
|
134
121
|
@clinvar_bp.route("/<submission>/download/json/<clinvar_id>", methods=["GET"])
|
135
122
|
def clinvar_download_json(submission, clinvar_id):
|
136
123
|
"""Download a json for a clinVar submission"""
|
@@ -10,6 +10,7 @@ from wtforms import (
|
|
10
10
|
SubmitField,
|
11
11
|
validators,
|
12
12
|
)
|
13
|
+
from wtforms.widgets import NumberInput
|
13
14
|
|
14
15
|
from scout.constants import CHROMOSOMES, SV_TYPES
|
15
16
|
|
@@ -24,8 +25,22 @@ CATEGORY_CHOICES = [
|
|
24
25
|
|
25
26
|
|
26
27
|
class ManagedVariantForm(FlaskForm):
|
27
|
-
position = IntegerField(
|
28
|
-
|
28
|
+
position = IntegerField(
|
29
|
+
"Start position",
|
30
|
+
[
|
31
|
+
validators.Optional(),
|
32
|
+
validators.NumberRange(min=0, message="Start position must be 1 or greater."),
|
33
|
+
],
|
34
|
+
widget=NumberInput(min=1),
|
35
|
+
)
|
36
|
+
end = IntegerField(
|
37
|
+
"End position",
|
38
|
+
[
|
39
|
+
validators.Optional(),
|
40
|
+
validators.NumberRange(min=0, message="End position must be 1 or greater."),
|
41
|
+
],
|
42
|
+
widget=NumberInput(min=1),
|
43
|
+
)
|
29
44
|
cytoband_start = SelectField("Cytoband start", choices=[])
|
30
45
|
cytoband_end = SelectField("Cytoband end", choices=[])
|
31
46
|
description = StringField(label="Description")
|
@@ -212,8 +212,8 @@
|
|
212
212
|
<tbody>
|
213
213
|
<tr>
|
214
214
|
<td>{{ add_form.chromosome(class_="form-control") }}</td>
|
215
|
-
<td><input type="number" name="position" id="position" class="form-control" required></td>
|
216
|
-
<td><input type="number" name="end" id="end" class="form-control"></td>
|
215
|
+
<td><input type="number" name="position" id="position" class="form-control" required min="1"></td>
|
216
|
+
<td><input type="number" name="end" id="end" class="form-control" min="1"></td>
|
217
217
|
<td><input type="text" name="reference" id="reference" class="form-control" required></td>
|
218
218
|
<td><input type="text" name="alternative" id="alternative" class="form-control" required></td>
|
219
219
|
<td>{{ add_form.category(class_="form-control", onchange="populateSubTypeSelect('add_variant')") }}</td>
|
@@ -235,10 +235,10 @@
|
|
235
235
|
<span class="text-muted">exon </span><strong>{{ primary_gene.exon }}</strong>
|
236
236
|
{% endif %}
|
237
237
|
{% if primary_transcript and primary_transcript.coding_sequence_name %}
|
238
|
-
{{ primary_transcript.coding_sequence_name | url_decode }}
|
238
|
+
{{ primary_transcript.coding_sequence_name | url_decode | truncate(50, True) }}
|
239
239
|
{% endif %}
|
240
240
|
{% if primary_transcript and primary_transcript.protein_sequence_name %}
|
241
|
-
<strong>{{ primary_transcript.protein_sequence_name | url_decode }}</strong>
|
241
|
+
<strong>{{ primary_transcript.protein_sequence_name | url_decode | truncate(50, True) }}</strong>
|
242
242
|
{% endif %}
|
243
243
|
{% if primary_gene and primary_gene.region_annotation %}
|
244
244
|
<div>(<span class="font-weight-bold">{{ primary_gene.functional_annotation|truncate(20, True) }}</span> in <span class="font-weight-bold">{{ primary_gene.region_annotation }} region</span>)</div>
|
@@ -38,9 +38,13 @@
|
|
38
38
|
<li class="list-group-item {{ 'list-group-item-info' if current_variant }}">
|
39
39
|
<div class="d-flex">
|
40
40
|
<span>
|
41
|
-
|
41
|
+
{% if variant.category in ["snv", "cancer_snv"] %}
|
42
|
+
<a href="{{ url_for('variant.evaluation', evaluation_id=data._id) }}">
|
43
|
+
{{ data.classification.label }}
|
44
|
+
</a>
|
45
|
+
{% else %}
|
42
46
|
{{ data.classification.label }}
|
43
|
-
|
47
|
+
{% endif %}
|
44
48
|
<span class="badge bg-info">{{ data.classification.short }}</span>
|
45
49
|
</span>
|
46
50
|
<span>
|
@@ -133,7 +137,6 @@
|
|
133
137
|
{% endmacro %}
|
134
138
|
|
135
139
|
{% macro acmg_form(institute, case, variant, ACMG_OPTIONS, selected=None) %}
|
136
|
-
<label class="mt-3" data-bs-toggle="tooltip" title="Richards et al 2015"><a href="https://www.acmg.net/docs/standards_guidelines_for_the_interpretation_of_sequence_variants.pdf" rel="noopener noreferrer" target="_blank" style="color: inherit; text-decoration: inherit;">ACMG classification</a></label>
|
137
140
|
<form action="{{ url_for('variant.variant_update', institute_id=institute._id, case_name=case.display_name, variant_id=variant._id) }}" method="POST">
|
138
141
|
<div class="d-flex justify-content-between">
|
139
142
|
{% for option in ACMG_OPTIONS %}
|
@@ -168,7 +171,7 @@
|
|
168
171
|
<form action="{{ url_for('variant.ccv_evaluation', evaluation_id=data._id) }}" method="POST" style="display: inline-block;">
|
169
172
|
<button class="btn btn-xs btn-link" >Delete</button>
|
170
173
|
</form>
|
171
|
-
{% if data.ccv_criteria %}
|
174
|
+
{% if data.ccv_criteria %}
|
172
175
|
<a class="btn btn-xs btn-link" href="{{ url_for('variant.ccv_evaluation', evaluation_id=data._id, edit=True) }}" data-bs-toggle="tooltip" title="Editing this classification will result in a new classification">Edit</a>
|
173
176
|
{% endif %}
|
174
177
|
{% endif %}
|
@@ -191,6 +194,25 @@
|
|
191
194
|
</form>
|
192
195
|
{% endmacro %}
|
193
196
|
|
197
|
+
{% macro panel_classify_sv(variant, institute, case, ACMG_OPTIONS, evaluations) %}
|
198
|
+
<div class="mt-3">
|
199
|
+
ACMG (INDEL) <a href="https://www.acmg.net/docs/standards_guidelines_for_the_interpretation_of_sequence_variants.pdf" rel="noopener noreferrer" target="_blank" style="text-decoration: inherit;" data-bs-toggle="tooltip" title="Richards et al 2015">classification</a>
|
200
|
+
</div>
|
201
|
+
<div>
|
202
|
+
{{ acmg_form(institute, case, variant, ACMG_OPTIONS, variant.acmg_classification.code if variant.acmg_classification) }}
|
203
|
+
</div>
|
204
|
+
<div class="mt-3">
|
205
|
+
ACMG SV classification <a href="https://pubmed.ncbi.nlm.nih.gov/31690835/" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">guidelines</a> & <a href="https://cnvcalc.clinicalgenome.org/cnvcalc/" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">guide</a>.
|
206
|
+
</div>
|
207
|
+
{% if evaluations %} <!-- scrollable previous ACMG evaluations div-->
|
208
|
+
<div class="list-group mt-3" style="max-height:200px;overflow-y: scroll;">
|
209
|
+
{% for evaluation in evaluations %}
|
210
|
+
{{ acmg_classification_item(variant, evaluation) }}
|
211
|
+
{% endfor %}
|
212
|
+
</div>
|
213
|
+
{% endif %}
|
214
|
+
{% endmacro %}
|
215
|
+
|
194
216
|
{% macro panel_classify(variant, institute, case, ACMG_OPTIONS, CCV_OPTIONS, manual_rank_options, cancer_tier_options, dismiss_variant_options, mosaic_variant_options, evaluations, ccv_evaluations) %}
|
195
217
|
<div class="card panel-default">
|
196
218
|
<div class="panel-heading">Classify</div>
|
@@ -203,6 +225,7 @@
|
|
203
225
|
{% if case.track != "cancer" %}
|
204
226
|
{{ mosaic_variant_button(variant, institute, case, mosaic_variant_options) }}
|
205
227
|
{% endif %}
|
228
|
+
ACMG <a href="https://www.acmg.net/docs/standards_guidelines_for_the_interpretation_of_sequence_variants.pdf" rel="noopener noreferrer" target="_blank" style="text-decoration: inherit;" data-bs-toggle="tooltip" title="Richards et al 2015">classification</a>
|
206
229
|
{{ acmg_form(institute, case, variant, ACMG_OPTIONS, variant.acmg_classification.code if variant.acmg_classification) }}
|
207
230
|
<div class="mt-3">
|
208
231
|
<a href="{{ url_for('variant.variant_acmg', institute_id=institute._id, case_name=case.display_name, variant_id=variant._id) }}" class="btn btn-outline-secondary form-control">Classify</a>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{% extends "layout.html" %}
|
2
2
|
{% from "utils.html" import activity_panel, comments_panel, pedigree_panel %}
|
3
3
|
{% from "variant/variant_details.html" import old_observations %}
|
4
|
-
{% from "variant/components.html" import external_scripts, external_stylesheets, matching_variants, variant_scripts %}
|
4
|
+
{% from "variant/components.html" import external_scripts, external_stylesheets, matching_variants, panel_classify_sv, variant_scripts %}
|
5
5
|
{% from "variant/utils.html" import causative_button, genes_panel, igv_track_selection, modal_causative, overlapping_panel, sv_alignments, pin_button, transcripts_panel, custom_annotations, gene_panels %}
|
6
6
|
{% from "variant/rank_score_results.html" import rankscore_panel %}
|
7
7
|
{% from "variant/gene_disease_relations.html" import orpha_omim_phenotypes %}
|
@@ -196,7 +196,7 @@
|
|
196
196
|
{{ variant_tag_button(variant, institute, case, manual_rank_options) }}
|
197
197
|
</div>
|
198
198
|
<div>
|
199
|
-
|
199
|
+
{{ panel_classify_sv(variant, institute, case, ACMG_OPTIONS, evaluations) }}
|
200
200
|
</div>
|
201
201
|
</div>
|
202
202
|
<div class="list-group mt-3">
|
@@ -375,7 +375,7 @@
|
|
375
375
|
</li>
|
376
376
|
<li class="list-group-item">
|
377
377
|
SPIDEX
|
378
|
-
<span class="float-end">{{ variant.spidex_human }}</span>
|
378
|
+
<span class="float-end">{{ variant.spidex|spidex_human if variant.spidex else none|spidex_human }}</span>
|
379
379
|
</li>
|
380
380
|
<li class="list-group-item">
|
381
381
|
<a href="{{ variant.spliceai_link }}" target="_blank" rel="noopener">SpliceAI</a> <a href="https://github.com/Illumina/SpliceAI" target="_blank" rel="noopener">DS max</a>
|
@@ -27,12 +27,19 @@ from scout.server.blueprints.variant.controllers import (
|
|
27
27
|
check_reset_variant_classification,
|
28
28
|
)
|
29
29
|
from scout.server.blueprints.variant.controllers import evaluation as evaluation_controller
|
30
|
-
from scout.server.blueprints.variant.controllers import
|
30
|
+
from scout.server.blueprints.variant.controllers import (
|
31
|
+
observations,
|
32
|
+
str_variant_reviewer,
|
33
|
+
)
|
31
34
|
from scout.server.blueprints.variant.controllers import variant as variant_controller
|
32
35
|
from scout.server.blueprints.variant.controllers import variant_acmg as acmg_controller
|
33
|
-
from scout.server.blueprints.variant.controllers import
|
36
|
+
from scout.server.blueprints.variant.controllers import (
|
37
|
+
variant_acmg_post,
|
38
|
+
)
|
34
39
|
from scout.server.blueprints.variant.controllers import variant_ccv as ccv_controller
|
35
|
-
from scout.server.blueprints.variant.controllers import
|
40
|
+
from scout.server.blueprints.variant.controllers import (
|
41
|
+
variant_ccv_post,
|
42
|
+
)
|
36
43
|
from scout.server.blueprints.variant.verification_controllers import (
|
37
44
|
MissingVerificationRecipientError,
|
38
45
|
variant_verification,
|
@@ -374,7 +381,6 @@ def variant_update(institute_id, case_name, variant_id):
|
|
374
381
|
@templated("variant/acmg.html")
|
375
382
|
def evaluation(evaluation_id):
|
376
383
|
"""Show, edit or delete an ACMG evaluation."""
|
377
|
-
|
378
384
|
evaluation_obj = store.get_evaluation(evaluation_id)
|
379
385
|
if evaluation_obj is None:
|
380
386
|
flash("Evaluation was not found in database", "warning")
|
@@ -392,7 +398,7 @@ def evaluation(evaluation_id):
|
|
392
398
|
if check_reset_variant_classification(store, evaluation_obj, link):
|
393
399
|
flash("Cleared ACMG classification.", "info")
|
394
400
|
|
395
|
-
return redirect(
|
401
|
+
return redirect(request.referrer)
|
396
402
|
|
397
403
|
return dict(
|
398
404
|
evaluation=evaluation_obj,
|
@@ -16,7 +16,7 @@ from wtforms import (
|
|
16
16
|
SubmitField,
|
17
17
|
validators,
|
18
18
|
)
|
19
|
-
from wtforms.widgets import TextInput
|
19
|
+
from wtforms.widgets import NumberInput, TextInput
|
20
20
|
|
21
21
|
from scout.constants import (
|
22
22
|
CALLERS,
|
@@ -132,8 +132,22 @@ class VariantFiltersForm(FlaskForm):
|
|
132
132
|
)
|
133
133
|
|
134
134
|
chrom = NonValidatingSelectMultipleField("Chromosome", choices=[], default="")
|
135
|
-
start = IntegerField(
|
136
|
-
|
135
|
+
start = IntegerField(
|
136
|
+
"Start position",
|
137
|
+
[
|
138
|
+
validators.Optional(),
|
139
|
+
validators.NumberRange(min=0, message="Start position must be 1 or greater."),
|
140
|
+
],
|
141
|
+
widget=NumberInput(min=1),
|
142
|
+
)
|
143
|
+
end = IntegerField(
|
144
|
+
"End position",
|
145
|
+
[
|
146
|
+
validators.Optional(),
|
147
|
+
validators.NumberRange(min=0, message="End position must be 1 or greater."),
|
148
|
+
],
|
149
|
+
widget=NumberInput(min=1),
|
150
|
+
)
|
137
151
|
cytoband_start = NonValidatingSelectField("Cytoband start", choices=[])
|
138
152
|
cytoband_end = NonValidatingSelectField("Cytoband end", choices=[])
|
139
153
|
|
@@ -264,8 +278,22 @@ class OutlierFiltersForm(FlaskForm):
|
|
264
278
|
)
|
265
279
|
|
266
280
|
chrom = NonValidatingSelectMultipleField("Chromosome", choices=[], default="")
|
267
|
-
start = IntegerField(
|
268
|
-
|
281
|
+
start = IntegerField(
|
282
|
+
"Start position",
|
283
|
+
[
|
284
|
+
validators.Optional(),
|
285
|
+
validators.NumberRange(min=1, message="Start position must be 0 or greater."),
|
286
|
+
],
|
287
|
+
widget=NumberInput(min=1),
|
288
|
+
)
|
289
|
+
end = IntegerField(
|
290
|
+
"End position",
|
291
|
+
[
|
292
|
+
validators.Optional(),
|
293
|
+
validators.NumberRange(min=1, message="End position must be 0 or greater."),
|
294
|
+
],
|
295
|
+
widget=NumberInput(min=1),
|
296
|
+
)
|
269
297
|
cytoband_start = NonValidatingSelectField("Cytoband start", choices=[])
|
270
298
|
cytoband_end = NonValidatingSelectField("Cytoband end", choices=[])
|
271
299
|
|
@@ -83,17 +83,18 @@
|
|
83
83
|
</td>
|
84
84
|
<td>{{ gene_cell(variant) }}</td>
|
85
85
|
<td>
|
86
|
+
|
86
87
|
<a target="_blank" href="{{ url_for('variant.cancer_variant', institute_id=institute._id, case_name=case.display_name,
|
87
88
|
variant_id=variant._id, cancer='yes') }}">
|
88
89
|
{% if variant.first_rep_gene.hgvs_identifier %}
|
89
|
-
<div>{{ variant.first_rep_gene.hgvs_identifier }}</div>
|
90
|
-
<div>{{ (variant.first_rep_gene.hgvsp_identifier or '') |url_decode }}</div>
|
90
|
+
<div>{{ variant.first_rep_gene.hgvs_identifier|truncate(30, True) }}</div>
|
91
|
+
<div>{{ (variant.first_rep_gene.hgvsp_identifier or '') |url_decode|truncate(30, True) }}</div>
|
91
92
|
{% else %}
|
92
|
-
<div>{{ variant.reference }}→
|
93
|
+
<div>{{ variant.reference|truncate(30, True) }}→
|
93
94
|
{% if variant.alternative | length > 5 %}
|
94
95
|
{{ variant.alternative[0] }}...{{ variant.alternative[-1] }}
|
95
96
|
{% else %}
|
96
|
-
{{ variant.alternative }}
|
97
|
+
{{ variant.alternative|truncate(30, True) }}
|
97
98
|
{% endif %}
|
98
99
|
</div>
|
99
100
|
{% endif %}
|