scout-browser 4.85__py3-none-any.whl → 4.86__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/mongo/base.py +17 -14
- scout/adapter/mongo/case.py +20 -1
- scout/adapter/mongo/filter.py +36 -1
- scout/adapter/mongo/omics_variant.py +145 -0
- scout/adapter/mongo/query.py +13 -3
- scout/adapter/mongo/variant.py +10 -4
- scout/build/case.py +5 -0
- scout/build/variant/variant.py +1 -0
- scout/constants/__init__.py +3 -1
- scout/constants/case_tags.py +1 -0
- scout/constants/clinvar.py +1 -1
- scout/constants/file_types.py +31 -0
- scout/constants/filters.py +4 -0
- scout/constants/indexes.py +30 -13
- scout/constants/variant_tags.py +3 -0
- scout/demo/643594.clinical.mei.vcf.gz +0 -0
- scout/demo/643594.clinical.mei.vcf.gz.tbi +0 -0
- scout/demo/643594.config.yaml +4 -0
- scout/demo/drop/fraser_top_hits_clinical.tsv +5 -0
- scout/demo/drop/outrider_top_hits_clinical.tsv +10 -0
- scout/load/setup.py +4 -4
- scout/models/case/case_loading_models.py +25 -2
- scout/models/omics_variant.py +227 -0
- scout/parse/omics_variant/__init__.py +11 -0
- scout/parse/omics_variant/drop.py +19 -0
- scout/parse/variant/callers.py +6 -3
- scout/parse/variant/frequency.py +10 -2
- scout/server/app.py +4 -1
- scout/server/blueprints/alignviewers/controllers.py +35 -24
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_sashimi_viewer.html +19 -15
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +45 -5
- scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
- scout/server/blueprints/alignviewers/views.py +10 -2
- scout/server/blueprints/cases/controllers.py +3 -0
- scout/server/blueprints/cases/templates/cases/case.html +27 -9
- scout/server/blueprints/cases/templates/cases/case_report.html +2 -17
- scout/server/blueprints/cases/templates/cases/phenotype.html +8 -5
- scout/server/blueprints/cases/templates/cases/utils.html +26 -3
- scout/server/blueprints/clinvar/controllers.py +9 -3
- scout/server/blueprints/dashboard/controllers.py +44 -13
- scout/server/blueprints/dashboard/static/charts.js +46 -36
- scout/server/blueprints/dashboard/templates/dashboard/dashboard_general.html +2 -2
- scout/server/blueprints/institutes/forms.py +2 -0
- scout/server/blueprints/institutes/templates/overview/cases.html +6 -4
- scout/server/blueprints/institutes/templates/overview/gene_variants.html +40 -27
- scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +1 -1
- scout/server/blueprints/institutes/views.py +5 -12
- scout/server/blueprints/omics_variants/__init__.py +1 -0
- scout/server/blueprints/omics_variants/controllers.py +122 -0
- scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +262 -0
- scout/server/blueprints/omics_variants/views.py +106 -0
- scout/server/blueprints/panels/controllers.py +1 -7
- scout/server/blueprints/panels/templates/panels/panels.html +12 -4
- scout/server/blueprints/panels/views.py +9 -11
- scout/server/blueprints/variant/templates/variant/buttons.html +7 -2
- scout/server/blueprints/variant/templates/variant/str-variant-reviewer.html +1 -1
- scout/server/blueprints/variant/templates/variant/utils.html +1 -1
- scout/server/blueprints/variant/utils.py +54 -103
- scout/server/blueprints/variant/views.py +1 -0
- scout/server/blueprints/variants/controllers.py +1 -4
- scout/server/blueprints/variants/forms.py +42 -0
- scout/server/blueprints/variants/templates/variants/utils.html +8 -4
- scout/server/blueprints/variants/views.py +28 -7
- scout/server/config.py +4 -0
- scout/server/extensions/clinvar_extension.py +7 -7
- scout/server/links.py +2 -2
- scout/server/templates/bootstrap_global.html +1 -4
- scout/server/templates/utils.html +3 -3
- scout/server/utils.py +4 -1
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/METADATA +10 -10
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/RECORD +76 -66
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/WHEEL +1 -1
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/LICENSE +0 -0
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
from flask import Blueprint, request
|
2
|
+
from flask_login import current_user
|
3
|
+
from markupsafe import Markup
|
4
|
+
|
5
|
+
from scout.server.blueprints.variants.controllers import (
|
6
|
+
activate_case,
|
7
|
+
case_default_panels,
|
8
|
+
gene_panel_choices,
|
9
|
+
get_expand_search,
|
10
|
+
get_variants_page,
|
11
|
+
populate_chrom_choices,
|
12
|
+
populate_filters_form,
|
13
|
+
)
|
14
|
+
from scout.server.blueprints.variants.forms import OutlierFiltersForm
|
15
|
+
from scout.server.extensions import store
|
16
|
+
from scout.server.utils import institute_and_case, templated
|
17
|
+
|
18
|
+
from . import controllers
|
19
|
+
|
20
|
+
omics_variants_bp = Blueprint(
|
21
|
+
"omics_variants",
|
22
|
+
__name__,
|
23
|
+
template_folder="templates",
|
24
|
+
)
|
25
|
+
|
26
|
+
|
27
|
+
@omics_variants_bp.route(
|
28
|
+
"/<institute_id>/<case_name>/omics_variants/outliers", methods=["GET", "POST"]
|
29
|
+
)
|
30
|
+
@templated("omics_variants/outliers.html")
|
31
|
+
def outliers(institute_id, case_name):
|
32
|
+
"""Display a list of outlier omics variants."""
|
33
|
+
|
34
|
+
page = get_variants_page(request.form)
|
35
|
+
category = "outlier"
|
36
|
+
institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
|
37
|
+
|
38
|
+
variant_type = Markup.escape(
|
39
|
+
request.args.get("variant_type", request.form.get("variant_type", "clinical"))
|
40
|
+
)
|
41
|
+
if variant_type not in ["clinical", "research"]:
|
42
|
+
variant_type = "clinical"
|
43
|
+
variants_stats = store.case_variants_count(case_obj["_id"], institute_id, variant_type, False)
|
44
|
+
|
45
|
+
if request.form.get("hpo_clinical_filter"):
|
46
|
+
case_obj["hpo_clinical_filter"] = True
|
47
|
+
|
48
|
+
# update status of case if visited for the first time
|
49
|
+
activate_case(store, institute_obj, case_obj, current_user)
|
50
|
+
|
51
|
+
if request.method == "GET":
|
52
|
+
form = OutlierFiltersForm(request.args)
|
53
|
+
variant_type = request.args.get("variant_type", "clinical")
|
54
|
+
form.variant_type.data = variant_type
|
55
|
+
# set chromosome to all chromosomes
|
56
|
+
form.chrom.data = request.args.get("chrom", "")
|
57
|
+
if form.gene_panels.data == [] and variant_type == "clinical":
|
58
|
+
form.gene_panels.data = case_default_panels(case_obj)
|
59
|
+
else: # POST
|
60
|
+
user_obj = store.user(current_user.email)
|
61
|
+
form = populate_filters_form(
|
62
|
+
store, institute_obj, case_obj, user_obj, category, request.form
|
63
|
+
)
|
64
|
+
|
65
|
+
# populate filters dropdown
|
66
|
+
available_filters = list(store.filters(institute_obj["_id"], category))
|
67
|
+
form.filters.choices = [
|
68
|
+
(filter.get("_id"), filter.get("display_name")) for filter in available_filters
|
69
|
+
]
|
70
|
+
|
71
|
+
# populate available panel choices
|
72
|
+
form.gene_panels.choices = gene_panel_choices(store, institute_obj, case_obj)
|
73
|
+
|
74
|
+
# Populate chromosome select choices
|
75
|
+
populate_chrom_choices(form, case_obj)
|
76
|
+
|
77
|
+
genome_build = "38" if "38" in str(case_obj.get("genome_build", "37")) else "37"
|
78
|
+
cytobands = store.cytoband_by_chrom(genome_build)
|
79
|
+
|
80
|
+
# controllers.update_form_hgnc_symbols(store, case_obj, form)
|
81
|
+
variants_query = store.omics_variants(
|
82
|
+
case_obj["_id"], query=form.data, category=category, build=genome_build
|
83
|
+
)
|
84
|
+
|
85
|
+
if request.form.get("export"):
|
86
|
+
return controllers.download_omics_variants(case_obj, variants_query)
|
87
|
+
|
88
|
+
result_size = store.count_omics_variants(
|
89
|
+
case_id=case_obj["_id"], query=form.data, category=category, build=genome_build
|
90
|
+
)
|
91
|
+
|
92
|
+
data = controllers.outliers(store, institute_obj, case_obj, variants_query, result_size, page)
|
93
|
+
|
94
|
+
return dict(
|
95
|
+
case=case_obj,
|
96
|
+
cytobands=cytobands,
|
97
|
+
expand_search=get_expand_search(request.form),
|
98
|
+
filters=available_filters,
|
99
|
+
form=form,
|
100
|
+
institute=institute_obj,
|
101
|
+
page=page,
|
102
|
+
result_size=result_size,
|
103
|
+
total_variants=variants_stats.get(variant_type, {}).get(category, "NA"),
|
104
|
+
variant_type=variant_type,
|
105
|
+
**data,
|
106
|
+
)
|
@@ -16,13 +16,7 @@ LOG = logging.getLogger(__name__)
|
|
16
16
|
INHERITANCE_MODELS = ["ar", "ad", "mt", "xr", "xd", "x", "y"]
|
17
17
|
|
18
18
|
|
19
|
-
def
|
20
|
-
"""Check if panel shall be displayed based on display status and user previleges."""
|
21
|
-
is_visible = not panel_obj.get("hidden", False)
|
22
|
-
return is_visible or panel_write_granted(panel_obj, user)
|
23
|
-
|
24
|
-
|
25
|
-
def panel_write_granted(panel_obj, user):
|
19
|
+
def panel_write_granted(panel_obj, user) -> bool:
|
26
20
|
return any(
|
27
21
|
["maintainer" not in panel_obj, user.is_admin, user._id in panel_obj.get("maintainer", [])]
|
28
22
|
)
|
@@ -150,7 +150,7 @@
|
|
150
150
|
<table class="table table-striped" id="panelTable" style="table-layout: fixed;">
|
151
151
|
<thead>
|
152
152
|
<tr>
|
153
|
-
<th>Name</th>
|
153
|
+
<th style="width: 30%">Name</th>
|
154
154
|
<th>Version</th>
|
155
155
|
<th>Number of genes</th>
|
156
156
|
<th>History</th>
|
@@ -163,8 +163,11 @@
|
|
163
163
|
<tr {% if panel.hidden %} class="hidableRow collapse" {% endif %}>
|
164
164
|
<td>
|
165
165
|
<a href="{{ url_for('panels.panel', panel_id=panel._id) }}">{{ panel.display_name }}</a>
|
166
|
+
{% if current_user.email in panel.maintainer %}
|
167
|
+
<span class="badge bg-dark" data-bs-toggle="tooltip" title="You are a maintainer of this panel"><span class="fa fa-tools"></span></span>
|
168
|
+
{% endif %}
|
166
169
|
{% if panel.hidden %}
|
167
|
-
<span class="badge bg-danger">Removed</span>
|
170
|
+
<span class="badge bg-danger" data-bs-toggle="tooltip" title="This panel was removed">Removed</span>
|
168
171
|
{% endif %}
|
169
172
|
</td>
|
170
173
|
<td>{{ panel.version }} ({{ panel.date.date()}})</td>
|
@@ -174,10 +177,10 @@
|
|
174
177
|
<td>
|
175
178
|
{% if panel.hidden %}
|
176
179
|
<form action="{{ url_for('panels.panel_restore', panel_id=panel._id) }}" method="POST">
|
177
|
-
<button id="{{panel._id}}" type="submit" title="Restore panel" class="btn btn-success btn-xs"><span class="fa fa-undo"></span></button>
|
180
|
+
<button {{panel.writable}} id="{{panel._id}}" type="submit" title="Restore panel" class="btn btn-success btn-xs"><span class="fa fa-undo"></span></button>
|
178
181
|
</form>
|
179
182
|
{% else %}
|
180
|
-
<button id="{{panel._id}}" type="button" data-bs-toggle="modal" data-bs-target="#remove-gene-panel-modal-{{ panel._id }}" class="btn btn-danger btn-xs"><span class="fa fa-trash"></span></button>
|
183
|
+
<button {{panel.writable}} id="{{panel._id}}" type="button" data-bs-toggle="modal" data-bs-target="#remove-gene-panel-modal-{{ panel._id }}" class="btn btn-danger btn-xs"><span class="fa fa-trash"></span></button>
|
181
184
|
{% endif %}
|
182
185
|
</td>
|
183
186
|
</tr>
|
@@ -269,6 +272,11 @@
|
|
269
272
|
<script src="https://cdn.datatables.net/1.12.0/js/jquery.dataTables.min.js" integrity="sha512-fu0WiDG5xqtX2iWk7cp17Q9so54SC+5lk/z/glzwlKFdEOwGG6piUseP2Sik9hlvlmyOJ0lKXRSuv1ltdVk9Jg==" referrerpolicy="no-referrer" crossorigin="anonymous"></script>
|
270
273
|
<script type="text/javascript">
|
271
274
|
|
275
|
+
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
276
|
+
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
277
|
+
return new bootstrap.Tooltip(tooltipTriggerEl)
|
278
|
+
})
|
279
|
+
|
272
280
|
$('.history_btn').on('click', function(){
|
273
281
|
var bid = $(this)[0].id;
|
274
282
|
var sel = '#paneldiv_' + bid;
|
@@ -52,7 +52,6 @@ def panels():
|
|
52
52
|
if request.method == "POST" and request.form.get("search_for"):
|
53
53
|
# Query db for panels containing the search string. This is done with autocompletion
|
54
54
|
# therefor only one(1) hgnc_id will be received from the form.
|
55
|
-
hgnc_symbols = []
|
56
55
|
search_string = escape(request.form.get("search_for"))
|
57
56
|
try:
|
58
57
|
hgnc_symbols = parse_raw_gene_ids([search_string])
|
@@ -84,18 +83,17 @@ def panels():
|
|
84
83
|
panel_versions = {}
|
85
84
|
for name in panel_names:
|
86
85
|
panels = store.gene_panels(panel_id=name, include_hidden=True)
|
87
|
-
panel_versions[name] = [
|
88
|
-
|
89
|
-
for panel_obj in panels
|
90
|
-
if controllers.shall_display_panel(panel_obj, current_user)
|
91
|
-
]
|
86
|
+
panel_versions[name] = [panel_obj for panel_obj in panels]
|
87
|
+
|
92
88
|
panel_groups = []
|
93
89
|
for institute_obj in institutes:
|
94
|
-
institute_panels =
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
90
|
+
institute_panels = []
|
91
|
+
for panel in store.latest_panels(institute_obj["_id"], include_hidden=True):
|
92
|
+
panel["writable"] = (
|
93
|
+
"" if controllers.panel_write_granted(panel, current_user) else "disabled"
|
94
|
+
)
|
95
|
+
institute_panels.append(panel)
|
96
|
+
|
99
97
|
panel_groups.append((institute_obj, institute_panels))
|
100
98
|
return dict(
|
101
99
|
panel_groups=panel_groups,
|
@@ -33,9 +33,14 @@
|
|
33
33
|
</div>
|
34
34
|
{% endmacro %}
|
35
35
|
|
36
|
-
{% macro splice_junctions_button(institute_id, case_name, variant_id) %}
|
37
|
-
|
36
|
+
{% macro splice_junctions_button(institute_id, case_name, variant_id, omics_variant_id) %}
|
37
|
+
{% if omics_variant_id %}
|
38
|
+
<a class="btn btn-sm btn-secondary text-white" href="{{url_for('alignviewers.sashimi_igv', institute_id=institute_id, case_name=case_name, omics_variant_id=omics_variant_id)}}" target="_blank"
|
38
39
|
data-bs-toggle="tooltip" data-bs-placement="top" title="Only available in build GRCh38">RNA splicing</a>
|
40
|
+
{% else %}
|
41
|
+
<a class="btn btn-sm btn-secondary text-white" href="{{url_for('alignviewers.sashimi_igv', institute_id=institute_id, case_name=case_name, variant_id=variant_id)}}" target="_blank"
|
42
|
+
data-bs-toggle="tooltip" data-bs-placement="top" title="Only available in build GRCh38">RNA splicing</a>
|
43
|
+
{% endif %}
|
39
44
|
{% endmacro %}
|
40
45
|
|
41
46
|
{% macro variant_tag_button(variant, institute, case, manual_rank_options) %}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
{% extends "layout.html" %}
|
2
2
|
{% from "utils.html" import comments_table, pedigree_panel %}
|
3
|
-
{% from "variants/components.html" import
|
3
|
+
{% from "variants/components.html" import frequency_cell_general %}
|
4
4
|
{% from "variants/utils.html" import cell_rank, dismiss_variants_block, filter_form_footer, update_stash_filter_button_status, pagination_footer, pagination_hidden_div, str_filters %}
|
5
5
|
{% from "variant/buttons.html" import igv_button, reviewer_button%}
|
6
6
|
|
@@ -100,7 +100,7 @@
|
|
100
100
|
<div class="col-3">Show tracks:</div>
|
101
101
|
<div class="col-6">
|
102
102
|
<select name="user_tracks" class="selectpicker" data-width="90%" data-style="btn-secondary" multiple>
|
103
|
-
{% for track in igv_tracks %}
|
103
|
+
{% for track in igv_tracks|sort %}
|
104
104
|
<!--pre-select option if user has saved it in preferences or select all options if user has no preferences yet-->
|
105
105
|
<option value="{{ track }}" {{ "selected" if current_user.igv_tracks and track in current_user.igv_tracks or current_user.igv_tracks is not defined }}>{{ track }}</option>
|
106
106
|
{% endfor %}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import logging
|
2
|
-
from typing import Dict, List, Optional
|
2
|
+
from typing import Dict, List, Optional, Tuple
|
3
3
|
|
4
4
|
from scout.adapter import MongoAdapter
|
5
5
|
from scout.constants import ACMG_COMPLETE_MAP, CALLERS, CLINSIG_MAP, SO_TERMS
|
@@ -333,122 +333,72 @@ def predictions(genes):
|
|
333
333
|
return data
|
334
334
|
|
335
335
|
|
336
|
-
def frequencies(variant_obj):
|
337
|
-
"""
|
338
|
-
|
339
|
-
This function converts the raw annotations to something better to visualize.
|
340
|
-
GnomAD is mandatory and will always be shown.
|
336
|
+
def frequencies(variant_obj: dict) -> list[Tuple]:
|
337
|
+
"""Convert raw annotations to a more visual format with frequencies.
|
341
338
|
|
342
339
|
Args:
|
343
340
|
variant_obj(scout.models.Variant)
|
344
341
|
|
345
342
|
Returns:
|
346
|
-
|
343
|
+
list of tuple: A list of frequencies to display.
|
347
344
|
"""
|
348
345
|
is_mitochondrial_variant = variant_obj.get("chromosome") == "MT"
|
346
|
+
category = variant_obj["category"]
|
347
|
+
|
348
|
+
# Define frequency mappings for each category
|
349
|
+
frequency_mappings = {
|
350
|
+
"sv": {
|
351
|
+
"gnomad_frequency": ("GnomAD", variant_obj.get("gnomad_sv_link")),
|
352
|
+
"clingen_cgh_benign": ("ClinGen CGH (benign)", None),
|
353
|
+
"clingen_cgh_pathogenic": ("ClinGen CGH (pathogenic)", None),
|
354
|
+
"clingen_ngi": ("ClinGen NGI", None),
|
355
|
+
"clingen_mip": ("ClinGen MIP", None),
|
356
|
+
"swegen": ("SweGen", None),
|
357
|
+
"decipher": ("Decipher", None),
|
358
|
+
"thousand_genomes_frequency": ("1000G", None),
|
359
|
+
"thousand_genomes_frequency_left": ("1000G(left)", None),
|
360
|
+
"thousand_genomes_frequency_right": ("1000G(right)", None),
|
361
|
+
"colorsdb_af": ("CoLoRSdb", None),
|
362
|
+
},
|
363
|
+
"mei": {
|
364
|
+
"swegen_alu": ("SweGen ALU", None),
|
365
|
+
"swegen_herv": ("SweGen HERV", None),
|
366
|
+
"swegen_l1": ("SweGen L1", None),
|
367
|
+
"swegen_sva": ("SweGen SVA", None),
|
368
|
+
"swegen_mei_max": ("SweGen MEI(max)", None),
|
369
|
+
},
|
370
|
+
"snv": {
|
371
|
+
"gnomad_frequency": ("GnomAD", variant_obj.get("gnomad_link")),
|
372
|
+
"thousand_genomes_frequency": ("1000G", variant_obj.get("thousandg_link")),
|
373
|
+
"max_thousand_genomes_frequency": ("1000G(max)", variant_obj.get("thousandg_link")),
|
374
|
+
"exac_frequency": ("ExAC", variant_obj.get("exac_link")),
|
375
|
+
"max_exac_frequency": ("ExAC(max)", variant_obj.get("exac_link")),
|
376
|
+
"swegen": ("SweGen", variant_obj.get("swegen_link")),
|
377
|
+
"gnomad_mt_homoplasmic_frequency": (
|
378
|
+
"GnomAD MT, homoplasmic",
|
379
|
+
variant_obj.get("gnomad_link"),
|
380
|
+
),
|
381
|
+
"gnomad_mt_heteroplasmic_frequency": (
|
382
|
+
"GnomAD MT, heteroplasmic",
|
383
|
+
variant_obj.get("gnomad_link"),
|
384
|
+
),
|
385
|
+
"colorsdb_af": ("CoLoRSdb", None),
|
386
|
+
},
|
387
|
+
}
|
388
|
+
|
389
|
+
# Select the appropriate frequency dictionary
|
390
|
+
freqs = frequency_mappings.get(category, frequency_mappings["snv"])
|
349
391
|
|
350
|
-
if variant_obj["category"] == "sv":
|
351
|
-
freqs = {
|
352
|
-
"gnomad_frequency": {
|
353
|
-
"display_name": "GnomAD",
|
354
|
-
"link": variant_obj.get("gnomad_sv_link"),
|
355
|
-
},
|
356
|
-
"clingen_cgh_benign": {
|
357
|
-
"display_name": "ClinGen CGH (benign)",
|
358
|
-
"link": None,
|
359
|
-
},
|
360
|
-
"clingen_cgh_pathogenic": {
|
361
|
-
"display_name": "ClinGen CGH (pathogenic)",
|
362
|
-
"link": None,
|
363
|
-
},
|
364
|
-
"clingen_ngi": {"display_name": "ClinGen NGI", "link": None},
|
365
|
-
"clingen_mip": {"display_name": "ClinGen MIP", "link": None},
|
366
|
-
"swegen": {"display_name": "SweGen", "link": None},
|
367
|
-
"decipher": {"display_name": "Decipher", "link": None},
|
368
|
-
"thousand_genomes_frequency": {"display_name": "1000G", "link": None},
|
369
|
-
"thousand_genomes_frequency_left": {
|
370
|
-
"display_name": "1000G(left)",
|
371
|
-
"link": None,
|
372
|
-
},
|
373
|
-
"thousand_genomes_frequency_right": {
|
374
|
-
"display_name": "1000G(right)",
|
375
|
-
"link": None,
|
376
|
-
},
|
377
|
-
}
|
378
|
-
elif variant_obj["category"] == "mei":
|
379
|
-
freqs = {
|
380
|
-
"swegen_alu": {
|
381
|
-
"display_name": "SweGen ALU",
|
382
|
-
"link": None,
|
383
|
-
},
|
384
|
-
"swegen_herv": {
|
385
|
-
"display_name": "SweGen HERV",
|
386
|
-
"link": None,
|
387
|
-
},
|
388
|
-
"swegen_l1": {
|
389
|
-
"display_name": "SweGen L1",
|
390
|
-
"link": None,
|
391
|
-
},
|
392
|
-
"swegen_sva": {
|
393
|
-
"display_name": "SweGen SVA",
|
394
|
-
"link": None,
|
395
|
-
},
|
396
|
-
"swegen_mei_max": {
|
397
|
-
"display_name": "SweGen MEI(max)",
|
398
|
-
"link": None,
|
399
|
-
},
|
400
|
-
}
|
401
|
-
else:
|
402
|
-
freqs = {
|
403
|
-
"gnomad_frequency": {
|
404
|
-
"display_name": "GnomAD",
|
405
|
-
"link": variant_obj.get("gnomad_link"),
|
406
|
-
},
|
407
|
-
"thousand_genomes_frequency": {
|
408
|
-
"display_name": "1000G",
|
409
|
-
"link": variant_obj.get("thousandg_link"),
|
410
|
-
},
|
411
|
-
"max_thousand_genomes_frequency": {
|
412
|
-
"display_name": "1000G(max)",
|
413
|
-
"link": variant_obj.get("thousandg_link"),
|
414
|
-
},
|
415
|
-
"exac_frequency": {
|
416
|
-
"display_name": "ExAC",
|
417
|
-
"link": variant_obj.get("exac_link"),
|
418
|
-
},
|
419
|
-
"max_exac_frequency": {
|
420
|
-
"display_name": "ExAC(max)",
|
421
|
-
"link": variant_obj.get("exac_link"),
|
422
|
-
},
|
423
|
-
"swegen": {
|
424
|
-
"display_name": "SweGen",
|
425
|
-
"link": variant_obj.get("swegen_link"),
|
426
|
-
},
|
427
|
-
"gnomad_mt_homoplasmic_frequency": {
|
428
|
-
"display_name": "GnomAD MT, homoplasmic",
|
429
|
-
"link": variant_obj.get("gnomad_link"),
|
430
|
-
},
|
431
|
-
"gnomad_mt_heteroplasmic_frequency": {
|
432
|
-
"display_name": "GnomAD MT, heteroplasmic",
|
433
|
-
"link": variant_obj.get("gnomad_link"),
|
434
|
-
},
|
435
|
-
}
|
436
392
|
frequency_list = []
|
437
|
-
for freq_key in freqs:
|
438
|
-
display_name = freqs[freq_key]["display_name"]
|
393
|
+
for freq_key, (display_name, link) in freqs.items():
|
439
394
|
value = variant_obj.get(freq_key)
|
440
|
-
|
441
|
-
# Always add gnomad for non-mitochondrial variants
|
395
|
+
|
442
396
|
if freq_key == "gnomad_frequency":
|
443
397
|
if is_mitochondrial_variant:
|
444
398
|
continue
|
445
|
-
|
446
|
-
if not value:
|
447
|
-
value = variant_obj.get("exac_frequency")
|
448
|
-
value = value or "NA"
|
399
|
+
value = value or variant_obj.get("exac_frequency") or "NA"
|
449
400
|
|
450
|
-
|
451
|
-
elif freq_key.startswith("gnomad_mt_") and is_mitochondrial_variant:
|
401
|
+
if freq_key.startswith("gnomad_mt_") and is_mitochondrial_variant:
|
452
402
|
value = value or "NA"
|
453
403
|
|
454
404
|
if value:
|
@@ -474,6 +424,7 @@ def frequency(variant_obj):
|
|
474
424
|
variant_obj.get("gnomad_frequency") or 0,
|
475
425
|
variant_obj.get("gnomad_mt_homoplasmic_frequency") or 0,
|
476
426
|
variant_obj.get("swegen_mei_max") or 0,
|
427
|
+
variant_obj.get("colorsdb_af") or 0,
|
477
428
|
)
|
478
429
|
|
479
430
|
if most_common_frequency > 0.05:
|
@@ -1496,9 +1496,6 @@ def populate_filters_form(store, institute_obj, case_obj, user_obj, category, re
|
|
1496
1496
|
Returns:
|
1497
1497
|
form: FiltersForm
|
1498
1498
|
"""
|
1499
|
-
form = None
|
1500
|
-
clinical_filter_panels = []
|
1501
|
-
|
1502
1499
|
default_panels = []
|
1503
1500
|
for panel in case_obj.get("panels", []):
|
1504
1501
|
if panel.get("is_default"):
|
@@ -1520,7 +1517,7 @@ def populate_filters_form(store, institute_obj, case_obj, user_obj, category, re
|
|
1520
1517
|
}
|
1521
1518
|
)
|
1522
1519
|
clinical_filter = MultiDict(clinical_filter_dict)
|
1523
|
-
elif category in ("sv", "cancer", "cancer_sv", "mei"):
|
1520
|
+
elif category in ("sv", "cancer", "cancer_sv", "mei", "outlier"):
|
1524
1521
|
clinical_filter_dict = FiltersFormClass.clinical_filter_base
|
1525
1522
|
clinical_filter_dict.update(
|
1526
1523
|
{
|
@@ -23,10 +23,12 @@ from scout.constants import (
|
|
23
23
|
CLINICAL_FILTER_BASE,
|
24
24
|
CLINICAL_FILTER_BASE_CANCER,
|
25
25
|
CLINICAL_FILTER_BASE_MEI,
|
26
|
+
CLINICAL_FILTER_BASE_OUTLIER,
|
26
27
|
CLINICAL_FILTER_BASE_SV,
|
27
28
|
CLINSIG_MAP,
|
28
29
|
FEATURE_TYPES,
|
29
30
|
GENETIC_MODELS,
|
31
|
+
OUTLIER_TYPES,
|
30
32
|
SO_TERMS,
|
31
33
|
SPIDEX_LEVELS,
|
32
34
|
SV_TYPES,
|
@@ -39,6 +41,7 @@ CLINSIG_OPTIONS = list(CLINSIG_MAP.items())
|
|
39
41
|
FUNC_ANNOTATIONS = [(term, term.replace("_", " ")) for term in SO_TERMS]
|
40
42
|
REGION_ANNOTATIONS = [(term, term.replace("_", " ")) for term in FEATURE_TYPES]
|
41
43
|
SV_TYPE_CHOICES = [(term, term.replace("_", " ").upper()) for term in SV_TYPES]
|
44
|
+
OUTLIER_TYPE_CHOICES = [(term, term.replace("_", " ").upper()) for term in OUTLIER_TYPES]
|
42
45
|
SPIDEX_CHOICES = [(term, term.replace("_", " ")) for term in SPIDEX_LEVELS]
|
43
46
|
FUSION_CALLER_CHOICES = [(term.get("id"), term.get("name")) for term in CALLERS.get("fusion")]
|
44
47
|
|
@@ -236,6 +239,44 @@ class FusionFiltersForm(VariantFiltersForm):
|
|
236
239
|
fusion_caller = SelectMultipleField("Fusion Caller", choices=FUSION_CALLER_CHOICES, default=[])
|
237
240
|
|
238
241
|
|
242
|
+
class OutlierFiltersForm(FlaskForm):
|
243
|
+
variant_type = HiddenField(default="clinical")
|
244
|
+
|
245
|
+
gene_panels = NonValidatingSelectMultipleField(choices=[])
|
246
|
+
gene_panels_exclude = BooleanField("Exclude genes")
|
247
|
+
hgnc_symbols = TagListField("HGNC Symbols/Ids (case sensitive)")
|
248
|
+
|
249
|
+
svtype = SelectMultipleField("Type", choices=OUTLIER_TYPE_CHOICES)
|
250
|
+
|
251
|
+
filters = NonValidatingSelectField(choices=[], validators=[validators.Optional()])
|
252
|
+
filter_display_name = StringField(default="")
|
253
|
+
save_filter = SubmitField(label="Save filter")
|
254
|
+
load_filter = SubmitField(label="Load filter")
|
255
|
+
lock_filter = SubmitField(label="Lock filter")
|
256
|
+
delete_filter = SubmitField(label="Delete filter")
|
257
|
+
audit_filter = SubmitField(label="Audit filter")
|
258
|
+
clinical_filter = SubmitField(label="Clinical filter")
|
259
|
+
|
260
|
+
chrom_pos = StringField(
|
261
|
+
"Chromosome position",
|
262
|
+
[validators.Optional()],
|
263
|
+
render_kw={"placeholder": "<chr>:<start pos>-<end pos>[optional +/-<span>]"},
|
264
|
+
)
|
265
|
+
|
266
|
+
chrom = NonValidatingSelectMultipleField("Chromosome", choices=[], default="")
|
267
|
+
start = IntegerField("Start position", [validators.Optional()])
|
268
|
+
end = IntegerField("End position", [validators.Optional()])
|
269
|
+
cytoband_start = NonValidatingSelectField("Cytoband start", choices=[])
|
270
|
+
cytoband_end = NonValidatingSelectField("Cytoband end", choices=[])
|
271
|
+
|
272
|
+
filter_variants = SubmitField(label="Filter variants")
|
273
|
+
export = SubmitField(label="Filter and export")
|
274
|
+
|
275
|
+
clinical_filter_base = CLINICAL_FILTER_BASE_OUTLIER
|
276
|
+
|
277
|
+
show_unaffected = BooleanField("Show also variants present only in unaffected", default=False)
|
278
|
+
|
279
|
+
|
239
280
|
FILTERSFORMCLASS = {
|
240
281
|
"snv": FiltersForm,
|
241
282
|
"str": StrFiltersForm,
|
@@ -244,4 +285,5 @@ FILTERSFORMCLASS = {
|
|
244
285
|
"cancer": CancerFiltersForm,
|
245
286
|
"mei": MeiFiltersForm,
|
246
287
|
"fusion": FusionFiltersForm,
|
288
|
+
"outlier": OutlierFiltersForm,
|
247
289
|
}
|
@@ -30,13 +30,17 @@
|
|
30
30
|
|
31
31
|
var the_form = document.forms['filters_form'];
|
32
32
|
|
33
|
-
document.getElementById('hide_dismissed')
|
33
|
+
var hide_dismissed = document.getElementById('hide_dismissed');
|
34
|
+
if (hide_dismissed) {
|
35
|
+
hide_dismissed.onchange = function() {
|
34
36
|
the_form.submit();
|
35
|
-
}
|
37
|
+
}}
|
36
38
|
|
37
|
-
document.getElementById('show_unaffected')
|
39
|
+
var show_unaffected =document.getElementById('show_unaffected');
|
40
|
+
if (show_unaffected) {
|
41
|
+
show_unaffected.onchange = function() {
|
38
42
|
the_form.submit();
|
39
|
-
}
|
43
|
+
}}
|
40
44
|
|
41
45
|
function resetPage(){
|
42
46
|
document.getElementById('page').value = "1";
|
@@ -137,7 +137,9 @@ def variants(institute_id, case_name):
|
|
137
137
|
variants_query = store.variants(
|
138
138
|
case_obj["_id"], query=form.data, category=category, build=genome_build
|
139
139
|
)
|
140
|
-
result_size = store.count_variants(
|
140
|
+
result_size = store.count_variants(
|
141
|
+
case_obj["_id"], form.data, None, category, build=genome_build
|
142
|
+
)
|
141
143
|
|
142
144
|
if request.form.get("export"):
|
143
145
|
return controllers.download_variants(store, case_obj, variants_query)
|
@@ -236,7 +238,7 @@ def str_variants(institute_id, case_name):
|
|
236
238
|
]
|
237
239
|
)
|
238
240
|
|
239
|
-
result_size = store.count_variants(case_obj["_id"], query, None, category)
|
241
|
+
result_size = store.count_variants(case_obj["_id"], query, None, category, build=genome_build)
|
240
242
|
|
241
243
|
if request.form.get("export"):
|
242
244
|
return controllers.download_str_variants(case_obj, variants_query)
|
@@ -312,7 +314,9 @@ def sv_variants(institute_id, case_name):
|
|
312
314
|
case_obj["_id"], category=category, query=form.data, build=genome_build
|
313
315
|
)
|
314
316
|
|
315
|
-
result_size = store.count_variants(
|
317
|
+
result_size = store.count_variants(
|
318
|
+
case_obj["_id"], form.data, None, category, build=genome_build
|
319
|
+
)
|
316
320
|
|
317
321
|
# if variants should be exported
|
318
322
|
if request.form.get("export"):
|
@@ -406,7 +410,9 @@ def mei_variants(institute_id, case_name):
|
|
406
410
|
case_obj["_id"], category=category, query=form.data, build=genome_build
|
407
411
|
)
|
408
412
|
|
409
|
-
result_size = store.count_variants(
|
413
|
+
result_size = store.count_variants(
|
414
|
+
case_obj["_id"], form.data, None, category, build=genome_build
|
415
|
+
)
|
410
416
|
|
411
417
|
# if variants should be exported
|
412
418
|
if request.form.get("export"):
|
@@ -515,7 +521,9 @@ def cancer_variants(institute_id, case_name):
|
|
515
521
|
variants_query = store.variants(
|
516
522
|
case_obj["_id"], category="cancer", query=form.data, build=genome_build
|
517
523
|
)
|
518
|
-
result_size = store.count_variants(
|
524
|
+
result_size = store.count_variants(
|
525
|
+
case_obj["_id"], form.data, None, category, build=genome_build
|
526
|
+
)
|
519
527
|
|
520
528
|
if request.form.get("export"):
|
521
529
|
return controllers.download_variants(store, case_obj, variants_query)
|
@@ -596,7 +604,9 @@ def cancer_sv_variants(institute_id, case_name):
|
|
596
604
|
case_obj["_id"], category=category, query=form.data, build=genome_build
|
597
605
|
)
|
598
606
|
|
599
|
-
result_size = store.count_variants(
|
607
|
+
result_size = store.count_variants(
|
608
|
+
case_obj["_id"], form.data, None, category, build=genome_build
|
609
|
+
)
|
600
610
|
|
601
611
|
# if variants should be exported
|
602
612
|
if request.form.get("export"):
|
@@ -681,7 +691,9 @@ def fusion_variants(institute_id, case_name):
|
|
681
691
|
case_obj["_id"], category=category, query=form.data, build=genome_build
|
682
692
|
)
|
683
693
|
|
684
|
-
result_size = store.count_variants(
|
694
|
+
result_size = store.count_variants(
|
695
|
+
case_obj["_id"], form.data, None, category, build=genome_build
|
696
|
+
)
|
685
697
|
|
686
698
|
# if variants should be exported
|
687
699
|
if request.form.get("export"):
|
@@ -765,3 +777,12 @@ def toggle_show_dismiss_block():
|
|
765
777
|
"""Endpoint to toggle the show dismiss block session variable."""
|
766
778
|
session["show_dismiss_block"] = not session.get("show_dismiss_block")
|
767
779
|
return f"Toggled to {session['show_dismiss_block']}"
|
780
|
+
|
781
|
+
|
782
|
+
@variants_bp.route("/variants/un-audit_filter", methods=["GET"])
|
783
|
+
def unaudit_filter():
|
784
|
+
"""Un-audit an audited filter."""
|
785
|
+
store.unaudit_filter(
|
786
|
+
audit_id=request.args.get("audit_id"), user_obj=store.user(current_user.email)
|
787
|
+
)
|
788
|
+
return redirect(request.referrer)
|
scout/server/config.py
CHANGED
@@ -64,6 +64,10 @@ ACCREDITATION_BADGE = "swedac-1926-iso17025.png"
|
|
64
64
|
# BEACON_URL = "http://localhost:6000/apiv1.0"
|
65
65
|
# BEACON_TOKEN = "DEMO"
|
66
66
|
|
67
|
+
# ClinVar API URL
|
68
|
+
# An alternative URL that will be used to send ClinVar submissions to. Just uncomment the line below to use the test API
|
69
|
+
# CLINVAR_API_URL = "https://submit.ncbi.nlm.nih.gov/apitest/v1/submissions/"
|
70
|
+
|
67
71
|
# connection details for LoqusDB MongoDB database
|
68
72
|
# Example with 2 instances of LoqusDB, one using a binary file and one instance connected via REST API
|
69
73
|
# When multiple instances are available, admin users can modify which one is in use for a given institute from the admin settings page
|