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.
Files changed (76) hide show
  1. scout/__version__.py +1 -1
  2. scout/adapter/mongo/base.py +17 -14
  3. scout/adapter/mongo/case.py +20 -1
  4. scout/adapter/mongo/filter.py +36 -1
  5. scout/adapter/mongo/omics_variant.py +145 -0
  6. scout/adapter/mongo/query.py +13 -3
  7. scout/adapter/mongo/variant.py +10 -4
  8. scout/build/case.py +5 -0
  9. scout/build/variant/variant.py +1 -0
  10. scout/constants/__init__.py +3 -1
  11. scout/constants/case_tags.py +1 -0
  12. scout/constants/clinvar.py +1 -1
  13. scout/constants/file_types.py +31 -0
  14. scout/constants/filters.py +4 -0
  15. scout/constants/indexes.py +30 -13
  16. scout/constants/variant_tags.py +3 -0
  17. scout/demo/643594.clinical.mei.vcf.gz +0 -0
  18. scout/demo/643594.clinical.mei.vcf.gz.tbi +0 -0
  19. scout/demo/643594.config.yaml +4 -0
  20. scout/demo/drop/fraser_top_hits_clinical.tsv +5 -0
  21. scout/demo/drop/outrider_top_hits_clinical.tsv +10 -0
  22. scout/load/setup.py +4 -4
  23. scout/models/case/case_loading_models.py +25 -2
  24. scout/models/omics_variant.py +227 -0
  25. scout/parse/omics_variant/__init__.py +11 -0
  26. scout/parse/omics_variant/drop.py +19 -0
  27. scout/parse/variant/callers.py +6 -3
  28. scout/parse/variant/frequency.py +10 -2
  29. scout/server/app.py +4 -1
  30. scout/server/blueprints/alignviewers/controllers.py +35 -24
  31. scout/server/blueprints/alignviewers/templates/alignviewers/igv_sashimi_viewer.html +19 -15
  32. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +45 -5
  33. scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
  34. scout/server/blueprints/alignviewers/views.py +10 -2
  35. scout/server/blueprints/cases/controllers.py +3 -0
  36. scout/server/blueprints/cases/templates/cases/case.html +27 -9
  37. scout/server/blueprints/cases/templates/cases/case_report.html +2 -17
  38. scout/server/blueprints/cases/templates/cases/phenotype.html +8 -5
  39. scout/server/blueprints/cases/templates/cases/utils.html +26 -3
  40. scout/server/blueprints/clinvar/controllers.py +9 -3
  41. scout/server/blueprints/dashboard/controllers.py +44 -13
  42. scout/server/blueprints/dashboard/static/charts.js +46 -36
  43. scout/server/blueprints/dashboard/templates/dashboard/dashboard_general.html +2 -2
  44. scout/server/blueprints/institutes/forms.py +2 -0
  45. scout/server/blueprints/institutes/templates/overview/cases.html +6 -4
  46. scout/server/blueprints/institutes/templates/overview/gene_variants.html +40 -27
  47. scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +1 -1
  48. scout/server/blueprints/institutes/views.py +5 -12
  49. scout/server/blueprints/omics_variants/__init__.py +1 -0
  50. scout/server/blueprints/omics_variants/controllers.py +122 -0
  51. scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +262 -0
  52. scout/server/blueprints/omics_variants/views.py +106 -0
  53. scout/server/blueprints/panels/controllers.py +1 -7
  54. scout/server/blueprints/panels/templates/panels/panels.html +12 -4
  55. scout/server/blueprints/panels/views.py +9 -11
  56. scout/server/blueprints/variant/templates/variant/buttons.html +7 -2
  57. scout/server/blueprints/variant/templates/variant/str-variant-reviewer.html +1 -1
  58. scout/server/blueprints/variant/templates/variant/utils.html +1 -1
  59. scout/server/blueprints/variant/utils.py +54 -103
  60. scout/server/blueprints/variant/views.py +1 -0
  61. scout/server/blueprints/variants/controllers.py +1 -4
  62. scout/server/blueprints/variants/forms.py +42 -0
  63. scout/server/blueprints/variants/templates/variants/utils.html +8 -4
  64. scout/server/blueprints/variants/views.py +28 -7
  65. scout/server/config.py +4 -0
  66. scout/server/extensions/clinvar_extension.py +7 -7
  67. scout/server/links.py +2 -2
  68. scout/server/templates/bootstrap_global.html +1 -4
  69. scout/server/templates/utils.html +3 -3
  70. scout/server/utils.py +4 -1
  71. {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/METADATA +10 -10
  72. {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/RECORD +76 -66
  73. {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/WHEEL +1 -1
  74. {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/LICENSE +0 -0
  75. {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/entry_points.txt +0 -0
  76. {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 shall_display_panel(panel_obj, user):
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
- panel_obj
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
- panel_obj
96
- for panel_obj in store.latest_panels(institute_obj["_id"], include_hidden=True)
97
- if controllers.shall_display_panel(panel_obj, current_user)
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
- <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"
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 gene_cell, frequency_cell_general %}
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
- """Add frequencies in the correct way for the template
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
- frequencies(list(tuple)): A list of frequencies to display
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
- link = freqs[freq_key]["link"]
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
- # If gnomad not found search for exac
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
- # Always add gnomad MT frequencies for mitochondrial variants
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:
@@ -47,6 +47,7 @@ def update_tracks_settings():
47
47
  # update user in database with custom tracks info
48
48
  user_obj["igv_tracks"] = selected_tracks
49
49
  store.update_user(user_obj)
50
+ setattr(current_user, "igv_tracks", selected_tracks)
50
51
  return redirect(request.referrer)
51
52
 
52
53
 
@@ -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').onchange = function() {
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').onchange = function() {
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(case_obj["_id"], form.data, None, category)
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(case_obj["_id"], form.data, None, category)
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(case_obj["_id"], form.data, None, category)
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(case_obj["_id"], form.data, None, category)
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(case_obj["_id"], form.data, None, category)
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(case_obj["_id"], form.data, None, category)
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