From c491b33dc76261effb06575c9b4bf585dce52165 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Jan 2026 04:56:14 +0000 Subject: [PATCH 1/2] Add Snakemake workflow for QA pipelines Create Snakemake pipelines for orchestrating QA analysis: - Motion plots: generates traces, FD plots, and violin plots from fMRIprep - tSNR plots: computes tSNR volumes then generates mosaics and plots - ISC plots: computes ISC then generates violin and surface plots Each pipeline properly handles dependencies (tSNR/ISC computation before plotting). Added snakemake>=8.0.0 as optional 'workflow' dependency. --- pyproject.toml | 3 + workflow/README.md | 119 ++++++++++++++++ workflow/Snakefile | 272 ++++++++++++++++++++++++++++++++++++ workflow/config/config.yaml | 17 +++ 4 files changed, 411 insertions(+) create mode 100644 workflow/README.md create mode 100644 workflow/Snakefile create mode 100644 workflow/config/config.yaml diff --git a/pyproject.toml b/pyproject.toml index aaead77..208ea59 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,9 @@ dev = [ "pytest>=6.0", "ruff>=0.1.0", ] +workflow = [ + "snakemake>=8.0.0", +] [project.urls] Homepage = "https://github.com/mvdoc/hyperface-data-paper" diff --git a/workflow/README.md b/workflow/README.md new file mode 100644 index 0000000..3708c80 --- /dev/null +++ b/workflow/README.md @@ -0,0 +1,119 @@ +# QA Pipeline Workflow + +This Snakemake workflow orchestrates the QA analysis pipelines for motion, tSNR, and ISC plots. + +## Installation + +Install snakemake as an optional dependency: + +```bash +uv sync --extra workflow +``` + +Or install snakemake directly: + +```bash +uv pip install snakemake>=8.0.0 +``` + +## Usage + +All commands should be run from the project root directory. + +### Run all QA pipelines + +```bash +snakemake --snakefile workflow/Snakefile --cores 1 +``` + +### Run specific pipelines + +```bash +# Motion plots only +snakemake --snakefile workflow/Snakefile motion_plots --cores 1 + +# tSNR plots (includes tSNR computation) +snakemake --snakefile workflow/Snakefile tsnr_plots --cores 1 + +# ISC plots (includes ISC computation) +snakemake --snakefile workflow/Snakefile isc_plots --cores 1 +``` + +### Dry run (preview what would be executed) + +```bash +snakemake --snakefile workflow/Snakefile -n +``` + +### Process specific subjects + +```bash +snakemake --snakefile workflow/Snakefile motion_plots --cores 1 --config subjects="sub-001 sub-002" +``` + +### Force regeneration + +```bash +snakemake --snakefile workflow/Snakefile tsnr_plots --cores 1 --forcerun +``` + +### Generate HTML reports + +```bash +# Motion HTML reports +snakemake --snakefile workflow/Snakefile motion_report --cores 1 + +# tSNR HTML reports +snakemake --snakefile workflow/Snakefile tsnr_report --cores 1 +``` + +### Summary statistics + +```bash +snakemake --snakefile workflow/Snakefile tsnr_summary motion_summary --cores 1 +``` + +## Pipeline Dependencies + +``` +fMRIprep outputs +├── motion_plots ─────────────────────→ motion figures +│ └── motion_report ────────────────→ HTML reports +│ └── motion_summary ───────────────→ summary stats +│ +├── compute_tsnr ─────→ tsnr volumes +│ └── tsnr_plots ───────────────────→ tsnr figures +│ └── tsnr_report ──────────────→ HTML reports +│ └── tsnr_summary ─────────────────→ summary stats +│ +└── compute_isc ──────→ ISC data + └── isc_plots ────────────────────→ ISC figures +``` + +## Configuration + +Edit `workflow/config/config.yaml` to: +- Enable/disable specific pipelines +- Filter subjects to process +- Force regeneration of outputs + +The actual data paths are read from `src/hyperface/assets/qa_config.yaml`. + +## Outputs + +All outputs are saved under `data/derivatives/qa/`: +- `motion/` - Motion traces, FD traces, violin plots +- `tsnr/` - tSNR mosaics, violin plots, surface plots +- `isc/` - ISC violin plots, surface plots + +## Cleanup + +```bash +# Remove specific outputs +snakemake --snakefile workflow/Snakefile clean_motion +snakemake --snakefile workflow/Snakefile clean_tsnr +snakemake --snakefile workflow/Snakefile clean_isc + +# Remove all QA outputs +snakemake --snakefile workflow/Snakefile clean_all +``` diff --git a/workflow/Snakefile b/workflow/Snakefile new file mode 100644 index 0000000..fbaa185 --- /dev/null +++ b/workflow/Snakefile @@ -0,0 +1,272 @@ +"""Snakemake workflow for QA pipeline plots. + +This workflow runs the QA pipeline scripts to generate: +- Motion plots (directly from fMRIprep confounds) +- tSNR plots (requires tSNR computation first) +- ISC plots (requires ISC computation first) + +Usage: + # Run all QA pipelines + snakemake --cores 1 + + # Run specific pipeline + snakemake motion_plots --cores 1 + snakemake tsnr_plots --cores 1 + snakemake isc_plots --cores 1 + + # Dry run to see what would be executed + snakemake -n + + # Process specific subjects (motion/tsnr only) + snakemake motion_plots --cores 1 --config subjects="sub-001 sub-002" + + # Force rerun of specific target + snakemake tsnr_plots --cores 1 --forcerun +""" + +import os +from pathlib import Path + +# Configuration +configfile: "workflow/config/config.yaml" + +# Get project root (parent of workflow directory) +PROJECT_ROOT = Path(workflow.basedir).parent + +# Python interpreter from virtual environment +PYTHON = PROJECT_ROOT / ".venv" / "bin" / "python" + +# Scripts directory +SCRIPTS_DIR = PROJECT_ROOT / "scripts" / "qa" + +# Data directories (from qa_config.yaml defaults) +DATA_DIR = PROJECT_ROOT / "data" +DERIVATIVES_DIR = DATA_DIR / "derivatives" +FMRIPREP_DIR = DERIVATIVES_DIR / "fmriprep" +QA_DIR = DERIVATIVES_DIR / "qa" +TSNR_DIR = QA_DIR / "tsnr" +MOTION_DIR = QA_DIR / "motion" +ISC_DIR = QA_DIR / "isc" + + +def get_subjects_arg(): + """Get --subjects argument if configured.""" + subjects = config.get("subjects", "") + if subjects: + return f"--subjects {subjects}" + return "" + + +# Default target: run all QA pipelines +rule all: + input: + rules.motion_plots.output if config.get("run_motion", True) else [], + rules.tsnr_plots.output if config.get("run_tsnr", True) else [], + rules.isc_plots.output if config.get("run_isc", True) else [], + + +# ============================================================================= +# Motion Plots Pipeline +# ============================================================================= +# Motion plots read directly from fMRIprep confounds files (no pre-computation) + +rule motion_plots: + """Generate motion QA plots from fMRIprep confounds.""" + input: + script=SCRIPTS_DIR / "qa-plot-motion.py", + # Sentinel file to check fMRIprep exists + fmriprep=ancient(FMRIPREP_DIR), + output: + done=touch(MOTION_DIR / ".motion_plots.done"), + log: + MOTION_DIR / "logs" / "motion_plots.log", + params: + subjects=get_subjects_arg(), + shell: + """ + mkdir -p {MOTION_DIR}/logs + {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} + """ + + +# ============================================================================= +# tSNR Pipeline +# ============================================================================= +# tSNR plots require pre-computed tSNR volumes + +rule compute_tsnr: + """Compute tSNR volumes from fMRIprep BOLD data.""" + input: + script=SCRIPTS_DIR / "qa-save-tsnr-volume.py", + fmriprep=ancient(FMRIPREP_DIR), + output: + done=touch(TSNR_DIR / ".tsnr_computed.done"), + log: + TSNR_DIR / "logs" / "compute_tsnr.log", + params: + subjects=get_subjects_arg(), + shell: + """ + mkdir -p {TSNR_DIR}/logs + {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} + """ + + +rule tsnr_plots: + """Generate tSNR QA plots from pre-computed tSNR volumes.""" + input: + script=SCRIPTS_DIR / "qa-plot-tsnr.py", + tsnr_computed=TSNR_DIR / ".tsnr_computed.done", + output: + done=touch(TSNR_DIR / ".tsnr_plots.done"), + log: + TSNR_DIR / "logs" / "tsnr_plots.log", + params: + subjects=get_subjects_arg(), + shell: + """ + {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} + """ + + +# ============================================================================= +# ISC Pipeline +# ============================================================================= +# ISC plots require pre-computed ISC data + +rule compute_isc: + """Compute inter-subject correlation for visualmemory task.""" + input: + script=SCRIPTS_DIR / "qa-save-isc.py", + fmriprep=ancient(FMRIPREP_DIR), + output: + done=touch(ISC_DIR / ".isc_computed.done"), + log: + ISC_DIR / "logs" / "compute_isc.log", + threads: + workflow.cores + shell: + """ + mkdir -p {ISC_DIR}/logs + {PYTHON} {input.script} --n-jobs {threads} 2>&1 | tee {log} + """ + + +rule isc_plots: + """Generate ISC visualization plots.""" + input: + script=SCRIPTS_DIR / "qa-plot-isc.py", + isc_computed=ISC_DIR / ".isc_computed.done", + output: + done=touch(ISC_DIR / ".isc_plots.done"), + log: + ISC_DIR / "logs" / "isc_plots.log", + shell: + """ + {PYTHON} {input.script} 2>&1 | tee {log} + """ + + +# ============================================================================= +# HTML Reports (optional, after plots) +# ============================================================================= + +rule motion_report: + """Generate HTML reports for motion QA.""" + input: + script=SCRIPTS_DIR / "qa-generate-html-reports-motion.py", + plots_done=MOTION_DIR / ".motion_plots.done", + output: + done=touch(MOTION_DIR / ".motion_reports.done"), + log: + MOTION_DIR / "logs" / "motion_reports.log", + params: + subjects=get_subjects_arg(), + shell: + """ + {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} + """ + + +rule tsnr_report: + """Generate HTML reports for tSNR QA.""" + input: + script=SCRIPTS_DIR / "qa-generate-html-reports-tsnr.py", + plots_done=TSNR_DIR / ".tsnr_plots.done", + output: + done=touch(TSNR_DIR / ".tsnr_reports.done"), + log: + TSNR_DIR / "logs" / "tsnr_reports.log", + params: + subjects=get_subjects_arg(), + shell: + """ + {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} + """ + + +# ============================================================================= +# Summary Statistics (optional) +# ============================================================================= + +rule tsnr_summary: + """Print tSNR summary statistics.""" + input: + script=SCRIPTS_DIR / "print-tsnr-summary.py", + tsnr_computed=TSNR_DIR / ".tsnr_computed.done", + output: + summary=TSNR_DIR / "tsnr_summary.txt", + log: + TSNR_DIR / "logs" / "tsnr_summary.log", + params: + subjects=get_subjects_arg(), + shell: + """ + {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} + """ + + +rule motion_summary: + """Print motion summary statistics.""" + input: + script=SCRIPTS_DIR / "print-motion-summary.py", + fmriprep=ancient(FMRIPREP_DIR), + output: + summary=MOTION_DIR / "motion_summary.txt", + log: + MOTION_DIR / "logs" / "motion_summary.log", + params: + subjects=get_subjects_arg(), + shell: + """ + mkdir -p {MOTION_DIR}/logs + {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} + """ + + +# ============================================================================= +# Cleanup Rules +# ============================================================================= + +rule clean_motion: + """Remove motion QA outputs (regenerate with snakemake motion_plots).""" + shell: + "rm -rf {MOTION_DIR}" + + +rule clean_tsnr: + """Remove tSNR QA outputs (regenerate with snakemake tsnr_plots).""" + shell: + "rm -rf {TSNR_DIR}" + + +rule clean_isc: + """Remove ISC QA outputs (regenerate with snakemake isc_plots).""" + shell: + "rm -rf {ISC_DIR}" + + +rule clean_all: + """Remove all QA outputs.""" + shell: + "rm -rf {QA_DIR}" diff --git a/workflow/config/config.yaml b/workflow/config/config.yaml new file mode 100644 index 0000000..490ca9f --- /dev/null +++ b/workflow/config/config.yaml @@ -0,0 +1,17 @@ +# Snakemake configuration for QA pipelines +# +# This config controls which pipelines run and their parameters. +# The actual data paths are read from the hyperface QA config +# (src/hyperface/assets/qa_config.yaml). + +# Which pipelines to run with the default 'all' target +run_motion: true +run_tsnr: true +run_isc: true + +# Subject filtering (space-separated list, leave empty for all subjects) +# Example: "sub-001 sub-002 sub-003" +subjects: "" + +# Whether to force regeneration of outputs that already exist +force: false From 6b0b120daf3bf16ed489ab1a10d2a1a03082f185 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Jan 2026 15:02:20 +0000 Subject: [PATCH 2/2] Fix issues in Snakemake workflow - Remove unused import (os) - Fix Python interpreter detection: prefer active interpreter, fallback to .venv - Fix configfile path using workflow.source_path() for portability - Add missing log directory creation in tsnr_plots, isc_plots rules - Add localrules declaration for clean targets (run locally, not on cluster) - Convert all Path objects to strings for shell commands - Remove unused 'force' config option - Remove unnecessary README.md --- workflow/README.md | 119 ------------------------------- workflow/Snakefile | 136 ++++++++++++++++++++---------------- workflow/config/config.yaml | 3 - 3 files changed, 74 insertions(+), 184 deletions(-) delete mode 100644 workflow/README.md diff --git a/workflow/README.md b/workflow/README.md deleted file mode 100644 index 3708c80..0000000 --- a/workflow/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# QA Pipeline Workflow - -This Snakemake workflow orchestrates the QA analysis pipelines for motion, tSNR, and ISC plots. - -## Installation - -Install snakemake as an optional dependency: - -```bash -uv sync --extra workflow -``` - -Or install snakemake directly: - -```bash -uv pip install snakemake>=8.0.0 -``` - -## Usage - -All commands should be run from the project root directory. - -### Run all QA pipelines - -```bash -snakemake --snakefile workflow/Snakefile --cores 1 -``` - -### Run specific pipelines - -```bash -# Motion plots only -snakemake --snakefile workflow/Snakefile motion_plots --cores 1 - -# tSNR plots (includes tSNR computation) -snakemake --snakefile workflow/Snakefile tsnr_plots --cores 1 - -# ISC plots (includes ISC computation) -snakemake --snakefile workflow/Snakefile isc_plots --cores 1 -``` - -### Dry run (preview what would be executed) - -```bash -snakemake --snakefile workflow/Snakefile -n -``` - -### Process specific subjects - -```bash -snakemake --snakefile workflow/Snakefile motion_plots --cores 1 --config subjects="sub-001 sub-002" -``` - -### Force regeneration - -```bash -snakemake --snakefile workflow/Snakefile tsnr_plots --cores 1 --forcerun -``` - -### Generate HTML reports - -```bash -# Motion HTML reports -snakemake --snakefile workflow/Snakefile motion_report --cores 1 - -# tSNR HTML reports -snakemake --snakefile workflow/Snakefile tsnr_report --cores 1 -``` - -### Summary statistics - -```bash -snakemake --snakefile workflow/Snakefile tsnr_summary motion_summary --cores 1 -``` - -## Pipeline Dependencies - -``` -fMRIprep outputs -├── motion_plots ─────────────────────→ motion figures -│ └── motion_report ────────────────→ HTML reports -│ └── motion_summary ───────────────→ summary stats -│ -├── compute_tsnr ─────→ tsnr volumes -│ └── tsnr_plots ───────────────────→ tsnr figures -│ └── tsnr_report ──────────────→ HTML reports -│ └── tsnr_summary ─────────────────→ summary stats -│ -└── compute_isc ──────→ ISC data - └── isc_plots ────────────────────→ ISC figures -``` - -## Configuration - -Edit `workflow/config/config.yaml` to: -- Enable/disable specific pipelines -- Filter subjects to process -- Force regeneration of outputs - -The actual data paths are read from `src/hyperface/assets/qa_config.yaml`. - -## Outputs - -All outputs are saved under `data/derivatives/qa/`: -- `motion/` - Motion traces, FD traces, violin plots -- `tsnr/` - tSNR mosaics, violin plots, surface plots -- `isc/` - ISC violin plots, surface plots - -## Cleanup - -```bash -# Remove specific outputs -snakemake --snakefile workflow/Snakefile clean_motion -snakemake --snakefile workflow/Snakefile clean_tsnr -snakemake --snakefile workflow/Snakefile clean_isc - -# Remove all QA outputs -snakemake --snakefile workflow/Snakefile clean_all -``` diff --git a/workflow/Snakefile b/workflow/Snakefile index fbaa185..c21e550 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -6,47 +6,55 @@ This workflow runs the QA pipeline scripts to generate: - ISC plots (requires ISC computation first) Usage: - # Run all QA pipelines - snakemake --cores 1 + # Run all QA pipelines (from project root) + snakemake --snakefile workflow/Snakefile --cores 1 # Run specific pipeline - snakemake motion_plots --cores 1 - snakemake tsnr_plots --cores 1 - snakemake isc_plots --cores 1 + snakemake --snakefile workflow/Snakefile motion_plots --cores 1 + snakemake --snakefile workflow/Snakefile tsnr_plots --cores 1 + snakemake --snakefile workflow/Snakefile isc_plots --cores 1 # Dry run to see what would be executed - snakemake -n + snakemake --snakefile workflow/Snakefile -n # Process specific subjects (motion/tsnr only) - snakemake motion_plots --cores 1 --config subjects="sub-001 sub-002" + snakemake --snakefile workflow/Snakefile motion_plots --cores 1 --config subjects="sub-001 sub-002" # Force rerun of specific target - snakemake tsnr_plots --cores 1 --forcerun + snakemake --snakefile workflow/Snakefile tsnr_plots --cores 1 --forcerun """ -import os +import shutil +import sys from pathlib import Path -# Configuration -configfile: "workflow/config/config.yaml" - # Get project root (parent of workflow directory) PROJECT_ROOT = Path(workflow.basedir).parent -# Python interpreter from virtual environment -PYTHON = PROJECT_ROOT / ".venv" / "bin" / "python" +# Configuration - use path relative to workflow directory +configfile: workflow.source_path("config/config.yaml") + +# Python interpreter - prefer active interpreter, fallback to venv +VENV_PYTHON = PROJECT_ROOT / ".venv" / "bin" / "python" +if Path(sys.executable).is_file(): + PYTHON = str(sys.executable) +elif VENV_PYTHON.is_file(): + PYTHON = str(VENV_PYTHON) +else: + # Fallback to system python + PYTHON = shutil.which("python3") or shutil.which("python") or "python" # Scripts directory -SCRIPTS_DIR = PROJECT_ROOT / "scripts" / "qa" +SCRIPTS_DIR = str(PROJECT_ROOT / "scripts" / "qa") # Data directories (from qa_config.yaml defaults) -DATA_DIR = PROJECT_ROOT / "data" -DERIVATIVES_DIR = DATA_DIR / "derivatives" -FMRIPREP_DIR = DERIVATIVES_DIR / "fmriprep" -QA_DIR = DERIVATIVES_DIR / "qa" -TSNR_DIR = QA_DIR / "tsnr" -MOTION_DIR = QA_DIR / "motion" -ISC_DIR = QA_DIR / "isc" +DATA_DIR = str(PROJECT_ROOT / "data") +DERIVATIVES_DIR = f"{DATA_DIR}/derivatives" +FMRIPREP_DIR = f"{DERIVATIVES_DIR}/fmriprep" +QA_DIR = f"{DERIVATIVES_DIR}/qa" +TSNR_DIR = f"{QA_DIR}/tsnr" +MOTION_DIR = f"{QA_DIR}/motion" +ISC_DIR = f"{QA_DIR}/isc" def get_subjects_arg(): @@ -57,12 +65,16 @@ def get_subjects_arg(): return "" +# Declare rules that don't create output files (run locally, not on cluster) +localrules: all, clean_motion, clean_tsnr, clean_isc, clean_all + + # Default target: run all QA pipelines rule all: input: - rules.motion_plots.output if config.get("run_motion", True) else [], - rules.tsnr_plots.output if config.get("run_tsnr", True) else [], - rules.isc_plots.output if config.get("run_isc", True) else [], + f"{MOTION_DIR}/.motion_plots.done" if config.get("run_motion", True) else [], + f"{TSNR_DIR}/.tsnr_plots.done" if config.get("run_tsnr", True) else [], + f"{ISC_DIR}/.isc_plots.done" if config.get("run_isc", True) else [], # ============================================================================= @@ -73,13 +85,11 @@ rule all: rule motion_plots: """Generate motion QA plots from fMRIprep confounds.""" input: - script=SCRIPTS_DIR / "qa-plot-motion.py", - # Sentinel file to check fMRIprep exists - fmriprep=ancient(FMRIPREP_DIR), + script=f"{SCRIPTS_DIR}/qa-plot-motion.py", output: - done=touch(MOTION_DIR / ".motion_plots.done"), + done=touch(f"{MOTION_DIR}/.motion_plots.done"), log: - MOTION_DIR / "logs" / "motion_plots.log", + f"{MOTION_DIR}/logs/motion_plots.log", params: subjects=get_subjects_arg(), shell: @@ -97,12 +107,11 @@ rule motion_plots: rule compute_tsnr: """Compute tSNR volumes from fMRIprep BOLD data.""" input: - script=SCRIPTS_DIR / "qa-save-tsnr-volume.py", - fmriprep=ancient(FMRIPREP_DIR), + script=f"{SCRIPTS_DIR}/qa-save-tsnr-volume.py", output: - done=touch(TSNR_DIR / ".tsnr_computed.done"), + done=touch(f"{TSNR_DIR}/.tsnr_computed.done"), log: - TSNR_DIR / "logs" / "compute_tsnr.log", + f"{TSNR_DIR}/logs/compute_tsnr.log", params: subjects=get_subjects_arg(), shell: @@ -115,16 +124,17 @@ rule compute_tsnr: rule tsnr_plots: """Generate tSNR QA plots from pre-computed tSNR volumes.""" input: - script=SCRIPTS_DIR / "qa-plot-tsnr.py", - tsnr_computed=TSNR_DIR / ".tsnr_computed.done", + script=f"{SCRIPTS_DIR}/qa-plot-tsnr.py", + tsnr_computed=f"{TSNR_DIR}/.tsnr_computed.done", output: - done=touch(TSNR_DIR / ".tsnr_plots.done"), + done=touch(f"{TSNR_DIR}/.tsnr_plots.done"), log: - TSNR_DIR / "logs" / "tsnr_plots.log", + f"{TSNR_DIR}/logs/tsnr_plots.log", params: subjects=get_subjects_arg(), shell: """ + mkdir -p {TSNR_DIR}/logs {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} """ @@ -137,12 +147,11 @@ rule tsnr_plots: rule compute_isc: """Compute inter-subject correlation for visualmemory task.""" input: - script=SCRIPTS_DIR / "qa-save-isc.py", - fmriprep=ancient(FMRIPREP_DIR), + script=f"{SCRIPTS_DIR}/qa-save-isc.py", output: - done=touch(ISC_DIR / ".isc_computed.done"), + done=touch(f"{ISC_DIR}/.isc_computed.done"), log: - ISC_DIR / "logs" / "compute_isc.log", + f"{ISC_DIR}/logs/compute_isc.log", threads: workflow.cores shell: @@ -155,14 +164,15 @@ rule compute_isc: rule isc_plots: """Generate ISC visualization plots.""" input: - script=SCRIPTS_DIR / "qa-plot-isc.py", - isc_computed=ISC_DIR / ".isc_computed.done", + script=f"{SCRIPTS_DIR}/qa-plot-isc.py", + isc_computed=f"{ISC_DIR}/.isc_computed.done", output: - done=touch(ISC_DIR / ".isc_plots.done"), + done=touch(f"{ISC_DIR}/.isc_plots.done"), log: - ISC_DIR / "logs" / "isc_plots.log", + f"{ISC_DIR}/logs/isc_plots.log", shell: """ + mkdir -p {ISC_DIR}/logs {PYTHON} {input.script} 2>&1 | tee {log} """ @@ -174,16 +184,17 @@ rule isc_plots: rule motion_report: """Generate HTML reports for motion QA.""" input: - script=SCRIPTS_DIR / "qa-generate-html-reports-motion.py", - plots_done=MOTION_DIR / ".motion_plots.done", + script=f"{SCRIPTS_DIR}/qa-generate-html-reports-motion.py", + plots_done=f"{MOTION_DIR}/.motion_plots.done", output: - done=touch(MOTION_DIR / ".motion_reports.done"), + done=touch(f"{MOTION_DIR}/.motion_reports.done"), log: - MOTION_DIR / "logs" / "motion_reports.log", + f"{MOTION_DIR}/logs/motion_reports.log", params: subjects=get_subjects_arg(), shell: """ + mkdir -p {MOTION_DIR}/logs {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} """ @@ -191,16 +202,17 @@ rule motion_report: rule tsnr_report: """Generate HTML reports for tSNR QA.""" input: - script=SCRIPTS_DIR / "qa-generate-html-reports-tsnr.py", - plots_done=TSNR_DIR / ".tsnr_plots.done", + script=f"{SCRIPTS_DIR}/qa-generate-html-reports-tsnr.py", + plots_done=f"{TSNR_DIR}/.tsnr_plots.done", output: - done=touch(TSNR_DIR / ".tsnr_reports.done"), + done=touch(f"{TSNR_DIR}/.tsnr_reports.done"), log: - TSNR_DIR / "logs" / "tsnr_reports.log", + f"{TSNR_DIR}/logs/tsnr_reports.log", params: subjects=get_subjects_arg(), shell: """ + mkdir -p {TSNR_DIR}/logs {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} """ @@ -212,16 +224,17 @@ rule tsnr_report: rule tsnr_summary: """Print tSNR summary statistics.""" input: - script=SCRIPTS_DIR / "print-tsnr-summary.py", - tsnr_computed=TSNR_DIR / ".tsnr_computed.done", + script=f"{SCRIPTS_DIR}/print-tsnr-summary.py", + tsnr_computed=f"{TSNR_DIR}/.tsnr_computed.done", output: - summary=TSNR_DIR / "tsnr_summary.txt", + summary=f"{TSNR_DIR}/tsnr_summary.txt", log: - TSNR_DIR / "logs" / "tsnr_summary.log", + f"{TSNR_DIR}/logs/tsnr_summary.log", params: subjects=get_subjects_arg(), shell: """ + mkdir -p {TSNR_DIR}/logs {PYTHON} {input.script} {params.subjects} 2>&1 | tee {log} """ @@ -229,12 +242,11 @@ rule tsnr_summary: rule motion_summary: """Print motion summary statistics.""" input: - script=SCRIPTS_DIR / "print-motion-summary.py", - fmriprep=ancient(FMRIPREP_DIR), + script=f"{SCRIPTS_DIR}/print-motion-summary.py", output: - summary=MOTION_DIR / "motion_summary.txt", + summary=f"{MOTION_DIR}/motion_summary.txt", log: - MOTION_DIR / "logs" / "motion_summary.log", + f"{MOTION_DIR}/logs/motion_summary.log", params: subjects=get_subjects_arg(), shell: diff --git a/workflow/config/config.yaml b/workflow/config/config.yaml index 490ca9f..9c2ca99 100644 --- a/workflow/config/config.yaml +++ b/workflow/config/config.yaml @@ -12,6 +12,3 @@ run_isc: true # Subject filtering (space-separated list, leave empty for all subjects) # Example: "sub-001 sub-002 sub-003" subjects: "" - -# Whether to force regeneration of outputs that already exist -force: false