scout-browser 4.100.2__py3-none-any.whl → 4.102.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 (52) hide show
  1. scout/adapter/mongo/case.py +20 -6
  2. scout/adapter/mongo/clinvar.py +7 -7
  3. scout/adapter/mongo/hgnc.py +7 -2
  4. scout/adapter/mongo/omics_variant.py +8 -0
  5. scout/adapter/mongo/variant_loader.py +6 -2
  6. scout/constants/__init__.py +1 -0
  7. scout/constants/file_types.py +1 -1
  8. scout/constants/igv_tracks.py +6 -2
  9. scout/constants/phenotype.py +1 -0
  10. scout/load/hpo.py +8 -2
  11. scout/server/blueprints/alignviewers/controllers.py +8 -6
  12. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +2 -0
  13. scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
  14. scout/server/blueprints/cases/controllers.py +56 -28
  15. scout/server/blueprints/cases/templates/cases/case_report.html +9 -88
  16. scout/server/blueprints/cases/templates/cases/matchmaker.html +1 -1
  17. scout/server/blueprints/cases/templates/cases/phenotype.html +1 -1
  18. scout/server/blueprints/cases/templates/cases/utils.html +27 -25
  19. scout/server/blueprints/cases/views.py +32 -33
  20. scout/server/blueprints/clinvar/controllers.py +18 -23
  21. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +0 -2
  22. scout/server/blueprints/diagnoses/controllers.py +4 -8
  23. scout/server/blueprints/diagnoses/templates/diagnoses/diagnoses.html +1 -1
  24. scout/server/blueprints/diagnoses/templates/diagnoses/disease_term.html +1 -1
  25. scout/server/blueprints/diagnoses/views.py +2 -2
  26. scout/server/blueprints/institutes/controllers.py +107 -73
  27. scout/server/blueprints/institutes/templates/overview/cases.html +8 -7
  28. scout/server/blueprints/login/controllers.py +2 -1
  29. scout/server/blueprints/login/views.py +5 -2
  30. scout/server/blueprints/omics_variants/views.py +2 -2
  31. scout/server/blueprints/panels/views.py +11 -2
  32. scout/server/blueprints/phenotypes/controllers.py +15 -2
  33. scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +1 -1
  34. scout/server/blueprints/variant/controllers.py +10 -11
  35. scout/server/blueprints/variant/templates/variant/utils.html +1 -1
  36. scout/server/blueprints/variant/templates/variant/variant_details.html +68 -60
  37. scout/server/blueprints/variant/utils.py +25 -0
  38. scout/server/blueprints/variants/controllers.py +11 -42
  39. scout/server/blueprints/variants/templates/variants/cancer-variants.html +3 -3
  40. scout/server/blueprints/variants/templates/variants/indicators.html +18 -15
  41. scout/server/blueprints/variants/templates/variants/utils.html +1 -1
  42. scout/server/blueprints/variants/views.py +9 -8
  43. scout/server/config.py +3 -0
  44. scout/server/extensions/beacon_extension.py +7 -2
  45. scout/server/templates/bootstrap_global.html +11 -1
  46. scout/server/templates/layout.html +6 -1
  47. scout/server/utils.py +24 -3
  48. {scout_browser-4.100.2.dist-info → scout_browser-4.102.0.dist-info}/METADATA +1 -1
  49. {scout_browser-4.100.2.dist-info → scout_browser-4.102.0.dist-info}/RECORD +52 -52
  50. {scout_browser-4.100.2.dist-info → scout_browser-4.102.0.dist-info}/WHEEL +0 -0
  51. {scout_browser-4.100.2.dist-info → scout_browser-4.102.0.dist-info}/entry_points.txt +0 -0
  52. {scout_browser-4.100.2.dist-info → scout_browser-4.102.0.dist-info}/licenses/LICENSE +0 -0
@@ -25,7 +25,11 @@ from scout.server.blueprints.variant.utils import (
25
25
  update_representative_gene,
26
26
  )
27
27
  from scout.server.extensions import beacon, store
28
- from scout.server.utils import institute_and_case, user_institutes
28
+ from scout.server.utils import (
29
+ get_case_genome_build,
30
+ institute_and_case,
31
+ user_institutes,
32
+ )
29
33
 
30
34
  from .forms import BeaconDatasetForm, CaseFilterForm
31
35
 
@@ -47,6 +51,28 @@ VAR_SPECIFIC_EVENTS = [
47
51
  "cancel_sanger",
48
52
  ]
49
53
 
54
+ # Projection for fetching cases
55
+ ALL_CASES_PROJECTION = {
56
+ "analysis_date": 1,
57
+ "assignees": 1,
58
+ "beacon": 1,
59
+ "case_id": 1,
60
+ "display_name": 1,
61
+ "genome_build": 1,
62
+ "individuals": 1,
63
+ "is_rerun": 1,
64
+ "is_research": 1,
65
+ "mme_submission": 1,
66
+ "owner": 1,
67
+ "panels": 1,
68
+ "phenotype_terms": 1,
69
+ "rank_model_version": 1,
70
+ "status": 1,
71
+ "sv_rank_model_version": 1,
72
+ "track": 1,
73
+ "vcf_files": 1,
74
+ }
75
+
50
76
 
51
77
  def get_timeline_data(limit):
52
78
  """Retrieve chronologially ordered events from the database to display them in the timeline page
@@ -519,92 +545,106 @@ def export_case_samples(institute_id, filtered_cases) -> Response:
519
545
  )
520
546
 
521
547
 
522
- def cases(store: MongoAdapter, request: request, institute_id: str) -> dict:
523
- """Preprocess case objects for the 'cases' view."""
524
- data = {}
548
+ def get_cases_by_query(
549
+ store: MongoAdapter,
550
+ request: request,
551
+ institute_id: str,
552
+ ) -> list:
553
+ """Fetch additional cases based on filters given in request query form.
525
554
 
526
- # Initialize data (institute info, filters, and case counts)
527
- institute_obj = institute_and_case(store, institute_id)
528
- data["institute"] = institute_obj
529
- data["form"] = CaseFilterForm(request.form)
530
- data["status_ncases"] = store.nr_cases_by_status(institute_id=institute_id)
531
- data["nr_cases"] = sum(data["status_ncases"].values())
555
+ Sets metadata in data, e.g. sort order.
556
+ """
532
557
 
533
- # Fetch Sanger unevaluated and validated cases
534
- sanger_ordered_not_validated = get_sanger_unevaluated(store, institute_id, current_user.email)
535
- data["sanger_unevaluated"], data["sanger_validated_by_others"] = sanger_ordered_not_validated
558
+ name_query = request.form
559
+ all_cases = store.cases(
560
+ collaborator=institute_id,
561
+ name_query=name_query,
562
+ skip_assigned=request.form.get("skip_assigned"),
563
+ is_research=request.form.get("is_research"),
564
+ has_rna_data=request.form.get("has_rna"),
565
+ verification_pending=request.form.get("validation_ordered"),
566
+ has_clinvar_submission=request.form.get("clinvar_submitted"),
567
+ projection=ALL_CASES_PROJECTION,
568
+ )
569
+ return all_cases
536
570
 
537
- # Projection for fetching cases
538
- ALL_CASES_PROJECTION = {
539
- "analysis_date": 1,
540
- "assignees": 1,
541
- "beacon": 1,
542
- "case_id": 1,
543
- "display_name": 1,
544
- "genome_build": 1,
545
- "individuals": 1,
546
- "is_rerun": 1,
547
- "is_research": 1,
548
- "mme_submission": 1,
549
- "owner": 1,
550
- "panels": 1,
551
- "phenotype_terms": 1,
552
- "rank_model_version": 1,
553
- "status": 1,
554
- "sv_rank_model_version": 1,
555
- "track": 1,
556
- "vcf_files": 1,
557
- }
571
+
572
+ def get_and_set_cases_by_status(
573
+ store: MongoAdapter,
574
+ request: request,
575
+ institute_obj: dict,
576
+ previous_query_result_cases: list,
577
+ data: dict,
578
+ ) -> dict:
579
+ """Process cases for statuses that require all cases to be shown.
580
+ Group cases by status, process additional cases for the remaining statuses
581
+ and ensure that we don't dim cases that already appeared in query search results.
582
+ """
583
+
584
+ status_show_all_cases = institute_obj.get("show_all_cases_status", ["prioritized"])
585
+ nr_cases_showall_statuses = 0
558
586
 
559
587
  # Group cases by status
560
588
  case_groups = {status: [] for status in CASE_STATUSES}
561
- nr_cases_showall_statuses = 0
562
- status_show_all_cases = institute_obj.get("show_all_cases_status") or ["prioritized"]
563
589
 
564
- # Process cases for statuses that require all cases to be shown
565
590
  for status in status_show_all_cases:
566
591
  cases_in_status = store.cases_by_status(
567
- institute_id=institute_id, status=status, projection=ALL_CASES_PROJECTION
592
+ institute_id=institute_obj["_id"], status=status, projection=ALL_CASES_PROJECTION
568
593
  )
569
594
  cases_in_status = _sort_cases(data, request, cases_in_status)
570
595
  for case_obj in cases_in_status:
571
596
  populate_case_obj(case_obj, store)
597
+ case_obj["dimmed_in_search"] = True
572
598
  case_groups[status].append(case_obj)
573
599
  nr_cases_showall_statuses += 1
574
600
 
575
- # Fetch additional cases based on filters
576
- name_query = request.form
577
- limit = int(request.form.get("search_limit")) if request.form.get("search_limit") else 100
578
- all_cases = store.cases(
579
- collaborator=institute_id,
580
- name_query=name_query,
581
- skip_assigned=request.form.get("skip_assigned"),
582
- is_research=request.form.get("is_research"),
583
- has_rna_data=request.form.get("has_rna"),
584
- verification_pending=request.form.get("validation_ordered"),
585
- has_clinvar_submission=request.form.get("clinvar_submitted"),
586
- projection=ALL_CASES_PROJECTION,
587
- )
601
+ nr_name_query_matching_displayed_cases = 0
602
+ limit = int(request.form.get("search_limit", 100))
603
+ for case_obj in previous_query_result_cases:
604
+ if case_obj["status"] in status_show_all_cases:
605
+ for group_case in case_groups[status]:
606
+ if group_case["_id"] == case_obj["_id"]:
607
+ group_case["dimmed_in_search"] = False
608
+ elif nr_name_query_matching_displayed_cases == limit:
609
+ break
610
+ else:
611
+ populate_case_obj(case_obj, store)
612
+ case_groups[case_obj["status"]].append(case_obj)
613
+ nr_name_query_matching_displayed_cases += 1
614
+
615
+ data["found_cases"] = nr_name_query_matching_displayed_cases + nr_cases_showall_statuses
616
+ data["limit"] = limit
617
+ return case_groups
618
+
619
+
620
+ def cases(store: MongoAdapter, request: request, institute_id: str):
621
+ """Preprocess case objects for the 'cases' view.
622
+
623
+ Returns data dict for view display, or response in case of file export.
624
+ """
625
+ data = {}
626
+
627
+ # Initialize data (institute info, filters, and case counts)
628
+ institute_obj = institute_and_case(store, institute_id)
629
+ data["institute"] = institute_obj
630
+ data["form"] = CaseFilterForm(request.form)
631
+ data["status_ncases"] = store.nr_cases_by_status(institute_id=institute_id)
632
+ data["nr_cases"] = sum(data["status_ncases"].values())
633
+
634
+ # Fetch Sanger unevaluated and validated cases
635
+ sanger_ordered_not_validated = get_sanger_unevaluated(store, institute_id, current_user.email)
636
+ data["sanger_unevaluated"], data["sanger_validated_by_others"] = sanger_ordered_not_validated
637
+
638
+ all_cases = get_cases_by_query(store, request, institute_id)
588
639
  all_cases = _sort_cases(data, request, all_cases)
589
640
 
590
641
  if request.form.get("export"):
591
642
  return export_case_samples(institute_id, all_cases)
592
643
 
593
- # Process additional cases for the remaining statuses
594
- nr_cases = 0
595
- for case_obj in all_cases:
596
- if case_obj["status"] not in status_show_all_cases:
597
- if nr_cases == limit:
598
- break
599
- populate_case_obj(case_obj, store)
600
- case_groups[case_obj["status"]].append(case_obj)
601
- nr_cases += 1
644
+ case_groups = get_and_set_cases_by_status(store, request, institute_obj, all_cases, data)
602
645
 
603
646
  # Compile the final data
604
647
  data["cases"] = [(status, case_groups[status]) for status in CASE_STATUSES]
605
- data["found_cases"] = nr_cases + nr_cases_showall_statuses
606
- data["limit"] = limit
607
-
608
648
  return data
609
649
 
610
650
 
@@ -615,7 +655,9 @@ def populate_case_obj(case_obj: dict, store: MongoAdapter):
615
655
  analysis_types = set(["mixed"])
616
656
  case_obj["analysis_types"] = list(analysis_types)
617
657
 
618
- case_obj["assignees"] = [store.user(user_email) for user_email in case_obj.get("assignees", [])]
658
+ case_obj["assignees"] = [
659
+ store.user(user_id=user_id) for user_id in case_obj.get("assignees", [])
660
+ ]
619
661
 
620
662
  last_analysis_date = case_obj.get("analysis_date", datetime.datetime.now())
621
663
  all_analyses_dates = {
@@ -821,7 +863,7 @@ def gene_variants(store, pymongo_cursor, variant_count, page=1, per_page=50):
821
863
  case_display_name = variant_case_obj.get("display_name")
822
864
  variant_obj["case_display_name"] = case_display_name
823
865
 
824
- genome_build = get_genome_build(variant_case_obj)
866
+ genome_build = get_case_genome_build(variant_case_obj)
825
867
  update_variant_genes(store, variant_obj, genome_build)
826
868
  variants.append(variant_obj)
827
869
 
@@ -885,14 +927,6 @@ def update_variant_genes(store, variant_obj, genome_build):
885
927
  variant_obj["functional_annotations"] = get_annotations(gene_symbols, functional_annotations)
886
928
 
887
929
 
888
- def get_genome_build(variant_case_obj):
889
- """Find genom build in `variant_case_obj`. If not found use build #37"""
890
- build = str(variant_case_obj.get("genome_build"))
891
- if build in ["37", "38"]:
892
- return build
893
- return "37"
894
-
895
-
896
930
  def get_hgvs(gene_obj: Dict) -> Tuple[str, str, str]:
897
931
  """Analyse gene object for hgvs info
898
932
  Return:
@@ -63,7 +63,7 @@
63
63
 
64
64
 
65
65
  {% macro case_row(case) %}
66
- <tr class="{% if case.status == 'solved' %}causative{% endif %}">
66
+ <tr class="{% if case.status == 'solved' %}causative{% elif case.dimmed_in_search %}dismiss{% endif %}">
67
67
  <td>
68
68
  <a class="me-2"
69
69
  {% if case.individuals|length == 1 %} data-bs-toggle="tooltip" title="{{case.individuals[0].display_name}}" {% endif %}
@@ -94,13 +94,14 @@
94
94
  <span class="badge bg-primary">migrated</span>
95
95
  {% endif %}
96
96
  </td>
97
- <td class="d-flex justify-content-between align-items-center">
98
- {{ case.analysis_date.date() }}
99
- {% if case.rerun_requested %}
97
+ <td>
98
+ <div class="d-flex align-items-center justify-content-between">{{ case.analysis_date.date() }}
99
+ {% if case.rerun_requested %}
100
100
  <span class="badge bg-secondary">rerun pending</span>
101
- {% elif case.is_rerun %}
102
- <span class="badge bg-secondary">rerun</span>
103
- {% endif %}
101
+ {% elif case.is_rerun %}
102
+ <span class="badge bg-secondary">rerun</span>
103
+ {% endif %}
104
+ </div>
104
105
  </td>
105
106
  <td>
106
107
  {% if case.is_research %}
@@ -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
 
@@ -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)
@@ -3,7 +3,16 @@ import datetime
3
3
  import json
4
4
  import logging
5
5
 
6
- from flask import Blueprint, Response, flash, redirect, render_template, request, send_file, url_for
6
+ from flask import (
7
+ Blueprint,
8
+ Response,
9
+ flash,
10
+ redirect,
11
+ render_template,
12
+ request,
13
+ send_file,
14
+ url_for,
15
+ )
7
16
  from flask_login import current_user
8
17
  from markupsafe import escape
9
18
 
@@ -96,7 +105,7 @@ def panels():
96
105
 
97
106
  panel_groups.append((institute_obj, institute_panels))
98
107
  return dict(
99
- panel_groups=panel_groups,
108
+ panel_groups=sorted(panel_groups, key=lambda x: x[0]["_id"]),
100
109
  panel_names=panel_names,
101
110
  panel_versions=panel_versions,
102
111
  institutes=institutes,
@@ -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)
@@ -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
 
@@ -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
 
@@ -164,38 +164,42 @@
164
164
  </div>
165
165
  {% endmacro %}
166
166
 
167
+ {% macro frequencies_table(variant) %}
168
+ <table class="table table-sm align-middle mb-1" style="font-size: 0.875rem;">
169
+ <thead class="table-light">
170
+ <tr>
171
+ <th scope="col" class="py-1 px-2">Source</th>
172
+ <th scope="col" class="py-1 px-2">Frequency</th>
173
+ </tr>
174
+ </thead>
175
+ <tbody>
176
+ {% for freq_name, value, link in variant.frequencies %}
177
+ <tr>
178
+ <td class="py-1 px-2">
179
+ {% if link %}
180
+ <a href="{{ link }}" target="_blank" rel="noopener" referrerpolicy="no-referrer">{{ freq_name }}</a>
181
+ {% else %}
182
+ {{ freq_name }}
183
+ {% endif %}
184
+ </td>
185
+ <td class="py-1 px-2">
186
+ {% if value %}
187
+ <span class="badge bg-secondary" style="font-size: 0.75rem;">{{ value|human_decimal }}</span>
188
+ {% else %}
189
+ -
190
+ {% endif %}
191
+ </td>
192
+ </tr>
193
+ {% endfor %}
194
+ </tbody>
195
+ </table>
196
+ {% endmacro %}
197
+
167
198
  {% macro frequencies(variant) %}
168
199
  <div class="card panel-default">
169
200
  <div class="panel-heading">Frequencies</div>
170
201
  <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>
202
+ {{ frequencies_table(variant) }}
199
203
  </div>
200
204
  </div>
201
205
  {% endmacro %}
@@ -282,6 +286,41 @@
282
286
  </div>
283
287
  {% endmacro %}
284
288
 
289
+ {% macro old_observations_table(variant) %}
290
+ <table class="table">
291
+ <thead class="thead table-light">
292
+ <tr>
293
+ <th scope="col">Local archive</th>
294
+ <th scope="col">Nr obs.</th>
295
+ <th scope="col">Nr homo.</th>
296
+ <th scope="col">Frequency</th>
297
+ </tr>
298
+ </thead>
299
+ <tbody>
300
+ <tr>
301
+ <td>RD</td>
302
+ <td>{{ variant.local_obs_old|default('N/A') }}</td>
303
+ <td>{{ variant.local_obs_hom_old|default('N/A') }}</td>
304
+ <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>
305
+ </tr>
306
+ {% if variant.category in ['cancer', 'cancer_sv'] %}
307
+ <tr>
308
+ <td>Cancer Germline</td>
309
+ <td>{{ variant.local_obs_cancer_germline_old|default('N/A') }}</td>
310
+ <td>{{ variant.local_obs_cancer_germline_hom_old|default('N/A') }}</td>
311
+ <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>
312
+ </tr>
313
+ <tr>
314
+ <td>Cancer Somatic</td>
315
+ <td>{{ variant.local_obs_cancer_somatic_old|default('N/A') }}</td>
316
+ <td>{{ variant.local_obs_cancer_somatic_hom_old|default('N/A') }}</td>
317
+ <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>
318
+ </tr>
319
+ {% endif %}
320
+ </tbody>
321
+ </table>
322
+ {% endmacro %}
323
+
285
324
  {% macro old_observations(variant, obs_date) %}
286
325
  {% if "SHOW_OBSERVED_VARIANT_ARCHIVE" in config and config["SHOW_OBSERVED_VARIANT_ARCHIVE"] == true %}
287
326
  <div class="card panel-default">
@@ -289,38 +328,7 @@
289
328
  <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
329
  </div>
291
330
  <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>
331
+ {{ old_observations_table(variant) }}
324
332
  </div>
325
333
  </div>
326
334
  {% endif %}