scout-browser 4.95.0__py3-none-any.whl → 4.97.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 +75 -70
- scout/adapter/mongo/filter.py +28 -11
- scout/adapter/mongo/institute.py +2 -0
- scout/adapter/mongo/omics_variant.py +20 -5
- scout/adapter/mongo/query.py +104 -95
- scout/adapter/mongo/variant.py +0 -5
- scout/adapter/mongo/variant_loader.py +10 -12
- scout/build/case.py +3 -1
- scout/build/individual.py +3 -11
- scout/commands/delete/delete_command.py +87 -49
- scout/commands/load/research.py +4 -4
- scout/commands/load/variants.py +25 -8
- scout/commands/setup/setup_scout.py +1 -1
- scout/commands/update/case.py +12 -0
- scout/commands/update/individual.py +1 -2
- scout/constants/__init__.py +7 -2
- scout/constants/acmg.py +25 -18
- scout/constants/file_types.py +68 -119
- scout/constants/filters.py +2 -1
- scout/constants/gene_tags.py +3 -3
- scout/constants/igv_tracks.py +7 -11
- scout/constants/query_terms.py +2 -2
- scout/demo/643594.config.yaml +6 -0
- scout/demo/643594.peddy.ped +1 -1
- scout/demo/643594.somalier.ancestry.tsv +4 -0
- scout/demo/643594.somalier.pairs.tsv +4 -0
- scout/demo/643594.somalier.samples.tsv +4 -0
- scout/demo/cancer.load_config.yaml +2 -3
- scout/demo/resources/__init__.py +1 -1
- scout/demo/resources/gnomad.v4.1.constraint_metrics_reduced.tsv +3755 -0
- scout/demo/rnafusion.load_config.yaml +1 -0
- scout/exceptions/database.py +1 -1
- scout/load/all.py +8 -16
- scout/models/case/case.py +1 -0
- scout/models/case/case_loading_models.py +15 -5
- scout/models/managed_variant.py +3 -3
- scout/models/omics_variant.py +3 -3
- scout/parse/case.py +113 -5
- scout/parse/pedqc.py +127 -0
- scout/parse/variant/frequency.py +9 -6
- scout/parse/variant/variant.py +71 -39
- scout/server/app.py +14 -0
- scout/server/blueprints/alignviewers/controllers.py +2 -0
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +3 -0
- scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
- scout/server/blueprints/cases/controllers.py +25 -3
- scout/server/blueprints/cases/templates/cases/case.html +3 -0
- scout/server/blueprints/cases/templates/cases/case_report.html +28 -2
- scout/server/blueprints/cases/templates/cases/chanjo2_form.html +2 -2
- scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +12 -0
- scout/server/blueprints/cases/templates/cases/gene_panel.html +9 -3
- scout/server/blueprints/cases/templates/cases/individuals_table.html +4 -1
- scout/server/blueprints/cases/templates/cases/utils.html +23 -19
- scout/server/blueprints/cases/views.py +5 -9
- scout/server/blueprints/clinvar/controllers.py +12 -11
- scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +10 -14
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +15 -7
- scout/server/blueprints/clinvar/views.py +18 -31
- scout/server/blueprints/institutes/controllers.py +20 -1
- scout/server/blueprints/institutes/forms.py +5 -1
- scout/server/blueprints/institutes/templates/overview/institute_settings.html +7 -0
- scout/server/blueprints/institutes/templates/overview/utils.html +20 -1
- scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +9 -2
- scout/server/blueprints/omics_variants/views.py +8 -10
- scout/server/blueprints/variant/controllers.py +30 -1
- scout/server/blueprints/variant/templates/variant/cancer-variant.html +21 -5
- scout/server/blueprints/variant/templates/variant/components.html +26 -9
- scout/server/blueprints/variant/templates/variant/variant.html +4 -2
- scout/server/blueprints/variant/templates/variant/variant_details.html +1 -1
- scout/server/blueprints/variant/utils.py +2 -0
- scout/server/blueprints/variant/views.py +10 -3
- scout/server/blueprints/variants/controllers.py +29 -3
- scout/server/blueprints/variants/forms.py +37 -10
- scout/server/blueprints/variants/templates/variants/cancer-variants.html +5 -4
- scout/server/blueprints/variants/templates/variants/components.html +12 -10
- scout/server/blueprints/variants/templates/variants/str-variants.html +13 -9
- scout/server/blueprints/variants/templates/variants/utils.html +59 -36
- scout/server/blueprints/variants/views.py +45 -60
- scout/server/extensions/beacon_extension.py +1 -1
- scout/server/extensions/bionano_extension.py +5 -5
- scout/server/extensions/chanjo2_extension.py +40 -1
- scout/server/extensions/chanjo_extension.py +1 -1
- scout/server/extensions/clinvar_extension.py +56 -2
- scout/server/extensions/matchmaker_extension.py +1 -1
- scout/server/links.py +0 -14
- scout/server/static/bs_styles.css +2 -0
- scout/server/templates/layout.html +1 -0
- scout/server/utils.py +5 -0
- scout/utils/acmg.py +5 -5
- scout/utils/ensembl_biomart_clients.py +2 -11
- scout/utils/scout_requests.py +1 -1
- {scout_browser-4.95.0.dist-info → scout_browser-4.97.0.dist-info}/METADATA +1 -1
- {scout_browser-4.95.0.dist-info → scout_browser-4.97.0.dist-info}/RECORD +96 -94
- scout/demo/resources/gnomad.v4.0.constraint_metrics_reduced.tsv +0 -3755
- scout/parse/peddy.py +0 -149
- scout/utils/sort.py +0 -21
- {scout_browser-4.95.0.dist-info → scout_browser-4.97.0.dist-info}/WHEEL +0 -0
- {scout_browser-4.95.0.dist-info → scout_browser-4.97.0.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.95.0.dist-info → scout_browser-4.97.0.dist-info}/licenses/LICENSE +0 -0
@@ -95,7 +95,11 @@ class InstituteForm(FlaskForm):
|
|
95
95
|
|
96
96
|
alamut_institution = StringField("Alamut Institution ID", validators=[validators.Optional()])
|
97
97
|
|
98
|
-
check_show_all_vars = BooleanField("Preselect '
|
98
|
+
check_show_all_vars = BooleanField("Preselect 'Include variants only present in unaffected'")
|
99
|
+
|
100
|
+
soft_filters = NonValidatingSelectMultipleField(
|
101
|
+
"Default soft filters", validators=[validators.Optional()]
|
102
|
+
)
|
99
103
|
|
100
104
|
clinvar_key = StringField("API key", widget=PasswordInput(hide_value=False))
|
101
105
|
|
@@ -104,6 +104,13 @@
|
|
104
104
|
placeholder: "Add Sanger email",
|
105
105
|
});
|
106
106
|
|
107
|
+
$('#soft_filters').select2({
|
108
|
+
tags: true,
|
109
|
+
theme: 'bootstrap-5',
|
110
|
+
tokenSeparators: [','],
|
111
|
+
placeholder: "germline_risk",
|
112
|
+
});
|
113
|
+
|
107
114
|
$('#clinvar_tags').select2({
|
108
115
|
tags: true,
|
109
116
|
theme: 'bootstrap-5',
|
@@ -285,7 +285,7 @@
|
|
285
285
|
</div>
|
286
286
|
<!-- End of cohorts settings -->
|
287
287
|
|
288
|
-
|
288
|
+
<!-- Variants and panels searching -->
|
289
289
|
<div class="row mt-5 d-flex align-items-center">
|
290
290
|
<fieldset>
|
291
291
|
<legend>Variants and gene panels searching</legend>
|
@@ -349,6 +349,25 @@
|
|
349
349
|
</div>
|
350
350
|
<!-- End of loqusdb settings -->
|
351
351
|
|
352
|
+
<!-- Custom soft filters for variants -->
|
353
|
+
<div class="row mt-5 d-flex align-items-center">
|
354
|
+
<fieldset>
|
355
|
+
<legend>Variants custom soft filters</legend>
|
356
|
+
<div class="row">
|
357
|
+
<div class="col-sm-6 col-lg-4">
|
358
|
+
{{form.soft_filters.label(class="control-label", data_bs_toggle="tooltip", data_bs_placement="top", title="Values to filter variant documents with by default. For example germline_risk or in_normal.")}}
|
359
|
+
<select class="select2" id="soft_filters" name="soft_filters" multiple="true" style="width:100%;">
|
360
|
+
{% if institute.soft_filters %}
|
361
|
+
{% for filter in institute.soft_filters %}
|
362
|
+
<option value="{{filter}}" selected>{{filter}}</option>
|
363
|
+
{% endfor %}
|
364
|
+
{% endif %}
|
365
|
+
</select>
|
366
|
+
</div>
|
367
|
+
</div>
|
368
|
+
</div>
|
369
|
+
<!-- End of custom soft filters for variants -->
|
370
|
+
|
352
371
|
<!-- Alamut settings -->
|
353
372
|
<div class="row mt-5 d-flex align-items-center">
|
354
373
|
<fieldset><legend>Alamut Plus<a class="ms-2 text-decoration-none" href="https://extranet.interactive-biosoftware.com/alamut-visual-plus_API.html" target="_blank" rel="noopener">*</a></legend>
|
@@ -59,7 +59,7 @@
|
|
59
59
|
<tr>
|
60
60
|
<th style="width:14%" title="HGNC symbols">Gene</th>
|
61
61
|
<th style="width:7%" title="Sub-category">Type</th>
|
62
|
-
<th style="width:7%" title="Value - delta Psi or l2fc
|
62
|
+
<th style="width:7%" title="Value - delta Psi or l2fc">Value</th>
|
63
63
|
<th title="Functional annotation">Func. annotation</th>
|
64
64
|
<th style="width:7%" title="P-value">P-value</th>
|
65
65
|
<th style="width:10%" title="Individual">Ind</th>
|
@@ -246,6 +246,13 @@
|
|
246
246
|
<div class="col-6">
|
247
247
|
{{ stash_filter_buttons(form, institute, case) }}
|
248
248
|
</div>
|
249
|
+
|
250
|
+
<div class="col-2 offset-2">
|
251
|
+
<div class="btn-group">
|
252
|
+
{{ form.sort_by(class="form-select btn btn-primary", style="width: auto;") }}
|
253
|
+
{{ form.sort_order(class="form-select btn btn-primary", style="width: auto;") }}
|
254
|
+
</div>
|
255
|
+
</div>
|
249
256
|
</div>
|
250
257
|
{% endmacro %}
|
251
258
|
|
@@ -266,7 +273,7 @@
|
|
266
273
|
<div class="col-4 d-flex justify-content-center">
|
267
274
|
Filter returns {{result_size}} / {{ total_variants }} variants.
|
268
275
|
</div>
|
269
|
-
<div class="col-4 d-flex
|
276
|
+
<div class="col-4 d-flex justify-content-end">
|
270
277
|
<div class="d-flex justify-content-end">Showing {%if more_variants %}page {{page}}{%else%}last page{% endif %} with {{nvars}} variants.</div>
|
271
278
|
</div>
|
272
279
|
</div>
|
@@ -11,6 +11,9 @@ from scout.server.blueprints.variants.controllers import (
|
|
11
11
|
get_variants_page,
|
12
12
|
populate_chrom_choices,
|
13
13
|
populate_filters_form,
|
14
|
+
populate_persistent_filters_choices,
|
15
|
+
set_hpo_clinical_filter,
|
16
|
+
update_form_hgnc_symbols,
|
14
17
|
)
|
15
18
|
from scout.server.blueprints.variants.forms import OutlierFiltersForm
|
16
19
|
from scout.server.extensions import store
|
@@ -43,8 +46,7 @@ def outliers(institute_id, case_name):
|
|
43
46
|
variant_type = "clinical"
|
44
47
|
variants_stats = store.case_variants_count(case_obj["_id"], institute_id, variant_type, False)
|
45
48
|
|
46
|
-
|
47
|
-
case_obj["hpo_clinical_filter"] = True
|
49
|
+
set_hpo_clinical_filter(case_obj, request.form)
|
48
50
|
|
49
51
|
# update status of case if visited for the first time
|
50
52
|
activate_case(store, institute_obj, case_obj, current_user)
|
@@ -63,12 +65,6 @@ def outliers(institute_id, case_name):
|
|
63
65
|
store, institute_obj, case_obj, user_obj, category, request.form
|
64
66
|
)
|
65
67
|
|
66
|
-
# populate filters dropdown
|
67
|
-
available_filters = list(store.filters(institute_obj["_id"], category))
|
68
|
-
form.filters.choices = [
|
69
|
-
(filter.get("_id"), filter.get("display_name")) for filter in available_filters
|
70
|
-
]
|
71
|
-
|
72
68
|
# populate available panel choices
|
73
69
|
form.gene_panels.choices = gene_panel_choices(store, institute_obj, case_obj)
|
74
70
|
|
@@ -78,7 +74,7 @@ def outliers(institute_id, case_name):
|
|
78
74
|
genome_build = "38" if "38" in str(case_obj.get("genome_build", "37")) else "37"
|
79
75
|
cytobands = store.cytoband_by_chrom(genome_build)
|
80
76
|
|
81
|
-
|
77
|
+
update_form_hgnc_symbols(store, case_obj, form)
|
82
78
|
variants_query = store.omics_variants(
|
83
79
|
case_obj["_id"], query=form.data, category=category, build=genome_build
|
84
80
|
)
|
@@ -96,7 +92,9 @@ def outliers(institute_id, case_name):
|
|
96
92
|
case=case_obj,
|
97
93
|
cytobands=cytobands,
|
98
94
|
expand_search=get_expand_search(request.form),
|
99
|
-
filters=
|
95
|
+
filters=populate_persistent_filters_choices(
|
96
|
+
institute_id=institute_id, category=category, form=form
|
97
|
+
),
|
100
98
|
form=form,
|
101
99
|
inherit_palette=INHERITANCE_PALETTE,
|
102
100
|
institute=institute_obj,
|
@@ -32,10 +32,12 @@ from scout.server.blueprints.variant.utils import (
|
|
32
32
|
update_variant_case_panels,
|
33
33
|
)
|
34
34
|
from scout.server.blueprints.variants.utils import update_case_panels
|
35
|
-
from scout.server.extensions import LoqusDB, config_igv_tracks, gens
|
35
|
+
from scout.server.extensions import LoqusDB, chanjo2, config_igv_tracks, gens
|
36
36
|
from scout.server.links import disease_link, get_variant_links
|
37
37
|
from scout.server.utils import (
|
38
38
|
case_has_alignments,
|
39
|
+
case_has_chanjo2_coverage,
|
40
|
+
case_has_chanjo_coverage,
|
39
41
|
case_has_mt_alignments,
|
40
42
|
case_has_rna_tracks,
|
41
43
|
user_institutes,
|
@@ -257,6 +259,8 @@ def variant(
|
|
257
259
|
# Provide basic info on alignment files availability for this case
|
258
260
|
case_has_alignments(case_obj)
|
259
261
|
case_has_mt_alignments(case_obj)
|
262
|
+
case_has_chanjo_coverage(case_obj)
|
263
|
+
case_has_chanjo2_coverage(case_obj)
|
260
264
|
|
261
265
|
# Collect all the events for the variant
|
262
266
|
events = list(store.events(institute_obj, case=case_obj, variant_id=variant_id))
|
@@ -402,6 +406,9 @@ def variant(
|
|
402
406
|
"inherit_palette": INHERITANCE_PALETTE,
|
403
407
|
"igv_tracks": get_igv_tracks("38" if variant_obj["is_mitochondrial"] else genome_build),
|
404
408
|
"has_rna_tracks": case_has_rna_tracks(case_obj),
|
409
|
+
"gene_has_full_coverage": get_gene_has_full_coverage(
|
410
|
+
institute_obj, case_obj, variant_obj, genome_build
|
411
|
+
),
|
405
412
|
"gens_info": gens.connection_settings(genome_build),
|
406
413
|
"evaluations": evaluations,
|
407
414
|
"ccv_evaluations": ccv_evaluations,
|
@@ -409,6 +416,28 @@ def variant(
|
|
409
416
|
}
|
410
417
|
|
411
418
|
|
419
|
+
def get_gene_has_full_coverage(
|
420
|
+
institute_obj, case_obj, variant_obj, genome_build
|
421
|
+
) -> Dict[int, bool]:
|
422
|
+
"""
|
423
|
+
Query chanjo2, if configured and d4 files are available for this case,
|
424
|
+
for coverage completeness on the genes touching this variant.
|
425
|
+
"""
|
426
|
+
if not case_obj.get("chanjo2_coverage"):
|
427
|
+
return {}
|
428
|
+
|
429
|
+
gene_has_full_coverage: dict = {
|
430
|
+
hgnc_id: chanjo2.get_gene_complete_coverage(
|
431
|
+
hgnc_id=hgnc_id,
|
432
|
+
threshold=institute_obj.get("coverage_cutoff") or 15,
|
433
|
+
individuals=case_obj.get("individuals"),
|
434
|
+
build=genome_build,
|
435
|
+
)
|
436
|
+
for hgnc_id in [gene.get("hgnc_id") for gene in variant_obj.get("genes")]
|
437
|
+
}
|
438
|
+
return gene_has_full_coverage
|
439
|
+
|
440
|
+
|
412
441
|
def variant_rank_scores(store: MongoAdapter, case_obj: dict, variant_obj: dict) -> list:
|
413
442
|
"""Retrive rank score values and ranges for the variant
|
414
443
|
|
@@ -223,9 +223,25 @@
|
|
223
223
|
{% if tmp_gene_info %}
|
224
224
|
{% set primary_transcript = tmp_gene_info[1] %}
|
225
225
|
{% set nr_genes = variant.genes | length %}
|
226
|
-
<
|
227
|
-
|
228
|
-
|
226
|
+
<table class="table table-bordered table-fixed table-sm">
|
227
|
+
<thead class="thead table-light border-top">
|
228
|
+
<tr class="active">
|
229
|
+
<th>Gene</th>
|
230
|
+
<th>Description</th>
|
231
|
+
<th data-bs-toggle="tooltip" title="Gene tolerance to a single copy of a truncating mutation (pLI) | Loss-of-function observed/expected upper bound fraction (LOEUF)">pLI score|LOEUF</th>
|
232
|
+
</tr>
|
233
|
+
</thead>
|
234
|
+
<tbody>
|
235
|
+
<tr>
|
236
|
+
<td>
|
237
|
+
<a target="_blank" href="{{ url_for('genes.gene', hgnc_id=primary_gene.hgnc_id) }}" data-bs-toggle="tooltip" title="Preferred gene for variant {% if nr_genes > 1 %} out of {{ nr_genes }} {% endif %}- shows conceptually highest impact change. See Severity card and Transcripts table below for more details.">{{ primary_gene.hgnc_symbol }}</a>
|
238
|
+
{% if nr_genes > 1 %} <span class="badge bg-info">+ {{ nr_genes-1 }} genes</span>{% endif %}
|
239
|
+
</td>
|
240
|
+
<td>{{primary_gene.description}}</td>
|
241
|
+
<td>{{primary_gene.pli_score|round(2) if primary_gene.pli_score else "n.a."}} | {{primary_gene.loeuf|round(2) if primary_gene.loeuf else "n.a."}}</td>
|
242
|
+
</tr>
|
243
|
+
</tbody>
|
244
|
+
</table>
|
229
245
|
{% endif %}
|
230
246
|
<ul class="list-group list-group-flush" style="margin-bottom: 1rem">
|
231
247
|
{% if primary_transcript %}
|
@@ -235,10 +251,10 @@
|
|
235
251
|
<span class="text-muted">exon </span><strong>{{ primary_gene.exon }}</strong>
|
236
252
|
{% endif %}
|
237
253
|
{% if primary_transcript and primary_transcript.coding_sequence_name %}
|
238
|
-
{{ primary_transcript.coding_sequence_name | url_decode }}
|
254
|
+
{{ primary_transcript.coding_sequence_name | url_decode | truncate(50, True) }}
|
239
255
|
{% endif %}
|
240
256
|
{% if primary_transcript and primary_transcript.protein_sequence_name %}
|
241
|
-
<strong>{{ primary_transcript.protein_sequence_name | url_decode }}</strong>
|
257
|
+
<strong>{{ primary_transcript.protein_sequence_name | url_decode | truncate(50, True) }}</strong>
|
242
258
|
{% endif %}
|
243
259
|
{% if primary_gene and primary_gene.region_annotation %}
|
244
260
|
<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>
|
@@ -1,3 +1,4 @@
|
|
1
|
+
{% from "cases/chanjo2_form.html" import chanjo2_report_form %}
|
1
2
|
{% from "variant/buttons.html" import variant_tag_button, variant_tier_button, dismiss_variant_button, mosaic_variant_button %}
|
2
3
|
{% from "variants/utils.html" import compounds_table %}
|
3
4
|
{% from "variant/variant_details.html" import severity_list %}
|
@@ -118,15 +119,7 @@
|
|
118
119
|
{% endif %}
|
119
120
|
</div>
|
120
121
|
{% endfor %}
|
121
|
-
{
|
122
|
-
<div class="d-flex flex-wrap ms-1">
|
123
|
-
{% for gene in variant.genes %}
|
124
|
-
<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) }}">
|
125
|
-
Gene coverage: {{ gene.common.hgnc_symbol if gene.common else gene.hgnc_id }}
|
126
|
-
</a>
|
127
|
-
{% endfor %}
|
128
|
-
</div>
|
129
|
-
{% endif %}
|
122
|
+
{{ gene_coverage(institute, variant, case, config) }}
|
130
123
|
</li>
|
131
124
|
<li class="list-group-item">
|
132
125
|
<div>
|
@@ -136,6 +129,30 @@
|
|
136
129
|
</ul>
|
137
130
|
{% endmacro %}
|
138
131
|
|
132
|
+
{% macro gene_coverage(institute, variant, case, config) %}
|
133
|
+
{% if case.chanjo_coverage and config.chanjo_report %}
|
134
|
+
<div class="d-flex flex-wrap ms-1">
|
135
|
+
{% for gene in variant.genes %}
|
136
|
+
<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) }}" data-bs-toggle="tooltip" title="Chanjo coverage report">
|
137
|
+
Gene coverage: {{ gene.common.hgnc_symbol if gene.common else gene.hgnc_id }}
|
138
|
+
</a>
|
139
|
+
{% endfor %}
|
140
|
+
</div>
|
141
|
+
{% endif %}
|
142
|
+
{% if case.chanjo2_coverage %}
|
143
|
+
<div class="d-flex flex-wrap ms-1" data-bs-toggle="tooltip" title="Chanjo2 coverage reports">
|
144
|
+
{% for gene in variant.genes %}
|
145
|
+
{{ chanjo2_report_form(institute, case, gene.hgnc_symbol, 'overview', gene.hgnc_id, "Gene coverage " + (gene.common.hgnc_symbol if gene.common else gene.hgnc_id), "btn btn-sm btn-secondary text-white") }} <!--chanjo2 genes overview -->
|
146
|
+
{% if gene.common.hgnc_id in gene_has_full_coverage and gene.common.hgnc_id[gene.common.hgnc_id] %}
|
147
|
+
<span class="bg-success fa-solid fa-circle-check" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Chanjo2 coverage is at 100% completeness."></span>
|
148
|
+
{% else %}
|
149
|
+
<span class="bg-warning fa-solid fa-triangle-exclamation" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Note that Chanjo2 coverage is below 100% completeness."></span>
|
150
|
+
{% endif %}
|
151
|
+
{% endfor %}
|
152
|
+
</div>
|
153
|
+
{% endif %}
|
154
|
+
{% endmacro %}
|
155
|
+
|
139
156
|
{% macro acmg_form(institute, case, variant, ACMG_OPTIONS, selected=None) %}
|
140
157
|
<form action="{{ url_for('variant.variant_update', institute_id=institute._id, case_name=case.display_name, variant_id=variant._id) }}" method="POST">
|
141
158
|
<div class="d-flex justify-content-between">
|
@@ -339,6 +339,7 @@
|
|
339
339
|
<thead class="thead table-light border-top">
|
340
340
|
<tr class="active">
|
341
341
|
<th>Gene</th>
|
342
|
+
<th data-bs-toggle="tooltip" title="Gene tolerance to a single copy of a truncating mutation (pLI) | Loss-of-function observed/expected upper bound fraction (LOEUF)">pLI score|LOEUF</th>
|
342
343
|
<th>Region</th>
|
343
344
|
<th>Consequence</th>
|
344
345
|
{% if case.genome_build == "38" %}
|
@@ -349,11 +350,12 @@
|
|
349
350
|
<tbody>
|
350
351
|
{% for gene in variant.genes %}
|
351
352
|
<tr>
|
352
|
-
<
|
353
|
+
<td>
|
353
354
|
<a href="{{ url_for('genes.gene', hgnc_id=gene.hgnc_id) }}">
|
354
355
|
{{ gene.common.hgnc_symbol if gene.common else gene.hgnc_id }}
|
355
356
|
</a>
|
356
|
-
</
|
357
|
+
</td>
|
358
|
+
<td>{{gene.pli_score|round(2) if gene.pli_score else "n.a."}} | {{gene.loeuf|round(2) if gene.loeuf else "n.a."}}</td>
|
357
359
|
<td>{{ gene.region_annotation }}</td>
|
358
360
|
<td>{{ gene.functional_annotation|truncate(20, True) }}</td>
|
359
361
|
{% if case.genome_build == "38" %} <!-- Display eventual functional annotations associated to MANE transcripts -->
|
@@ -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>
|
@@ -282,6 +282,8 @@ def add_gene_info(
|
|
282
282
|
)
|
283
283
|
|
284
284
|
all_models.update(set(variant_gene["manual_inheritance"]))
|
285
|
+
variant_gene["pli_score"] = hgnc_gene.get("pli_score")
|
286
|
+
variant_gene["loeuf"] = hgnc_gene.get("constraint_lof_oe_ci_upper")
|
285
287
|
|
286
288
|
update_inheritance_model(variant_gene, all_models, disease_terms)
|
287
289
|
|
@@ -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,
|
@@ -40,6 +40,7 @@ from scout.server.blueprints.variant.utils import (
|
|
40
40
|
update_variant_case_panels,
|
41
41
|
)
|
42
42
|
from scout.server.blueprints.variants.forms import BetterDecimalField
|
43
|
+
from scout.server.extensions import store
|
43
44
|
from scout.server.links import add_gene_links, cosmic_links, str_source_link
|
44
45
|
from scout.server.utils import (
|
45
46
|
case_has_alignments,
|
@@ -83,6 +84,20 @@ def populate_force_show_unaffected_vars(institute_obj, form):
|
|
83
84
|
form.show_unaffected.data = True
|
84
85
|
|
85
86
|
|
87
|
+
def populate_persistent_filters_choices(
|
88
|
+
institute_id: str, category: str, form: ImmutableMultiDict
|
89
|
+
) -> List[dict]:
|
90
|
+
"""Populate the options present in the form.filters on variants page, directly setting on the form.
|
91
|
+
Also return a convenient list of filters for use in filter update macros.
|
92
|
+
"""
|
93
|
+
available_filters = list(store.filters(institute_id, category))
|
94
|
+
form.filters.choices = [
|
95
|
+
(filter.get("_id"), filter.get("display_name"))
|
96
|
+
for filter in sorted(available_filters, key=lambda f: f.get("display_name", "").lower())
|
97
|
+
]
|
98
|
+
return available_filters
|
99
|
+
|
100
|
+
|
86
101
|
def populate_chrom_choices(form, case_obj):
|
87
102
|
"""Populate the option of the chromosome select according to the case genome build"""
|
88
103
|
# Populate chromosome choices
|
@@ -90,6 +105,12 @@ def populate_chrom_choices(form, case_obj):
|
|
90
105
|
form.chrom.choices = [(chrom, chrom) for chrom in chromosomes]
|
91
106
|
|
92
107
|
|
108
|
+
def populate_institute_soft_filters(form, institute_obj):
|
109
|
+
"""Populate the hidden field 'institute_soft_filters' with a string containing all institute's soft filters."""
|
110
|
+
if institute_obj.get("soft_filters"):
|
111
|
+
form.institute_soft_filters.data = ",".join(institute_obj["soft_filters"])
|
112
|
+
|
113
|
+
|
93
114
|
def variants(
|
94
115
|
store,
|
95
116
|
institute_obj,
|
@@ -711,7 +732,7 @@ def _compound_follow_filter_clnsig(compound, compound_var_obj, query_form):
|
|
711
732
|
|
712
733
|
There are some filter options that are rather unique, like the ClinVar one.
|
713
734
|
|
714
|
-
If
|
735
|
+
If prioritise_clinvar is checked, variants are currently never dismissed on ClinSig alone.
|
715
736
|
|
716
737
|
Args:
|
717
738
|
compound(dict)
|
@@ -722,8 +743,7 @@ def _compound_follow_filter_clnsig(compound, compound_var_obj, query_form):
|
|
722
743
|
query_rank = []
|
723
744
|
query_str_rank = []
|
724
745
|
|
725
|
-
|
726
|
-
if clinsig_always_returned:
|
746
|
+
if query_form.get("prioritise_clinvar"):
|
727
747
|
return False
|
728
748
|
|
729
749
|
clinsig = query_form.get("clinsig")
|
@@ -2088,3 +2108,9 @@ def get_show_dismiss_block():
|
|
2088
2108
|
session["show_dismiss_block"] = show_dismiss_block
|
2089
2109
|
|
2090
2110
|
return show_dismiss_block
|
2111
|
+
|
2112
|
+
|
2113
|
+
def set_hpo_clinical_filter(case_obj: dict, request_form: dict):
|
2114
|
+
"""Set HPO clinical filter on case if requested through the form."""
|
2115
|
+
if request_form.get("hpo_clinical_filter"):
|
2116
|
+
case_obj["hpo_clinical_filter"] = True
|
@@ -113,6 +113,8 @@ class VariantFiltersForm(FlaskForm):
|
|
113
113
|
compound_follow_filter = BooleanField("Compounds follow filter")
|
114
114
|
cadd_inclusive = BooleanField("CADD inclusive")
|
115
115
|
clinsig = NonValidatingSelectMultipleField("ClinVar CLINSIG", choices=CLINSIG_OPTIONS)
|
116
|
+
clinsig_exclude = BooleanField("Exclude")
|
117
|
+
prioritise_clinvar = BooleanField("Prioritise ClinVar")
|
116
118
|
|
117
119
|
gnomad_frequency = BetterDecimalField("gnomadAF", validators=[validators.Optional()])
|
118
120
|
local_obs_old = IntegerField("Local obs. (archive)", validators=[validators.Optional()])
|
@@ -151,11 +153,25 @@ class VariantFiltersForm(FlaskForm):
|
|
151
153
|
cytoband_start = NonValidatingSelectField("Cytoband start", choices=[])
|
152
154
|
cytoband_end = NonValidatingSelectField("Cytoband end", choices=[])
|
153
155
|
|
156
|
+
size_selector = NonValidatingSelectField(
|
157
|
+
"Variant size in bp", choices=[("$gte", ">="), ("$lt", "<")]
|
158
|
+
)
|
159
|
+
size = IntegerField(
|
160
|
+
"",
|
161
|
+
[
|
162
|
+
validators.Optional(),
|
163
|
+
validators.NumberRange(min=0, message="Number of bases must be 1 or greater."),
|
164
|
+
],
|
165
|
+
widget=NumberInput(min=1),
|
166
|
+
)
|
167
|
+
|
154
168
|
hide_dismissed = BooleanField("Hide dismissed", default=False)
|
155
169
|
filter_variants = SubmitField(label="Filter variants")
|
156
170
|
export = SubmitField(label="Filter and export")
|
157
171
|
|
158
|
-
show_unaffected = BooleanField("
|
172
|
+
show_unaffected = BooleanField("Include variants present only in unaffected", default=True)
|
173
|
+
show_soft_filtered = BooleanField(f"Include soft-filtered variants", default=False)
|
174
|
+
institute_soft_filters = HiddenField()
|
159
175
|
|
160
176
|
|
161
177
|
class FiltersForm(VariantFiltersForm):
|
@@ -163,11 +179,11 @@ class FiltersForm(VariantFiltersForm):
|
|
163
179
|
|
164
180
|
symbol_file = FileField("Symbol File")
|
165
181
|
|
166
|
-
|
182
|
+
clinvar_trusted_revstat = BooleanField("CLINSIG Confident")
|
167
183
|
spidex_human = SelectMultipleField("SPIDEX", choices=SPIDEX_CHOICES)
|
168
184
|
|
169
185
|
clinical_filter = SubmitField(label="Clinical filter")
|
170
|
-
clinvar_tag = BooleanField("ClinVar hits")
|
186
|
+
clinvar_tag = BooleanField("ClinVar hits only")
|
171
187
|
|
172
188
|
# polymorphic constant base for clinical filter
|
173
189
|
clinical_filter_base = CLINICAL_FILTER_BASE
|
@@ -180,7 +196,7 @@ class CancerFiltersForm(VariantFiltersForm):
|
|
180
196
|
alt_count = IntegerField("Min alt count", validators=[validators.Optional()])
|
181
197
|
control_frequency = BetterDecimalField("Normal alt AF <", validators=[validators.Optional()])
|
182
198
|
tumor_frequency = BetterDecimalField("Tumor alt AF >", validators=[validators.Optional()])
|
183
|
-
clinvar_tag = BooleanField("ClinVar hits")
|
199
|
+
clinvar_tag = BooleanField("ClinVar hits only")
|
184
200
|
cosmic_tag = BooleanField("Cosmic hits")
|
185
201
|
mvl_tag = BooleanField("Managed Variants hits")
|
186
202
|
local_obs_cancer_somatic_old = IntegerField(
|
@@ -189,7 +205,6 @@ class CancerFiltersForm(VariantFiltersForm):
|
|
189
205
|
local_obs_cancer_germline_old = IntegerField(
|
190
206
|
"Local germline obs. (archive)", validators=[validators.Optional()]
|
191
207
|
)
|
192
|
-
|
193
208
|
# polymorphic constant base for clinical filter
|
194
209
|
clinical_filter_base = CLINICAL_FILTER_BASE_CANCER
|
195
210
|
|
@@ -212,8 +227,6 @@ class StrFiltersForm(VariantFiltersForm):
|
|
212
227
|
class SvFiltersForm(VariantFiltersForm):
|
213
228
|
"""Extends FiltersForm for structural variants"""
|
214
229
|
|
215
|
-
size = StringField("Length")
|
216
|
-
size_shorter = BooleanField("Length shorter than")
|
217
230
|
svtype = SelectMultipleField("SVType", choices=SV_TYPE_CHOICES)
|
218
231
|
decipher = BooleanField("Decipher")
|
219
232
|
clingen_ngi = IntegerField("ClinGen NGI obs")
|
@@ -242,8 +255,6 @@ class CancerSvFiltersForm(SvFiltersForm):
|
|
242
255
|
class FusionFiltersForm(VariantFiltersForm):
|
243
256
|
"""Extends FiltersForm for fusion variants"""
|
244
257
|
|
245
|
-
size = StringField("Length")
|
246
|
-
size_shorter = BooleanField("Length shorter than")
|
247
258
|
decipher = BooleanField("Decipher")
|
248
259
|
clinical_filter = SubmitField(label="Clinical filter")
|
249
260
|
fusion_score = BetterDecimalField("Fusion score >=", validators=[validators.Optional()])
|
@@ -302,7 +313,23 @@ class OutlierFiltersForm(FlaskForm):
|
|
302
313
|
|
303
314
|
clinical_filter_base = CLINICAL_FILTER_BASE_OUTLIER
|
304
315
|
|
305
|
-
show_unaffected = BooleanField("
|
316
|
+
show_unaffected = BooleanField("Include variants present only in unaffected", default=False)
|
317
|
+
|
318
|
+
sort_by = NonValidatingSelectField(
|
319
|
+
choices=[
|
320
|
+
("", "Sort by"),
|
321
|
+
("p_value", "Sort by P-value"),
|
322
|
+
("delta_psi", "Sort by Δψ"),
|
323
|
+
("psi_value", "Sort by ψ value"),
|
324
|
+
("zscore", "Sort by zscore"),
|
325
|
+
("l2fc", "Sort by l2fc"),
|
326
|
+
],
|
327
|
+
validators=[validators.Optional()],
|
328
|
+
)
|
329
|
+
sort_order = NonValidatingSelectField(
|
330
|
+
choices=[("", "Sort order"), ("asc", "asc"), ("desc", "desc")],
|
331
|
+
validators=[validators.Optional()],
|
332
|
+
)
|
306
333
|
|
307
334
|
|
308
335
|
FILTERSFORMCLASS = {
|
@@ -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 %}
|
@@ -203,16 +203,18 @@
|
|
203
203
|
{% endfor %}
|
204
204
|
|
205
205
|
{% set panel_count = matching_panels|length %}
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
{
|
214
|
-
|
215
|
-
|
206
|
+
{% if panel_count %}
|
207
|
+
<a
|
208
|
+
class="badge bg-secondary text-white"
|
209
|
+
data-bs-toggle="popover"
|
210
|
+
data-bs-html="true"
|
211
|
+
data-bs-trigger="hover click"
|
212
|
+
title="Overlapping gene panels"
|
213
|
+
data-bs-content="{% for panel in matching_panels %}
|
214
|
+
{{ panel.panel_name|safe }}<br>
|
215
|
+
{% endfor %}"
|
216
|
+
>{{ panel_count }}</a>
|
217
|
+
{% endif %}
|
216
218
|
{% endmacro %}
|
217
219
|
|
218
220
|
{% if variant.category in ["cancer", "sv_cancer"] %}
|