diff --git a/VERSIONLOG.md b/VERSIONLOG.md index db6c83b8..bbfd07d1 100644 --- a/VERSIONLOG.md +++ b/VERSIONLOG.md @@ -1,9 +1,21 @@ # TACA Version Log -## 20241211.1 +## 20241216.1 Email user instead of craching if unable to create Illumina runobject +## 20241212.2 + +Fix for ONT demultiplexing status in bioinfo deliveries script + +## 20241212.1 + +Add ONT instrument data to bioinfo_tab + +## 20241211.1 + +No longer reserve PromethION column 3 for Clinical Genomics. + ## 20241210.4 Change server_status nas to update statusdb by default diff --git a/taca/nanopore/instrument_transfer.py b/taca/nanopore/instrument_transfer.py index 2937119a..2330089c 100644 --- a/taca/nanopore/instrument_transfer.py +++ b/taca/nanopore/instrument_transfer.py @@ -12,10 +12,9 @@ from glob import glob RUN_PATTERN = re.compile( - # Run folder name expected as yyyymmdd_HHMM_1A-3H/MN19414_flowCellId_randomHash + # Run folder name expected as yyyymmdd_HHMM_positionOrInstrument_flowCellId_randomHash # Flow cell names starting with "CTC" are configuration test cells and should not be included - # As of december 2023, the third column (3A-3H) is excluded, because it will be used by Clinical Genomics - r"^\d{8}_\d{4}_(([1-2][A-H])|(MN19414))_(?!CTC)[A-Za-z0-9]+_[A-Za-z0-9]+$" + r"^\d{8}_\d{4}_(([1-3][A-H])|(MN19414))_(?!CTC)[A-Za-z0-9]+_[A-Za-z0-9]+$" ) diff --git a/taca/utils/bioinfo_tab.py b/taca/utils/bioinfo_tab.py index 99b2b32f..14e68681 100644 --- a/taca/utils/bioinfo_tab.py +++ b/taca/utils/bioinfo_tab.py @@ -8,6 +8,7 @@ from flowcell_parser.classes import RunParametersParser, SampleSheetParser from taca.element.Aviti_Runs import Aviti_Run +from taca.nanopore.ONT_run_classes import ONT_RUN_PATTERN, ONT_run from taca.utils import statusdb from taca.utils.config import CONFIG from taca.utils.misc import send_mail @@ -47,6 +48,14 @@ def collect_runs(): continue logger.info(f"Working on {run_dir}") update_statusdb(run_dir, inst_brand) + elif inst_brand == "ont": + # Skip archived, no_backup, nosync and qc folders + if re.match( + ONT_RUN_PATTERN, + os.path.basename(os.path.abspath(run_dir)), + ): + logger.info(f"Working on {run_dir}") + update_statusdb(run_dir, inst_brand) nosync_data_dir = os.path.join(data_dir, "nosync") potential_nosync_run_dirs = glob.glob( @@ -59,7 +68,7 @@ def collect_runs(): and illumina_rundir_re.match( os.path.basename(os.path.abspath(run_dir)) ) - ) or inst_brand == "element": + ) or (inst_brand == "element" or inst_brand == "ont"): # Skip archived dirs if run_dir == os.path.join(nosync_data_dir, "archived"): continue @@ -79,6 +88,15 @@ def update_statusdb(run_dir, inst_brand): # Logger in Aviti_Run.parse_run_parameters() will print the warning # WARNING - Run parameters file not found for ElementRun(), might not be ready yet return + elif inst_brand == "ont": + run_dir = os.path.abspath(run_dir) + try: + ont_run = ONT_run(run_dir) + except AssertionError as e: + logger.error(f"ONT Run folder error: {e}") + return + + run_id = ont_run.run_name statusdb_conf = CONFIG.get("statusdb") couch_connection = statusdb.StatusdbSession(statusdb_conf).connection @@ -91,6 +109,8 @@ def update_statusdb(run_dir, inst_brand): project_info = get_ss_projects_illumina(run_dir) elif inst_brand == "element": project_info = get_ss_projects_element(aviti_run) + elif inst_brand == "ont": + project_info = get_ss_projects_ont(ont_run, couch_connection) # Construction and sending of individual records, if samplesheet is incorrectly formatted the loop is skipped if project_info: for flowcell in project_info: @@ -103,6 +123,8 @@ def update_statusdb(run_dir, inst_brand): sample_status = get_status(run_dir) elif inst_brand == "element": sample_status = get_status_element(aviti_run) + elif inst_brand == "ont": + sample_status = get_status_ont(ont_run) project_info[flowcell][lane][sample].value = sample_status obj = { "run_id": run_id, @@ -234,6 +256,46 @@ def get_status_element(aviti_run): return status +def get_status_ont(ont_run): + """Gets status of a ONT sample run, based on flowcell info.""" + # Default state, should never occur + status = "ERROR" + run_status = ont_run.db.check_run_status(ont_run) + + if run_status in ["finished"]: + status = "New" + elif run_status in ["ongoing"]: + status = "Sequencing" + + return status + + +def get_ss_projects_ont(ont_run, couch_connection): + """Fetches project, FC, lane & sample (sample-run) status for a given folder for ONT runs""" + proj_tree = Tree() + flowcell_id = ont_run.run_name + flowcell_info = ( + couch_connection["nanopore_runs"].view("info/lims")[flowcell_id].rows[0] + ) + if ( + flowcell_info.value + and flowcell_info.value.get("loading", []) + and "sample_data" in flowcell_info.value["loading"][-1] + ): + samples = flowcell_info.value["loading"][-1]["sample_data"] + for sample_dict in samples: + sample_id = sample_dict["sample_name"] + project = sample_id.split("_")[0] + # Use default lane of 0 for ONT + proj_tree[flowcell_id]["0"][sample_id][project] + + if list(proj_tree.keys()) == []: + logger.info( + f"There was no data in StatusDB for the ONT run, CHECK {flowcell_id}" + ) + return proj_tree + + def get_ss_projects_element(aviti_run): """Fetches project, FC, lane & sample (sample-run) status for a given folder for element runs""" proj_tree = Tree() diff --git a/taca/utils/cli.py b/taca/utils/cli.py index e7f46069..7b3397e5 100644 --- a/taca/utils/cli.py +++ b/taca/utils/cli.py @@ -14,9 +14,15 @@ def bioinfo_deliveries(): # bioinfo subcommands @bioinfo_deliveries.command() @click.argument("rundir") -def updaterun(rundir): +@click.option( + "-i", + "--inst_type", + type=click.Choice(["illumina", "element", "ont"]), + required=True, +) +def updaterun(rundir, inst_type): """Saves the bioinfo data to statusdb.""" - bt.update_statusdb(rundir) + bt.update_statusdb(rundir, inst_brand=inst_type) @bioinfo_deliveries.command() diff --git a/tests/nanopore/test_instrument_transfer.py b/tests/nanopore/test_instrument_transfer.py index 21b1a8cc..ccede9fb 100644 --- a/tests/nanopore/test_instrument_transfer.py +++ b/tests/nanopore/test_instrument_transfer.py @@ -135,28 +135,6 @@ def test_main_ignore_CTC(setup_test_fixture): mock_dump_path.assert_not_called() -def test_main_ignore_col3(setup_test_fixture): - """Check so that runs on column 3 (set aside for Clinical Genomics as of december 2023) - are not picked up. - """ - - # Run fixture - args, tmp, file_paths = setup_test_fixture - - # Setup run - run_path = ( - f"{args.source_dir}/experiment/sample/{DUMMY_RUN_NAME.replace('MN19414', '3A')}" - ) - os.makedirs(run_path) - - with patch("taca.nanopore.instrument_transfer.dump_path") as mock_dump_path: - # Start testing - instrument_transfer.main(args) - - # Check dump_path was not called - mock_dump_path.assert_not_called() - - @pytest.mark.parametrize( "finished, qc", [(True, True), (True, False), (False, True), (False, False)] )