scout-browser 4.96.0__py3-none-any.whl → 4.98.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 +51 -47
- 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/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/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 +1 -0
- scout/demo/resources/__init__.py +1 -1
- scout/demo/resources/gnomad.v4.1.constraint_metrics_reduced.tsv +3755 -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 +12 -5
- scout/models/managed_variant.py +3 -3
- scout/models/omics_variant.py +3 -3
- scout/parse/case.py +112 -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 +2 -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 +23 -3
- scout/server/blueprints/cases/templates/cases/case.html +3 -0
- scout/server/blueprints/cases/templates/cases/chanjo2_form.html +2 -2
- 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 +11 -11
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +15 -7
- scout/server/blueprints/institutes/controllers.py +20 -1
- scout/server/blueprints/institutes/forms.py +5 -1
- scout/server/blueprints/institutes/templates/overview/filters.html +14 -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 +19 -3
- scout/server/blueprints/variant/templates/variant/components.html +26 -9
- scout/server/blueprints/variant/templates/variant/variant.html +4 -2
- scout/server/blueprints/variant/utils.py +2 -0
- scout/server/blueprints/variants/controllers.py +29 -3
- scout/server/blueprints/variants/forms.py +37 -10
- scout/server/blueprints/variants/templates/variants/components.html +12 -10
- 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/matchmaker_extension.py +1 -1
- scout/server/static/bs_styles.css +2 -0
- scout/server/templates/layout.html +1 -0
- scout/server/utils.py +5 -0
- scout/utils/ensembl_biomart_clients.py +2 -11
- scout/utils/scout_requests.py +1 -1
- {scout_browser-4.96.0.dist-info → scout_browser-4.98.0.dist-info}/METADATA +1 -1
- {scout_browser-4.96.0.dist-info → scout_browser-4.98.0.dist-info}/RECORD +83 -81
- 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.96.0.dist-info → scout_browser-4.98.0.dist-info}/WHEEL +0 -0
- {scout_browser-4.96.0.dist-info → scout_browser-4.98.0.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.96.0.dist-info → scout_browser-4.98.0.dist-info}/licenses/LICENSE +0 -0
@@ -1088,11 +1088,12 @@ def update_default_panels(store, current_user, institute_id, case_name, panel_id
|
|
1088
1088
|
store.update_default_panels(institute_obj, case_obj, user_obj, link, panel_objs)
|
1089
1089
|
|
1090
1090
|
|
1091
|
-
def update_clinical_filter_hpo(store, current_user,
|
1091
|
+
def update_clinical_filter_hpo(store, current_user, institute_obj, case_obj, hpo_clinical_filter):
|
1092
1092
|
"""Update HPO clinical filter use for a case."""
|
1093
|
-
institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
|
1094
1093
|
user_obj = store.user(current_user.email)
|
1095
|
-
link = url_for(
|
1094
|
+
link = url_for(
|
1095
|
+
"cases.case", institute_id=institute_obj["_id"], case_name=case_obj["display_name"]
|
1096
|
+
)
|
1096
1097
|
store.update_clinical_filter_hpo(institute_obj, case_obj, user_obj, link, hpo_clinical_filter)
|
1097
1098
|
|
1098
1099
|
|
@@ -1493,3 +1494,22 @@ def add_link_for_disease(case_obj: dict):
|
|
1493
1494
|
for diagnosis in case_diagnoses:
|
1494
1495
|
#: Add link
|
1495
1496
|
diagnosis.update({"disease_link": disease_link(disease_id=diagnosis["disease_id"])})
|
1497
|
+
|
1498
|
+
|
1499
|
+
def remove_dynamic_genes(store: dict, case_obj: dict, institute_obj: dict, request_form: dict):
|
1500
|
+
"""Remove one or more genes from the dynamic gene list. If there are no more
|
1501
|
+
genes on the list, also stop using the HPO panel for clinical filter."""
|
1502
|
+
case_dynamic_genes = [dyn_gene["hgnc_id"] for dyn_gene in case_obj.get("dynamic_gene_list")]
|
1503
|
+
genes_to_remove = [int(gene_id) for gene_id in request_form.getlist("dynamicGene")]
|
1504
|
+
hgnc_ids = list(set(case_dynamic_genes) - set(genes_to_remove))
|
1505
|
+
store.update_dynamic_gene_list(
|
1506
|
+
case_obj,
|
1507
|
+
hgnc_ids=hgnc_ids,
|
1508
|
+
delete_only=True,
|
1509
|
+
)
|
1510
|
+
if not hgnc_ids:
|
1511
|
+
hpo_clinical_filter = False
|
1512
|
+
case_obj["hpo_clinical_filter"] = hpo_clinical_filter
|
1513
|
+
update_clinical_filter_hpo(
|
1514
|
+
store, current_user, institute_obj, case_obj, hpo_clinical_filter
|
1515
|
+
)
|
@@ -295,6 +295,9 @@
|
|
295
295
|
{% if case.vcf_files.vcf_fusion_research %}
|
296
296
|
<a class="btn btn-dark btn-sm text-white" href="{{ url_for('variants.fusion_variants', institute_id=institute._id, case_name=case.display_name, variant_type='research') }}">Research fusion variants</a>
|
297
297
|
{% endif %}
|
298
|
+
{% if case.omics_files and (case.omics_files.fraser_research or case.omics_files.outrider_research) %}
|
299
|
+
<a class="btn btn-dark btn-sm text-white" href="{{ url_for('omics_variants.outliers', institute_id=institute._id, case_name=case.display_name, variant_type='research') }}">Research WTS variants</a>
|
300
|
+
{% endif %}
|
298
301
|
</div>
|
299
302
|
</div>
|
300
303
|
</div>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
{% macro chanjo2_report_form(institute_obj, case_obj, panel_name, report_type, hgnc_ids, link_text) %}
|
1
|
+
{% macro chanjo2_report_form(institute_obj, case_obj, panel_name, report_type, hgnc_ids, link_text, link_class = "link-primary") %}
|
2
2
|
|
3
3
|
{% set form_name = "chanjo2_"+panel_name+"_"+report_type %}
|
4
4
|
{% set form_action = config.CHANJO2_URL+"/"+report_type %}
|
@@ -40,7 +40,7 @@
|
|
40
40
|
{% if panel_name == "HPO Panel" %}
|
41
41
|
<button type="submit" class="btn btn-secondary btn-sm text-white"> Coverage {{report_type}} (Chanjo2) </button>
|
42
42
|
{% else %}
|
43
|
-
<a class="
|
43
|
+
<a class="{{ link_class }}" onclick="this.closest('form').submit();return false;">{{link_text}}</a>
|
44
44
|
{% endif %}
|
45
45
|
</form>
|
46
46
|
|
@@ -79,8 +79,7 @@
|
|
79
79
|
<span class="mr-3" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{ case.dynamic_panel_phenotypes|join(', ') }}">
|
80
80
|
{{ case.dynamic_panel_phenotypes|length }} phenotypes
|
81
81
|
</span>
|
82
|
-
{% endif %}
|
83
|
-
)
|
82
|
+
{% endif %})
|
84
83
|
{% if case.dynamic_gene_list_edited %} <span class="text-danger ml-3"> (edited) </span> {% endif %}
|
85
84
|
</h6>
|
86
85
|
</div>
|
@@ -119,6 +118,7 @@
|
|
119
118
|
</div>
|
120
119
|
|
121
120
|
<div>
|
121
|
+
{% if case.track == 'rare' %}
|
122
122
|
<div class="row"> <!-- Show variants in dynamic gene list -->
|
123
123
|
<form action="{{ url_for('cases.update_clinical_filter_hpo', institute_id=institute._id, case_name=case.display_name)+'#hpo_clinical_filter' }}" method="POST">
|
124
124
|
<div class="form-check form-switch">
|
@@ -128,6 +128,7 @@
|
|
128
128
|
</div>
|
129
129
|
</form>
|
130
130
|
</div>
|
131
|
+
{% endif %}
|
131
132
|
<div class="row mt-3">
|
132
133
|
<div class="btn-group btn-group-sm">
|
133
134
|
{% if case.track == 'cancer' %}
|
@@ -180,7 +181,12 @@
|
|
180
181
|
Clinical HPO MEIs
|
181
182
|
</a>
|
182
183
|
{% endif %}
|
183
|
-
|
184
|
+
{% if case.has_outliers %}
|
185
|
+
<a class="btn btn-secondary btn-sm text-white" href="{{ url_for('omics_variants.outliers',
|
186
|
+
institute_id=institute._id, case_name=case.display_name, variant_type='clinical',
|
187
|
+
gene_panels=['hpo']) }}" target="_blank" rel="noopener">Clinical HPO WTS</a>
|
188
|
+
{% endif %}
|
189
|
+
{% endif %}
|
184
190
|
</div>
|
185
191
|
</div>
|
186
192
|
{% if case.chanjo_coverage or case.chanjo2_coverage and case.dynamic_gene_list|length > 0 %}
|
@@ -17,7 +17,9 @@
|
|
17
17
|
title="Measure of the tumor mutational burden">TMB</th>
|
18
18
|
<th data-bs-toggle='tooltip' data-bs-container='body' class="col-xs-1"
|
19
19
|
title="Measure of microsatellite instability">MSI</th>
|
20
|
-
|
20
|
+
<th data-bs-toggle='tooltip' data-bs-container='body' class="col-xs-1"
|
21
|
+
title="Homologous Recombination Deficiency">HRD</th>
|
22
|
+
<th data-bs-toggle='tooltip' data-bs-container='body' class="col-xs-1"
|
21
23
|
title="Measure of tumor purity">Tumor Purity</th>
|
22
24
|
<th data-bs-toggle='tooltip' data-bs-container='body' class="col-xs-1"
|
23
25
|
title="Downloadable CytoSure file">CGH</th>
|
@@ -41,6 +43,7 @@
|
|
41
43
|
<td>{{ ind.analysis_type|upper }}</td>
|
42
44
|
<td>{{ ind.tmb or 'N/A' }}</td>
|
43
45
|
<td>{{ ind.msi or 'N/A' }}</td>
|
46
|
+
<td>{{ ind.hrd or 'N/A' }}</td>
|
44
47
|
{% if ind.phenotype_human == "tumor" %}
|
45
48
|
<td>
|
46
49
|
<div class="d-flex align-items-center">
|
@@ -291,29 +291,33 @@
|
|
291
291
|
|
292
292
|
{% macro pretty_variant(variant) %}
|
293
293
|
|
294
|
-
{% if variant.category
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
{%
|
299
|
-
|
300
|
-
{%
|
301
|
-
{
|
302
|
-
|
303
|
-
{
|
304
|
-
|
305
|
-
{
|
294
|
+
{% if variant.category %}
|
295
|
+
{% if variant.category in "str" %}
|
296
|
+
{{ variant.str_repid }} {{ variant.alternative | replace("<", " ") | replace(">", "")}}
|
297
|
+
|
298
|
+
{% elif variant.category in ("snv", "cancer") %}
|
299
|
+
{% set display_genes = [] %}
|
300
|
+
{% for gene in variant.genes %}
|
301
|
+
{% if gene.hgvs_identifier and gene.hgnc_symbol %}
|
302
|
+
{{ "" if display_genes.append(gene.hgnc_symbol + ' ' + gene.hgvs_identifier|truncate(20,True)) }}
|
303
|
+
{% elif gene.hgnc_symbol %}
|
304
|
+
{{ "" if display_genes.append(gene.hgnc_symbol) }}
|
305
|
+
{% elif gene.hgvs_identifier and gene.hgnc_id %}
|
306
|
+
{{ "" if display_genes.append( gene.hgnc_id|string + ' ' + gene.hgvs_identifier|truncate(20,True)) }}
|
307
|
+
{% endif %}
|
308
|
+
{% endfor %}
|
309
|
+
|
310
|
+
{% if not display_genes %}
|
311
|
+
{{ "" if display_genes.append( variant.simple_id|truncate(40,True) ) }}
|
306
312
|
{% endif %}
|
307
|
-
{% endfor %}
|
308
313
|
|
309
|
-
|
310
|
-
{{ "" if display_genes.append( variant.simple_id|truncate(40,True) ) }}
|
311
|
-
{% endif %}
|
314
|
+
{{ display_genes|join(", ") }}
|
312
315
|
|
313
|
-
{
|
314
|
-
|
315
|
-
{
|
316
|
+
{% else %} <!-- structural variants -->
|
317
|
+
{{ variant.sub_category|upper }}({{ variant.chromosome }}{{ variant.cytoband_start }}-{{ variant.end_chrom }}{{ variant.cytoband_end }})
|
318
|
+
{% endif %}
|
316
319
|
{% endif %}
|
320
|
+
|
317
321
|
{% endmacro %}
|
318
322
|
|
319
323
|
{% macro pretty_link_variant(variant, case) %}
|
@@ -486,6 +486,7 @@ def phenotypes_actions(institute_id, case_name):
|
|
486
486
|
hpo_ids = request.form.getlist("hpo_id")
|
487
487
|
user_obj = store.user(current_user.email)
|
488
488
|
|
489
|
+
### match action for 3.10
|
489
490
|
if action == "PHENOMIZER":
|
490
491
|
diseases = controllers.phenomizer_diseases(hpo_ids, case_obj)
|
491
492
|
if diseases:
|
@@ -511,14 +512,8 @@ def phenotypes_actions(institute_id, case_name):
|
|
511
512
|
)
|
512
513
|
store.update_dynamic_gene_list(case_obj, hgnc_ids=list(hgnc_ids), add_only=True)
|
513
514
|
|
514
|
-
if action == "REMOVEGENES":
|
515
|
-
|
516
|
-
genes_to_remove = [int(gene_id) for gene_id in request.form.getlist("dynamicGene")]
|
517
|
-
store.update_dynamic_gene_list(
|
518
|
-
case_obj,
|
519
|
-
hgnc_ids=list(set(case_dynamic_genes) - set(genes_to_remove)),
|
520
|
-
delete_only=True,
|
521
|
-
)
|
515
|
+
if action == "REMOVEGENES":
|
516
|
+
controllers.remove_dynamic_genes(store, case_obj, institute_obj, request.form)
|
522
517
|
|
523
518
|
if action == "GENES":
|
524
519
|
hgnc_symbols = parse_raw_gene_symbols(request.form.getlist("genes"))
|
@@ -928,9 +923,10 @@ def default_panels(institute_id, case_name):
|
|
928
923
|
@cases_bp.route("/<institute_id>/<case_name>/update-clinical-filter-hpo", methods=["POST"])
|
929
924
|
def update_clinical_filter_hpo(institute_id, case_name):
|
930
925
|
"""Update default panels for a case."""
|
926
|
+
institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
|
931
927
|
hpo_clinical_filter = request.form.get("hpo_clinical_filter")
|
932
928
|
controllers.update_clinical_filter_hpo(
|
933
|
-
store, current_user,
|
929
|
+
store, current_user, institute_obj, case_obj, hpo_clinical_filter
|
934
930
|
)
|
935
931
|
return redirect(request.referrer)
|
936
932
|
|
@@ -2,7 +2,7 @@ import csv
|
|
2
2
|
import logging
|
3
3
|
from datetime import datetime
|
4
4
|
from tempfile import NamedTemporaryFile
|
5
|
-
from typing import List, Optional
|
5
|
+
from typing import List, Optional, Tuple
|
6
6
|
|
7
7
|
from flask import flash
|
8
8
|
from flask_login import current_user
|
@@ -17,6 +17,7 @@ from scout.constants.clinvar import (
|
|
17
17
|
)
|
18
18
|
from scout.constants.variant_tags import MANUAL_RANK_OPTIONS
|
19
19
|
from scout.models.clinvar import clinvar_variant
|
20
|
+
from scout.server.blueprints.variant.utils import add_gene_info
|
20
21
|
from scout.server.extensions import clinvar_api, store
|
21
22
|
from scout.utils.hgvs import validate_hgvs
|
22
23
|
from scout.utils.scout_requests import fetch_refseq_version
|
@@ -26,18 +27,15 @@ from .form import CaseDataForm, SNVariantForm, SVariantForm
|
|
26
27
|
LOG = logging.getLogger(__name__)
|
27
28
|
|
28
29
|
|
29
|
-
def _get_var_tx_hgvs(case_obj, variant_obj):
|
30
|
-
"""Retrieve all transcripts / hgvs for a given variant
|
31
|
-
|
32
|
-
case_obj(scout.models.Case)
|
33
|
-
variant_obj(scout.models.Variant)
|
34
|
-
Returns:
|
35
|
-
list of tuples. example: [("NM_002340.6:c.1840C>T", "NM_002340.6:c.1840C>T (validated)" ), ("NM_001145436.2:c.1840C>T", "NM_001145436.2:c.1840C>T"), .. ]
|
36
|
-
"""
|
30
|
+
def _get_var_tx_hgvs(case_obj: dict, variant_obj: dict) -> List[Tuple[str, str]]:
|
31
|
+
"""Retrieve all transcripts / hgvs for a given variant."""
|
32
|
+
|
37
33
|
build = str(case_obj.get("genome_build", "37"))
|
38
34
|
tx_hgvs_list = [("", "Do not specify")]
|
35
|
+
add_gene_info(store, variant_obj, genome_build=build)
|
39
36
|
for gene in variant_obj.get("genes", []):
|
40
37
|
for tx in gene.get("transcripts", []):
|
38
|
+
mane_select = tx.get("mane_select_transcript")
|
41
39
|
if all([tx.get("refseq_id"), tx.get("coding_sequence_name")]):
|
42
40
|
for refseq in tx.get("refseq_identifiers"):
|
43
41
|
refseq_version = fetch_refseq_version(refseq) # adds version to a refseq ID
|
@@ -48,10 +46,12 @@ def _get_var_tx_hgvs(case_obj, variant_obj):
|
|
48
46
|
|
49
47
|
label = hgvs_simple
|
50
48
|
if validated:
|
51
|
-
label += "
|
49
|
+
label += "_validated_"
|
52
50
|
|
53
|
-
|
51
|
+
if mane_select and mane_select == refseq_version:
|
52
|
+
label += "_mane-select_"
|
54
53
|
|
54
|
+
tx_hgvs_list.append((hgvs_simple, label))
|
55
55
|
return tx_hgvs_list
|
56
56
|
|
57
57
|
|
@@ -110,26 +110,34 @@
|
|
110
110
|
<table style="width:100%; table-layout: auto; border-collapse: collapse;">
|
111
111
|
<caption></caption>
|
112
112
|
<tr><th></th></tr>
|
113
|
-
{% set ns = namespace(label='', validated=false) %}
|
114
113
|
{% for item_row in variant_data.var_form.tx_hgvs | batch(3) %}
|
115
114
|
<tr>
|
116
115
|
{% for item in item_row %}
|
117
|
-
|
116
|
+
{% set ns = namespace(label='', validated=false, mane_select=false) %}
|
118
117
|
|
119
|
-
{% if "
|
118
|
+
{% if "_validated_" in item.label.text %}
|
120
119
|
{% set ns.validated = true %}
|
121
|
-
{% else %}
|
122
|
-
{% set ns.validated = false %}
|
123
120
|
{% endif %}
|
124
|
-
{%
|
121
|
+
{% if "_mane-select_" in item.label.text %}
|
122
|
+
{% set ns.mane_select = true %}
|
123
|
+
{% endif %}
|
124
|
+
{% set ns.label = item.label.text | replace("_validated_", "") | replace("_mane-select_", "") %}
|
125
|
+
|
126
|
+
<td style="width: 3%; text-align: right; vertical-align: baseline;">
|
127
|
+
<input type="radio" name="tx_hgvs" value="{{ ns.label }}"
|
128
|
+
{% if ns.mane_select %}checked{% endif %}>
|
129
|
+
</td>
|
125
130
|
|
126
131
|
<td style="width: 30%; text-align: left; ">
|
127
132
|
<p class="text-dark"
|
128
|
-
{% if ns.label|length >
|
133
|
+
{% if ns.label|length > 50 %}
|
129
134
|
data-bs-toggle="tooltip" title="{{ns.label}}">{{ ns.label|truncate(25,true,'..') }}
|
130
135
|
{% else %}
|
131
136
|
>{{ ns.label }}
|
132
137
|
{% endif %}
|
138
|
+
{% if ns.mane_select %}
|
139
|
+
<span class='badge bg-dark'>MANE SELECT</span>
|
140
|
+
{% endif %}
|
133
141
|
{% if ns.validated %}
|
134
142
|
<em class="fa fa-check text-success" aria-hidden="true" data-bs-toggle="tooltip" title="Verified by VariantValidator"></em>
|
135
143
|
{% endif %}
|
@@ -20,7 +20,10 @@ from scout.constants import (
|
|
20
20
|
SEX_MAP,
|
21
21
|
VARIANTS_TARGET_FROM_CATEGORY,
|
22
22
|
)
|
23
|
-
from scout.server.blueprints.variant.utils import
|
23
|
+
from scout.server.blueprints.variant.utils import (
|
24
|
+
predictions,
|
25
|
+
update_representative_gene,
|
26
|
+
)
|
24
27
|
from scout.server.extensions import beacon, store
|
25
28
|
from scout.server.utils import institute_and_case, user_institutes
|
26
29
|
|
@@ -328,6 +331,21 @@ def get_clinvar_submitters(form: MultiDict) -> Optional[List[str]]:
|
|
328
331
|
return clinvar_submitters
|
329
332
|
|
330
333
|
|
334
|
+
def get_soft_filters(form: MultiDict) -> Optional[list]:
|
335
|
+
"""
|
336
|
+
Return a list with custom soft filters or None.
|
337
|
+
This is not available on the form for unprivileged users, only admin.
|
338
|
+
"""
|
339
|
+
if current_user.is_admin is False:
|
340
|
+
return None
|
341
|
+
|
342
|
+
soft_filters = []
|
343
|
+
for filter in form.getlist("soft_filters"):
|
344
|
+
soft_filters.append(filter)
|
345
|
+
|
346
|
+
return soft_filters
|
347
|
+
|
348
|
+
|
331
349
|
def get_loqusdb_ids(form: MultiDict) -> Optional[List[str]]:
|
332
350
|
"""
|
333
351
|
Return loqusdb ids from the form multiselect.
|
@@ -390,6 +408,7 @@ def update_institute_settings(store: MongoAdapter, institute_obj: Dict, form: Mu
|
|
390
408
|
check_show_all_vars=form.get("check_show_all_vars"),
|
391
409
|
clinvar_key=form.get("clinvar_key"),
|
392
410
|
clinvar_submitters=get_clinvar_submitters(form),
|
411
|
+
soft_filters=get_soft_filters(form),
|
393
412
|
)
|
394
413
|
return updated_institute
|
395
414
|
|
@@ -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
|
|
@@ -34,8 +34,21 @@
|
|
34
34
|
<div class="row" id="body-row"> <!--sidebar and main container are on the same row-->
|
35
35
|
{{ institute_actionbar(institute) }} <!-- This is the sidebar -->
|
36
36
|
<div class="col">
|
37
|
+
|
37
38
|
<div class="card">
|
38
|
-
<div class="card-header"><b>
|
39
|
+
<div class="card-header"><b>Soft filters created by an admin on the institute's settings page</b></div>
|
40
|
+
<div class="card-body">
|
41
|
+
{% for filter_tag in institute.soft_filters %}
|
42
|
+
<span class="badge bg-secondary">{{filter_tag}}</span>
|
43
|
+
{% else%}
|
44
|
+
No soft filters available
|
45
|
+
{% endfor %}
|
46
|
+
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
|
50
|
+
<div class="card mt-3">
|
51
|
+
<div class="card-header"><b>Custom filters created for institute on variants pages</b></div>
|
39
52
|
<div class="card-body">
|
40
53
|
<ul class="list-group">
|
41
54
|
<li class="list-group-item list-group-item-heading">
|
@@ -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 %}
|