scout-browser 4.101.0__py3-none-any.whl → 4.103.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. scout/adapter/mongo/case.py +26 -122
  2. scout/adapter/mongo/clinvar.py +98 -32
  3. scout/adapter/mongo/event.py +0 -47
  4. scout/adapter/mongo/hgnc.py +7 -2
  5. scout/adapter/mongo/omics_variant.py +8 -0
  6. scout/adapter/mongo/variant_loader.py +12 -4
  7. scout/build/variant/variant.py +1 -0
  8. scout/commands/load/variants.py +1 -1
  9. scout/commands/update/user.py +87 -49
  10. scout/constants/__init__.py +4 -0
  11. scout/constants/clinvar.py +10 -0
  12. scout/constants/igv_tracks.py +6 -2
  13. scout/constants/phenotype.py +1 -0
  14. scout/constants/variant_tags.py +18 -0
  15. scout/demo/NIST.trgt.stranger.vcf.gz +0 -0
  16. scout/demo/NIST.trgt.stranger.vcf.gz.tbi +0 -0
  17. scout/demo/__init__.py +1 -0
  18. scout/load/hpo.py +8 -2
  19. scout/models/clinvar.py +86 -0
  20. scout/parse/variant/coordinates.py +5 -1
  21. scout/parse/variant/gene.py +5 -9
  22. scout/parse/variant/genotype.py +66 -42
  23. scout/parse/variant/variant.py +2 -0
  24. scout/server/app.py +71 -2
  25. scout/server/blueprints/alignviewers/controllers.py +8 -6
  26. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +4 -0
  27. scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
  28. scout/server/blueprints/cases/controllers.py +57 -29
  29. scout/server/blueprints/cases/templates/cases/case_report.html +28 -90
  30. scout/server/blueprints/cases/templates/cases/matchmaker.html +1 -1
  31. scout/server/blueprints/cases/templates/cases/phenotype.html +1 -1
  32. scout/server/blueprints/cases/templates/cases/utils.html +34 -53
  33. scout/server/blueprints/cases/views.py +32 -33
  34. scout/server/blueprints/clinvar/controllers.py +235 -54
  35. scout/server/blueprints/clinvar/form.py +38 -1
  36. scout/server/blueprints/clinvar/static/form_style.css +8 -1
  37. scout/server/blueprints/clinvar/templates/clinvar/clinvar_onc_submissions.html +200 -0
  38. scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +3 -2
  39. scout/server/blueprints/clinvar/templates/clinvar/components.html +198 -0
  40. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_onc_variant.html +187 -0
  41. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +9 -348
  42. scout/server/blueprints/clinvar/templates/clinvar/scripts.html +193 -0
  43. scout/server/blueprints/clinvar/views.py +90 -13
  44. scout/server/blueprints/diagnoses/controllers.py +4 -8
  45. scout/server/blueprints/diagnoses/templates/diagnoses/diagnoses.html +1 -1
  46. scout/server/blueprints/diagnoses/templates/diagnoses/disease_term.html +1 -1
  47. scout/server/blueprints/diagnoses/views.py +2 -2
  48. scout/server/blueprints/institutes/controllers.py +148 -75
  49. scout/server/blueprints/institutes/forms.py +1 -0
  50. scout/server/blueprints/institutes/templates/overview/cases.html +1 -1
  51. scout/server/blueprints/institutes/templates/overview/gene_variants.html +15 -6
  52. scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +28 -2
  53. scout/server/blueprints/institutes/templates/overview/utils.html +1 -1
  54. scout/server/blueprints/institutes/views.py +17 -4
  55. scout/server/blueprints/login/controllers.py +2 -1
  56. scout/server/blueprints/login/views.py +5 -2
  57. scout/server/blueprints/mme/templates/mme/mme_submissions.html +2 -2
  58. scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +2 -2
  59. scout/server/blueprints/omics_variants/views.py +2 -2
  60. scout/server/blueprints/phenotypes/controllers.py +15 -2
  61. scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +1 -1
  62. scout/server/blueprints/variant/controllers.py +11 -12
  63. scout/server/blueprints/variant/templates/variant/cancer-variant.html +2 -1
  64. scout/server/blueprints/variant/templates/variant/components.html +0 -1
  65. scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -1
  66. scout/server/blueprints/variant/templates/variant/utils.html +1 -1
  67. scout/server/blueprints/variant/templates/variant/variant.html +2 -2
  68. scout/server/blueprints/variant/templates/variant/variant_details.html +100 -84
  69. scout/server/blueprints/variant/utils.py +25 -0
  70. scout/server/blueprints/variants/controllers.py +11 -42
  71. scout/server/blueprints/variants/templates/variants/cancer-variants.html +5 -3
  72. scout/server/blueprints/variants/templates/variants/str-variants.html +4 -1
  73. scout/server/blueprints/variants/templates/variants/sv-variants.html +3 -3
  74. scout/server/blueprints/variants/templates/variants/utils.html +4 -0
  75. scout/server/blueprints/variants/templates/variants/variants.html +4 -4
  76. scout/server/blueprints/variants/views.py +9 -8
  77. scout/server/config.py +3 -0
  78. scout/server/extensions/beacon_extension.py +7 -2
  79. scout/server/extensions/clinvar_extension.py +2 -2
  80. scout/server/templates/bootstrap_global.html +11 -1
  81. scout/server/templates/layout.html +6 -1
  82. scout/server/utils.py +24 -3
  83. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/METADATA +1 -1
  84. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/RECORD +87 -81
  85. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/WHEEL +0 -0
  86. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/entry_points.txt +0 -0
  87. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/licenses/LICENSE +0 -0
@@ -18,7 +18,12 @@ from scout.constants import (
18
18
  )
19
19
  from scout.server.blueprints.variants.controllers import update_form_hgnc_symbols
20
20
  from scout.server.extensions import beacon, loqusdb, store
21
- from scout.server.utils import institute_and_case, jsonconverter, templated, user_institutes
21
+ from scout.server.utils import (
22
+ institute_and_case,
23
+ jsonconverter,
24
+ templated,
25
+ user_institutes,
26
+ )
22
27
 
23
28
  from . import controllers
24
29
  from .forms import GeneVariantFiltersForm, InstituteForm
@@ -138,10 +143,18 @@ def gene_variants(institute_id):
138
143
 
139
144
  data = {}
140
145
 
146
+ institute_choices = [
147
+ (inst["_id"], f"{inst['display_name']} ({inst['_id']})")
148
+ for inst in user_institutes(store, current_user)
149
+ ]
150
+ form = GeneVariantFiltersForm()
151
+ form.institute.choices = institute_choices
152
+ users_institute_ids = [choice[0] for choice in institute_choices]
153
+
141
154
  if request.method == "GET":
142
- form = GeneVariantFiltersForm(request.args)
155
+ form.process(request.args)
143
156
  else: # POST
144
- form = GeneVariantFiltersForm(request.form)
157
+ form.process(request.form)
145
158
 
146
159
  if form.variant_type.data == []:
147
160
  form.variant_type.data = ["clinical"]
@@ -158,7 +171,7 @@ def gene_variants(institute_id):
158
171
 
159
172
  variants_query = store.build_variant_query(
160
173
  query=form.data,
161
- institute_ids=[inst["_id"] for inst in user_institutes(store, current_user)],
174
+ institute_ids=[inst for inst in form.institute.data if inst in users_institute_ids],
162
175
  category=category,
163
176
  variant_type=variant_type,
164
177
  ) # This is the actual query dictionary, not the cursor with results
@@ -98,7 +98,7 @@ def google_login() -> Optional[Response]:
98
98
 
99
99
  redirect_uri: str = url_for("login.authorized", _external=True)
100
100
  try:
101
- return oauth_client.google.authorize_redirect(redirect_uri)
101
+ return oauth_client.google.authorize_redirect(redirect_uri, prompt="select_account")
102
102
  except Exception:
103
103
  flash("An error has occurred while logging in user using Google OAuth", "warning")
104
104
  return None
@@ -151,6 +151,7 @@ def perform_flask_login(user_dict: "LoginUser") -> Response:
151
151
 
152
152
  def logout_oidc_user(session, provider: str):
153
153
  """Log out a user from an OIDC login provider-"""
154
+
154
155
  logout_url = current_app.config[provider].get("logout_url")
155
156
  if not logout_url or not session.get("token_response"):
156
157
  return
@@ -13,7 +13,7 @@ from flask import (
13
13
  session,
14
14
  url_for,
15
15
  )
16
- from flask_login import login_user, logout_user
16
+ from flask_login import logout_user
17
17
 
18
18
  from scout.server.extensions import login_manager, oauth_client, store
19
19
  from scout.server.utils import public_endpoint
@@ -102,11 +102,14 @@ def authorized():
102
102
 
103
103
  @login_bp.route("/logout")
104
104
  def logout():
105
+ session.pop("email", None)
106
+ session.pop("name", None)
107
+ session.pop("locale", None)
108
+ session.pop("consent_given", None)
105
109
  logout_user() # logs out user from scout
106
110
  for provider in ["GOOGLE", "KEYCLOAK"]:
107
111
  if current_app.config.get(provider):
108
112
  controllers.logout_oidc_user(session, provider)
109
- session.clear()
110
113
  flash("you logged out", "success")
111
114
  return redirect(url_for("public.index"))
112
115
 
@@ -60,7 +60,7 @@
60
60
  {% set collapse_id = 'collapse-' + case._id %}
61
61
  {% set max_chars = 300 %}
62
62
 
63
- {% if case.synopsis|length > max_chars %}
63
+ {% if case.synopsis and case.synopsis|length > max_chars %}
64
64
  <div>
65
65
  {{ case.synopsis[:max_chars] }}…
66
66
  </div>
@@ -80,7 +80,7 @@
80
80
  </div>
81
81
  </div>
82
82
  {% else %}
83
- {{ case.synopsis }}
83
+ {{ case.synopsis or 'NA' }}
84
84
  {% endif %}
85
85
  </td>
86
86
  <td>{{ case.status }}</td>
@@ -100,8 +100,8 @@
100
100
  <span data-bs-toggle="tooltip" data-bs-html="true" title='The ∆ψ-value, which is the difference between the actual observed ψ (intron Jaccard Index splice metric) and the expected ψ - see FRASER vignette for details.'>
101
101
  {{ variant.delta_psi }}&nbsp;<a target="_blank" href="https://www.bioconductor.org/packages/devel/bioc/vignettes/FRASER/inst/doc/FRASER.pdf" rel="noopener noreferrer">&Delta;&psi;</a></span>
102
102
  {% else %}
103
- <span data-bs-toggle="tooltip" data-bs-html="true" title='The log2 fold change - click to see OUTRIDER vignette for details.'>
104
- {{ variant.l2fc }}&nbsp;&nbsp;<a target="_blank" href="https://www.bioconductor.org/packages/devel/bioc/vignettes/OUTRIDER/inst/doc/OUTRIDER.pdf" rel="noopener noreferrer">{% if variant.l2fc > 0 %}&uarr;{% elif variant.l2fc < 0 %}&darr;{% endif %}</a></span>
103
+ <span data-bs-toggle="tooltip" data-bs-html="true" title='The log2 fold change (fold change) - click to see OUTRIDER vignette for details.'>
104
+ {{ variant.l2fc }} ({{variant.l2fc|l2fc_2_fc|round(2)}}x) &nbsp;&nbsp;<a target="_blank" href="https://www.bioconductor.org/packages/devel/bioc/vignettes/OUTRIDER/inst/doc/OUTRIDER.pdf" rel="noopener noreferrer">{% if variant.l2fc > 0 %}&uarr;{% elif variant.l2fc < 0 %}&darr;{% endif %}</a></span>
105
105
  {% endif %}
106
106
  </td>
107
107
  <td>
@@ -17,7 +17,7 @@ from scout.server.blueprints.variants.controllers import (
17
17
  )
18
18
  from scout.server.blueprints.variants.forms import OutlierFiltersForm
19
19
  from scout.server.extensions import store
20
- from scout.server.utils import institute_and_case, templated
20
+ from scout.server.utils import get_case_genome_build, institute_and_case, templated
21
21
 
22
22
  from . import controllers
23
23
 
@@ -71,7 +71,7 @@ def outliers(institute_id, case_name):
71
71
  # Populate chromosome select choices
72
72
  populate_chrom_choices(form, case_obj)
73
73
 
74
- genome_build = "38" if "38" in str(case_obj.get("genome_build", "37")) else "37"
74
+ genome_build = get_case_genome_build(case_obj)
75
75
  cytobands = store.cytoband_by_chrom(genome_build)
76
76
 
77
77
  update_form_hgnc_symbols(store, case_obj, form)
@@ -1,7 +1,17 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ from typing import Optional
3
4
 
4
- def hpo_terms(store, query=None, limit=None, page=None):
5
+ from scout.adapter import MongoAdapter
6
+ from scout.constants import HPO_LINK_URL
7
+
8
+
9
+ def hpo_terms(
10
+ store: MongoAdapter,
11
+ query: Optional[str] = None,
12
+ limit: Optional[str] = None,
13
+ page: Optional[str] = None,
14
+ ) -> dict:
5
15
  """Retrieves a list of HPO terms from scout database
6
16
 
7
17
  Args:
@@ -29,4 +39,7 @@ def hpo_terms(store, query=None, limit=None, page=None):
29
39
  hpo_phenotypes["phenotypes"] = list(store.hpo_terms(query=query, limit=limit, skip=skip))
30
40
  else:
31
41
  hpo_phenotypes["phenotypes"] = list(store.hpo_terms(limit=limit, skip=skip))
32
- return hpo_phenotypes
42
+
43
+ data = hpo_phenotypes
44
+ data["hpo_link_url"] = HPO_LINK_URL
45
+ return data
@@ -34,7 +34,7 @@
34
34
  </thead>
35
35
  {% for pheno in phenotypes %}
36
36
  <tr>
37
- <td><a href="http://hpo.jax.org/app/browse/term/{{pheno.hpo_id}}" referrerpolicy="no-referrer" target="_blank">{{ pheno.hpo_id }}</a></td>
37
+ <td><a href="{{hpo_link_url}}{{pheno.hpo_id}}" referrerpolicy="no-referrer" target="_blank">{{ pheno.hpo_id }}</a></td>
38
38
  <td><span class="text-body">{{ pheno.description }}</span></td>
39
39
  <td><span class="text-body">{{pheno.genes|length}}</span></td>
40
40
  </tr>
@@ -28,6 +28,7 @@ from scout.constants import (
28
28
  VERBS_MAP,
29
29
  )
30
30
  from scout.server.blueprints.variant.utils import (
31
+ get_str_mc,
31
32
  update_representative_gene,
32
33
  update_variant_case_panels,
33
34
  )
@@ -40,6 +41,7 @@ from scout.server.utils import (
40
41
  case_has_chanjo_coverage,
41
42
  case_has_mt_alignments,
42
43
  case_has_rna_tracks,
44
+ get_case_genome_build,
43
45
  user_institutes,
44
46
  variant_institute_and_case,
45
47
  )
@@ -232,9 +234,7 @@ def variant(
232
234
 
233
235
  variant_id = variant_obj["variant_id"]
234
236
 
235
- genome_build = str(case_obj.get("genome_build", "37"))
236
- if genome_build not in ["37", "38"]:
237
- genome_build = "37"
237
+ genome_build = get_case_genome_build(case_obj)
238
238
 
239
239
  # is variant located on the mitochondria
240
240
  variant_obj["is_mitochondrial"] = any(
@@ -314,6 +314,9 @@ def variant(
314
314
 
315
315
  variant_obj["end_position"] = end_position(variant_obj)
316
316
 
317
+ # common motif count for STR variants
318
+ variant_obj["str_mc"] = get_str_mc(variant_obj)
319
+
317
320
  # Add general variant links
318
321
  variant_obj.update(get_variant_links(institute_obj, variant_obj, int(genome_build)))
319
322
  variant_obj["frequencies"] = frequencies(variant_obj)
@@ -362,7 +365,7 @@ def variant(
362
365
  ccv_evaluation(store, evaluation_obj)
363
366
  ccv_evaluations.append(evaluation_obj)
364
367
 
365
- case_clinvars = store.case_to_clinVars(case_obj.get("display_name"))
368
+ case_clinvars = store.case_to_clinvars(case_obj.get("display_name"))
366
369
 
367
370
  if variant_id in case_clinvars:
368
371
  variant_obj["clinvar_clinsig"] = case_clinvars.get(variant_id)["clinsig"]
@@ -425,9 +428,7 @@ def get_gene_has_full_coverage(institute_obj, case_obj, variant_obj) -> Dict[int
425
428
  if not case_obj.get("chanjo2_coverage"):
426
429
  return {}
427
430
 
428
- genome_build = str(case_obj.get("genome_build", "37"))
429
- if genome_build not in ["37", "38"]:
430
- genome_build = "37"
431
+ genome_build = get_case_genome_build(case_obj)
431
432
 
432
433
  gene_has_full_coverage: dict = {
433
434
  hgnc_id: chanjo2.get_gene_complete_coverage(
@@ -436,7 +437,7 @@ def get_gene_has_full_coverage(institute_obj, case_obj, variant_obj) -> Dict[int
436
437
  individuals=case_obj.get("individuals"),
437
438
  build=genome_build,
438
439
  )
439
- for hgnc_id in [gene.get("hgnc_id") for gene in variant_obj.get("genes")]
440
+ for hgnc_id in [gene.get("hgnc_id") for gene in variant_obj.get("genes", [])]
440
441
  }
441
442
  return gene_has_full_coverage
442
443
 
@@ -555,7 +556,7 @@ def observations(store: MongoAdapter, loqusdb: LoqusDB, variant_obj: dict) -> Di
555
556
  f"Could not find a Loqus instance with id:{loqus_id}",
556
557
  "warning",
557
558
  )
558
- obs_data[loqus_id]["observations"] = "N/A"
559
+ obs_data[loqus_id] = {"observations": "N/A"}
559
560
  continue
560
561
 
561
562
  if obs_data[loqus_id] == {}: # Variant was not found
@@ -656,9 +657,7 @@ def variant_acmg(store: MongoAdapter, institute_id: str, case_name: str, variant
656
657
  store, variant_obj, institute_id, case_name
657
658
  )
658
659
 
659
- genome_build = str(case_obj.get("genome_build", "37"))
660
- if genome_build not in ["37", "38"]:
661
- genome_build = "37"
660
+ genome_build = get_case_genome_build(case_obj)
662
661
 
663
662
  add_gene_info(store, variant_obj, genome_build=genome_build)
664
663
 
@@ -8,6 +8,7 @@
8
8
  {% from "variant/sanger.html" import modal_cancel_sanger, modal_sanger, sanger_button %}
9
9
  {% from "variant/gene_disease_relations.html" import orpha_omim_phenotypes %}
10
10
  {% from "variant/rank_score_results.html" import rankscore_panel %}
11
+ {% from "variants/utils.html" import variant_rank_score %}
11
12
 
12
13
  {% block title %}
13
14
  {{ super() }} - {{ institute.display_name }} - {{ case.display_name }} - {{ variant.display_name }}
@@ -282,7 +283,7 @@
282
283
  <strong>{{ variant.alternative }}</strong>
283
284
  {%- endif -%}
284
285
  </li>
285
- <li class="list-group-item">Rank score: <span class="font-weight-bold">{{ variant.rank_score }}</span>
286
+ <li class="list-group-item">Rank score: <span class="font-weight-bold">{{ variant_rank_score(variant) }}</span>
286
287
  </li>
287
288
  <li class="list-group-item">
288
289
  {{ clinsig_table(variant) }}
@@ -1,7 +1,6 @@
1
1
  {% from "cases/chanjo2_form.html" import chanjo2_report_form %}
2
2
  {% from "variant/buttons.html" import variant_tag_button, variant_tier_button, dismiss_variant_button, mosaic_variant_button %}
3
3
  {% from "variants/utils.html" import compounds_table %}
4
- {% from "variant/variant_details.html" import severity_list %}
5
4
  {% from "variant/buttons.html" import reviewer_button, splice_junctions_button %}
6
5
  {% from "variant/utils.html" import igv_track_selection %}
7
6
 
@@ -7,6 +7,7 @@
7
7
  {% from "variant/gene_disease_relations.html" import orpha_omim_phenotypes %}
8
8
  {% from "variant/variant_details.html" import frequencies, gtcall_panel, observations_panel %}
9
9
  {% from "variant/buttons.html" import dismiss_variant_button, splice_junctions_button, variant_tag_button %}
10
+ {% from "variants/utils.html" import variant_rank_score %}
10
11
 
11
12
  {% block title %}
12
13
  {{ super() }} - {{ institute.display_name }} - {{ case.display_name }} - {{ variant.display_name }}
@@ -174,7 +175,7 @@
174
175
  <li class="list-group-item">Rank <span class="badge rounded-pill bg-secondary float-end">{{ variant.variant_rank }}</span></li>
175
176
  <li class="list-group-item">
176
177
  Rank score
177
- <span class="badge rounded-pill bg-secondary float-end">{{ variant.rank_score }}</span>
178
+ <span class="badge rounded-pill bg-secondary float-end">{{variant_rank_score(variant)}}</span>
178
179
  </li>
179
180
 
180
181
  <li class="list-group-item">
@@ -396,7 +396,7 @@
396
396
  <div class="col-1">
397
397
  <!--Define build variable to be used in the UCSC link-->
398
398
  {% set build = "hg19" %}
399
- {% if case.genome_build == 38 or variant.chromosome == "MT" %}
399
+ {% if case.genome_build == "38" or variant.chromosome == "MT" %}
400
400
  {% set build = "hg38" %}
401
401
  {% endif %}
402
402
 
@@ -1,7 +1,7 @@
1
1
  {% extends "layout.html" %}
2
2
  {% from "variant/buttons.html" import database_buttons %}
3
3
  {% from "utils.html" import activity_panel, comments_panel, pedigree_panel %}
4
- {% from "variants/utils.html" import compounds_table %}
4
+ {% from "variants/utils.html" import compounds_table, variant_rank_score %}
5
5
  {% from "variant/utils.html" import causative_button, genes_panel, modal_causative, overlapping_panel, pin_button, proteins_panel, transcripts_panel, custom_annotations, gene_panels %}
6
6
  {% from "variant/tx_overview.html" import disease_associated, transcripts_overview %}
7
7
  {% from "variant/gene_disease_relations.html" import autozygosity_panel, genemodels_panel, inheritance_panel, orpha_omim_phenotypes %}
@@ -263,7 +263,7 @@
263
263
  </td>
264
264
  <td>
265
265
  Rank score
266
- <span><strong>{{ variant.rank_score }}</strong></span>
266
+ <span><strong>{{variant_rank_score(variant)}}</strong></span>
267
267
  </td>
268
268
  <td>
269
269
  CADD score
@@ -19,7 +19,7 @@
19
19
  {% elif variant.category == "sv" %}
20
20
  <th rowspan="2" colspan="1" title="SV caller specific quality score. Note different scales for different callers.">SV quality</th>
21
21
  {% elif variant.category == "str" %}
22
- <th rowspan="2" colspan="1">ExpansionHunter support</th>
22
+ <th rowspan="2" colspan="1">Expansion support</th>
23
23
  {% else %}
24
24
  {% if variant.chromosome in ["MT","M"] %}
25
25
  <th rowspan="2" colspan="1" title="Variant Allele Frequency.">Variant Allele Frequency (VAF)</th>
@@ -54,7 +54,8 @@
54
54
 
55
55
  {% if variant.category == "snv" and variant.chromosome in ["MT","M"] %}
56
56
  <td>
57
- {% if sample.alt_frequency and sample.alt_frequency != -1 %}
57
+ {# Using number == number as a test for NaN value numbers here. Given that they exist, NaN values are the only time a number is not considered equal to itself. #}
58
+ {% if sample.alt_frequency and sample.alt_frequency != -1 and sample.alt_frequency == sample.alt_frequency %}
58
59
  {{ (100*sample.alt_frequency)|round(2) }}%
59
60
  {% else %}
60
61
  N/A
@@ -80,7 +81,7 @@
80
81
  <small>N/A</small>
81
82
  {% endif %} (<small>VQ</small>
82
83
  {% if variant.quality not in ["None", None, "-1", -1] %}
83
- {{variant.quality}}
84
+ {{variant.quality|round(1)}}
84
85
  {% else %}
85
86
  <small>N/A</small>
86
87
  {% endif%})
@@ -164,38 +165,42 @@
164
165
  </div>
165
166
  {% endmacro %}
166
167
 
168
+ {% macro frequencies_table(variant) %}
169
+ <table class="table table-sm align-middle mb-1" style="font-size: 0.875rem;">
170
+ <thead class="table-light">
171
+ <tr>
172
+ <th scope="col" class="py-1 px-2">Source</th>
173
+ <th scope="col" class="py-1 px-2">Frequency</th>
174
+ </tr>
175
+ </thead>
176
+ <tbody>
177
+ {% for freq_name, value, link in variant.frequencies %}
178
+ <tr>
179
+ <td class="py-1 px-2">
180
+ {% if link %}
181
+ <a href="{{ link }}" target="_blank" rel="noopener" referrerpolicy="no-referrer">{{ freq_name }}</a>
182
+ {% else %}
183
+ {{ freq_name }}
184
+ {% endif %}
185
+ </td>
186
+ <td class="py-1 px-2">
187
+ {% if value %}
188
+ <span class="badge bg-secondary" style="font-size: 0.75rem;">{{ value|human_decimal }}</span>
189
+ {% else %}
190
+ -
191
+ {% endif %}
192
+ </td>
193
+ </tr>
194
+ {% endfor %}
195
+ </tbody>
196
+ </table>
197
+ {% endmacro %}
198
+
167
199
  {% macro frequencies(variant) %}
168
200
  <div class="card panel-default">
169
201
  <div class="panel-heading">Frequencies</div>
170
202
  <div class="card-body">
171
- <table class="table">
172
- <thead class="thead table-light">
173
- <tr>
174
- <th scope="col">Source</th>
175
- <th scope="col">Frequency</th>
176
- </tr>
177
- </thead>
178
- <tbody>
179
- {% for freq_name, value, link in variant.frequencies %}
180
- <tr>
181
- <td>
182
- {% if link %}
183
- <a href="{{ link }}" target="_blank" rel="noopener" referrerpolicy="no-referrer">{{ freq_name }}</a>
184
- {% else %}
185
- {{ freq_name }}
186
- {% endif %}
187
- </td>
188
- <td>
189
- {% if value %}
190
- <span class="badge bg-secondary">{{ value|human_decimal }}</span>
191
- {% else %}
192
- -
193
- {% endif %}
194
- </td>
195
- </tr>
196
- {% endfor %}
197
- </tbody>
198
- </table>
203
+ {{ frequencies_table(variant) }}
199
204
  </div>
200
205
  </div>
201
206
  {% endmacro %}
@@ -282,6 +287,41 @@
282
287
  </div>
283
288
  {% endmacro %}
284
289
 
290
+ {% macro old_observations_table(variant) %}
291
+ <table class="table">
292
+ <thead class="thead table-light">
293
+ <tr>
294
+ <th scope="col">Local archive</th>
295
+ <th scope="col">Nr obs.</th>
296
+ <th scope="col">Nr homo.</th>
297
+ <th scope="col">Frequency</th>
298
+ </tr>
299
+ </thead>
300
+ <tbody>
301
+ <tr>
302
+ <td>RD</td>
303
+ <td>{{ variant.local_obs_old|default('N/A') }}</td>
304
+ <td>{{ variant.local_obs_hom_old|default('N/A') }}</td>
305
+ <td>{% if variant.local_obs_old_freq %} {{variant.local_obs_old_freq|round(6)}} {% elif variant.local_obs_old_nr_cases and variant.local_obs_old %} {{ (variant.local_obs_old/variant.local_obs_old_nr_cases)|round(5) }} {% elif variant.local_obs_old_nr_cases %} 0 / {{variant.local_obs_old_nr_cases}} {% else %} N/A {% endif %}</td>
306
+ </tr>
307
+ {% if variant.category in ['cancer', 'cancer_sv'] %}
308
+ <tr>
309
+ <td>Cancer Germline</td>
310
+ <td>{{ variant.local_obs_cancer_germline_old|default('N/A') }}</td>
311
+ <td>{{ variant.local_obs_cancer_germline_hom_old|default('N/A') }}</td>
312
+ <td>{% if variant.local_obs_cancer_germline_old_freq %} {{variant.local_obs_cancer_germline_old_freq|round(6)}} {% elif variant.local_obs_cancer_germline_old_nr_cases and variant.local_obs_cancer_germline_old %} {{ (variant.local_obs_cancer_germline_old/variant.local_obs_cancer_germline_old_nr_cases)|round(5) }} {% elif variant.local_obs_cancer_germline_old_nr_cases %} 0 / {{variant.local_obs_cancer_germline_old_nr_cases}} {% else %} N/A {% endif %}</td>
313
+ </tr>
314
+ <tr>
315
+ <td>Cancer Somatic</td>
316
+ <td>{{ variant.local_obs_cancer_somatic_old|default('N/A') }}</td>
317
+ <td>{{ variant.local_obs_cancer_somatic_hom_old|default('N/A') }}</td>
318
+ <td>{% if variant.local_obs_cancer_somatic_old_freq %} {{variant.local_obs_cancer_somatic_old_freq|round(6)}} {% elif variant.local_obs_cancer_somatic_old_nr_cases and variant.local_obs_cancer_somatic_old %} {{ (variant.local_obs_cancer_somatic_old/variant.local_obs_cancer_somatic_old_nr_cases)|round(5) }} {% elif variant.local_obs_cancer_somatic_old_nr_cases %} 0 / {{variant.local_obs_cancer_somatic_old_nr_cases}} {% else %} N/A {% endif %}</td>
319
+ </tr>
320
+ {% endif %}
321
+ </tbody>
322
+ </table>
323
+ {% endmacro %}
324
+
285
325
  {% macro old_observations(variant, obs_date) %}
286
326
  {% if "SHOW_OBSERVED_VARIANT_ARCHIVE" in config and config["SHOW_OBSERVED_VARIANT_ARCHIVE"] == true %}
287
327
  <div class="card panel-default">
@@ -289,38 +329,7 @@
289
329
  <a href="https://github.com/moonso/loqusdb" target="_blank" data-bs-toggle="tooltip" title="Local observations annotated from an archive copy of the loqusdb local frequency database. Only variants above a quality threshold are included, and each case is included only once. {% if variant.local_obs_old_desc %} {{variant.local_obs_old_desc}} {% endif %}">Local observations (archive {{ obs_date or variant.local_obs_old_date or "version"}})</a>
290
330
  </div>
291
331
  <div class="card-body">
292
- <table class="table">
293
- <thead class="thead table-light">
294
- <tr>
295
- <th scope="col">Archive</th>
296
- <th scope="col">Nr obs.</th>
297
- <th scope="col">Nr homo.</th>
298
- <th scope="col">Frequency</th>
299
- </tr>
300
- </thead>
301
- <tbody>
302
- <tr>
303
- <td>RD</td>
304
- <td>{{ variant.local_obs_old|default('N/A') }}</td>
305
- <td>{{ variant.local_obs_hom_old|default('N/A') }}</td>
306
- <td>{% if variant.local_obs_old_freq %} {{variant.local_obs_old_freq|round(6)}} {% elif variant.local_obs_old_nr_cases and variant.local_obs_old %} {{ (variant.local_obs_old/variant.local_obs_old_nr_cases)|round(5) }} {% elif variant.local_obs_old_nr_cases %} 0 / {{variant.local_obs_old_nr_cases}} {% else %} N/A {% endif %}</td>
307
- </tr>
308
- {% if variant.category in ['cancer', 'cancer_sv'] %}
309
- <tr>
310
- <td>Cancer Germline</td>
311
- <td>{{ variant.local_obs_cancer_germline_old|default('N/A') }}</td>
312
- <td>{{ variant.local_obs_cancer_germline_hom_old|default('N/A') }}</td>
313
- <td>{% if variant.local_obs_cancer_germline_old_freq %} {{variant.local_obs_cancer_germline_old_freq|round(6)}} {% elif variant.local_obs_cancer_germline_old_nr_cases and variant.local_obs_cancer_germline_old %} {{ (variant.local_obs_cancer_germline_old/variant.local_obs_cancer_germline_old_nr_cases)|round(5) }} {% elif variant.local_obs_cancer_germline_old_nr_cases %} 0 / {{variant.local_obs_cancer_germline_old_nr_cases}} {% else %} N/A {% endif %}</td>
314
- </tr>
315
- <tr>
316
- <td>Cancer Somatic</td>
317
- <td>{{ variant.local_obs_cancer_somatic_old|default('N/A') }}</td>
318
- <td>{{ variant.local_obs_cancer_somatic_hom_old|default('N/A') }}</td>
319
- <td>{% if variant.local_obs_cancer_somatic_old_freq %} {{variant.local_obs_cancer_somatic_old_freq|round(6)}} {% elif variant.local_obs_cancer_somatic_old_nr_cases and variant.local_obs_cancer_somatic_old %} {{ (variant.local_obs_cancer_somatic_old/variant.local_obs_cancer_somatic_old_nr_cases)|round(5) }} {% elif variant.local_obs_cancer_somatic_old_nr_cases %} 0 / {{variant.local_obs_cancer_somatic_old_nr_cases}} {% else %} N/A {% endif %}</td>
320
- </tr>
321
- {% endif %}
322
- </tbody>
323
- </table>
332
+ {{ old_observations_table(variant) }}
324
333
  </div>
325
334
  </div>
326
335
  {% endif %}
@@ -344,13 +353,15 @@
344
353
  <li class="list-group-item">
345
354
  <a href="https://sites.google.com/site/revelgenomics/about" target="_blank" data-bs-toggle="tooltip"
346
355
  title="An ensemble score based on 13 individual scores for predicting the pathogenicity of missense variants. Scores range from 0 to 1. The larger the score the more likely the SNP has damaging effect">REVEL score</a>
347
- <span class="float-end">
348
- {% if variant.revel %}
349
- {{ variant.revel }}
356
+ {% if variant.revel and variant.revel != "-" %}
357
+ <span class="badge float-end bg-{{variant.revel|get_label_or_color_by_score('revel', 'color')}}" data-bs-toggle="tooltip" title="{{variant.revel|get_label_or_color_by_score('revel', 'label')}}">
358
+ {{ variant.revel }}
359
+ </span>
350
360
  {% else %}
351
- {{ "-" }}
361
+ <span class="float-end">
362
+ {{ "-" }}
363
+ </span>
352
364
  {% endif %}
353
- </span>
354
365
  </li>
355
366
  <li class="list-group-item">
356
367
  <a href="http://database.liulab.science/dbNSFP" target="_blank" data-bs-toggle="tooltip"
@@ -378,23 +389,28 @@
378
389
  <span class="float-end">{{ variant.spidex|spidex_human if variant.spidex else none|spidex_human }}</span>
379
390
  </li>
380
391
  <li class="list-group-item">
392
+ {% set spliceai_highest = variant.spliceai_scores | spliceai_max %}
381
393
  <a href="{{ variant.spliceai_link }}" target="_blank" rel="noopener">SpliceAI</a> <a href="https://github.com/Illumina/SpliceAI" target="_blank" rel="noopener">DS max</a>
382
- <span class="float-end control-label" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="bottom"
383
- title="<strong>
384
- {% for entry in variant.spliceai_scores %}
385
- {% if entry is not none %}
386
- SpliceAI highest delta score {{ entry }} </strong> at position {{ variant.spliceai_positions[loop.index0]}} relative to this variant.
387
- {% if variant.spliceai_predictions[loop.index0] %}
388
- <br>All scores and positions(relative to variant):<br>
389
- {{ variant.spliceai_predictions[loop.index0] }} <br>
390
- {% endif %}
391
- <br>
392
- {% else %}
393
- No SpliceAI positions annotated for this variant.
394
- {% endif %}
395
- {% endfor %}
396
- ">
397
- {{ variant.spliceai_scores|join(', ') or "-"}}
394
+ {% if spliceai_highest %}
395
+ <span class="badge bg-{{ spliceai_highest | get_label_or_color_by_score('spliceai', 'color') }} float-end" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="bottom"
396
+ title="<strong>
397
+ {% for entry in variant.spliceai_scores %}
398
+ {% if entry is not none %}
399
+ SpliceAI highest delta score {{ entry }} </strong> at position {{ variant.spliceai_positions[loop.index0]}} relative to this variant. {{ spliceai_highest | get_label_or_color_by_score('spliceai', 'label')}} according to Walker et al 2023.
400
+ {% if variant.spliceai_predictions[loop.index0] %}
401
+ <br>All scores and positions(relative to variant):<br>
402
+ {{ variant.spliceai_predictions[loop.index0] }} <br>
403
+ {% endif %}
404
+ <br>
405
+ {% else %}
406
+ No SpliceAI positions annotated for this variant.
407
+ {% endif %}
408
+ {% endfor %}
409
+ ">
410
+ {{ variant.spliceai_scores|join(', ') }}
411
+ {% else %}
412
+ <span class="float-end">{{ "-" }}
413
+ {% endif %}
398
414
  </span>
399
415
  </li>
400
416
  </ul>
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import re
2
3
  from typing import Dict, List, Optional, Tuple
3
4
 
4
5
  from scout.adapter import MongoAdapter
@@ -683,3 +684,27 @@ def associate_variant_genes_with_case_panels(case_obj: Dict, variant_obj: Dict)
683
684
  geneid_gene[hgnc_id]["associated_gene_panels"] = matching_panels
684
685
 
685
686
  variant_obj["genes"] = list(geneid_gene.values())
687
+
688
+
689
+ def get_str_mc(variant_obj: dict) -> Optional[int]:
690
+ """Return variant Short Tandem Repeat motif count, either as given by its ALT MC value
691
+ from the variant FORMAT field, or as a number given in the ALT on the form
692
+ '<STR123>'.
693
+ """
694
+ NUM = re.compile(r"\d+")
695
+
696
+ alt_mc = None
697
+ if variant_obj["alternative"] == ".":
698
+ return alt_mc
699
+
700
+ for sample in variant_obj["samples"]:
701
+ if sample["genotype_call"] in ["./.", ".|", "0/0", "0|0"]:
702
+ continue
703
+ alt_mc = sample.get("alt_mc")
704
+ if alt_mc:
705
+ return alt_mc
706
+
707
+ alt_num = NUM.search(variant_obj["alternative"])
708
+ if alt_num:
709
+ alt_mc = int(alt_num.group())
710
+ return alt_mc