diff --git a/Dockerfile b/Dockerfile index 7d645fac..957306e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,7 +61,7 @@ COPY --from=bcftools_compiler /bcftools_install/usr/local/lib/ /usr/local/lib/ RUN ldconfig ARG ECHTVAR_VERSION=v0.2.2 -ARG VERSION=11.0.1 +ARG VERSION=11.0.2 RUN wget -q -O /bin/echtvar "https://github.com/brentp/echtvar/releases/download/${ECHTVAR_VERSION}/echtvar" && \ chmod +x /bin/echtvar diff --git a/README.md b/README.md index 6fb8561e..07814d9b 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ There are two primary workflows: To build the Docker image: ``` -docker build -t talos:11.0.1 . +docker build -t talos:11.0.2 . ``` ### **2. Download Annotation Resources** diff --git a/docs/getting-started.md b/docs/getting-started.md index d4d987e1..9fa1ca0e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -25,7 +25,7 @@ You will need: Build the Talos Docker image locally: ```bash -docker build -t talos:11.0.1 . +docker build -t talos:11.0.2 . ``` --- diff --git a/nextflow.config b/nextflow.config index 24ebc30d..2f745b4b 100644 --- a/nextflow.config +++ b/nextflow.config @@ -16,8 +16,8 @@ params { large_files = "large_files" ref_genome = "${params.large_files}/ref.fa" - // Docker container - "docker build -t talos:11.0.1 ." - container = 'talos:11.0.1' + // Docker container - "docker build -t talos:11.0.2 ." + container = 'talos:11.0.2' // MANE transcript resource mane = "${params.large_files}/MANE.GRCh38.v1.5.summary.txt.gz" diff --git a/nextflow_schema.json b/nextflow_schema.json index 36a48058..92600956 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -33,7 +33,7 @@ }, "container": { "type": "string", - "default": "talos:11.0.1", + "default": "talos:11.0.2", "description": "Docker container used by processes." }, "mane": { diff --git a/pyproject.toml b/pyproject.toml index b86eda76..d9d4667c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" name='talos' description='Centre for Population Genomics Variant Prioritisation' readme = "README.md" -version="11.0.1" +version="11.0.2" requires-python = ">=3.10,<3.12" license-files = ["LICENSE"] classifiers=[ @@ -124,9 +124,10 @@ hail = ["hail", "hailtop"] ## - we don't need generic cpg-flow arguments for every Stage, but need to fit the required method signature "src/talos/cpg_internal_scripts/annotation_stages.py" = ["ARG002"] "src/talos/cpg_internal_scripts/talos_stages.py" = ["ARG002"] +"test/test_cpg_flow_utils.py" = ['ANN202', 'ARG001'] [tool.bumpversion] -current_version = "11.0.1" +current_version = "11.0.2" parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)" serialize = ["{major}.{minor}.{patch}"] commit = true diff --git a/src/talos/cpg_internal_scripts/CPG_Dockerfile b/src/talos/cpg_internal_scripts/CPG_Dockerfile index f36e5eb0..24475cfb 100644 --- a/src/talos/cpg_internal_scripts/CPG_Dockerfile +++ b/src/talos/cpg_internal_scripts/CPG_Dockerfile @@ -16,7 +16,7 @@ FROM base AS bcftools_compiler # BCFtools 1.22+ doesn't do any inference on chr-prefixes during CSQ, so the mismatch between fasta/vcf, and the gff3 (no chr-prefix) needs to be handled explicitly # BCFtools >=1.22 is required internally as it's the first version with a built-in Mitochondrial lookup table. -# AS OF 11.0.1, Talos is building BCFtools from a private fork. This fork contains a single change - csq applies annotations +# AS OF 11.0.2, Talos is building BCFtools from a private fork. This fork contains a single change - csq applies annotations # to both coding and non-coding genes in the event of overlapping genes. By default BCFtools skips non-coding gene annotation # if a coding transcript consequence was detected, but in practice this is masking clinically relevant non-coding gene variation # in cases where the non-coding gene overlaps with a non-clinically relevant coding gene. @@ -53,7 +53,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ FROM base AS talos ARG ECHTVAR_VERSION=v0.2.2 -ARG VERSION=11.0.1 +ARG VERSION=11.0.2 COPY --from=bcftools_compiler /bcftools_install/usr/local/bin/* /usr/local/bin/ COPY --from=bcftools_compiler /bcftools_install/usr/local/libexec/bcftools/* /usr/local/libexec/bcftools/ diff --git a/src/talos/cpg_internal_scripts/annotation_stages.py b/src/talos/cpg_internal_scripts/annotation_stages.py index 2418e50e..ea11ed9b 100644 --- a/src/talos/cpg_internal_scripts/annotation_stages.py +++ b/src/talos/cpg_internal_scripts/annotation_stages.py @@ -65,9 +65,8 @@ def does_final_file_path_exist(cohort: targets.Cohort) -> bool: return utils.exists( cpg_flow_utils.generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name='AnnotateSpliceAi', - hash_value=cohort.id, ) / f'{cohort.id}.mt', ) @@ -111,10 +110,9 @@ class ExtractVcfFromMt(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> dict[str, Path | str]: temp_prefix = cpg_flow_utils.generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, category='tmp', stage_name=self.name, - hash_value=cohort.id, ) return { 'id_file': str(temp_prefix / f'{cohort.id}-SG-ids.txt'), @@ -145,10 +143,9 @@ class AnnotateUsingEchtvar(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> dict[str, Path | str]: temp_prefix = cpg_flow_utils.generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, category='tmp', stage_name=self.name, - hash_value=cohort.id, ) return { 'success': temp_prefix / 'success.txt', @@ -186,10 +183,9 @@ class AnnotateWithBcftoolsCsq(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> dict[str, Path | str]: temp_prefix = cpg_flow_utils.generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, category='tmp', stage_name=self.name, - hash_value=cohort.id, ) return { 'success': temp_prefix / 'success.txt', @@ -223,10 +219,9 @@ def queue_jobs(self, cohort: targets.Cohort, inputs: stage.StageInput) -> stage. class AnnotatedVcfIntoMt(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> dict[str, Path | str]: temp_prefix = cpg_flow_utils.generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, category='tmp', stage_name=self.name, - hash_value=cohort.id, ) return { 'success': temp_prefix / 'success.txt', @@ -250,10 +245,9 @@ def queue_jobs(self, cohort, inputs: stage.StageInput) -> stage.StageOutput: bcftools_template=bcftools_template, checkpoint=str( cpg_flow_utils.generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, category='tmp', stage_name=self.name, - hash_value=cohort.id, ) / f'{cohort.id}_{{part}}annotation_checkpoint', ), @@ -274,9 +268,8 @@ class AnnotateSpliceAi(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> Path: return ( cpg_flow_utils.generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name=self.name, - hash_value=cohort.id, ) / f'{cohort.id}.mt' ) diff --git a/src/talos/cpg_internal_scripts/build_report_index.py b/src/talos/cpg_internal_scripts/build_report_index.py index 4c33097f..94dd8a73 100644 --- a/src/talos/cpg_internal_scripts/build_report_index.py +++ b/src/talos/cpg_internal_scripts/build_report_index.py @@ -16,6 +16,7 @@ from metamist.graphql import gql, query +COHORT_RE = re.compile(r'COH\d+') DATE_REGEX = re.compile(r'(\d{4}-\d{2}-\d{2})') JINJA_TEMPLATE_DIR = Path(__file__).absolute().parent.parent / 'templates' @@ -56,9 +57,8 @@ class Report: dataset: str address: str is_exome: bool - is_long_read: bool + is_long_read: bool | str date: str - title: str @lru_cache(1) @@ -72,21 +72,22 @@ def get_my_projects() -> set[str]: return all_projects -def get_project_analyses(project: str) -> dict[tuple[bool, bool], str]: +def get_project_analyses(project: str) -> dict[tuple[bool | str, bool], str]: """ Find all the active analysis entries for this project, subdivide the analyses by long/short read and exome/genome - Create a dictionary indexed on a double boolean key: - - the file is long_read + Create a dictionary indexed on a double key: + - the file is long_read (either False, or the Cohort ID) - the file is exome This was chosen as in the CPG infrastructure, 'genome' is the default, so 'exome' is in the path if relevant. - Likewise for short-read vs. "long-read". This is not a perfect solution, but it works for now. + Long-read is a little more tricky, as we maintain separate callsets for each sequencer/library prep, so we can have + several live long-read analyses for a single project Also... you can use a tuple as a dictionary key and that's cool. """ - project_reports: dict[tuple[bool, bool], str] = {} + project_reports: dict[tuple[bool | str, bool], str] = {} all_analyses = query(REPORT_QUERY, variables={'project': project})['project']['analyses'] for analysis in all_analyses: @@ -98,7 +99,15 @@ def get_project_analyses(project: str) -> dict[tuple[bool, bool], str]: output_path = outputs['path'] - long = 'long_read' in output_path + if 'long_read' in output_path: + # bridging statement until all reports are reissued with new paths + try: + long = COHORT_RE.findall(output_path)[0] + except IndexError: + long = True + else: + long = False + exome = 'exome' in output_path project_reports[(long, exome)] = output_path @@ -106,36 +115,34 @@ def get_project_analyses(project: str) -> dict[tuple[bool, bool], str]: return project_reports -def main(dataset: str = 'aip') -> None: +def main(dataset: str) -> None: """ Finds all existing reports, generates an HTML file as an index page. Args: dataset (str): The dataset to generate the index for, defaults to 'aip' for legacy reasons. """ - parsed_reports = {cohort: get_project_analyses(cohort) for cohort in get_my_projects()} + parsed_reports = {each_dataset: get_project_analyses(each_dataset) for each_dataset in get_my_projects()} report_list: list[Report] = [] - for cohort, cohort_results in parsed_reports.items(): - for (long_read, exome), report_path in cohort_results.items(): + for each_dataset, dataset_results in parsed_reports.items(): + for (long_read, exome), report_path in dataset_results.items(): # general - only one of these if report_path: this_file_name = Path(report_path).name trimmed_path = report_path.rstrip(this_file_name).rstrip('/') for entry in list(map(str, to_anypath(trimmed_path).glob('*.html'))): - report_address = entry.replace(WEB_BASE.format(cohort), WEB_URL_BASE.format(cohort)) - report_name = entry.split('/')[-1] + report_address = entry.replace(WEB_BASE.format(each_dataset), WEB_URL_BASE.format(each_dataset)) if report_date := DATE_REGEX.search(report_address): report_list.append( Report( - dataset=cohort, + dataset=each_dataset, address=report_address, is_exome=exome, is_long_read=long_read, date=report_date.group(1), - title=report_name, ), ) @@ -165,8 +172,8 @@ def cli_main(): """ Command line interface for the script. """ - parser = ArgumentParser(description='Generate an index page for AIP reports') - parser.add_argument('--dataset', help='Dataset for the index page', default='aip') + parser = ArgumentParser(description='Generate an index page for Talos reports') + parser.add_argument('--dataset', help='Dataset for the index page') args = parser.parse_args() main(dataset=args.dataset) diff --git a/src/talos/cpg_internal_scripts/cpg_flow_utils.py b/src/talos/cpg_internal_scripts/cpg_flow_utils.py index d9b95dc9..46a09a6e 100644 --- a/src/talos/cpg_internal_scripts/cpg_flow_utils.py +++ b/src/talos/cpg_internal_scripts/cpg_flow_utils.py @@ -2,7 +2,7 @@ import loguru -from cpg_flow import workflow +from cpg_flow import targets, workflow from cpg_utils import Path, config, to_path from metamist import graphql @@ -72,23 +72,24 @@ def check_for_dataset_centric_cohorts() -> bool: @cache def generate_dataset_prefix( - dataset: str, + cohort: targets.Cohort | None = None, + dataset: str | None = None, category: str | None = None, stage_name: str | None = None, hash_value: str | None = None, ) -> Path: """ - Generate a dictionary of prefixes for the current workflow and Stage. + Generate a prefix for the current workflow and Stage. Needed because CPG-Flow currently lacks the granularity we need for both exome/genome and short/long read This is intended to generate the exact same prefix as CPG-Flow would generate, so that we continue previous work """ - # mandatory value in a cpg-flow config - workflow_name = config.config_retrieve(['workflow', 'name'], 'talos') + if cohort is None and dataset is None: + raise RuntimeError('Must populate either cohort or dataset when calling generate_dataset_prefix') - # generated from the included samples in the workflow - # or passed directly if provided, e.g. to target outputs to a date-specific folder, not a callset-specific one - hash_element = hash_value or workflow.get_workflow().output_version + # allow for a missing Cohort (index page case) + cohort_id = cohort.id if cohort else None + dataset = cohort.dataset.name if cohort else dataset # allow subdivision by short/long read, and exome/genome # the current protocol here is to treat short read and genome as standard, and insert clarifying elements if needed @@ -97,7 +98,11 @@ def generate_dataset_prefix( # line up all the elements into an ordered list, and then join the non-None elements suffix = '/'.join( - [x for x in [long_read_element, exome_element, workflow_name, hash_element, stage_name] if isinstance(x, str)], + [ + x + for x in [long_read_element, exome_element, 'talos', cohort_id, hash_value, stage_name] + if isinstance(x, str) + ], ) return to_path(config.dataset_path(suffix=suffix, dataset=dataset, category=category)) diff --git a/src/talos/cpg_internal_scripts/talos_stages.py b/src/talos/cpg_internal_scripts/talos_stages.py index cf3bc50d..c118490f 100644 --- a/src/talos/cpg_internal_scripts/talos_stages.py +++ b/src/talos/cpg_internal_scripts/talos_stages.py @@ -187,7 +187,7 @@ class MakeRuntimeConfig(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> dict[str, Path]: prefix = generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name=self.name, hash_value=get_date_string(), ) @@ -215,7 +215,7 @@ class MakeHpoPedigree(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> Path: return ( generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name=self.name, hash_value=get_date_string(), ) @@ -260,7 +260,7 @@ class UnifiedPanelAppParser(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> Path: return ( generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name=self.name, hash_value=get_date_string(), ) @@ -319,7 +319,7 @@ def expected_outputs(self, cohort: targets.Cohort) -> dict[str, Path] | None: return None prefix = generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name=self.name, hash_value=get_date_string(), ) @@ -404,7 +404,7 @@ class RunHailFiltering(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> Path: return ( generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name=self.name, hash_value=get_date_string(), ) @@ -479,7 +479,7 @@ class RunHailFilteringSv(stage.CohortStage): hail job to filter & label the SV MT """ - def expected_outputs(self, cohort: targets.Cohort) -> Path: + def expected_outputs(self, cohort: targets.Cohort) -> Path | None: if ( query_for_latest_analysis( dataset=cohort.dataset.name, @@ -490,13 +490,13 @@ def expected_outputs(self, cohort: targets.Cohort) -> Path: ): return ( generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name=self.name, hash_value=get_date_string(), ) / 'labelled_svs.vcf.bgz' ) - return {} + return None def queue_jobs(self, cohort: targets.Cohort, inputs: stage.StageInput) -> stage.StageOutput: # early skip if the stage has nothing to run on @@ -573,7 +573,7 @@ class ValidateVariantInheritance(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> Path: return ( generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name=self.name, hash_value=get_date_string(), ) @@ -675,7 +675,7 @@ class HpoFlagging(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> Path: return ( generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name=self.name, hash_value=get_date_string(), ) @@ -732,18 +732,18 @@ def queue_jobs(self, cohort: targets.Cohort, inputs: stage.StageInput) -> stage. class CreateTalosHtml(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> dict[str, Path]: std_prefix = generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, stage_name=self.name, hash_value=get_date_string(), ) web_prefix = generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, category='web', stage_name=self.name, hash_value=get_date_string(), ) static_web_prefix = generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, category='web', stage_name=self.name, hash_value='talos_static', @@ -808,7 +808,7 @@ class MinimiseOutputForSeqr(stage.CohortStage): def expected_outputs(self, cohort: targets.Cohort) -> dict[str, Path]: analysis_prefix = ( generate_dataset_prefix( - dataset=cohort.dataset.name, + cohort=cohort, category='analysis', stage_name=self.name, hash_value=get_date_string(), diff --git a/src/talos/templates/report_index.html.jinja b/src/talos/templates/report_index.html.jinja index a4675544..79287889 100644 --- a/src/talos/templates/report_index.html.jinja +++ b/src/talos/templates/report_index.html.jinja @@ -32,7 +32,6 @@ Dataset Exome/Genome Short/Long Read - Title Date @@ -49,10 +48,11 @@ {{ 'Exome' if report.is_exome else 'Genome' }} - {{ 'Long-Read' if report.is_long_read else 'Short-Read' }} - - - {{report.title}} + {% if report.is_long_read %} + Long-Read ({{report.is_long_read}}) + {% else %} + Short-Read + {% endif %} {{ report.date }} diff --git a/src/talos/version.py b/src/talos/version.py index 46edd612..6b49d906 100644 --- a/src/talos/version.py +++ b/src/talos/version.py @@ -1 +1 @@ -__version__ = '11.0.1' +__version__ = '11.0.2' diff --git a/test/test_cpg_flow_utils.py b/test/test_cpg_flow_utils.py new file mode 100644 index 00000000..682d6ad4 --- /dev/null +++ b/test/test_cpg_flow_utils.py @@ -0,0 +1,148 @@ +""" +Unit tests for generate_dataset_prefix in cpg_flow_utils. + +The function depends on the CPG config singleton (config_retrieve / dataset_path) +and on cpg_flow Cohort objects, so all external touch-points are mocked. The goal +is full line coverage of generate_dataset_prefix. +""" + +from unittest.mock import patch + +import pytest + +from talos.cpg_internal_scripts import cpg_flow_utils +from talos.cpg_internal_scripts.cpg_flow_utils import generate_dataset_prefix + + +class FakeDataset: + """minimal stand-in for a cpg_flow Dataset (only .name is read)""" + + def __init__(self, name: str): + self.name = name + + +class FakeCohort: + """ + minimal stand-in for a cpg_flow Cohort exposing .id and .dataset.name. + Must be hashable (default object identity hash) because generate_dataset_prefix + is @cache decorated and receives the cohort as a key. + """ + + def __init__(self, cohort_id: str, dataset_name: str): + self.id = cohort_id + self.dataset = FakeDataset(dataset_name) + + +def make_cohort(cohort_id: str = 'COH123', dataset_name: str = 'cohort-dataset'): + """build a stand-in for a cpg_flow Cohort with the attributes the function reads""" + return FakeCohort(cohort_id, dataset_name) + + +def config_side_effect(seq_type: str = 'genome', long_read: bool = False): + """ + return a side_effect callable mimicking config.config_retrieve for the two + keys the function queries + """ + + def _retrieve(keys, default=None): + if keys == ['workflow', 'sequencing_type']: + return seq_type + if keys == ['workflow', 'long_read']: + return long_read + raise AssertionError(f'unexpected config key requested: {keys}') + + return _retrieve + + +@pytest.fixture(autouse=True) +def _clear_cache(): + """generate_dataset_prefix is @cache decorated - reset between tests""" + generate_dataset_prefix.cache_clear() + yield + generate_dataset_prefix.cache_clear() + + +def test_raises_when_no_cohort_or_dataset(): + """both cohort and dataset None -> RuntimeError""" + with pytest.raises(RuntimeError, match='Must populate either cohort or dataset'): + generate_dataset_prefix() + + +def test_cohort_path_standard(): + """ + cohort supplied (short-read, genome): cohort_id and dataset are pulled from the + Cohort, no exome/long_read elements, just 'talos/' + """ + cohort = make_cohort(cohort_id='COHORTX', dataset_name='ds-from-cohort') + + with ( + patch.object(cpg_flow_utils.config, 'config_retrieve', side_effect=config_side_effect()), + patch.object(cpg_flow_utils.config, 'dataset_path', return_value='gs://bucket/talos/COHORTX') as ds_path, + patch.object(cpg_flow_utils, 'to_path', side_effect=lambda x: f'PATH::{x}') as to_path, + ): + result = generate_dataset_prefix(cohort=cohort) + + ds_path.assert_called_once_with(suffix='talos/COHORTX', dataset='ds-from-cohort', category=None) + to_path.assert_called_once_with('gs://bucket/talos/COHORTX') + assert result == 'PATH::gs://bucket/talos/COHORTX' + + +def test_dataset_only_no_cohort_id(): + """ + dataset supplied without a cohort (index-page case): cohort_id is None and is + therefore dropped from the suffix + """ + with ( + patch.object(cpg_flow_utils.config, 'config_retrieve', side_effect=config_side_effect()), + patch.object(cpg_flow_utils.config, 'dataset_path', return_value='gs://b/talos') as ds_path, + patch.object(cpg_flow_utils, 'to_path', side_effect=lambda x: x), + ): + result = generate_dataset_prefix(dataset='plain-dataset', category='analysis') + + ds_path.assert_called_once_with(suffix='talos', dataset='plain-dataset', category='analysis') + assert result == 'gs://b/talos' + + +def test_exome_and_long_read_and_hash_and_stage(): + """ + exercise the exome and long_read branches plus hash_value and stage_name so every + element of the ordered suffix list is a non-None string + """ + cohort = make_cohort(cohort_id='CID', dataset_name='ds') + + with ( + patch.object(cpg_flow_utils.config, 'config_retrieve', side_effect=config_side_effect('exome', True)), + patch.object(cpg_flow_utils.config, 'dataset_path', return_value='out') as ds_path, + patch.object(cpg_flow_utils, 'to_path', side_effect=lambda x: x), + ): + generate_dataset_prefix(cohort=cohort, hash_value='abc123', stage_name='MyStage') + + ds_path.assert_called_once_with( + suffix='long_read/exome/talos/CID/abc123/MyStage', + dataset='ds', + category=None, + ) + + +def test_long_read_only(): + """long_read set but genome sequencing -> long_read element present, no exome""" + with ( + patch.object(cpg_flow_utils.config, 'config_retrieve', side_effect=config_side_effect('genome', True)), + patch.object(cpg_flow_utils.config, 'dataset_path', return_value='out') as ds_path, + patch.object(cpg_flow_utils, 'to_path', side_effect=lambda x: x), + ): + generate_dataset_prefix(dataset='ds', stage_name='S') + + ds_path.assert_called_once_with(suffix='long_read/talos/S', dataset='ds', category=None) + + +def test_exome_only(): + """exome sequencing but short read -> exome element present, no long_read""" + with ( + patch.object(cpg_flow_utils.config, 'config_retrieve', side_effect=config_side_effect('exome', False)), + patch.object(cpg_flow_utils.config, 'dataset_path', return_value='out') as ds_path, + patch.object(cpg_flow_utils, 'to_path', side_effect=lambda x: x), + ): + generate_dataset_prefix(dataset='ds', hash_value='HSH') + + ds_path.assert_called_once_with(suffix='exome/talos/HSH', dataset='ds', category=None)