Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
136 commits
Select commit Hold shift + click to select a range
9ed780d
Added new liftover option
Apr 22, 2026
fc630bf
Updated changelog
Apr 22, 2026
efe4c42
Fix code style issues with Black
lint-action Apr 22, 2026
269642e
Merge branch 'main' into managed_liftover
northwestwitch Apr 22, 2026
ad66873
Reduce complexity
Apr 22, 2026
0fd7439
Fix code style issues with Black
lint-action Apr 22, 2026
189646b
Removed unused import
Apr 22, 2026
d1d757d
Renamed vars and removed unused import
Apr 22, 2026
12c88fd
PR ref in changelog
Apr 22, 2026
7453155
Add a test
Apr 22, 2026
fa713ff
Thanks SonarQube
Apr 22, 2026
633a8c2
How about this
Apr 22, 2026
0f35e53
Like this instead?
Apr 22, 2026
aeb8c53
Fix code style issues with Black
lint-action Apr 22, 2026
459c044
trigger tests
Apr 22, 2026
6c13c83
Hate that linter
Apr 22, 2026
686bcac
How about an iterable
Apr 22, 2026
dc51478
Fix none institutes
Apr 22, 2026
5b960cb
Do not lift if already in the right build
Apr 22, 2026
ef95458
Do not skip those in the right build
Apr 22, 2026
b065ec1
Fix it for reals
Apr 22, 2026
bbad863
Im an idiot
Apr 22, 2026
7944cc2
Final touch
Apr 22, 2026
9d8a103
Fix code style issues with Black
lint-action Apr 22, 2026
35c4fc5
trigger tests
Apr 22, 2026
5159e1a
Merge branch 'main' into managed_liftover
dnil Apr 23, 2026
69aef4c
Merge branch 'main' into managed_liftover
northwestwitch Apr 23, 2026
4fd27cf
Merge branch 'main' into managed_liftover
dnil Apr 24, 2026
7740667
Merge branch 'main' into managed_liftover
dnil Apr 24, 2026
676d4be
Merge branch 'main' into managed_liftover
northwestwitch Apr 24, 2026
2871c88
Merge branch 'main' into managed_liftover
northwestwitch Apr 24, 2026
be24066
Merge branch 'main' into managed_liftover
dnil Apr 29, 2026
2beeba9
Merge branch 'main' into managed_liftover
northwestwitch Apr 29, 2026
3deb0c8
Merge branch 'main' into managed_liftover
northwestwitch Apr 29, 2026
a43ff5c
Add some progress logs and retries
Apr 30, 2026
003e044
Merge branch 'main' into managed_liftover
northwestwitch Apr 30, 2026
f36578c
Merge branch 'main' into managed_liftover
dnil Apr 30, 2026
06ce7e5
Merge branch 'main' into managed_liftover
northwestwitch Apr 30, 2026
9b9dcf1
Merge branch 'main' into managed_liftover
dnil Apr 30, 2026
699ec2a
Merge branch 'main' into managed_liftover
northwestwitch May 4, 2026
f24dd1d
Merge branch 'main' into managed_liftover
northwestwitch May 4, 2026
aa97cf3
Merge branch 'main' into managed_liftover
northwestwitch May 5, 2026
5f94fb2
Merge branch 'main' into managed_liftover
northwestwitch May 5, 2026
1a9285a
Merge branch 'main' into managed_liftover
dnil May 5, 2026
8a381b9
Liftover using bcftools via broad
May 6, 2026
f06c751
Silence test for now
May 6, 2026
d33a093
Of course run them all, not just a part
May 6, 2026
77d6560
Error return 200 status also it seems
May 6, 2026
3811fae
Simpler
May 6, 2026
83a6837
Merge branch 'main' into managed_liftover
northwestwitch May 7, 2026
1b7be88
Merge branch 'main' into managed_liftover
dnil May 7, 2026
ec54f49
Merge branch 'main' into managed_liftover
dnil May 11, 2026
23160c3
Added liftover tag in managed description field
May 11, 2026
0be2fb1
Do not hardcode build
May 11, 2026
a650ec1
Fix code style issues with Black
lint-action May 11, 2026
8a50b51
Merge branch 'main' into managed_liftover
dnil May 11, 2026
9658f1b
Fixed description string according to the latest convention
May 12, 2026
ee1a535
Make it more flexible
May 12, 2026
54d1112
Merge branch 'main' into managed_liftover
northwestwitch May 13, 2026
3f39231
Fix code style issues with Black
lint-action May 13, 2026
da0076f
Trigger tests
May 13, 2026
f534d80
Merge branch 'main' into managed_liftover
dnil May 15, 2026
44edff8
Merge branch 'main' into managed_liftover
northwestwitch May 19, 2026
6627f38
Merge branch 'main' into managed_liftover
northwestwitch May 19, 2026
19e2ba9
Merge branch 'main' into managed_liftover
northwestwitch May 19, 2026
eac577d
Merge branch 'main' into managed_liftover
northwestwitch May 21, 2026
f5bb236
Merge branch 'main' into managed_liftover
northwestwitch May 21, 2026
9fafa5b
Fix isort
May 21, 2026
02cdf7b
Merge branch 'main' into managed_liftover
dnil May 21, 2026
690ea21
Merge branch 'main' into managed_liftover
dnil May 22, 2026
b8bfea9
Merge branch 'main' into managed_liftover
northwestwitch May 22, 2026
c97d731
Merge branch 'main' into managed_liftover
dnil May 22, 2026
7bbe94e
Merge branch 'main' into managed_liftover
northwestwitch May 22, 2026
a3fa9b9
Merge branch 'main' into managed_liftover
northwestwitch May 22, 2026
b49867c
Merge branch 'main' into managed_liftover
northwestwitch May 22, 2026
7110036
Fix code style issues with Black
lint-action May 22, 2026
770b829
How about this
May 22, 2026
049ad37
Fix renamed import in test
May 22, 2026
986bacf
Merge branch 'main' into managed_liftover
dnil May 22, 2026
39e46aa
Merge branch 'main' into managed_liftover
northwestwitch May 25, 2026
d39b7be
Merge branch 'main' into managed_liftover
northwestwitch May 25, 2026
dd8bf83
Merge branch 'main' into managed_liftover
northwestwitch May 26, 2026
6f8efbd
Merge branch 'main' into managed_liftover
dnil May 26, 2026
714e8b1
Merge branch 'main' into managed_liftover
northwestwitch May 26, 2026
9f89b88
Merge branch 'main' into managed_liftover
northwestwitch May 26, 2026
af48e86
Merge branch 'main' into managed_liftover
northwestwitch May 27, 2026
352db04
Merge branch 'main' into managed_liftover
dnil May 27, 2026
6bc2216
Merge branch 'main' into managed_liftover
northwestwitch May 27, 2026
5736a6a
Merge branch 'main' into managed_liftover
northwestwitch May 28, 2026
7fd66f3
Merge branch 'main' into managed_liftover
northwestwitch May 29, 2026
ac98729
Fix conflicts
May 29, 2026
932e386
Remove old imports from test file
May 29, 2026
6c4d2fd
Full rever vcf utils
May 29, 2026
0b40a0e
Fixed changelog
May 29, 2026
f4bfa5a
Fix final typo
May 29, 2026
d8eba1a
Remove redundant code
May 29, 2026
21d37ea
I removed too much
May 29, 2026
ea401b3
Some more fixes
May 29, 2026
9471d7a
Simplify code
Jun 1, 2026
4a9d1f5
Merge branch 'main' into managed_liftover
northwestwitch Jun 2, 2026
0cbe04e
Merge branch 'main' into managed_liftover
northwestwitch Jun 3, 2026
da0b3e6
Merge branch 'main' into managed_liftover
dnil Jun 4, 2026
e60e6fa
Merge branch 'main' into managed_liftover
northwestwitch Jun 4, 2026
4a8c994
Merge branch 'main' into managed_liftover
dnil Jun 5, 2026
9a92a15
Merge branch 'main' into managed_liftover
northwestwitch Jun 5, 2026
341ce98
Merge branch 'main' into managed_liftover
northwestwitch Jun 5, 2026
94ec298
Merge branch 'main' into managed_liftover
dnil Jun 5, 2026
2865420
Merge branch 'main' into managed_liftover
dnil Jun 8, 2026
c1c0090
Merge branch 'main' into managed_liftover
dnil Jun 8, 2026
759b557
Merge branch 'main' into managed_liftover
northwestwitch Jun 8, 2026
0ae8d22
Merge branch 'main' into managed_liftover
northwestwitch Jun 9, 2026
8b7fb78
Merge branch 'main' into managed_liftover
dnil Jun 10, 2026
b14a002
Merge branch 'main' into managed_liftover
northwestwitch Jun 10, 2026
2ab154a
Merge branch 'main' into managed_liftover
dnil Jun 11, 2026
7e87341
Merge branch 'main' into managed_liftover
northwestwitch Jun 12, 2026
0c50789
Merge branch 'main' into managed_liftover
northwestwitch Jun 12, 2026
bfc69fd
Merge branch 'main' into managed_liftover
northwestwitch Jun 15, 2026
b730384
Merge branch 'main' into managed_liftover
dnil Jun 15, 2026
e6576be
Merge branch 'main' into managed_liftover
northwestwitch Jun 15, 2026
1fe30c4
Merge branch 'main' into managed_liftover
northwestwitch Jun 15, 2026
c816db6
Add a test
Jun 17, 2026
d661fab
Merge branch 'main' into managed_liftover
northwestwitch Jun 18, 2026
89a5abd
Merge branch 'main' into managed_liftover
northwestwitch Jun 18, 2026
a8befb5
Merge branch 'main' into managed_liftover
dnil Jun 18, 2026
f8b66e2
Merge branch 'main' into managed_liftover
northwestwitch Jun 18, 2026
57364a8
Merge branch 'main' into managed_liftover
northwestwitch Jun 18, 2026
d5b82f3
Merge branch 'main' into managed_liftover
dnil Jun 22, 2026
88a1d8c
Merge branch 'main' into managed_liftover
dnil Jun 23, 2026
96ad316
Merge branch 'main' into managed_liftover
northwestwitch Jun 23, 2026
9b234bb
Fixed sorting of a file
Jun 23, 2026
10d4ac0
Merge branch 'main' into managed_liftover
northwestwitch Jun 23, 2026
026beb7
Merge branch 'main' into managed_liftover
dnil Jun 24, 2026
f36c771
Merge branch 'main' into managed_liftover
northwestwitch Jun 24, 2026
0d0ff9f
Merge branch 'main' into managed_liftover
northwestwitch Jun 25, 2026
0c33e4c
Merge branch 'main' into managed_liftover
dnil Jun 29, 2026
e93a2ef
Merge branch 'main' into managed_liftover
dnil Jun 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ About changelog [here](https://keepachangelog.com/en/1.0.0/)
- An icon to the Gens-to-cases link to distinguish it from Gens-to-individuals links on the SV variant page (#6410)
- Display both GnomAD and GnomAD non-UK-Biobank links on variants pages for build 38 SNVs (#6413)
- Variant dismiss tags shown on pages can be customised in the institute settings page by admin users (#6416)
- A `--liftover-from` option to the `export managed` command line, so managed variants can be exported and imported again in another genome build (#6225)
### Changed
- Replaced Ensembl rest liftover service with liftover API from the Broad Institute (#6293)
- Avoid fetching genes and panels multiple times when loading variants (#6350)
Expand Down
15 changes: 13 additions & 2 deletions scout/commands/export/variant.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
from flask.cli import with_appcontext
from xlsxwriter import Workbook

from scout.constants import CALLERS, DATE_DAY_FORMATTER
from scout.constants import BUILDS, CALLERS, DATE_DAY_FORMATTER
from scout.constants.managed_variant import MANAGED_CATEGORIES
from scout.constants.variants_export import VERIFIED_VARIANTS_HEADER
from scout.export.variant import (
export_lift_over_managed_variants,
export_managed_variants,
export_verified_variants,
)
Expand Down Expand Up @@ -124,8 +125,15 @@ def verified(collaborator, test, outpath=None):
@collaborator_option
@build_option
@json_option
@click.option(
"--liftover-from",
type=click.Choice(BUILDS),
help="Perform liftover on coordinates and export as managed variants infile.",
)
@with_appcontext
def managed(collaborator: str, category: Tuple[str], build: str, json: bool):
def managed(
collaborator: str, category: Tuple[str], build: str, json: bool, liftover_from: Optional[str]
):
"""Export managed variants for a collaborator in VCF or JSON format"""
LOG.info("Running scout export managed variants")
adapter = store
Expand All @@ -141,6 +149,9 @@ def managed(collaborator: str, category: Tuple[str], build: str, json: bool):
if json:
click.echo(json_lib.dumps([var for var in variants], default=bson_handler))
return
if liftover_from:
export_lift_over_managed_variants(managed_variants=variants, liftover_from=liftover_from)
return

print_vcf(variants=variants, build=build, export_category="MANAGED")

Expand Down
75 changes: 73 additions & 2 deletions scout/export/variant.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# -*- coding: utf-8 -*-
import logging
import urllib.parse
from typing import List
from typing import Iterable, List

import click

from scout.adapter.mongo.base import MongoAdapter
from scout.constants import CHROMOSOME_INTEGERS
from scout.constants.managed_variant import MANAGED_CATEGORIES
from scout.constants.managed_variant import MANAGED_CATEGORIES, MANAGED_VARIANTS_INFILE_HEADER
from scout.constants.query_terms import GT_NO_ALT_CALL
from scout.models.managed_variant import ManagedVariant
from scout.utils.broad_liftover_client import BroadLiftoverApiClient

LOG = logging.getLogger(__name__)

Expand All @@ -24,6 +27,74 @@
return sorted(variants, key=sort_key)


def export_lift_over_managed_variants(managed_variants: Iterable, liftover_from: str):

Check failure on line 30 in scout/export/variant.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 19 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=Clinical-Genomics_scout&issues=AZ5P2tXfFWZNvFTrCLjb&open=AZ5P2tXfFWZNvFTrCLjb&pullRequest=6225
"""Perform liftover over a list of managed variants and print a list of lines formatted as a managed variants upload infile."""

export_lines = [MANAGED_VARIANTS_INFILE_HEADER]
client = BroadLiftoverApiClient()

lifted_build = "38" if liftover_from == "37" else "37"

nfailed = 0
nprocessed = 0

for nprocessed, variant_obj in enumerate(managed_variants, 1):
if nprocessed % 50 == 0:
LOG.info(f"Processed {nprocessed} variants")

category = variant_obj.get("category", "snv")
if category not in ["snv", "cancer_snv"]:
continue

build = variant_obj.get("build")

if build == lifted_build:
chrom = variant_obj["chromosome"]
pos = variant_obj["position"]
end = variant_obj.get("end", pos)
ref = variant_obj.get("reference")
alt = variant_obj.get("alternative")
else:
result = client.liftover(
build_from=liftover_from,
chrom=variant_obj.get("chromosome"),
start=variant_obj.get("position"),
end=variant_obj.get("end"),
ref=variant_obj.get("reference", ""),
alt=variant_obj.get("alternative", ""),
)

if "error" in result:
nfailed += 1
LOG.error(result)
continue

chrom = result["output_chrom"].replace("chr", "")
pos = result["output_pos"]
end = result.get("output_end") or result.get("output_pos")
ref = result["output_ref"]
alt = result["output_alt"]

sub_category = variant_obj.get("sub_category", "snv")

desc = variant_obj.get("description")
if "(causatives" not in (desc or ""):
description = f"{desc} (managed, build{liftover_from})"
else:
description = desc

institutes = ",".join(variant_obj.get("institute") or [])

export_lines.append(
f"{chrom};{pos};{end};{ref};{alt};"
f"{category};{sub_category};{lifted_build};{description};;{institutes}"
)

LOG.info(f"Done. Total processed: {nprocessed} - total failed: {nfailed}")
for line in export_lines:
click.echo(line)


def export_managed_variants(
adapter: MongoAdapter,
institute: str = None,
Expand Down
63 changes: 59 additions & 4 deletions tests/export/test_export_variants.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
import responses

from scout.constants.managed_variant import MANAGED_VARIANTS_INFILE_HEADER
from scout.constants.variants_export import MT_EXPORT_HEADER
from scout.export.variant import export_mt_variants
from scout.export.variant import export_lift_over_managed_variants, export_mt_variants
from scout.utils.broad_liftover_client import LIFTOVER_URL


def test_export_mt_variants(case_obj, real_populated_database):
Expand All @@ -26,7 +28,60 @@ def test_export_mt_variants(case_obj, real_populated_database):
for sample in samples:
sample_lines = export_mt_variants(variants=mt_variants, sample_id=sample["individual_id"])

# check that rows to write to excel corespond to number of variants
# check that rows to write to excel correspond to number of variants
assert len(sample_lines) == len(mt_variants)
# check that cols to write to excel corespond to fields of excel header
# check that cols to write to excel correspond to fields of Excel header
assert len(sample_lines[0]) == len(MT_EXPORT_HEADER)


@responses.activate
def test_export_lift_over_managed_variants(broad_bcftools_liftover_response, capsys):
"""Test the function lifts over and formats managed variants into a managed variants infile."""

# GIVEN an Iterable with managed variants:
managed_variant = {
"chromosome": "8",
"position": 141310715,
"end": 141310715,
"reference": "T",
"alternative": "G",
"category": "snv",
"build": "37",
}

managed_variants = [managed_variant]

url = (
f"{LIFTOVER_URL}/"
"?hg=hg19-to-hg38"
"&format=variant"
f"&chrom={managed_variant['chromosome']}"
f"&pos={managed_variant['position']}"
f"&end={managed_variant['end']}"
f"&ref={managed_variant['reference']}"
f"&alt={managed_variant['alternative']}"
)

# GIVEN a mocked call to the liftover service (BCFTools via Broad institute's API)
resp = broad_bcftools_liftover_response
responses.add(
responses.GET,
url,
json=resp,
status=200,
)

# WHEN exporting the managed variants
export_lift_over_managed_variants(
managed_variants=managed_variants,
liftover_from="37",
)

# THEN a header and a lifted variant line should be printed
out = capsys.readouterr().out.splitlines()

assert out[0] == MANAGED_VARIANTS_INFILE_HEADER
assert (
out[1]
== f"{resp['chrom'].replace('chr','')};{resp['output_pos']};{resp['output_pos']};{resp['output_ref']};{resp['output_alt']};snv;snv;38;None (managed, build37);;"
)
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test_add_and_remove_managed_variants(app, mocker, mock_redirect):
resp = client.post(url_for("managed_variants.add_managed_variant"), data=add_form_data)
# THEN the status code should still be redirect
assert resp.status_code == 302
# THEN the database should still contatain only one variant
# THEN the database should still contain only one variant
assert sum(1 for i in store.managed_variant_collection.find()) == 1

# WHEN requesting to remove the selected variant
Expand Down
Loading