scout-browser 4.84__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 (85) 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/cytoband.py +13 -0
  5. scout/adapter/mongo/filter.py +36 -1
  6. scout/adapter/mongo/hgnc.py +1 -1
  7. scout/adapter/mongo/omics_variant.py +145 -0
  8. scout/adapter/mongo/query.py +13 -3
  9. scout/adapter/mongo/variant.py +10 -4
  10. scout/build/case.py +5 -0
  11. scout/build/variant/variant.py +1 -0
  12. scout/commands/update/genes.py +9 -13
  13. scout/constants/__init__.py +3 -1
  14. scout/constants/case_tags.py +1 -0
  15. scout/constants/clinvar.py +1 -1
  16. scout/constants/file_types.py +31 -0
  17. scout/constants/filters.py +4 -0
  18. scout/constants/indexes.py +30 -13
  19. scout/constants/variant_tags.py +3 -0
  20. scout/demo/643594.clinical.mei.vcf.gz +0 -0
  21. scout/demo/643594.clinical.mei.vcf.gz.tbi +0 -0
  22. scout/demo/643594.config.yaml +4 -0
  23. scout/demo/drop/fraser_top_hits_clinical.tsv +5 -0
  24. scout/demo/drop/outrider_top_hits_clinical.tsv +10 -0
  25. scout/load/hgnc_gene.py +39 -6
  26. scout/load/setup.py +4 -4
  27. scout/models/case/case_loading_models.py +25 -2
  28. scout/models/omics_variant.py +227 -0
  29. scout/parse/hgnc.py +1 -0
  30. scout/parse/omics_variant/__init__.py +11 -0
  31. scout/parse/omics_variant/drop.py +19 -0
  32. scout/parse/variant/callers.py +6 -3
  33. scout/parse/variant/frequency.py +10 -2
  34. scout/parse/variant/transcript.py +1 -1
  35. scout/parse/variant/variant.py +10 -4
  36. scout/server/app.py +4 -1
  37. scout/server/blueprints/alignviewers/controllers.py +35 -24
  38. scout/server/blueprints/alignviewers/templates/alignviewers/igv_sashimi_viewer.html +19 -15
  39. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +45 -5
  40. scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
  41. scout/server/blueprints/alignviewers/views.py +10 -2
  42. scout/server/blueprints/cases/controllers.py +18 -1
  43. scout/server/blueprints/cases/templates/cases/case.html +28 -10
  44. scout/server/blueprints/cases/templates/cases/case_report.html +2 -17
  45. scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +1 -1
  46. scout/server/blueprints/cases/templates/cases/gene_panel.html +27 -41
  47. scout/server/blueprints/cases/templates/cases/phenotype.html +8 -5
  48. scout/server/blueprints/cases/templates/cases/utils.html +27 -4
  49. scout/server/blueprints/clinvar/controllers.py +9 -3
  50. scout/server/blueprints/dashboard/controllers.py +44 -13
  51. scout/server/blueprints/dashboard/static/charts.js +46 -36
  52. scout/server/blueprints/dashboard/templates/dashboard/dashboard_general.html +2 -2
  53. scout/server/blueprints/institutes/forms.py +2 -0
  54. scout/server/blueprints/institutes/templates/overview/cases.html +6 -4
  55. scout/server/blueprints/institutes/templates/overview/gene_variants.html +40 -27
  56. scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +1 -1
  57. scout/server/blueprints/institutes/views.py +5 -12
  58. scout/server/blueprints/omics_variants/__init__.py +1 -0
  59. scout/server/blueprints/omics_variants/controllers.py +122 -0
  60. scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +262 -0
  61. scout/server/blueprints/omics_variants/views.py +106 -0
  62. scout/server/blueprints/panels/controllers.py +1 -7
  63. scout/server/blueprints/panels/templates/panels/panels.html +12 -4
  64. scout/server/blueprints/panels/views.py +9 -11
  65. scout/server/blueprints/variant/templates/variant/buttons.html +7 -2
  66. scout/server/blueprints/variant/templates/variant/str-variant-reviewer.html +1 -1
  67. scout/server/blueprints/variant/templates/variant/utils.html +1 -1
  68. scout/server/blueprints/variant/utils.py +54 -103
  69. scout/server/blueprints/variant/views.py +1 -0
  70. scout/server/blueprints/variants/controllers.py +1 -4
  71. scout/server/blueprints/variants/forms.py +42 -0
  72. scout/server/blueprints/variants/templates/variants/utils.html +8 -4
  73. scout/server/blueprints/variants/views.py +28 -7
  74. scout/server/config.py +4 -0
  75. scout/server/extensions/clinvar_extension.py +7 -7
  76. scout/server/links.py +2 -2
  77. scout/server/templates/bootstrap_global.html +1 -4
  78. scout/server/templates/utils.html +4 -4
  79. scout/server/utils.py +4 -1
  80. {scout_browser-4.84.dist-info → scout_browser-4.86.dist-info}/METADATA +11 -11
  81. {scout_browser-4.84.dist-info → scout_browser-4.86.dist-info}/RECORD +85 -75
  82. {scout_browser-4.84.dist-info → scout_browser-4.86.dist-info}/WHEEL +1 -1
  83. {scout_browser-4.84.dist-info → scout_browser-4.86.dist-info}/LICENSE +0 -0
  84. {scout_browser-4.84.dist-info → scout_browser-4.86.dist-info}/entry_points.txt +0 -0
  85. {scout_browser-4.84.dist-info → scout_browser-4.86.dist-info}/top_level.txt +0 -0
@@ -324,7 +324,7 @@ def set_variant_frequencies(transcript, entry):
324
324
  continue
325
325
 
326
326
  value = entry[key]
327
- if not value or value == ".":
327
+ if not value or value == "." or value.isalpha():
328
328
  continue
329
329
 
330
330
  if key in THOUSAND_GENOMES_CSQ_KEYS:
@@ -231,10 +231,7 @@ def parse_variant(
231
231
  parsed_variant["revel_score"] = parsed_transcripts[0].get(
232
232
  "revel_rankscore"
233
233
  ) # This is actually the value of REVEL_rankscore
234
-
235
- parsed_variant["revel"] = parsed_transcripts[0].get(
236
- "revel_raw_score"
237
- ) # This is actually the value of REVEL_score
234
+ parsed_variant["revel"] = get_highest_revel_score(parsed_transcripts)
238
235
 
239
236
  ###################### Add conservation ######################
240
237
  parsed_variant["conservation"] = parse_conservations(variant, parsed_transcripts)
@@ -261,6 +258,15 @@ def parse_variant(
261
258
  return parsed_variant
262
259
 
263
260
 
261
+ def get_highest_revel_score(parsed_transcripts: List[dict]) -> Optional[float]:
262
+ """Retrieve the highest REVEL_score value from parsed variant transcripts."""
263
+ tx_revel_scores: List(float) = [
264
+ tx.get("revel_raw_score") for tx in parsed_transcripts if tx.get("revel_raw_score") != None
265
+ ]
266
+ if tx_revel_scores:
267
+ return max(tx_revel_scores)
268
+
269
+
264
270
  def get_genmod_key(case):
265
271
  """Gen genmod key
266
272
 
scout/server/app.py CHANGED
@@ -7,7 +7,7 @@ from typing import Dict, Union
7
7
  from urllib.parse import parse_qsl, unquote, urlsplit
8
8
 
9
9
  import coloredlogs
10
- from flask import Flask, current_app, redirect, request, url_for
10
+ from flask import Flask, redirect, request, url_for
11
11
  from flask_cors import CORS
12
12
  from flask_login import current_user
13
13
  from markdown import markdown as python_markdown
@@ -25,6 +25,7 @@ from .blueprints import (
25
25
  institutes,
26
26
  login,
27
27
  managed_variants,
28
+ omics_variants,
28
29
  panels,
29
30
  phenomodels,
30
31
  phenotypes,
@@ -98,6 +99,7 @@ def configure_extensions(app):
98
99
  extensions.store.init_app(app)
99
100
  extensions.login_manager.init_app(app)
100
101
  extensions.mail.init_app(app)
102
+ extensions.clinvar_api.init_app(app)
101
103
 
102
104
  if app.config.get("SQLALCHEMY_DATABASE_URI"):
103
105
  extensions.chanjo_report.init_app(app)
@@ -177,6 +179,7 @@ def register_blueprints(app):
177
179
  app.register_blueprint(diagnoses.omim_bp)
178
180
  app.register_blueprint(institutes.overview)
179
181
  app.register_blueprint(managed_variants.managed_variants_bp)
182
+ app.register_blueprint(omics_variants.omics_variants_bp)
180
183
 
181
184
 
182
185
  def register_filters(app):
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import logging
3
3
  import os.path
4
- from typing import Dict
4
+ from typing import Dict, Optional
5
5
 
6
6
  from flask import flash, session
7
7
  from flask_login import current_user
@@ -12,7 +12,7 @@ from scout.server.utils import case_append_alignments, find_index
12
12
  from scout.utils.ensembl_rest_clients import EnsemblRestApiClient
13
13
 
14
14
  LOG = logging.getLogger(__name__)
15
- CUSTOM_TRACK_NAMES = ["Genes", "ClinVar", "ClinVar CNVs"]
15
+ DEFAULT_TRACK_NAMES = ["Genes", "ClinVar", "ClinVar CNVs"]
16
16
 
17
17
 
18
18
  def check_session_tracks(resource):
@@ -51,33 +51,39 @@ def set_session_tracks(display_obj: dict):
51
51
  session["igv_tracks"] = session_tracks
52
52
 
53
53
 
54
- def make_igv_tracks(case_obj, variant_id, chrom=None, start=None, stop=None):
54
+ def make_igv_tracks(
55
+ case_obj: dict,
56
+ variant_id: str,
57
+ chrom: Optional[str] = None,
58
+ start: Optional[int] = None,
59
+ stop: Optional[int] = None,
60
+ ) -> dict:
55
61
  """Create a dictionary containing the required tracks for displaying IGV tracks for case or a group of cases
56
62
 
57
63
  Args:
58
- institute_id(str): institute _id
64
+ institute_id: institute _id
59
65
  case_obj(scout.models.Case)
60
- variant_id(str): _id of a variant
61
- chrom(str/None): requested chromosome [1-22], X, Y, [M-MT]
62
- start(int/None): start of the genomic interval to be displayed
63
- stop(int/None): stop of the genomic interval to be displayed
66
+ variant_id: _id of a variant
67
+ chrom: requested chromosome [1-22], X, Y, [M-MT]
68
+ start: start of the genomic interval to be displayed
69
+ stop: stop of the genomic interval to be displayed
64
70
 
65
71
  Returns:
66
- display_obj(dict): A display object containing case name, list of genes, lucus and tracks
72
+ display_obj: A display object containing case name, list of genes, locus and tracks
67
73
  """
68
74
  display_obj = {}
69
75
  variant_obj = store.variant(document_id=variant_id)
70
76
 
77
+ chromosome = "All"
71
78
  if variant_obj:
72
79
  # Set display locus
73
80
  start = start or variant_obj["position"]
74
81
  stop = stop or variant_obj["end"]
82
+ chrom = chrom or variant_obj.get("chromosome")
75
83
 
76
- chromosome = chrom or variant_obj.get("chromosome")
77
- chromosome = chromosome.replace("MT", "M")
84
+ if all([start, stop, chrom]):
85
+ chromosome = chrom.replace("MT", "M")
78
86
  display_obj["locus"] = "chr{0}:{1}-{2}".format(chromosome, start, stop)
79
- else:
80
- chromosome = "All"
81
87
 
82
88
  # Set genome build for displaying alignments:
83
89
  if "38" in str(case_obj.get("genome_build", "37")) or chromosome == "M":
@@ -115,20 +121,27 @@ def make_igv_tracks(case_obj, variant_id, chrom=None, start=None, stop=None):
115
121
  return display_obj
116
122
 
117
123
 
118
- def make_sashimi_tracks(case_obj, variant_id=None):
124
+ def make_sashimi_tracks(
125
+ case_obj: dict, variant_id: Optional[str] = None, omics_variant_id: Optional[str] = None
126
+ ):
119
127
  """Create a dictionary containing the required tracks for a splice junction plot
128
+ If either a regular variant_id or an omics variant id is passed, set display to a particular locus.
129
+ Otherwise defaults to whole genome "All" view.
120
130
 
121
- Args:
122
- case_obj(scout.models.Case)
123
- variant_id(str) _id of a variant
124
131
  Returns:
125
- display_obj(dict): A display object containing case name, list of genes, lucus and tracks
132
+ display_obj(dict): A display object containing case name, list of genes, locus and tracks
126
133
  """
127
134
  build = "38" # This feature is only available for RNA tracks in build 38
128
135
 
129
136
  locus = "All"
137
+ variant_obj = None
138
+
130
139
  if variant_id:
131
140
  variant_obj = store.variant(document_id=variant_id)
141
+ if omics_variant_id:
142
+ variant_obj = store.omics_variant(variant_id=omics_variant_id)
143
+
144
+ if variant_obj:
132
145
  locus = make_locus_from_variant(variant_obj, case_obj, build)
133
146
 
134
147
  display_obj = {"locus": locus, "tracks": []}
@@ -234,9 +247,9 @@ def set_common_tracks(display_obj, build):
234
247
  # Set up IGV tracks that are common for all cases:
235
248
  display_obj["reference_track"] = HUMAN_REFERENCE[build] # Human reference is always present
236
249
 
237
- # if user settings for igv tracks exist -> use these settings, otherwise display all tracks
250
+ # if user settings for igv tracks exist -> use these settings, otherwise display default tracks ---> Genes, ClinVar and ClinVar CNVs
238
251
  custom_tracks_names = (
239
- user_obj.get("igv_tracks") if "igv_tracks" in user_obj else CUSTOM_TRACK_NAMES
252
+ user_obj.get("igv_tracks") if "igv_tracks" in user_obj else DEFAULT_TRACK_NAMES
240
253
  )
241
254
 
242
255
  display_obj["custom_tracks"] = []
@@ -300,7 +313,6 @@ def set_config_custom_tracks(display_obj: dict, build: str):
300
313
  """Set up custom public or private tracks stored in a cloud bucket or locally. These tracks were those specified in the Scout config file.
301
314
  Respect user's preferences."""
302
315
  user_obj = store.user(email=current_user.email)
303
- custom_tracks_names = user_obj.get("igv_tracks")
304
316
 
305
317
  config_custom_tracks = []
306
318
 
@@ -308,8 +320,7 @@ def set_config_custom_tracks(display_obj: dict, build: str):
308
320
  build_tracks = config_igv_tracks.tracks.get(build, [])
309
321
  for track in build_tracks:
310
322
  # Do not display track if user doesn't want to see it
311
- if custom_tracks_names and track["name"] not in custom_tracks_names:
312
- continue
313
- config_custom_tracks.append(track)
323
+ if "igv_tracks" not in user_obj or track["name"] in user_obj.get("igv_tracks"):
324
+ config_custom_tracks.append(track)
314
325
  if config_custom_tracks:
315
326
  display_obj["config_custom_tracks"] = config_custom_tracks
@@ -79,6 +79,7 @@
79
79
  type: 'wig',
80
80
  format: "bigwig",
81
81
  url: "{{ url_for('alignviewers.remote_static', file=track.coverage_wig) }}",
82
+ height: 500,
82
83
  },
83
84
  {
84
85
  type: 'spliceJunctions',
@@ -88,24 +89,27 @@
88
89
  labelUniqueReadCount: true,
89
90
  url: "{{ url_for('alignviewers.remote_static', file=track.splicej_bed) }}",
90
91
  indexURL: "{{ url_for('alignviewers.remote_static', file=track.splicej_bed_index) }}",
91
- minUniquelyMappedReads: 1
92
+ minUniquelyMappedReads: 1,
93
+ height: 500,
92
94
  },
93
95
  ]
94
96
  }, // end of sashimi track with data
95
- { // genes track
96
- name: geneTrack.name,
97
- type: geneTrack.type,
98
- format: geneTrack.format,
99
- sourceType: geneTrack.sourceType,
100
- url: geneTrack.url,
101
- indexURL: geneTrack.indexURL,
102
- displayMode: geneTrack.displayMode,
103
- visibilityWindow: 300000000,
104
- height: 100,
105
- searchable: true,
106
- order: {{counter.loop}},
107
- infoURL: "https://www.ncbi.nlm.nih.gov/gene/?term=$$"
108
- }, // end of genes track
97
+ {% if custom_tracks|selectattr("name","equalto", "Genes")|list|length > 0 %}
98
+ { // genes track
99
+ name: geneTrack.name,
100
+ type: geneTrack.type,
101
+ format: geneTrack.format,
102
+ sourceType: geneTrack.sourceType,
103
+ url: geneTrack.url,
104
+ indexURL: geneTrack.indexURL,
105
+ displayMode: geneTrack.displayMode,
106
+ visibilityWindow: 300000000,
107
+ height: 100,
108
+ searchable: true,
109
+ order: {{counter.loop}},
110
+ infoURL: "https://www.ncbi.nlm.nih.gov/gene/?term=$$"
111
+ }, // end of genes track
112
+ {% endif %}
109
113
  {% endif %}
110
114
  {% endfor %}
111
115
  ] // end of tracks
@@ -14,14 +14,11 @@
14
14
  <link rel="stylesheet" type="text/css"
15
15
  href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/themes/smoothness/jquery-ui.css"/>
16
16
 
17
- <!-- Font Awesome CSS -->
18
- <link rel="stylesheet" type="text/css"
19
- href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css"/>
20
-
21
17
  <!-- jQuery JS -->
22
18
  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
23
19
  <script type="text/javascript"
24
20
  src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>
21
+
25
22
  {{ igv_script() }}
26
23
  </head>
27
24
 
@@ -30,6 +27,11 @@
30
27
  </body>
31
28
 
32
29
  <script type="text/javascript">
30
+
31
+ var outLinks = {
32
+ COSV: "https://cancer.sanger.ac.uk/cosmic/search?q=", // COSMIC variant search
33
+ };
34
+
33
35
  $(document).ready(function () {
34
36
  var div = $("#igvDiv")[0],
35
37
  options = {
@@ -146,7 +148,45 @@
146
148
  {% endfor %}
147
149
  ]
148
150
  };
149
- igv.createBrowser(div, options);
151
+ igv.createBrowser(div, options)
152
+ .then(function (browser) {
153
+ browser.on('trackclick', function (track, popoverData) {
154
+
155
+ var markup = "<table class=\"igv-popover-table\">";
156
+
157
+ // Don't show a pop-over when there's no data.
158
+ if (!popoverData || !popoverData.length) {
159
+ return false;
160
+ }
161
+
162
+ popoverData.forEach(function (nameValue) {
163
+
164
+ if (nameValue.name) {
165
+
166
+ var value = nameValue.value;
167
+ for (var key in outLinks) {
168
+ if (nameValue.value.toString().startsWith(key) && nameValue.name.toLowerCase() === 'name') {
169
+ value = '<a href="' + outLinks[key] + value + '" target="_blank" rel="noopener noreferrer">' + nameValue.value + '</a>';
170
+ }
171
+ }
172
+
173
+ markup += "<tr><td class=\"igv-popover-td\">"
174
+ + "<span class=\"igv-popover-name\">" + nameValue.name + "</span>&nbsp;"
175
+ + "<span class=\"igv-popover-value\">" + value + "</span>"
176
+ + "</td></tr>";
177
+ }
178
+ else {
179
+ // not a name/value pair
180
+ markup += "<tr><td>" + nameValue.toString() + "</td></tr>";
181
+ }
182
+ });
183
+
184
+ markup += "</table>";
185
+
186
+ // By returning a string from the trackclick handler we're asking IGV to use our custom HTML in its pop-over.
187
+ return markup;
188
+ });
189
+ });
150
190
  });
151
191
  </script>
152
192
 
@@ -1,5 +1,5 @@
1
1
  {% macro igv_script() %}
2
2
  <link rel="shortcut icon" href="//igv.org/web/img/favicon.ico">
3
3
  <!-- IGV JS-->
4
- <script src="https://cdn.jsdelivr.net/npm/igv@2.15.11/dist/igv.min.js"></script>
4
+ <script src="https://cdn.jsdelivr.net/npm/igv@3.0.1/dist/igv.min.js"></script>
5
5
  {% endmacro %}
@@ -91,7 +91,15 @@ def remote_static():
91
91
  @alignviewers_bp.route(
92
92
  "/<institute_id>/<case_name>/<variant_id>/igv-splice-junctions", methods=["GET"]
93
93
  )
94
- def sashimi_igv(institute_id, case_name, variant_id=None):
94
+ @alignviewers_bp.route(
95
+ "/<institute_id>/<case_name>/outliers/<omics_variant_id>/igv-splice-junctions", methods=["GET"]
96
+ )
97
+ def sashimi_igv(
98
+ institute_id: str,
99
+ case_name: str,
100
+ variant_id: Optional[str] = None,
101
+ omics_variant_id: Optional[str] = None,
102
+ ):
95
103
  """Visualize splice junctions on igv.js sashimi-like viewer for one or more individuals of a case.
96
104
  wiki: https://github.com/igvteam/igv.js/wiki/Splice-Junctions
97
105
  """
@@ -99,7 +107,7 @@ def sashimi_igv(institute_id, case_name, variant_id=None):
99
107
  store, institute_id, case_name
100
108
  ) # This function takes care of checking if user is authorized to see resource
101
109
 
102
- display_obj = controllers.make_sashimi_tracks(case_obj, variant_id)
110
+ display_obj = controllers.make_sashimi_tracks(case_obj, variant_id, omics_variant_id)
103
111
  controllers.set_session_tracks(display_obj)
104
112
 
105
113
  response = Response(render_template("alignviewers/igv_sashimi_viewer.html", **display_obj))
@@ -64,6 +64,7 @@ JSON_HEADERS = {
64
64
  COVERAGE_REPORT_TIMEOUT = 20
65
65
 
66
66
  PANEL_PROJECTION = {"version": 1, "display_name": 1, "genes": 1}
67
+ PANEL_HIDDEN_PROJECTION = {"version": 1, "display_name": 1, "hidden": 1}
67
68
 
68
69
 
69
70
  def phenomizer_diseases(hpo_ids, case_obj, p_value_treshold=1):
@@ -339,6 +340,8 @@ def case(
339
340
 
340
341
  case_obj["default_genes"] = _get_default_panel_genes(store, case_obj)
341
342
 
343
+ _set_panel_removed(store, case_obj)
344
+
342
345
  for hpo_term in itertools.chain(
343
346
  case_obj.get("phenotype_groups") or [], case_obj.get("phenotype_terms") or []
344
347
  ):
@@ -449,6 +452,9 @@ def case(
449
452
  "gens_info": gens.connection_settings(case_obj.get("genome_build")),
450
453
  "display_rerunner": rerunner.connection_settings.get("display", False),
451
454
  "hide_matching": hide_matching,
455
+ "audits": store.case_events_by_verb(
456
+ category="case", institute=institute_obj, case=case_obj, verb="filter_audit"
457
+ ),
452
458
  }
453
459
 
454
460
  return data
@@ -479,6 +485,18 @@ def _limit_genes_on_default_panels(default_genes: list, limit_genes: list) -> li
479
485
  return list(default_genes_set.intersection(limit_genes_set))
480
486
 
481
487
 
488
+ def _set_panel_removed(store: MongoAdapter, case_obj: dict) -> list:
489
+ """Flag panel on list removed if the latest panel version is marked hidden."""
490
+
491
+ for panel_info in case_obj.get("panels", []):
492
+ latest_panel = store.gene_panel(
493
+ panel_info["panel_name"], projection=PANEL_HIDDEN_PROJECTION
494
+ )
495
+ panel_info["removed"] = (
496
+ latest_panel.get("hidden", False) if latest_panel is not None else False
497
+ )
498
+
499
+
482
500
  def _get_default_panel_genes(store: MongoAdapter, case_obj: dict) -> list:
483
501
  """Get unique genes on case default panels.
484
502
 
@@ -510,7 +528,6 @@ def _get_default_panel_genes(store: MongoAdapter, case_obj: dict) -> list:
510
528
  projection=PANEL_PROJECTION,
511
529
  )
512
530
  latest_panel = store.gene_panel(panel_name, projection=PANEL_PROJECTION)
513
- panel_info["removed"] = False if latest_panel is None else latest_panel.get("hidden", False)
514
531
  if not panel_obj:
515
532
  panel_obj = latest_panel
516
533
  if not panel_obj:
@@ -1,7 +1,7 @@
1
1
  {% extends "layout.html" %}
2
2
  {% from "cases/collapsible_actionbar.html" import action_bar, research_modal, reanalysis_modal %}
3
3
  {% from "utils.html" import comments_panel, activity_panel, pedigree_panel %}
4
- {% from "cases/utils.html" import causatives_list, suspects_list, remove_form, matching_causatives, matching_managed_variants, beacon_modal, matchmaker_modal %}
4
+ {% from "cases/utils.html" import causatives_list, suspects_list, remove_form, matching_causatives, matching_managed_variants, beacon_modal, matchmaker_modal, filter_audits %}
5
5
  {% from "cases/individuals_table.html" import cancer_individuals_table, individuals_table %}
6
6
  {% from "cases/phenotype.html" import hpo_item, cohort_panel, diagnosis_phenotypes, phenotype_groups_panel, add_phenotype_terms_panel, hpo_panel %}
7
7
  {% from "cases/gene_panel.html" import genepanels_table, hpo_genelist_panel %}
@@ -119,8 +119,16 @@
119
119
  </div>
120
120
  </div>
121
121
 
122
+ <div class="row ms-1">
122
123
  {{ matching_variants() }}
123
124
 
125
+ {% if audits | count_cursor > 0 %}
126
+ <div class="card panel-default col-4 ms-5 me-1">
127
+ {{ filter_audits(audits, true) }}
128
+ </div>
129
+ {% endif %}
130
+ </div>
131
+
124
132
  <div class="card panel-default" >
125
133
  <div class="row">
126
134
  <div class="col">{{ causatives_list(causatives, partial_causatives, evaluated_variants, institute, case, manual_rank_options, cancer_tier_options) }}</div>
@@ -258,6 +266,9 @@
258
266
  {% if case.vcf_files.vcf_fusion %}
259
267
  <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='clinical') }}">Clinical fusion variants</a>
260
268
  {% endif %}
269
+ {% if case.has_outliers %}
270
+ <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='clinical') }}">Clinical WTS outliers</a>
271
+ {% endif %}
261
272
  </div>
262
273
  </div>
263
274
  </div>
@@ -352,7 +363,7 @@
352
363
  <a href="{{ url_for('cases.case', institute_id=grouped_case.owner, case_name=grouped_case.display_name) }}">{{ grouped_case.display_name }}</a>
353
364
 
354
365
  <a href="{{ url_for('cases.remove_case_group', institute_id=institute._id, case_name=grouped_case.display_name, case_group=group_id) }}" class="btn btn-link btn-sm">
355
- <span class="fa fa-remove text-dark"></span></a>
366
+ <span class="fa fa-times text-dark"></span></a>
356
367
 
357
368
  </div>
358
369
  {% endfor %}
@@ -537,9 +548,9 @@
537
548
  {% endmacro %}
538
549
 
539
550
  {% macro matching_variants() %}
540
- <div class="card mt-3">
541
- <div class="row mt-0 ms-3">
542
- <div class="col-sm-12 col-md-12">
551
+ <div class="card mt-3 col-7">
552
+ <div class="mt-0">
553
+ <div class="col-md-7">
543
554
  <div data-bs-toggle='tooltip' class="panel-heading" title="Check if there are any variants in this case
544
555
  marked as causative in another case for this institute, or are on the managed variants list.">
545
556
  <strong>
@@ -554,27 +565,27 @@
554
565
  {% if hide_matching == false %}
555
566
  {% if other_causatives|length > 0 %}
556
567
  <div class="row mt-0 ms-3">
557
- <div class="col-xs-12 col-md-12">{{ matching_causatives(other_causatives, institute, case) }}</div>
568
+ <div class="col-md-7">{{ matching_causatives(other_causatives, institute, case) }}</div>
558
569
  </div>
559
570
  {% endif %}
560
571
  {% if default_other_causatives|length > 0%}
561
572
  <div class="row mt-0 ms-3">
562
- <div class="col-xs-12 col-md-12">{{ matching_causatives(default_other_causatives, institute, case, default=True) }}</div>
573
+ <div class="col-md-7">{{ matching_causatives(default_other_causatives, institute, case, default=True) }}</div>
563
574
  </div>
564
575
  {% endif %}
565
576
  {% if managed_variants|length > 0%}
566
577
  <div class="row mt-0 ms-3">
567
- <div class="col-sm-12 col-md-12">{{ matching_managed_variants(managed_variants, institute, case) }}</div>
578
+ <div class="col-md-7">{{ matching_managed_variants(managed_variants, institute, case) }}</div>
568
579
  </div>
569
580
  {% endif %}
570
581
  {% if default_managed_variants|length > 0%}
571
582
  <div class="row mt-0 ms-3">
572
- <div class="col-sm-12 col-md-12">{{ matching_managed_variants(default_managed_variants, institute, case, default=True) }}</div>
583
+ <div class="col-md-7">{{ matching_managed_variants(default_managed_variants, institute, case, default=True) }}</div>
573
584
  </div>
574
585
  {% endif %}
575
586
  {% if other_causatives|length == 0 and default_other_causatives|length == 0 and managed_variants|length == 0 and default_managed_variants|length == 0%}
576
587
  <div class="row mt-0 ms-3">
577
- <div class="col-sm-12 col-md-12">No matching causatives or managed variants found</div>
588
+ <div class="col-md-7">No matching causatives or managed variants found</div>
578
589
  </div>
579
590
  {% endif %}
580
591
  {% endif %}
@@ -772,5 +783,12 @@ $('#collapse-icon').addClass('fa-angle-double-left');
772
783
  $('[data-bs-toggle=sidebar-collapse]').click(function() {
773
784
  SidebarCollapse();
774
785
  });
786
+
787
+ function dynamicPhenotypeCheck(source){
788
+ var checkboxes = document.getElementsByName('hpo_id');
789
+ for(var i=0, n=checkboxes.length;i<n;i++) {
790
+ checkboxes[i].checked = source.checked;
791
+ }
792
+ }
775
793
  </script>
776
794
  {% endblock %}
@@ -1,4 +1,4 @@
1
- {% from "cases/utils.html" import variant_transcripts %}
1
+ {% from "cases/utils.html" import variant_transcripts, filter_audits %}
2
2
  {% from "utils.html" import comments_table, variant_related_comments_table %}
3
3
  {% from "variants/components.html" import fusion_variants_header, default_fusion_variant_cells %}
4
4
 
@@ -266,22 +266,7 @@
266
266
  {% if audits | count_cursor > 0 %}
267
267
  <tr>
268
268
  <td colspan=2>
269
- <table id="audit-table" class="table table-sm" style="background-color: transparent">
270
- <thead>
271
- <tr>
272
- <th>Filters marked audited for case</th>
273
- </tr>
274
- </thead>
275
- <tbody>
276
- {% set audit_query = namespace() %}
277
- {% for audit in audits %}
278
- {% set audit_query = audit.link|url_args %}
279
- <tr>
280
- <td><strong>{{ audit.subject }} ({{audit_query.variant_type if audit_query.variant_type else "type unavailable -"}} {{ audit_query.category if audit_query.category else "category unavailable"}})</strong> was marked checked by {{ audit.user_name }} on {{audit.created_at.strftime('%Y-%m-%d')}}.</td>
281
- </tr>
282
- {% endfor %}
283
- </tbody>
284
- </table>
269
+ {{filter_audits(audits)}}
285
270
  </td>
286
271
  </tr>
287
272
  {% endif %}
@@ -247,7 +247,7 @@
247
247
  {% else %}
248
248
  <button type="submit" name="action" value="DELETE" class="btn btn-warning btn-xs form-control">
249
249
  {% endif %}
250
- <span class="fa fa-remove"></span>
250
+ <span class="fa fa-times"></span>
251
251
  {{ user.name }}
252
252
  </button>
253
253
  </form>