scout-browser 4.82.2__py3-none-any.whl → 4.84__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/__version__.py +1 -1
- scout/adapter/client.py +1 -0
- scout/adapter/mongo/base.py +0 -1
- scout/adapter/mongo/case.py +19 -37
- scout/adapter/mongo/case_events.py +98 -2
- scout/adapter/mongo/hgnc.py +39 -22
- scout/adapter/mongo/institute.py +3 -9
- scout/adapter/mongo/panel.py +2 -1
- scout/adapter/mongo/variant.py +12 -2
- scout/adapter/mongo/variant_loader.py +156 -141
- scout/build/genes/hgnc_gene.py +5 -134
- scout/commands/base.py +1 -0
- scout/commands/download/ensembl.py +1 -0
- scout/commands/download/everything.py +1 -0
- scout/commands/download/exac.py +1 -0
- scout/commands/download/hgnc.py +1 -0
- scout/commands/download/hpo.py +1 -0
- scout/commands/download/omim.py +1 -0
- scout/commands/export/database.py +1 -0
- scout/commands/load/panel.py +1 -0
- scout/commands/load/report.py +1 -0
- scout/commands/update/case.py +10 -10
- scout/commands/update/individual.py +6 -1
- scout/commands/update/omim.py +1 -0
- scout/commands/update/panelapp.py +1 -0
- scout/constants/file_types.py +86 -13
- scout/export/exon.py +1 -0
- scout/load/__init__.py +0 -1
- scout/load/all.py +8 -5
- scout/load/hgnc_gene.py +1 -1
- scout/load/panel.py +8 -4
- scout/load/setup.py +1 -0
- scout/models/case/case_loading_models.py +6 -16
- scout/models/hgnc_map.py +50 -87
- scout/models/phenotype_term.py +3 -3
- scout/parse/case.py +0 -1
- scout/parse/disease_terms.py +1 -0
- scout/parse/omim.py +1 -0
- scout/parse/orpha.py +1 -0
- scout/parse/panel.py +40 -15
- scout/parse/variant/conservation.py +1 -0
- scout/resources/__init__.py +3 -0
- scout/server/app.py +4 -50
- scout/server/blueprints/alignviewers/controllers.py +15 -17
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +13 -3
- scout/server/blueprints/alignviewers/views.py +10 -15
- scout/server/blueprints/cases/controllers.py +70 -73
- scout/server/blueprints/cases/templates/cases/case.html +94 -71
- scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +1 -1
- scout/server/blueprints/cases/templates/cases/phenotype.html +8 -6
- scout/server/blueprints/cases/templates/cases/utils.html +3 -3
- scout/server/blueprints/cases/views.py +8 -6
- scout/server/blueprints/panels/forms.py +1 -0
- scout/server/blueprints/variant/controllers.py +14 -19
- scout/server/blueprints/variant/templates/variant/acmg.html +25 -16
- scout/server/blueprints/variant/templates/variant/components.html +11 -6
- scout/server/blueprints/variant/views.py +5 -2
- scout/server/blueprints/variants/controllers.py +12 -28
- scout/server/blueprints/variants/views.py +1 -1
- scout/server/config.py +16 -4
- scout/server/extensions/__init__.py +4 -2
- scout/server/extensions/beacon_extension.py +1 -0
- scout/server/extensions/bionano_extension.py +1 -0
- scout/server/extensions/chanjo_extension.py +59 -0
- scout/server/extensions/gens_extension.py +1 -0
- scout/server/extensions/ldap_extension.py +5 -3
- scout/server/extensions/loqus_extension.py +16 -14
- scout/server/extensions/matchmaker_extension.py +1 -0
- scout/server/extensions/mongo_extension.py +1 -0
- scout/server/extensions/phenopacket_extension.py +1 -0
- scout/server/extensions/rerunner_extension.py +1 -0
- scout/server/links.py +4 -4
- scout/server/static/bs_styles.css +20 -2
- scout/server/utils.py +16 -2
- scout/utils/acmg.py +33 -20
- scout/utils/ensembl_rest_clients.py +1 -0
- scout/utils/scout_requests.py +1 -0
- scout/utils/sort.py +21 -0
- scout/utils/track_resources.py +70 -0
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/METADATA +2 -5
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/RECORD +85 -84
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/WHEEL +1 -1
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/entry_points.txt +0 -1
- scout/load/case.py +0 -36
- scout/utils/cloud_resources.py +0 -61
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/LICENSE +0 -0
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/top_level.txt +0 -0
@@ -65,7 +65,7 @@
|
|
65
65
|
<span class="badge rounded-pill bg-info text-body" data-bs-toggle="tooltip" data-bs-placement="left" title="Matching causatives and managed variants displayed on case page are NOT filtered by gene panel. Use caution to avoid incidental findings.">off</span>
|
66
66
|
{% endif %}
|
67
67
|
</div>
|
68
|
-
</div> <!--end of
|
68
|
+
</div> <!--end of row -->
|
69
69
|
<div class="row">
|
70
70
|
<form id="case_status_form" method="POST" action="{{ url_for('cases.status', institute_id=institute._id, case_name=case.display_name) }}">
|
71
71
|
<div class="btn-toolbar ms-2" role="toolbar">
|
@@ -105,7 +105,7 @@
|
|
105
105
|
{{ 'Inactive' if case.status == 'ignored' else 'Ignored' }}
|
106
106
|
</button>
|
107
107
|
</div>
|
108
|
-
<div class="btn-group ms-2 role="group">
|
108
|
+
<div class="btn-group ms-2" role="group">
|
109
109
|
<select name="tags" id="status_tags_case" multiple class="selectpicker" data-style="btn btn-secondary">
|
110
110
|
{% for tag, data in case_tag_options.items() %}
|
111
111
|
<option {% if 'tags' in case and tag~"" in case.tags %} selected {% endif %} value="{{ tag }}" title="{{ data.label }}">
|
@@ -119,87 +119,65 @@
|
|
119
119
|
</div>
|
120
120
|
</div>
|
121
121
|
|
122
|
+
{{ matching_variants() }}
|
123
|
+
|
122
124
|
<div class="card panel-default" >
|
123
|
-
|
124
|
-
|
125
|
-
<div class="col
|
126
|
-
|
127
|
-
{% endif %}
|
128
|
-
{% if default_other_causatives|length > 0%}
|
129
|
-
<div class="row mt-0">
|
130
|
-
<div class="col-xs-12 col-md-12">{{ matching_causatives(default_other_causatives, institute, case, default=True) }}</div>
|
125
|
+
<div class="row">
|
126
|
+
<div class="col">{{ causatives_list(causatives, partial_causatives, evaluated_variants, institute, case, manual_rank_options, cancer_tier_options) }}</div>
|
127
|
+
<div class="col">{{ suspects_list(suspects, institute, case, manual_rank_options, cancer_tier_options) }}</div>
|
128
|
+
<!-- end of data sharing panels -->
|
131
129
|
</div>
|
132
|
-
{% endif %}
|
133
130
|
|
134
|
-
|
135
|
-
|
136
|
-
|
131
|
+
<div class="row">
|
132
|
+
{% if case.track == 'cancer' %}
|
133
|
+
<div class="col-sm-12 col-md-12 mt-3">{{ cancer_individuals_table(case, institute, tissue_types, gens_info) }}</div>
|
134
|
+
{% else %}
|
135
|
+
<div class="mt-3 col-sm-8 col-md-{{"8" if case.madeline_info and case.individuals|length > 1 else "12"}}">{{ individuals_table(case, institute, tissue_types, display_rerunner, gens_info) }}</div>
|
136
|
+
{% if case.madeline_info and case.individuals|length > 1 %}
|
137
|
+
<div class="col-sm-4">
|
138
|
+
{{ pedigree_panel(case) }}
|
139
|
+
</div>
|
140
|
+
{% endif %}
|
141
|
+
{% endif %}
|
137
142
|
</div>
|
138
|
-
{% endif %}
|
139
143
|
|
140
|
-
|
141
|
-
|
142
|
-
<div class="col-
|
144
|
+
<div class="row mt-3">
|
145
|
+
<div class="col-6">{{ synopsis_panel() }}</div>
|
146
|
+
<div class="col-6">{{ comments_panel(institute, case, current_user, comments) }}</div>
|
143
147
|
</div>
|
144
|
-
{% endif %}
|
145
|
-
</div>
|
146
|
-
|
147
|
-
<div class="row">
|
148
|
-
<div class="col">{{ causatives_list(causatives, partial_causatives, evaluated_variants, institute, case, manual_rank_options, cancer_tier_options) }}</div>
|
149
|
-
<div class="col">{{ suspects_list(suspects, institute, case, manual_rank_options, cancer_tier_options) }}</div>
|
150
|
-
<!-- end of data sharing panels -->
|
151
|
-
</div>
|
152
|
-
|
153
|
-
<div class="row">
|
154
|
-
{% if case.track == 'cancer' %}
|
155
|
-
<div class="col-sm-12 col-md-12 mt-3">{{ cancer_individuals_table(case, institute, tissue_types, gens_info) }}</div>
|
156
|
-
{% else %}
|
157
|
-
<div class="mt-3 col-sm-8 col-md-{{"8" if case.madeline_info and case.individuals|length > 1 else "12"}}">{{ individuals_table(case, institute, tissue_types, display_rerunner, gens_info) }}</div>
|
158
|
-
{% if case.madeline_info and case.individuals|length > 1 %}
|
159
|
-
<div class="col-sm-4">
|
160
|
-
{{ pedigree_panel(case) }}
|
161
|
-
</div>
|
162
|
-
{% endif %}
|
163
|
-
{% endif %}
|
164
|
-
</div>
|
165
148
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
<div class="row">
|
172
|
-
<div class="col-sm-12">
|
173
|
-
{{ insert_multi_image_panel() }}
|
149
|
+
<div class="row">
|
150
|
+
<div class="col-sm-12">
|
151
|
+
{{ insert_multi_image_panel() }}
|
152
|
+
</div>
|
174
153
|
</div>
|
175
|
-
</div>
|
176
154
|
|
177
|
-
|
178
|
-
|
179
|
-
|
155
|
+
<div class="row">
|
156
|
+
<div class="col-sm-12">
|
157
|
+
{{ custom_image_panels() }}
|
158
|
+
</div>
|
180
159
|
</div>
|
181
|
-
</div>
|
182
160
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
161
|
+
<!-- CASE DIAGNOSES AND PHENOTYPES -->
|
162
|
+
<div class="panel-default">
|
163
|
+
<div class="panel-heading"><span class="fa fa-user-md"></span> Phenotypes & diagnoses</div>
|
164
|
+
<div class="row">
|
165
|
+
<div class="col-sm-6 ">
|
166
|
+
<div class="card h-100">
|
167
|
+
<div class="card-body">
|
168
|
+
{{ hpo_panel(case, institute, config) }}
|
169
|
+
</div>
|
191
170
|
</div>
|
192
171
|
</div>
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
172
|
+
<div class="col-sm-6">
|
173
|
+
<div class="card h-100">
|
174
|
+
<div class="card-body">
|
175
|
+
{{ hpo_genelist_panel(case, institute, config) }}
|
176
|
+
</div>
|
198
177
|
</div>
|
199
178
|
</div>
|
200
|
-
</div>
|
201
|
-
</div> <!--end of
|
202
|
-
</div>
|
179
|
+
</div> <!--end of row>-->
|
180
|
+
</div> <!--end of card panel-default -->
|
203
181
|
|
204
182
|
<!-- diagnoses-related code-->
|
205
183
|
{% if not case.track == 'cancer' %}
|
@@ -243,8 +221,8 @@
|
|
243
221
|
{{ reanalysis_modal(institute, case) }}
|
244
222
|
{{ beacon_modal(institute, case) }}
|
245
223
|
{{ matchmaker_modal(institute, case, suspects, mme_nodes) }}
|
246
|
-
</div><!-- end of
|
247
|
-
</div><!-- end of
|
224
|
+
</div><!-- end of containter -->
|
225
|
+
</div><!-- end of col -->
|
248
226
|
{% endmacro %}
|
249
227
|
|
250
228
|
{% macro variants_buttons() %}
|
@@ -382,7 +360,7 @@
|
|
382
360
|
</div>
|
383
361
|
{% endfor %}
|
384
362
|
{% endif %}
|
385
|
-
</div> <!-- end of
|
363
|
+
</div> <!-- end of list-group -->
|
386
364
|
</div>
|
387
365
|
</div>
|
388
366
|
{% endmacro %}
|
@@ -558,6 +536,51 @@
|
|
558
536
|
</div>
|
559
537
|
{% endmacro %}
|
560
538
|
|
539
|
+
{% macro matching_variants() %}
|
540
|
+
<div class="card mt-3">
|
541
|
+
<div class="row mt-0 ms-3">
|
542
|
+
<div class="col-sm-12 col-md-12">
|
543
|
+
<div data-bs-toggle='tooltip' class="panel-heading" title="Check if there are any variants in this case
|
544
|
+
marked as causative in another case for this institute, or are on the managed variants list.">
|
545
|
+
<strong>
|
546
|
+
{% if hide_matching == false %}
|
547
|
+
<a href="{{ url_for('cases.case', institute_id=institute._id, case_name=case.display_name, hide_matching='True') }}" class="text-body"><span class="me-1 fa fa-caret-down"></span>Search for matching causatives and managed variants</a>
|
548
|
+
{% else %}
|
549
|
+
<a href="{{ url_for('cases.case', institute_id=institute._id, case_name=case.display_name, hide_matching='False') }}" class="text-body"><span class="me-1 fa fa-caret-right"></span>Search for matching causatives and managed variants</a>
|
550
|
+
{% endif %}
|
551
|
+
</strong>
|
552
|
+
</div>
|
553
|
+
</div>
|
554
|
+
{% if hide_matching == false %}
|
555
|
+
{% if other_causatives|length > 0 %}
|
556
|
+
<div class="row mt-0 ms-3">
|
557
|
+
<div class="col-xs-12 col-md-12">{{ matching_causatives(other_causatives, institute, case) }}</div>
|
558
|
+
</div>
|
559
|
+
{% endif %}
|
560
|
+
{% if default_other_causatives|length > 0%}
|
561
|
+
<div class="row mt-0 ms-3">
|
562
|
+
<div class="col-xs-12 col-md-12">{{ matching_causatives(default_other_causatives, institute, case, default=True) }}</div>
|
563
|
+
</div>
|
564
|
+
{% endif %}
|
565
|
+
{% if managed_variants|length > 0%}
|
566
|
+
<div class="row mt-0 ms-3">
|
567
|
+
<div class="col-sm-12 col-md-12">{{ matching_managed_variants(managed_variants, institute, case) }}</div>
|
568
|
+
</div>
|
569
|
+
{% endif %}
|
570
|
+
{% if default_managed_variants|length > 0%}
|
571
|
+
<div class="row mt-0 ms-3">
|
572
|
+
<div class="col-sm-12 col-md-12">{{ matching_managed_variants(default_managed_variants, institute, case, default=True) }}</div>
|
573
|
+
</div>
|
574
|
+
{% endif %}
|
575
|
+
{% if other_causatives|length == 0 and default_other_causatives|length == 0 and managed_variants|length == 0 and default_managed_variants|length == 0%}
|
576
|
+
<div class="row mt-0 ms-3">
|
577
|
+
<div class="col-sm-12 col-md-12">No matching causatives or managed variants found</div>
|
578
|
+
</div>
|
579
|
+
{% endif %}
|
580
|
+
{% endif %}
|
581
|
+
</div>
|
582
|
+
</div>
|
583
|
+
{% endmacro %}
|
561
584
|
|
562
585
|
{% block scripts %}
|
563
586
|
{{ super() }}
|
@@ -108,7 +108,7 @@
|
|
108
108
|
{% endif %}
|
109
109
|
|
110
110
|
<!-- Display mtDNA report for non-cancer cases -->
|
111
|
-
{% if case.
|
111
|
+
{% if case.mtdna_report %}
|
112
112
|
<div href="#" class="bg-dark list-group-item text-white">
|
113
113
|
<div class="d-flex flex-row flex-fill bd-highlight">
|
114
114
|
<div>
|
@@ -236,11 +236,13 @@
|
|
236
236
|
<!-- Display and remove added HPO terms -->
|
237
237
|
<div class="row mt-3">
|
238
238
|
<div class="col-12 ms-3">
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
239
|
+
{% if "phenotype_terms" in case and case.phenotype_terms|length > 0 %}
|
240
|
+
{% for hpo_term in case.phenotype_terms %}
|
241
|
+
{{ hpo_item(hpo_term, case) }}
|
242
|
+
{% endfor %}
|
243
|
+
{% else %}
|
244
|
+
<span class="text-mute">No phenotypes added yet</span>
|
245
|
+
{% endif %}
|
244
246
|
</div>
|
245
247
|
</div>
|
246
248
|
|
@@ -251,7 +253,7 @@
|
|
251
253
|
<input class="ms-3" name="min_match" type="number" min="0" step="1" placeholder="Min matches" style="width:130px"/>
|
252
254
|
{% if config.PHENOMIZER_USERNAME %}
|
253
255
|
<button class="btn btn-secondary btn-sm" type="submit" name="action" value="PHENOMIZER"
|
254
|
-
{%if case.phenotype_terms|length == 0 %} disabled {%endif%}>Phenomizer</button>
|
256
|
+
{% if "phenotype_terms" not in case or case.phenotype_terms|length == 0 %} disabled {% endif %}>Phenomizer</button>
|
255
257
|
{% endif %}
|
256
258
|
</div>
|
257
259
|
<div class="col-1">
|
@@ -633,8 +633,8 @@
|
|
633
633
|
|
634
634
|
{% macro matching_causatives(other_causatives, institute, case, default=False) %}
|
635
635
|
<div data-bs-toggle='tooltip' class="panel-heading" title="If there are any variants in this case
|
636
|
-
|
637
|
-
<strong><a data-bs-toggle="collapse" href="#matchingCausatives{% if default %}Default{% endif %}" class="text-body">Matching causatives {% if default %}in case default panels{% endif %}({{other_causatives|length}})</a></strong>
|
636
|
+
matching a causative in another case for this institute. {% if default %}Variants in default panels for the case only.{% endif %}">
|
637
|
+
<strong><a data-bs-toggle="collapse" href="#matchingCausatives{% if default %}Default{% endif %}" class="text-body" aria-expanded="false"><span class="collapse-icon me-1"></span>Matching causatives {% if default %}in case default panels{% endif %}({{other_causatives|length}})</a></strong>
|
638
638
|
</div>
|
639
639
|
<ul class="list-group collapse" id="matchingCausatives{% if default %}Default{% endif %}">
|
640
640
|
{% for variant in other_causatives %}
|
@@ -650,7 +650,7 @@
|
|
650
650
|
{% macro matching_managed_variants(managed_variants, institute, case, default=False) %}
|
651
651
|
<div data-bs-toggle='tooltip' class="panel-heading" title="Any variants in this case
|
652
652
|
that have been marked as managed. {% if default %}Variants in default panels for the case only.{% endif %}">
|
653
|
-
<strong><a data-bs-toggle="collapse" class="text-body" href="#matchingManaged{% if default %}Default{% endif %}">Managed variants {% if default %}in case default panels{% endif %}({{managed_variants|length}})</a></strong>
|
653
|
+
<strong><a data-bs-toggle="collapse" class="text-body" href="#matchingManaged{% if default %}Default{% endif %}" aria-expanded="false"><span class="collapse-icon me-1"></span>Managed variants {% if default %}in case default panels{% endif %}({{managed_variants|length}})</a></strong>
|
654
654
|
</div>
|
655
655
|
<ul class="list-group">
|
656
656
|
{% for variant in managed_variants %}
|
@@ -4,6 +4,7 @@ import json
|
|
4
4
|
import logging
|
5
5
|
import os.path
|
6
6
|
import shutil
|
7
|
+
from ast import literal_eval
|
7
8
|
from io import BytesIO
|
8
9
|
from operator import itemgetter
|
9
10
|
from typing import Generator, Optional, Union
|
@@ -86,7 +87,6 @@ def case(
|
|
86
87
|
So do case_id, but we still call institute_and_case again to fetch institute
|
87
88
|
and reuse its user access verification.
|
88
89
|
"""
|
89
|
-
|
90
90
|
if case_id:
|
91
91
|
case_obj = store.case(case_id=case_id, projection={"display_name": 1, "owner": 1})
|
92
92
|
|
@@ -102,7 +102,12 @@ def case(
|
|
102
102
|
flash("Case {} does not exist in database!".format(case_name))
|
103
103
|
return redirect(request.referrer)
|
104
104
|
|
105
|
-
|
105
|
+
hide_matching = (
|
106
|
+
literal_eval(request.args.get("hide_matching"))
|
107
|
+
if request.args.get("hide_matching")
|
108
|
+
else True
|
109
|
+
)
|
110
|
+
data = controllers.case(store, institute_obj, case_obj, hide_matching)
|
106
111
|
|
107
112
|
return dict(
|
108
113
|
**data,
|
@@ -271,10 +276,7 @@ def pdf_case_report(institute_id, case_name):
|
|
271
276
|
store=store, institute_obj=institute_obj, case_obj=case_obj
|
272
277
|
)
|
273
278
|
# add coverage report on the bottom of this report
|
274
|
-
if (
|
275
|
-
current_app.config.get("SQLALCHEMY_DATABASE_URI")
|
276
|
-
and case_obj.get("track", "rare") != "cancer"
|
277
|
-
):
|
279
|
+
if current_app.config.get("chanjo_report") and case_obj.get("track", "rare") != "cancer":
|
278
280
|
data["coverage_report"] = controllers.coverage_report_contents(
|
279
281
|
request.url_root, institute_obj, case_obj
|
280
282
|
)
|
@@ -28,7 +28,7 @@ from scout.server.blueprints.variant.utils import (
|
|
28
28
|
update_variant_case_panels,
|
29
29
|
)
|
30
30
|
from scout.server.blueprints.variants.utils import update_case_panels
|
31
|
-
from scout.server.extensions import LoqusDB,
|
31
|
+
from scout.server.extensions import LoqusDB, config_igv_tracks, gens
|
32
32
|
from scout.server.links import disease_link, get_variant_links
|
33
33
|
from scout.server.utils import (
|
34
34
|
case_has_alignments,
|
@@ -142,9 +142,9 @@ def get_igv_tracks(build: str = "37") -> set:
|
|
142
142
|
# Collect hardcoded tracks, common for all Scout instances
|
143
143
|
for track in IGV_TRACKS.get(build, []):
|
144
144
|
igv_tracks.add(track.get("name"))
|
145
|
-
# Collect instance-
|
146
|
-
if hasattr(
|
147
|
-
for track in
|
145
|
+
# Collect instance-specific public tracks, if available
|
146
|
+
if hasattr(config_igv_tracks, "tracks"):
|
147
|
+
for track in config_igv_tracks.tracks.get(build, []):
|
148
148
|
igv_tracks.add(track.get("name"))
|
149
149
|
return igv_tracks
|
150
150
|
|
@@ -372,7 +372,7 @@ def variant(
|
|
372
372
|
"ACMG_OPTIONS": ACMG_OPTIONS,
|
373
373
|
"case_tag_options": CASE_TAGS,
|
374
374
|
"inherit_palette": INHERITANCE_PALETTE,
|
375
|
-
"igv_tracks": get_igv_tracks(genome_build),
|
375
|
+
"igv_tracks": get_igv_tracks("38" if variant_obj["is_mitochondrial"] else genome_build),
|
376
376
|
"has_rna_tracks": case_has_rna_tracks(case_obj),
|
377
377
|
"gens_info": gens.connection_settings(genome_build),
|
378
378
|
"evaluations": evaluations,
|
@@ -486,26 +486,21 @@ def observations(store: MongoAdapter, loqusdb: LoqusDB, variant_obj: dict) -> Di
|
|
486
486
|
loqus_query = loqusdb.get_loqus_query(variant_obj, category)
|
487
487
|
|
488
488
|
for loqus_id in inst_loqus_ids: # Loop over all loqusdb instances of an institute
|
489
|
-
|
490
|
-
|
489
|
+
# collect observation on that loqusdb instance
|
490
|
+
obs_data[loqus_id] = loqusdb.get_variant(loqus_query, loqusdb_id=loqus_id)
|
491
491
|
|
492
|
-
if
|
492
|
+
if obs_data[loqus_id] is None:
|
493
493
|
flash(
|
494
|
-
f"Could not
|
494
|
+
f"Could not find a Loqus instance with id:{loqus_id}",
|
495
495
|
"warning",
|
496
496
|
)
|
497
|
-
obs_data[loqus_id]["
|
497
|
+
obs_data[loqus_id]["observations"] = "N/A"
|
498
498
|
continue
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
if not obs_data[loqus_id]: # data is an empty dictionary
|
504
|
-
# Collect count of variants in variant's case
|
505
|
-
obs_data[loqus_id] = loqusdb.get_variant(loqus_query, loqusdb_id=loqus_id)
|
506
|
-
if obs_data[loqus_id].get("total"):
|
507
|
-
obs_data[loqus_id]["observations"] = 0
|
499
|
+
|
500
|
+
if obs_data[loqus_id] == {}: # Variant was not found
|
501
|
+
obs_data[loqus_id]["observations"] = 0
|
508
502
|
continue
|
503
|
+
|
509
504
|
# Check if the current case is represented in the loqusdb instance
|
510
505
|
obs_data[loqus_id]["case_match"] = variant_obj["case_id"] in obs_data[loqus_id].get(
|
511
506
|
"families", []
|
@@ -29,22 +29,27 @@
|
|
29
29
|
<form action="{{ url_for('variant.variant_acmg', institute_id=institute._id, case_name=case.display_name, variant_id=variant._id) }}" method="POST">
|
30
30
|
<div class="card panel-default mt-3">
|
31
31
|
<div class="card-body">
|
32
|
-
{% if
|
33
|
-
|
34
|
-
<div class="text-center">
|
35
|
-
{% for option in ACMG_OPTIONS %}
|
36
|
-
<a id="acmg-{{ option.code }}" class="btn acmg-preview">{{ option.label }}</a>
|
37
|
-
{% endfor %}
|
38
|
-
</div>
|
39
|
-
</div>
|
40
|
-
<button class="btn btn-primary form-control">Submit</button>
|
41
|
-
{% else %}
|
42
|
-
<h4>
|
32
|
+
{% if evaluation %}
|
33
|
+
<h4>
|
43
34
|
{{ evaluation.classification.label }}
|
44
35
|
<span class="badge bg-info">{{ evaluation.classification.short }}</span>
|
45
36
|
</h4>
|
46
37
|
By {{ evaluation.user_name }} on {{ evaluation.created_at.date() }}
|
38
|
+
{% if edit %}
|
39
|
+
<br><br>
|
40
|
+
<button class="btn btn-primary form-control" data-bs-toggle="tooltip" title="Editing this classification will result in a new classification">Reclassify</button>
|
41
|
+
{% endif %}
|
42
|
+
{% elif not evaluation %}
|
43
|
+
<button class="btn btn-primary form-control">Submit</button>
|
47
44
|
{% endif %}
|
45
|
+
<!-- classification preview in the footer-->
|
46
|
+
<div class="mt-3 fixed-bottom bg-light border">
|
47
|
+
<div class="text-center">
|
48
|
+
{% for option in ACMG_OPTIONS %}
|
49
|
+
<a id="acmg-{{ option.code }}" class="btn acmg-preview">{{ option.label }}</a>
|
50
|
+
{% endfor %}
|
51
|
+
</div>
|
52
|
+
</div>
|
48
53
|
</div>
|
49
54
|
</div>
|
50
55
|
|
@@ -72,14 +77,14 @@
|
|
72
77
|
{{ criterion.short }}
|
73
78
|
</div>
|
74
79
|
<div class="form-check form-switch">
|
75
|
-
<input type="checkbox" class="form-check-input" id="checkbox-{{ criterion_code }}" name="criteria" value="{{ criterion_code }}" {{ 'checked' if evaluation and criterion_code in evaluation.criteria }} {{ 'disabled' if evaluation }}>
|
80
|
+
<input type="checkbox" class="form-check-input" id="checkbox-{{ criterion_code }}" name="criteria" value="{{ criterion_code }}" {{ 'checked' if evaluation and criterion_code in evaluation.criteria }} {{ 'disabled' if evaluation and edit is false}}>
|
76
81
|
<label class="form-check-label" for="checkbox-{{ criterion_code }}"></label>
|
77
82
|
</div>
|
78
83
|
</div>
|
79
84
|
<div id="comment-{{ criterion_code }}" class="{{ 'collapse' if not (comment or link or modifier) }} mt-2">
|
80
85
|
{% if criterion.documentation %}<div class="row"><span class="me-1 fw-light text-wrap">{{ criterion.documentation | safe}}</span></div>{% endif %}
|
81
86
|
<div class="row">
|
82
|
-
<select {{ 'disabled' if evaluation }} id="modifier-{{ criterion_code }}" name="modifier-{{ criterion_code }}" class="form-control form-select">
|
87
|
+
<select {{ 'disabled' if evaluation and edit is false}} id="modifier-{{ criterion_code }}" name="modifier-{{ criterion_code }}" class="form-control form-select">
|
83
88
|
<option value="" {% if not modifier %}selected{% endif %}>Strength modifier...</option>
|
84
89
|
{% for level in "Strong", "Moderate", "Supporting" %}
|
85
90
|
{% if(level != evidence) %}
|
@@ -89,10 +94,10 @@
|
|
89
94
|
</select>
|
90
95
|
</div>
|
91
96
|
<div class="row">
|
92
|
-
<textarea {{ 'disabled' if evaluation }} class="form-control"
|
97
|
+
<textarea {{ 'disabled' if evaluation and edit is false }} class="form-control"
|
93
98
|
name="comment-{{ criterion_code }}" rows="3" placeholder="Comment (optional)">{{ comment }}</textarea>
|
94
99
|
</div>
|
95
|
-
<input type="url" {{ 'disabled' if evaluation }} class="form-control" name="link-{{ criterion_code }}"
|
100
|
+
<input type="url" {{ 'disabled' if evaluation and edit is false }} class="form-control" name="link-{{ criterion_code }}"
|
96
101
|
placeholder="{{ link or 'Supporting link (optional)' }}" value="">
|
97
102
|
</div>
|
98
103
|
</div>
|
@@ -120,6 +125,11 @@
|
|
120
125
|
{{ super() }}
|
121
126
|
|
122
127
|
<script>
|
128
|
+
|
129
|
+
window.onload=function() {
|
130
|
+
update_classification();
|
131
|
+
}
|
132
|
+
|
123
133
|
$(function () {
|
124
134
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
125
135
|
|
@@ -130,7 +140,6 @@
|
|
130
140
|
} else {
|
131
141
|
el.collapse('hide');
|
132
142
|
}
|
133
|
-
|
134
143
|
update_classification()
|
135
144
|
});
|
136
145
|
|
@@ -49,14 +49,19 @@
|
|
49
49
|
{% endif %}
|
50
50
|
</span>
|
51
51
|
</div>
|
52
|
-
<
|
53
|
-
<
|
52
|
+
<span>
|
53
|
+
<small class="text-muted">
|
54
54
|
{{ data.user_name }} on {{ data.created_at.date() }}
|
55
55
|
{% if current_variant %}
|
56
|
-
|
56
|
+
<form action="{{ url_for('variant.evaluation', evaluation_id=data._id) }}" method="POST" style="display: inline-block;">
|
57
|
+
<button class="btn btn-xs btn-link" >Delete</button>
|
58
|
+
</form>
|
59
|
+
{% if data.criteria %}
|
60
|
+
<a class="btn btn-xs btn-link" href="{{ url_for('variant.evaluation', evaluation_id=data._id, edit=True) }}" data-bs-toggle="tooltip" title="Editing this classification will result in a new classification">Edit</a>
|
61
|
+
{% endif %}
|
57
62
|
{% endif %}
|
58
|
-
</
|
59
|
-
</
|
63
|
+
</small>
|
64
|
+
</span>
|
60
65
|
</li>
|
61
66
|
{% endmacro %}
|
62
67
|
|
@@ -90,7 +95,7 @@
|
|
90
95
|
{% endif %}
|
91
96
|
</div>
|
92
97
|
{% endfor %}
|
93
|
-
{% if config.
|
98
|
+
{% if config.chanjo_report %}
|
94
99
|
<div class="d-flex flex-wrap ms-1">
|
95
100
|
{% for gene in variant.genes %}
|
96
101
|
<a class="btn btn-sm btn-secondary text-white" rel="noopener noreferrer" target="_blank" href="{{ url_for('report.gene', gene_id=gene.hgnc_id, sample_id=variant.samples|map(attribute='sample_id')|list) }}">
|
@@ -13,7 +13,7 @@ from flask import (
|
|
13
13
|
from flask_login import current_user
|
14
14
|
from markupsafe import Markup
|
15
15
|
|
16
|
-
from scout.constants import ACMG_CRITERIA, ACMG_MAP
|
16
|
+
from scout.constants import ACMG_CRITERIA, ACMG_MAP, ACMG_OPTIONS
|
17
17
|
from scout.server.blueprints.variant.controllers import check_reset_variant_classification
|
18
18
|
from scout.server.blueprints.variant.controllers import evaluation as evaluation_controller
|
19
19
|
from scout.server.blueprints.variant.controllers import observations, str_variant_reviewer
|
@@ -308,7 +308,7 @@ def variant_update(institute_id, case_name, variant_id):
|
|
308
308
|
@variant_bp.route("/evaluations/<evaluation_id>", methods=["GET", "POST"])
|
309
309
|
@templated("variant/acmg.html")
|
310
310
|
def evaluation(evaluation_id):
|
311
|
-
"""Show or delete an ACMG evaluation."""
|
311
|
+
"""Show, edit or delete an ACMG evaluation."""
|
312
312
|
|
313
313
|
evaluation_obj = store.get_evaluation(evaluation_id)
|
314
314
|
if evaluation_obj is None:
|
@@ -328,12 +328,15 @@ def evaluation(evaluation_id):
|
|
328
328
|
flash("Cleared ACMG classification.", "info")
|
329
329
|
|
330
330
|
return redirect(link)
|
331
|
+
|
331
332
|
return dict(
|
332
333
|
evaluation=evaluation_obj,
|
334
|
+
edit=bool(request.args.get("edit")),
|
333
335
|
institute=evaluation_obj["institute"],
|
334
336
|
case=evaluation_obj["case"],
|
335
337
|
variant=evaluation_obj["variant"],
|
336
338
|
CRITERIA=ACMG_CRITERIA,
|
339
|
+
ACMG_OPTIONS=ACMG_OPTIONS,
|
337
340
|
)
|
338
341
|
|
339
342
|
|
@@ -3,12 +3,10 @@ import logging
|
|
3
3
|
import re
|
4
4
|
from typing import Any, Dict, List, Optional
|
5
5
|
|
6
|
-
import bson
|
7
6
|
from flask import Response, flash, session, url_for
|
8
7
|
from flask_login import current_user
|
9
8
|
from markupsafe import Markup
|
10
9
|
from pymongo.cursor import CursorType
|
11
|
-
from pymongo.errors import DocumentTooLarge
|
12
10
|
from werkzeug.datastructures import Headers, ImmutableMultiDict, MultiDict
|
13
11
|
from wtforms import DecimalField
|
14
12
|
|
@@ -513,7 +511,7 @@ def update_variant_genes(store, variant_obj, genome_build):
|
|
513
511
|
|
514
512
|
if variant_genes is not None:
|
515
513
|
for gene_obj in variant_genes:
|
516
|
-
# If there is no hgnc id there is
|
514
|
+
# If there is no hgnc id there is nothing we can do
|
517
515
|
if not gene_obj["hgnc_id"]:
|
518
516
|
continue
|
519
517
|
# Else we collect the gene object and check the id
|
@@ -530,27 +528,6 @@ def update_variant_genes(store, variant_obj, genome_build):
|
|
530
528
|
return has_changed
|
531
529
|
|
532
530
|
|
533
|
-
def update_variant_store(store, variant_obj):
|
534
|
-
"""
|
535
|
-
Update variants in db store if anything changed.
|
536
|
-
Args:
|
537
|
-
store(scout.adapter.MongoAdapter)
|
538
|
-
variant_obj(scout.models.Variant)
|
539
|
-
|
540
|
-
update(boolean): upsert only if this is set true
|
541
|
-
|
542
|
-
get_compounds(bool): if compounds should be added to added to the returned variant object
|
543
|
-
|
544
|
-
"""
|
545
|
-
try:
|
546
|
-
variant_obj = store.update_variant(variant_obj)
|
547
|
-
except DocumentTooLarge:
|
548
|
-
flash(
|
549
|
-
f"An error occurred while updating info for variant: {variant_obj['_id']} (pymongo_errors.DocumentTooLarge: {len(bson.BSON.encode(variant_obj))})",
|
550
|
-
"warning",
|
551
|
-
)
|
552
|
-
|
553
|
-
|
554
531
|
def _compound_follow_filter_freq(compound, compound_var_obj, query_form):
|
555
532
|
"""When compound follow filter is selected, apply relevant settings from the query filter onto dismissing compounds.
|
556
533
|
|
@@ -895,11 +872,18 @@ def parse_variant(
|
|
895
872
|
compounds_have_changed = False
|
896
873
|
if get_compounds:
|
897
874
|
compounds_have_changed = update_compounds(store, variant_obj, case_dismissed_vars)
|
875
|
+
if update and compounds_have_changed:
|
876
|
+
store.variant_update_field(
|
877
|
+
variant_id=variant_obj["_id"],
|
878
|
+
field_name="compounds",
|
879
|
+
field_value=variant_obj["compounds"],
|
880
|
+
)
|
898
881
|
|
899
882
|
genes_have_changed = update_variant_genes(store, variant_obj, genome_build)
|
900
|
-
|
901
|
-
|
902
|
-
|
883
|
+
if update and genes_have_changed:
|
884
|
+
store.variant_update_field(
|
885
|
+
variant_id=variant_obj["_id"], field_name="genes", field_value=variant_obj["genes"]
|
886
|
+
)
|
903
887
|
|
904
888
|
variant_obj["comments"] = store.events(
|
905
889
|
institute_obj,
|
@@ -1996,7 +1980,7 @@ def activate_case(store, institute_obj, case_obj, current_user):
|
|
1996
1980
|
store.update_status(institute_obj, case_obj, user_obj, "active", case_link)
|
1997
1981
|
|
1998
1982
|
|
1999
|
-
def
|
1983
|
+
def reset_all_dismissed(store, institute_obj, case_obj):
|
2000
1984
|
"""Reset all dismissed variants for a case.
|
2001
1985
|
|
2002
1986
|
Args:
|
@@ -47,7 +47,7 @@ variants_bp = Blueprint(
|
|
47
47
|
def reset_dismissed(institute_id, case_name):
|
48
48
|
"""Reset all dismissed variants for a case"""
|
49
49
|
institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
|
50
|
-
controllers.
|
50
|
+
controllers.reset_all_dismissed(store, institute_obj, case_obj)
|
51
51
|
return redirect(request.referrer)
|
52
52
|
|
53
53
|
|