Repository: Clinical-Genomics/scout
Pull request #5455: Fix #5454 - long STRs display on case page
Author: login: dnil
Labels: name: Bugfix
name: Frontend
Created: 2025-05-12T08:38:56Z | Merged: 2025-05-13T05:18:41Z

This PR adds a functionality or fixes a bug.

- Fix #5454

![Screenshot 2025-05-12 at 11 22 39](https://github.com/user-attachments/assets/d406b1b7-2b69-4a76-9e39-a21157d066cd)

<details>
<summary>Testing on cg-vm1 server (Clinical Genomics Stockholm)</summary>

**Prepare for testing**
1. Make sure the PR is pushed and available on [Docker Hub](https://hub.docker.com/repository/docker/clinicalgenomics/scout-server-stage)
1. Fist book your testing time using the Pax software available at [https://pax.scilifelab.se/](https://pax.scilifelab.se). The resource you are going to call dibs on is `scout-stage` and the server is `cg-vm1`.
1. `ssh <USER.NAME>@cg-vm1.scilifelab.se`
1. `sudo -iu hiseq.clinical`
1. `ssh localhost`
1. (optional) Find out which scout branch is currently deployed on cg-vm1: `podman ps`
1. Stop the service with current deployed branch: `systemctl --user stop scout@<name_of_currently_deployed_branch>`
1. Start the scout service with the branch to test: `systemctl --user start scout@<this_branch>`
1. Make sure the branch is deployed: `systemctl --user status scout.target`
1. After testing is done, repeat procedure at [https://pax.scilifelab.se/](https://pax.scilifelab.se), which will release the allocated resource (`scout-stage`) to be used for testing by other users.
</details>

<details>
<summary>Testing on hasta server (Clinical Genomics Stockholm)</summary>

**Prepare for testing**
1. `ssh <USER.NAME>@hasta.scilifelab.se`
1. Book your testing time using the Pax software. `us; paxa -u <user> -s hasta -r scout-stage`. You can also use the WSGI Pax app available at [https://pax.scilifelab.se/](https://pax.scilifelab.se).
1. (optional) Find out which scout branch is currently deployed on cg-vm1: `conda activate S_scout; pip freeze | grep scout-browser`
1. Deploy the branch to test: `bash /home/proj/production/servers/resources/hasta.scilifelab.se/update-tool-stage.sh -e S_scout -t scout -b <this_branch>`
1. Make sure the branch is deployed: `us; scout --version`
1. After testing is done, repeat the `paxa` procedure, which will release the allocated resource (`scout-stage`) to be used for testing by other users.
</details>


**How to test**:
1. how to test it, possibly with real cases/data

**Expected outcome**:
The functionality should be working
Take a screenshot and attach or copy/paste the output.

**Review:**
- [ ] code approved by
- [x] tests executed by DN


Linked issue #5454: Long STRs (from TRGT) dont display nicely when pinned
**Describe the bug**
Pretty link STR needs a TRGT adaption...

**Screenshots**

![Image](https://github.com/user-attachments/assets/4cc3f65f-a545-472e-aadb-1f82f8ed81a6)



Diff:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3534576c41..47974bad4b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 
 About changelog [here](https://keepachangelog.com/en/1.0.0/)
 
+## [unreleased]
+### Fixed
+- Fix long STR variant pinned display on case page (#5455)
+
+
 ## [4.101]
 ### Changed
 - Institutes are now sorted by ID on gene panels page (#5436)
diff --git a/scout/server/blueprints/cases/controllers.py b/scout/server/blueprints/cases/controllers.py
index 848c6def4a..9fe28a1786 100644
--- a/scout/server/blueprints/cases/controllers.py
+++ b/scout/server/blueprints/cases/controllers.py
@@ -157,7 +157,12 @@ def coverage_report_contents(base_url, institute_obj, case_obj):
     return html_body_content
 
 
-def _populate_case_groups(store, case_obj, case_groups, case_group_label):
+def _populate_case_groups(
+    store: MongoAdapter,
+    case_obj: dict,
+    case_groups: Dict[str, List[str]],
+    case_group_label: Dict[str, str],
+):
     """Case groups allow display of information about user linked cases together on one case.
     Notably variantS lists show shared annotations and alignment views show all alignments
     available for the group.
@@ -178,20 +183,21 @@ def _populate_case_groups(store, case_obj, case_groups, case_group_label):
             case_group_label[group] = store.case_group_label(group)
 
 
-def _get_partial_causatives(store: MongoAdapter, case_obj: Dict) -> List[Dict]:
-    """Return any partial causatives a case has, populated as causative objs.
-    Args:
-        store(adapter.MongoAdapter)
-        case_obj(models.Case)
-    Returns:
-        partial(list(dict))
+def _get_partial_causatives(store: MongoAdapter, institute_obj: dict, case_obj: dict) -> List[Dict]:
+    """Check for partial causatives and associated phenotypes.
+    Return any partial causatives a case has, populated as causative objs.
     """
 
     partial_causatives = []
     if case_obj.get("partial_causatives"):
         for var_id, values in case_obj["partial_causatives"].items():
+            variant_obj = store.variant(var_id)
+            if variant_obj:
+                decorated_variant_obj = _get_decorated_var(
+                    store, var_obj=variant_obj, institute_obj=institute_obj, case_obj=case_obj
+                )
             causative_obj = {
-                "variant": store.variant(var_id) or var_id,
+                "variant": decorated_variant_obj or var_id,
                 "disease_terms": values.get("diagnosis_phenotypes"),
                 "hpo_terms": values.get("phenotype_terms"),
             }
@@ -287,7 +293,7 @@ def bionano_case(store, institute_obj, case_obj) -> Dict:
     return data
 
 
-def sma_case(store, institute_obj, case_obj):
+def sma_case(store: MongoAdapter, institute_obj: dict, case_obj: dict) -> dict:
     """Preprocess a case for tabular view, SMA."""
 
     _populate_case_individuals(case_obj)
@@ -304,6 +310,26 @@ def sma_case(store, institute_obj, case_obj):
     return data
 
 
+def _get_suspects_or_causatives(
+    store: MongoAdapter, institute_obj: dict, case_obj: dict, kind: str = "suspects"
+) -> list:
+    """Fetch the variant objects for suspects and causatives and decorate them.
+    If no longer available, append variant_id instead."""
+
+    marked_vars = []
+    for variant_id in case_obj.get(kind, []):
+        variant_obj = store.variant(variant_id)
+        if variant_obj:
+            marked_vars.append(
+                _get_decorated_var(
+                    store, var_obj=variant_obj, institute_obj=institute_obj, case_obj=case_obj
+                )
+            )
+        else:
+            marked_vars.append(variant_id)
+    return marked_vars
+
+
 def case(
     store: MongoAdapter, institute_obj: dict, case_obj: dict, hide_matching: bool = True
 ) -> dict:
@@ -331,22 +357,16 @@ def case(
     case_group_label = {}
     _populate_case_groups(store, case_obj, case_groups, case_group_label)
 
-    # Fetch the variant objects for suspects and causatives
-    suspects = [
-        store.variant(variant_id) or variant_id for variant_id in case_obj.get("suspects", [])
-    ]
+    suspects = _get_suspects_or_causatives(store, institute_obj, case_obj, "suspects")
     _populate_assessments(suspects)
-    causatives = [
-        store.variant(variant_id) or variant_id for variant_id in case_obj.get("causatives", [])
-    ]
+
+    causatives = _get_suspects_or_causatives(store, institute_obj, case_obj, "causatives")
     _populate_assessments(causatives)
 
-    # get evaluated variants
     evaluated_variants = store.evaluated_variants(case_obj["_id"], case_obj["owner"])
     _populate_assessments(evaluated_variants)
 
-    # check for partial causatives and associated phenotypes
-    partial_causatives = _get_partial_causatives(store, case_obj)
+    partial_causatives = _get_partial_causatives(store, institute_obj, case_obj)
     _populate_assessments(partial_causatives)
 
     case_obj["clinvar_variants"] = store.case_to_clinVars(case_obj["_id"])
@@ -676,7 +696,9 @@ def case_report_variants(store: MongoAdapter, case_obj: dict, institute_obj: dic
             add_bayesian_acmg_classification(var_obj)
             add_bayesian_ccv_classification(var_obj)
             evaluated_variants_by_type[eval_category].append(
-                _get_decorated_var(var_obj=var_obj, institute_obj=institute_obj, case_obj=case_obj)
+                _get_decorated_var(
+                    store, var_obj=var_obj, institute_obj=institute_obj, case_obj=case_obj
+                )
             )
 
     for var_obj in store.evaluated_variants(
@@ -689,7 +711,9 @@ def case_report_variants(store: MongoAdapter, case_obj: dict, institute_obj: dic
     data["variants"] = evaluated_variants_by_type
 
 
-def _get_decorated_var(var_obj: dict, institute_obj: dict, case_obj: dict) -> dict:
+def _get_decorated_var(
+    store: MongoAdapter, var_obj: dict, institute_obj: dict, case_obj: dict
+) -> dict:
     """Decorate a variant object for display using the variant controller"""
     return variant_decorator(
         store=store,
@@ -719,7 +743,9 @@ def _append_evaluated_variant_by_type(
             add_bayesian_ccv_classification(var_obj)
 
             evaluated_variants_by_type[eval_category].append(
-                _get_decorated_var(var_obj=var_obj, institute_obj=institute_obj, case_obj=case_obj)
+                _get_decorated_var(
+                    store, var_obj=var_obj, institute_obj=institute_obj, case_obj=case_obj
+                )
             )
 
 
diff --git a/scout/server/blueprints/cases/templates/cases/utils.html b/scout/server/blueprints/cases/templates/cases/utils.html
index 547c5833d5..2688c916c5 100644
--- a/scout/server/blueprints/cases/templates/cases/utils.html
+++ b/scout/server/blueprints/cases/templates/cases/utils.html
@@ -293,8 +293,18 @@ <h5 class="modal-title" id="exampleModalLabel">Beacon submission</h5>
 
   {% if variant.category %}
     {% if variant.category in "str" %}
-      {{ variant.str_repid }} {{ variant.alternative | replace("<", " ") | replace(">", "")}}
-
+      {% if variant.str_repid %}
+        {{ variant.str_repid }}
+      {% elif variant.str_trid %}
+        {{ variant.str_trid  }}
+      {% else %}
+        {% for gene in variant.genes %} {{ gene.symbol }} {% endfor %}
+      {% endif %}
+      {% if variant.str_mc %}
+        STR{{ variant.str_mc }}
+      {% else %}
+        {{ variant.alternative|truncate(20,True) }}
+      {% endif %}
     {% elif variant.category in ("snv", "cancer") %}
       {% set display_genes = [] %}
       {% for gene in variant.genes %}
@@ -339,18 +349,24 @@ <h5 class="modal-title" id="exampleModalLabel">Beacon submission</h5>
 {% macro pretty_link_variant(variant, case) %}
 {# Returns human readable links to the corresponding variant page #}
 
-  {% if variant.category in ("mei", "str", "snv", "cancer") %}
-    {% if variant.category == "cancer" %}
-    <a href='{{ url_for('variant.cancer_variant',
+  {% if variant.category in ("mei", "snv") %}
+    <a href='{{ url_for('variant.variant',
                    institute_id=variant.institute,
                    case_name=case.display_name,
                    variant_id=variant._id) }}' target='_blank'>
-    {% else %}
+  {% elif variant.category == "str" %}
     <a href='{{ url_for('variant.variant',
+                   institute_id=variant.institute,
+                   case_name=case.display_name,
+                   variant_id=variant._id) }}'
+       data-bs-toggle='tooltip'
+       title='{{ variant.alternative | replace("<", " ") | replace(">", "")}}'
+       target='_blank'>
+  {% elif variant.category == "cancer" %}
+    <a href='{{ url_for('variant.cancer_variant',
                    institute_id=variant.institute,
                    case_name=case.display_name,
                    variant_id=variant._id) }}' target='_blank'>
-    {% endif %}
   {% elif variant.category == "outlier" %}
     <a href='{{ url_for('omics_variants.outliers',
        institute_id=variant.institute, case_name=case.display_name) }}' target='_blank'>
@@ -360,7 +376,7 @@ <h5 class="modal-title" id="exampleModalLabel">Beacon submission</h5>
                               case_name=case.display_name,
                               variant_id=variant._id) }}' target='_blank'>
   {% endif %}
-    {{ pretty_variant(variant) }}
+  {{ pretty_variant(variant) }}
   </a>
 {% endmacro %}
 
@@ -442,21 +458,7 @@ <h6>No causative variants</h6>
               <div class="row">
                 <div class="col">
                   <i class="far fa-check-circle"></i>
-                  {% if variant.category == "snv" %}
-                    <a href="{{ url_for('variant.variant',
-                                        institute_id=variant.institute,
-                                        case_name=case.display_name,
-                                        variant_id=variant._id) }}">
-                          {{ variant.hgnc_symbols|join(', ') }} (partial causative)
-  		             {% else %}
-          		      <a href="{{ url_for('variant.sv_variant',
-                                              institute_id=variant.institute,
-                                              case_name=case.display_name,
-                                              variant_id=variant._id) }}">
-  		              {{ variant.sub_category|upper }}({{ variant.chromosome }}{{ variant.cytoband_start }}-{{ variant.chromosome }}{{ variant.cytoband_end }})
-                    (partial causative)
-  		             {% endif %}
-                   </a>
+                  {{ pretty_link_variant(variant, case) }} (partial causative)
                 </div>
                 <div class="col-2" style="width:fit-content;">
                   {{ remove_form(url_for('cases.mark_causative',
diff --git a/scout/server/blueprints/variant/controllers.py b/scout/server/blueprints/variant/controllers.py
index 8724d856ef..b28294bdae 100644
--- a/scout/server/blueprints/variant/controllers.py
+++ b/scout/server/blueprints/variant/controllers.py
@@ -28,6 +28,7 @@
     VERBS_MAP,
 )
 from scout.server.blueprints.variant.utils import (
+    get_str_mc,
     update_representative_gene,
     update_variant_case_panels,
 )
@@ -314,6 +315,9 @@ def variant(
 
     variant_obj["end_position"] = end_position(variant_obj)
 
+    # common motif count for STR variants
+    variant_obj["str_mc"] = get_str_mc(variant_obj)
+
     # Add general variant links
     variant_obj.update(get_variant_links(institute_obj, variant_obj, int(genome_build)))
     variant_obj["frequencies"] = frequencies(variant_obj)
diff --git a/scout/server/blueprints/variant/utils.py b/scout/server/blueprints/variant/utils.py
index e311e46969..7635c308c2 100644
--- a/scout/server/blueprints/variant/utils.py
+++ b/scout/server/blueprints/variant/utils.py
@@ -1,4 +1,5 @@
 import logging
+import re
 from typing import Dict, List, Optional, Tuple
 
 from scout.adapter import MongoAdapter
@@ -683,3 +684,27 @@ def associate_variant_genes_with_case_panels(case_obj: Dict, variant_obj: Dict)
             geneid_gene[hgnc_id]["associated_gene_panels"] = matching_panels
 
     variant_obj["genes"] = list(geneid_gene.values())
+
+
+def get_str_mc(variant_obj: dict) -> Optional[int]:
+    """Return variant Short Tandem Repeat motif count, either as given by its ALT MC value
+    from the variant FORMAT field, or as a number given in the ALT on the form
+    '<STR123>'.
+    """
+    NUM = re.compile(r"\d+")
+
+    alt_mc = None
+    if variant_obj["alternative"] == ".":
+        return alt_mc
+
+    for sample in variant_obj["samples"]:
+        if sample["genotype_call"] in ["./.", ".|", "0/0", "0|0"]:
+            continue
+        alt_mc = sample.get("alt_mc")
+    if alt_mc:
+        return alt_mc
+
+    alt_num = NUM.search(variant_obj["alternative"])
+    if alt_num:
+        alt_mc = int(alt_num.group())
+        return alt_mc
diff --git a/scout/server/blueprints/variants/controllers.py b/scout/server/blueprints/variants/controllers.py
index 36d900aa81..e2fc8fa0be 100644
--- a/scout/server/blueprints/variants/controllers.py
+++ b/scout/server/blueprints/variants/controllers.py
@@ -35,6 +35,7 @@
     clinsig_human,
     get_callers,
     get_filters,
+    get_str_mc,
     predictions,
     update_representative_gene,
     update_variant_case_panels,
@@ -57,8 +58,6 @@
 )
 from .utils import update_case_panels
 
-NUM = re.compile(r"\d+")
-
 LOG = logging.getLogger(__name__)
 
 
@@ -997,28 +996,6 @@ def parse_variant(
     return variant_obj
 
 
-def get_str_mc(variant_obj: dict) -> Optional[int]:
-    """Return variant Short Tandem Repeat motif count, either as given by its ALT MC value
-    from the variant FORMAT field, or as a number given in the ALT on the form
-    '<STR123>'.
-    """
-    alt_mc = None
-    if variant_obj["alternative"] == ".":
-        return alt_mc
-
-    for sample in variant_obj["samples"]:
-        if sample["genotype_call"] in ["./.", ".|", "0/0", "0|0"]:
-            continue
-        alt_mc = sample.get("alt_mc")
-    if alt_mc:
-        return alt_mc
-
-    alt_num = NUM.search(variant_obj["alternative"])
-    if alt_num:
-        alt_mc = int(alt_num.group())
-        return alt_mc
-
-
 def download_str_variants(case_obj, variant_objs):
     """Download filtered STR variants for a case to a CSV file
 


Reviews:
user: login: dnil
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-12T10:43:35Z
user: login: northwestwitch
body: Nice refactor and fix! 🏅 
Could you write down somewhere the case with the very long description string, so we can reuse it while testing again?
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-12T11:23:53Z

Review comments:
diff_hunk: @@ -157,7 +157,12 @@ def coverage_report_contents(base_url, institute_obj, case_obj):
     return html_body_content
 
 
-def _populate_case_groups(store, case_obj, case_groups, case_group_label):
+def _populate_case_groups(
path: scout/server/blueprints/cases/controllers.py
user: login: dnil
body: en passant...
created_at: 2025-05-12T10:00:32Z
author_association: MEMBER
diff_hunk: @@ -304,6 +310,26 @@ def sma_case(store, institute_obj, case_obj):
     return data
 
 
+def _get_marked_variants(
path: scout/server/blueprints/cases/controllers.py
user: login: dnil
body: The name is a spur of the moment thing - feel free to suggest another! 😊 
created_at: 2025-05-12T10:30:09Z
author_association: MEMBER
diff_hunk: @@ -689,7 +711,9 @@ def case_report_variants(store: MongoAdapter, case_obj: dict, institute_obj: dic
     data["variants"] = evaluated_variants_by_type
 
 
-def _get_decorated_var(var_obj: dict, institute_obj: dict, case_obj: dict) -> dict:
+def _get_decorated_var(
path: scout/server/blueprints/cases/controllers.py
user: login: dnil
body: I'm not super fond of a function named get decorated - the latter clashes with "decorator" in my mind. But enough refactoring here I think.
created_at: 2025-05-12T10:31:56Z
author_association: MEMBER
diff_hunk: @@ -683,3 +684,27 @@ def associate_variant_genes_with_case_panels(case_obj: Dict, variant_obj: Dict)
             geneid_gene[hgnc_id]["associated_gene_panels"] = matching_panels
 
     variant_obj["genes"] = list(geneid_gene.values())
+
+
+def get_str_mc(variant_obj: dict) -> Optional[int]:
path: scout/server/blueprints/variant/utils.py
user: login: dnil
body: Moved this here as it is now used from both variants and variant. 
created_at: 2025-05-12T10:33:10Z
author_association: MEMBER
diff_hunk: @@ -178,20 +183,21 @@ def _populate_case_groups(store, case_obj, case_groups, case_group_label):
             case_group_label[group] = store.case_group_label(group)
 
 
-def _get_partial_causatives(store: MongoAdapter, case_obj: Dict) -> List[Dict]:
-    """Return any partial causatives a case has, populated as causative objs.
-    Args:
-        store(adapter.MongoAdapter)
-        case_obj(models.Case)
-    Returns:
-        partial(list(dict))
+def _get_partial_causatives(store: MongoAdapter, institute_obj: dict, case_obj: dict) -> List[Dict]:
+    """Check for partial causatives and associated phenotypes.
+    Return any partial causatives a case has, populated as causative objs.
     """
 
     partial_causatives = []
     if case_obj.get("partial_causatives"):
         for var_id, values in case_obj["partial_causatives"].items():
+            variant_obj = store.variant(var_id)
+            if variant_obj:
+                decorated_variant_obj = _get_decorated_var(
path: scout/server/blueprints/cases/controllers.py
user: login: dnil
body: The causatives/suspects were not "decorated" or "parsed" so did not have e.g. the `str_mc` field set to display.
created_at: 2025-05-12T10:40:29Z
author_association: MEMBER
diff_hunk: @@ -293,8 +293,18 @@ <h5 class="modal-title" id="exampleModalLabel">Beacon submission</h5>
 
   {% if variant.category %}
     {% if variant.category in "str" %}
-      {{ variant.str_repid }} {{ variant.alternative | replace("<", " ") | replace(">", "")}}
-
+      {% if variant.str_repid %}
path: scout/server/blueprints/cases/templates/cases/utils.html
user: login: dnil
body: This takes into account the various IDs and expansion designations possible for different callers.
created_at: 2025-05-12T10:41:22Z
author_association: MEMBER
diff_hunk: @@ -339,18 +349,24 @@ <h5 class="modal-title" id="exampleModalLabel">Beacon submission</h5>
 {% macro pretty_link_variant(variant, case) %}
 {# Returns human readable links to the corresponding variant page #}
 
-  {% if variant.category in ("mei", "str", "snv", "cancer") %}
-    {% if variant.category == "cancer" %}
-    <a href='{{ url_for('variant.cancer_variant',
+  {% if variant.category in ("mei", "snv") %}
path: scout/server/blueprints/cases/templates/cases/utils.html
user: login: dnil
body: This was mostly a weird nested if?!
created_at: 2025-05-12T10:42:03Z
author_association: MEMBER
diff_hunk: @@ -339,18 +349,24 @@ <h5 class="modal-title" id="exampleModalLabel">Beacon submission</h5>
 {% macro pretty_link_variant(variant, case) %}
 {# Returns human readable links to the corresponding variant page #}
 
-  {% if variant.category in ("mei", "str", "snv", "cancer") %}
-    {% if variant.category == "cancer" %}
-    <a href='{{ url_for('variant.cancer_variant',
+  {% if variant.category in ("mei", "snv") %}
+    <a href='{{ url_for('variant.variant',
                    institute_id=variant.institute,
                    case_name=case.display_name,
                    variant_id=variant._id) }}' target='_blank'>
-    {% else %}
+  {% elif variant.category == "str" %}
     <a href='{{ url_for('variant.variant',
+                   institute_id=variant.institute,
+                   case_name=case.display_name,
+                   variant_id=variant._id) }}'
+       data-bs-toggle='tooltip'
+       title='{{ variant.alternative | replace("<", " ") | replace(">", "")}}'
path: scout/server/blueprints/cases/templates/cases/utils.html
user: login: dnil
body: Adding a tooltip with the ALT allele for STRs.
created_at: 2025-05-12T10:42:47Z
author_association: MEMBER
diff_hunk: @@ -442,21 +458,7 @@ <h6>No causative variants</h6>
               <div class="row">
                 <div class="col">
                   <i class="far fa-check-circle"></i>
-                  {% if variant.category == "snv" %}
-                    <a href="{{ url_for('variant.variant',
-                                        institute_id=variant.institute,
-                                        case_name=case.display_name,
-                                        variant_id=variant._id) }}">
-                          {{ variant.hgnc_symbols|join(', ') }} (partial causative)
-  		             {% else %}
-          		      <a href="{{ url_for('variant.sv_variant',
-                                              institute_id=variant.institute,
-                                              case_name=case.display_name,
-                                              variant_id=variant._id) }}">
-  		              {{ variant.sub_category|upper }}({{ variant.chromosome }}{{ variant.cytoband_start }}-{{ variant.chromosome }}{{ variant.cytoband_end }})
-                    (partial causative)
-  		             {% endif %}
-                   </a>
+                  {{ pretty_link_variant(variant, case) }} (partial causative)
path: scout/server/blueprints/cases/templates/cases/utils.html
user: login: dnil
body: Better with the same routine everywhere... 
created_at: 2025-05-12T10:43:23Z
author_association: MEMBER
diff_hunk: @@ -157,7 +157,12 @@ def coverage_report_contents(base_url, institute_obj, case_obj):
     return html_body_content
 
 
-def _populate_case_groups(store, case_obj, case_groups, case_group_label):
+def _populate_case_groups(
+    store: MongoAdapter,
+    case_obj: dict,
+    case_groups: Dict[str, List[str]],
+    case_group_label: Dict[str, str],
path: scout/server/blueprints/cases/controllers.py
user: login: northwestwitch
body: 💯 
created_at: 2025-05-12T11:18:11Z
author_association: MEMBER
diff_hunk: @@ -304,6 +310,26 @@ def sma_case(store, institute_obj, case_obj):
     return data
 
 
+def _get_marked_variants(
path: scout/server/blueprints/cases/controllers.py
user: login: northwestwitch
body: _get_decorated_suspect_causative_variants?
created_at: 2025-05-12T11:20:10Z
author_association: MEMBER
diff_hunk: @@ -689,7 +711,9 @@ def case_report_variants(store: MongoAdapter, case_obj: dict, institute_obj: dic
     data["variants"] = evaluated_variants_by_type
 
 
-def _get_decorated_var(var_obj: dict, institute_obj: dict, case_obj: dict) -> dict:
+def _get_decorated_var(
path: scout/server/blueprints/cases/controllers.py
user: login: northwestwitch
body: It's fine, we are used to this name anyway
created_at: 2025-05-12T11:20:51Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-05-12T08:49:09Z
body: ## [Codecov](https://app.codecov.io/gh/Clinical-Genomics/scout/pull/5455?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Clinical-Genomics) Report
Attention: Patch coverage is `74.28571%` with `9 lines` in your changes missing coverage. Please review.
> Project coverage is 84.36%. Comparing base [(`8ec24e8`)](https://app.codecov.io/gh/Clinical-Genomics/scout/commit/8ec24e823a96ecf57e5a2752a5fe1069698672a5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Clinical-Genomics) to head [(`96cf13a`)](https://app.codecov.io/gh/Clinical-Genomics/scout/commit/96cf13aeef9b8369d1f69fe80fff26e3de4d63bf?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Clinical-Genomics).
> Report is 1 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/Clinical-Genomics/scout/pull/5455?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Clinical-Genomics) | Patch % | Lines |
|---|---|---|
| [scout/server/blueprints/cases/controllers.py](https://app.codecov.io/gh/Clinical-Genomics/scout/pull/5455?src=pr&el=tree&filepath=scout%2Fserver%2Fblueprints%2Fcases%2Fcontrollers.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Clinical-Genomics#diff-c2NvdXQvc2VydmVyL2JsdWVwcmludHMvY2FzZXMvY29udHJvbGxlcnMucHk=) | 61.11% | [7 Missing :warning: ](https://app.codecov.io/gh/Clinical-Genomics/scout/pull/5455?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Clinical-Genomics) |
| [scout/server/blueprints/variant/utils.py](https://app.codecov.io/gh/Clinical-Genomics/scout/pull/5455?src=pr&el=tree&filepath=scout%2Fserver%2Fblueprints%2Fvariant%2Futils.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Clinical-Genomics#diff-c2NvdXQvc2VydmVyL2JsdWVwcmludHMvdmFyaWFudC91dGlscy5weQ==) | 87.50% | [2 Missing :warning: ](https://app.codecov.io/gh/Clinical-Genomics/scout/pull/5455?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Clinical-Genomics) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #5455      +/-   ##
==========================================
- Coverage   84.38%   84.36%   -0.02%     
==========================================
  Files         329      329              
  Lines       20141    20154      +13     
==========================================
+ Hits        16995    17003       +8     
- Misses       3146     3151       +5     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Clinical-Genomics/scout/pull/5455?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Clinical-Genomics).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Clinical-Genomics).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: dnil
created_at: 2025-05-12T10:45:53Z
body: Longer PR than expected - I removed the "easy" label post hoc. 😂 The marked variants were not decorated, in contrast to the evaluated variants.
author_association: MEMBER
user: login: dnil
created_at: 2025-05-12T14:12:52Z
body: > Nice refactor and fix! 🏅 Could you write down somewhere the case with the very long description string, so we can reuse it while testing again?

I can do better; I've pinned one that should be large enough for most screens on a stage case, here:
https://scout38-stage.sys.scilifelab.se/cust000/NIST-trio-pacbio

✅ <img width="1051" alt="Screenshot 2025-05-13 at 07 02 58" src="https://github.com/user-attachments/assets/2cbbfac9-13eb-4622-9b69-4d4531d16668" />

author_association: MEMBER
user: login: sonarqubecloud[bot]
created_at: 2025-05-13T05:09:34Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=Clinical-Genomics_scout&pullRequest=5455) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=Clinical-Genomics_scout&pullRequest=5455&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=Clinical-Genomics_scout&pullRequest=5455&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=Clinical-Genomics_scout&pullRequest=5455&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=Clinical-Genomics_scout&pullRequest=5455&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=Clinical-Genomics_scout&pullRequest=5455&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=Clinical-Genomics_scout&pullRequest=5455)


author_association: NONE