From 71e1e3971eee76ec7f90190cf6f66036f9743596 Mon Sep 17 00:00:00 2001 From: Annick Renevey <47788523+rannick@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:58:15 +0200 Subject: [PATCH] use mip-rna rna upload for tomte --- cg/apps/scout/scoutapi.py | 48 +++++++++ cg/cli/upload/base.py | 2 + cg/cli/upload/scout.py | 26 ++++- cg/constants/housekeeper_tags.py | 7 ++ cg/meta/upload/mip/mip_rna.py | 4 +- cg/meta/upload/nf_analysis.py | 6 +- cg/meta/upload/scout/uploadscoutapi.py | 130 +++++++++++++++++++++++-- cg/meta/upload/tomte/tomte.py | 0 8 files changed, 210 insertions(+), 13 deletions(-) create mode 100644 cg/meta/upload/tomte/tomte.py diff --git a/cg/apps/scout/scoutapi.py b/cg/apps/scout/scoutapi.py index 5b362ecc16..32a80be7b4 100644 --- a/cg/apps/scout/scoutapi.py +++ b/cg/apps/scout/scoutapi.py @@ -278,6 +278,54 @@ def upload_rna_coverage_bigwig( "Something went wrong when uploading rna coverage bigwig file" ) from error + + def upload_rna_fraser( + self, file_path: str, case_id: str, customer_sample_id: str + ) -> None: + """Load a rna fraser file into a case in the database.""" + + upload_command: list[str] = [ + "update", + "individual", + "--case-id", + case_id, + "--ind", + customer_sample_id, + "--fraser", + file_path, + ] + try: + LOG.info(f"Uploading rna fraser file {file_path} to case {case_id}") + self.process.run_command(upload_command) + except CalledProcessError as error: + raise ScoutUploadError( + "Something went wrong when uploading rna fraser file" + ) from error + + def upload_rna_outrider( + self, file_path: str, case_id: str, customer_sample_id: str + ) -> None: + """Load a rna outrider file into a case in the database.""" + + upload_command: list[str] = [ + "update", + "individual", + "--case-id", + case_id, + "--ind", + customer_sample_id, + "--outrider", + file_path, + ] + try: + LOG.info(f"Uploading rna outrider file {file_path} to case {case_id}") + self.process.run_command(upload_command) + except CalledProcessError as error: + raise ScoutUploadError( + "Something went wrong when uploading rna outrider file" + ) from error + + def upload_rna_alignment_file( self, case_id: str, customer_sample_id: str, file_path: str ) -> None: diff --git a/cg/cli/upload/base.py b/cg/cli/upload/base.py index eb1e156dbe..c35d1d4570 100644 --- a/cg/cli/upload/base.py +++ b/cg/cli/upload/base.py @@ -23,6 +23,7 @@ create_scout_load_config, upload_case_to_scout, upload_multiqc_to_scout, + upload_omics_to_scout, upload_rna_alignment_file_to_scout, upload_rna_fusion_report_to_scout, upload_rna_junctions_to_scout, @@ -144,6 +145,7 @@ def upload_all_completed_analyses(context: click.Context, workflow: Workflow = N upload.add_command(upload_coverage) upload.add_command(upload_delivery_report_to_scout) upload.add_command(upload_genotypes) +upload.add_command(upload_omics_to_scout) upload.add_command(upload_multiqc_to_scout) upload.add_command(upload_observations_to_loqusdb) upload.add_command(upload_rna_alignment_file_to_scout) diff --git a/cg/cli/upload/scout.py b/cg/cli/upload/scout.py index 341b0d5bc5..31fedfca9b 100644 --- a/cg/cli/upload/scout.py +++ b/cg/cli/upload/scout.py @@ -21,6 +21,7 @@ from cg.meta.workflow.mip_dna import MipDNAAnalysisAPI from cg.meta.workflow.mip_rna import MipRNAAnalysisAPI from cg.meta.workflow.rnafusion import RnafusionAnalysisAPI +from cg.meta.workflow.tomte import TomteAnalysisAPI from cg.models.cg_config import CGConfig from cg.models.scout.scout_load_config import ScoutLoadConfig from cg.store.models import Case @@ -173,16 +174,18 @@ def validate_case_samples_are_rna(context: CGConfig, case_id: str) -> None: @click.option("-r", "--research", is_flag=True, help="Upload research report instead of clinical") @click.argument("case_id") @click.pass_context -def upload_rna_to_scout(context, case_id: str, dry_run: bool, research: bool) -> None: +def upload_rna_to_scout(context, case_id: str, dry_run: bool, research: bool, analysis: str) -> None: """Upload an RNA case's gene fusion report and junction splice files for all samples connect via subject ID.""" LOG.info("----------------- UPLOAD RNA TO SCOUT -----------------------") context.invoke(validate_case_samples_are_rna, case_id=case_id) context.invoke(upload_rna_alignment_file_to_scout, case_id=case_id, dry_run=dry_run) context.invoke(upload_multiqc_to_scout, case_id=case_id, dry_run=dry_run) - context.invoke( - upload_rna_fusion_report_to_scout, case_id=case_id, dry_run=dry_run, research=research - ) + if analysis != Workflow.TOMTE: + context.invoke( + upload_rna_fusion_report_to_scout, case_id=case_id, dry_run=dry_run, research=research + ) + context.invoke(upload_omics_to_scout, case_id=case_id, dry_run=dry_run) context.invoke(upload_rna_junctions_to_scout, case_id=case_id, dry_run=dry_run) @@ -228,6 +231,20 @@ def upload_rna_junctions_to_scout(context: CGConfig, case_id: str, dry_run: bool scout_upload_api.upload_rna_junctions_to_scout(dry_run=dry_run, case_id=case_id) +@click.command(name="rna-omics-to-scout") +@DRY_RUN +@click.argument("case_id") +@click.pass_obj +def upload_omics_to_scout(context: CGConfig, case_id: str, dry_run: bool) -> None: + """Upload RNA omics files to Scout.""" + LOG.info("----------------- UPLOAD RNA OMICS TO SCOUT -----------------------") + + scout_upload_api: UploadScoutAPI = context.meta_apis["upload_api"].scout_upload_api + + scout_upload_api.upload_omics_to_scout(dry_run=dry_run, case_id=case_id) + + + @click.command(name="multiqc-to-scout") @DRY_RUN @click.argument("case_id") @@ -267,6 +284,7 @@ def get_upload_api(case: Case, cg_config: CGConfig) -> UploadAPI: Workflow.MIP_RNA: MipRNAAnalysisAPI, Workflow.MIP_DNA: MipDNAAnalysisAPI, Workflow.RNAFUSION: RnafusionAnalysisAPI, + Workflow.TOMTE: TomteAnalysisAPI, } return UploadAPI( diff --git a/cg/constants/housekeeper_tags.py b/cg/constants/housekeeper_tags.py index 934603f4e6..bc4fe69dd4 100644 --- a/cg/constants/housekeeper_tags.py +++ b/cg/constants/housekeeper_tags.py @@ -64,6 +64,11 @@ class AnalysisTag(StrEnum): ARRIBA: str = "arriba" ARRIBA_VISUALIZATION: str = "arriba-visualisation" + BED: str = "bed" + BIGWIG: str = "bigwig" + CLINICAL: str = "clinical" + COVERAGE: str = "coverage" + FRASER: str = "fraser" FUSION: str = "fusion" FUSIONCATCHER: str = "fusioncatcher" FUSIONCATCHER_SUMMARY: str = "fusioncatcher-summary" @@ -71,7 +76,9 @@ class AnalysisTag(StrEnum): FUSIONINSPECTOR_HTML: str = "fusioninspector-html" FUSIONREPORT: str = "fusionreport" GENE_COUNTS: str = "gene-counts" + JUNCTION: str = "junction" MULTIQC_HTML: str = "multiqc-html" + OUTRIDER: str = "outrider" RESEARCH: str = "research" RNA: str = "rna" STARFUSION: str = "star-fusion" diff --git a/cg/meta/upload/mip/mip_rna.py b/cg/meta/upload/mip/mip_rna.py index 2380b7eb8e..840af194d6 100644 --- a/cg/meta/upload/mip/mip_rna.py +++ b/cg/meta/upload/mip/mip_rna.py @@ -7,7 +7,7 @@ from cg.cli.upload.clinical_delivery import upload_clinical_delivery from cg.cli.upload.scout import upload_rna_to_scout -from cg.constants import DataDelivery +from cg.constants import DataDelivery, Workflow from cg.meta.upload.upload_api import UploadAPI from cg.meta.workflow.mip_rna import MipRNAAnalysisAPI from cg.models.cg_config import CGConfig @@ -34,7 +34,7 @@ def upload(self, ctx: click.Context, case: Case, restart: bool) -> None: # Scout specific upload if DataDelivery.SCOUT in case.data_delivery: try: - ctx.invoke(upload_rna_to_scout, case_id=case.internal_id) + ctx.invoke(upload_rna_to_scout, case_id=case.internal_id, analysis=Workflow.MIP_RNA) except CalledProcessError as error: LOG.error(error) return diff --git a/cg/meta/upload/nf_analysis.py b/cg/meta/upload/nf_analysis.py index e42d3963e9..8f17ffcd89 100644 --- a/cg/meta/upload/nf_analysis.py +++ b/cg/meta/upload/nf_analysis.py @@ -7,6 +7,7 @@ from cg.cli.generate.report.base import generate_delivery_report from cg.cli.upload.clinical_delivery import upload_clinical_delivery +from cg.cli.upload.scout import upload_rna_to_scout from cg.cli.upload.scout import upload_to_scout from cg.constants import ( REPORT_SUPPORTED_DATA_DELIVERY, @@ -46,7 +47,10 @@ def upload(self, ctx: click.Context, case: Case, restart: bool) -> None: # Scout specific upload if DataDelivery.SCOUT in case.data_delivery: - ctx.invoke(upload_to_scout, case_id=case.internal_id, re_upload=restart) + if case.data_analysis == Workflow.TOMTE: + ctx.invoke(upload_rna_to_scout, case_id=case.internal_id, re_upload=restart, analysis=case.data_analysis) + else: + ctx.invoke(upload_to_scout, case_id=case.internal_id, re_upload=restart) LOG.info( f"Upload of case {case.internal_id} was successful. Setting uploaded at to {dt.datetime.now()}" ) diff --git a/cg/meta/upload/scout/uploadscoutapi.py b/cg/meta/upload/scout/uploadscoutapi.py index 57e6df049f..dcd3508ef8 100644 --- a/cg/meta/upload/scout/uploadscoutapi.py +++ b/cg/meta/upload/scout/uploadscoutapi.py @@ -12,7 +12,7 @@ from cg.apps.scout.scoutapi import ScoutAPI from cg.constants import HK_MULTIQC_HTML_TAG, Workflow from cg.constants.constants import FileFormat, PrepCategory -from cg.constants.housekeeper_tags import AlignmentFileTag +from cg.constants.housekeeper_tags import AlignmentFileTag, AnalysisTag from cg.constants.scout import ScoutCustomCaseReportTags from cg.exc import CgDataError, HousekeeperBundleVersionMissingError from cg.io.controller import WriteFile @@ -132,18 +132,18 @@ def get_multiqc_html_report( def get_fusion_report(self, case_id: str, research: bool) -> File | None: """Return a fusion report for a case in housekeeper.""" - tags = {"fusion"} + tags = {AnalysisTag.FUSION} if research: - tags.add("research") + tags.add(AnalysisTag.RESEARCH) else: - tags.add("clinical") + tags.add(AnalysisTag.CLINICAL) return self.housekeeper.get_file_from_latest_version(bundle_name=case_id, tags=tags) def get_splice_junctions_bed(self, case_id: str, sample_id: str) -> File | None: """Return a splice junctions bed file for a case in Housekeeper.""" - tags: set[str] = {"junction", "bed", sample_id} + tags: set[str] = {AnalysisTag.JUNCTION, AnalysisTag.BED, sample_id} splice_junctions_bed: File | None = None try: splice_junctions_bed = self.housekeeper.get_file_from_latest_version( @@ -156,11 +156,22 @@ def get_splice_junctions_bed(self, case_id: str, sample_id: str) -> File | None: def get_rna_coverage_bigwig(self, case_id: str, sample_id: str) -> File | None: """Return an RNA coverage bigwig file for a case in Housekeeper.""" + tags: set[str] = {AnalysisTag.COVERAGE, AnalysisTag.BIGWIG, sample_id} + return self.housekeeper.get_file_from_latest_version(bundle_name=case_id, tags=tags) + + + def get_rna_omics_fraser(self, case_id: str, sample_id: str) -> File | None: + """Return an fraser file for a case in Housekeeper.""" + tags: set[str] = {AnalysisTag.COVERAGE, AnalysisTag.FRASER, sample_id} + return self.housekeeper.get_file_from_latest_version(bundle_name=case_id, tags=tags) - tags: set[str] = {"coverage", "bigwig", sample_id} + def get_rna_omics_outrider(self, case_id: str, sample_id: str) -> File | None: + """Return an outrider file for a case in Housekeeper.""" + tags: set[str] = {AnalysisTag.COVERAGE, AnalysisTag.OUTRIDER, sample_id} return self.housekeeper.get_file_from_latest_version(bundle_name=case_id, tags=tags) + def get_unique_dna_cases_related_to_rna_case(self, case_id: str) -> set[str]: """Return a set of unique DNA cases related to an RNA case.""" case: Case = self.status_db.get_case_by_internal_id(case_id) @@ -326,6 +337,82 @@ def upload_rna_coverage_bigwig_to_scout(self, case_id: str, dry_run: bool) -> No LOG.info(upload_statement) LOG.info("Upload RNA coverage bigwig file finished!") + + def upload_rna_fraser_to_scout(self, dry_run: bool, case_id: str) -> None: + """Upload omics fraser file for a case to Scout.""" + status_db: Store = self.status_db + rna_case = status_db.get_case_by_internal_id(case_id) + rna_dna_collections: list[RNADNACollection] = self.create_rna_dna_collections(rna_case) + for rna_dna_collection in rna_dna_collections: + rna_sample_internal_id: str = rna_dna_collection.rna_sample_internal_id + dna_sample_name: str = rna_dna_collection.dna_sample_name + rna_fraser: File | None = self.get_rna_omics_fraser( + case_id=case_id, sample_id=rna_sample_internal_id + ) + + if not rna_fraser: + raise FileNotFoundError( + f"No RNA fraser file was found in housekeeper for {rna_sample_internal_id}." + ) + + LOG.debug(f"RNA fraser file {rna_fraser.path} found.") + for dna_case_id in rna_dna_collection.dna_case_ids: + LOG.info( + f"Uploading RNA fraser file for sample {dna_sample_name} " + f"in case {dna_case_id} in Scout." + ) + + if dry_run: + continue + + self.scout_api.upload_rna_fraser( + file_path=rna_fraser.full_path, + case_id=dna_case_id, + customer_sample_id=dna_sample_name, + ) + + for upload_statement in self.get_rna_fraser_upload_summary(rna_dna_collections): + LOG.info(upload_statement) + LOG.info("Upload RNA fraser file finished!") + + + def upload_rna_outrider_to_scout(self, dry_run: bool, case_id: str) -> None: + """Upload omics outrider file for a case to Scout.""" + status_db: Store = self.status_db + rna_case = status_db.get_case_by_internal_id(case_id) + rna_dna_collections: list[RNADNACollection] = self.create_rna_dna_collections(rna_case) + for rna_dna_collection in rna_dna_collections: + rna_sample_internal_id: str = rna_dna_collection.rna_sample_internal_id + dna_sample_name: str = rna_dna_collection.dna_sample_name + rna_outrider: File | None = self.get_rna_omics_outrider( + case_id=case_id, sample_id=rna_sample_internal_id + ) + if not rna_outrider: + raise FileNotFoundError( + f"No RNA outrider file was found in housekeeper for {rna_sample_internal_id}." + ) + + LOG.debug(f"RNA outrider file {rna_outrider.path} found.") + for dna_case_id in rna_dna_collection.dna_case_ids: + LOG.info( + f"Uploading RNA outrider file for sample {dna_sample_name} " + f"in case {dna_case_id} in Scout." + ) + + if dry_run: + continue + + self.scout_api.upload_rna_outrider( + file_path=rna_outrider.full_path, + case_id=dna_case_id, + customer_sample_id=dna_sample_name, + ) + for upload_statement in self.get_rna_outrider_upload_summary(rna_dna_collections): + LOG.info(upload_statement) + LOG.info("Upload RNA outrider file finished!") + + + def upload_splice_junctions_bed_to_scout(self, dry_run: bool, case_id: str) -> None: """Upload splice_junctions_bed file for a case to Scout.""" @@ -400,11 +487,42 @@ def get_rna_bigwig_coverage_upload_summary( ) return upload_summary + @staticmethod + def get_rna_fraser_upload_summary( + rna_dna_collections: list[RNADNACollection], + ) -> list[str]: + upload_summary: list[str] = [] + for rna_dna_collection in rna_dna_collections: + upload_summary.extend( + f"Uploaded fraser coverage file for sample {rna_dna_collection.dna_sample_name} in case {dna_case}." + for dna_case in rna_dna_collection.dna_case_ids + ) + return upload_summary + + @staticmethod + def get_rna_outrider_upload_summary( + rna_dna_collections: list[RNADNACollection], + ) -> list[str]: + upload_summary: list[str] = [] + for rna_dna_collection in rna_dna_collections: + upload_summary.extend( + f"Uploaded outrider coverage file for sample {rna_dna_collection.dna_sample_name} in case {dna_case}." + for dna_case in rna_dna_collection.dna_case_ids + ) + return upload_summary + def upload_rna_junctions_to_scout(self, dry_run: bool, case_id: str) -> None: """Upload RNA junctions splice files to Scout.""" self.upload_splice_junctions_bed_to_scout(dry_run=dry_run, case_id=case_id) self.upload_rna_coverage_bigwig_to_scout(case_id=case_id, dry_run=dry_run) + + def upload_omics_to_scout(self, dry_run: bool, case_id: str) -> None: + """Upload RNA omics files to Scout.""" + self.upload_rna_fraser_to_scout(dry_run=dry_run, case_id=case_id) + self.upload_rna_outrider_to_scout(case_id=case_id, dry_run=dry_run) + + def get_config_builder(self, analysis, hk_version) -> ScoutConfigBuilder: config_builders = { Workflow.BALSAMIC: BalsamicConfigBuilder( diff --git a/cg/meta/upload/tomte/tomte.py b/cg/meta/upload/tomte/tomte.py new file mode 100644 index 0000000000..e69de29bb2