diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 65ce573..8086ad8 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,6 +34,7 @@ jobs: run: uv run make -C docs clean html - name: GitHub Pages action + if: github.event_name == 'push' uses: peaceiris/actions-gh-pages@v3.6.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/Dockerfile b/Dockerfile index 4437740..bddc98a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Generated by Neurodocker and Reproenv. -FROM debian:bullseye-slim +FROM debian:bookworm-slim ENV LANG="en_US.UTF-8" \ LC_ALL="en_US.UTF-8" \ ND_ENTRYPOINT="/neurodocker/startup.sh" @@ -38,6 +38,7 @@ RUN curl -fsSL https://deno.land/install.sh | env DENO_INSTALL=/usr/local sh -s RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin INSTALLER_NO_MODIFY_PATH=1 sh ENV UV_LINK_MODE="copy" ENV UV_PYTHON_INSTALL_DIR="/opt/uv-python" +ENV UV_PYTHON="3.11" ENV UV_PROJECT_ENVIRONMENT="/home/ffrprep/.venv" ENV PYTHONDONTWRITEBYTECODE="1" COPY [".", \ @@ -61,7 +62,7 @@ RUN printf '{ \ { \ "name": "from_", \ "kwds": { \ - "base_image": "debian:bullseye-slim" \ + "base_image": "debian:bookworm-slim" \ } \ }, \ { \ @@ -127,6 +128,12 @@ RUN printf '{ \ "UV_PYTHON_INSTALL_DIR": "/opt/uv-python" \ } \ }, \ + { \ + "name": "env", \ + "kwds": { \ + "UV_PYTHON": "3.11" \ + } \ + }, \ { \ "name": "env", \ "kwds": { \ diff --git a/Singularity.def b/Singularity.def index 45be799..9bf8f7d 100644 --- a/Singularity.def +++ b/Singularity.def @@ -1,7 +1,7 @@ # Generated by Neurodocker and Reproenv. Bootstrap: docker -From: debian:bullseye-slim +From: debian:bookworm-slim %files . /home/ffrprep @@ -12,6 +12,7 @@ export LC_ALL="en_US.UTF-8" export ND_ENTRYPOINT="/neurodocker/startup.sh" export UV_LINK_MODE="copy" export UV_PYTHON_INSTALL_DIR="/opt/uv-python" +export UV_PYTHON="3.11" export UV_PROJECT_ENVIRONMENT="/home/ffrprep/.venv" export PYTHONDONTWRITEBYTECODE="1" export IS_DOCKER="1" @@ -78,7 +79,7 @@ printf '{ \ { \ "name": "from_", \ "kwds": { \ - "base_image": "debian:bullseye-slim" \ + "base_image": "debian:bookworm-slim" \ } \ }, \ { \ @@ -144,6 +145,12 @@ printf '{ \ "UV_PYTHON_INSTALL_DIR": "/opt/uv-python" \ } \ }, \ + { \ + "name": "env", \ + "kwds": { \ + "UV_PYTHON": "3.11" \ + } \ + }, \ { \ "name": "env", \ "kwds": { \ diff --git a/ffrprep/ffrprep_cli.py b/ffrprep/ffrprep_cli.py index b9bd3db..c64c2cb 100644 --- a/ffrprep/ffrprep_cli.py +++ b/ffrprep/ffrprep_cli.py @@ -1561,8 +1561,10 @@ def get_parser(): help=( "Reference channel(s) for re-referencing. Provide space-separated " "channel names (e.g. --ref_channels M1 M2). Use 'average' for " - "average reference. Comma-separated single-argument styles are " - "also accepted for backward compatibility (e.g. 'M1,M2')." + "average reference, or 'skip' if the data is already referenced " + "and should not be re-referenced. Comma-separated single-argument " + "styles are also accepted for backward compatibility (e.g. " + "'M1,M2')." ), nargs="+", type=str, @@ -1813,6 +1815,10 @@ def parse_ref_channels(ref_str): # Already a sequence; ensure items are stripped and handle # comma-separated tokens inside any element for backward # compatibility (e.g. ['M1,M2'] -> ['M1','M2']). + if len(ref_str) == 1 and isinstance(ref_str[0], str) and ref_str[0].strip().lower() == "skip": + return [] # Data is already referenced; do not re-reference + if len(ref_str) == 1 and isinstance(ref_str[0], str) and ref_str[0].strip().lower() == "average": + return None # Average reference out = [] for item in ref_str: if isinstance(item, str) and "," in item: @@ -1824,6 +1830,8 @@ def parse_ref_channels(ref_str): s = str(ref_str) if s.lower() == "average": return None # Average reference + if s.lower() == "skip": + return [] # Data is already referenced; do not re-reference if "," in s: # Split and strip whitespace around channel names return [c.strip() for c in s.split(",") if c.strip()] diff --git a/ffrprep/preproc.py b/ffrprep/preproc.py index 9b521df..65bc329 100644 --- a/ffrprep/preproc.py +++ b/ffrprep/preproc.py @@ -587,7 +587,8 @@ def reference_data(eeg_data=None, ref_channels=None): Channels to be used as reference. If more than one channel in list, the average of the channels in `ref_channels` will be used as the reference. If `None`, all channels will be - averaged as the reference. + averaged as the reference. If an empty list, the data is + marked as already referenced and is not modified. Default = None. Returns diff --git a/ffrprep/tests/test_cli.py b/ffrprep/tests/test_cli.py index 892ca57..abec3d3 100644 --- a/ffrprep/tests/test_cli.py +++ b/ffrprep/tests/test_cli.py @@ -220,6 +220,13 @@ def test_parse_ref_channels_average(): assert parse_ref_channels("Average") is None +def test_parse_ref_channels_average_from_argparse_list(): + """--ref_channels uses nargs='+', so real CLI usage always passes a + list (e.g. ['average']) rather than a bare string.""" + assert parse_ref_channels(["average"]) is None + assert parse_ref_channels(["AVERAGE"]) is None + + def test_parse_ref_channels_single(): assert parse_ref_channels("Cz") == "Cz" assert parse_ref_channels("TP9") == "TP9" @@ -235,6 +242,20 @@ def test_parse_ref_channels_empty_string(): assert parse_ref_channels("") is None +def test_parse_ref_channels_skip(): + """'skip' normalizes to [] (data already referenced; do not modify).""" + assert parse_ref_channels("skip") == [] + assert parse_ref_channels("SKIP") == [] + assert parse_ref_channels("Skip") == [] + + +def test_parse_ref_channels_skip_from_argparse_list(): + """--ref_channels uses nargs='+', so real CLI usage always passes a + list (e.g. ['skip']) rather than a bare string.""" + assert parse_ref_channels(["skip"]) == [] + assert parse_ref_channels(["SKIP"]) == [] + + # --------------------------------------------------------------------------- # run_ffrprep — mocked end-to-end behavior # --------------------------------------------------------------------------- diff --git a/generate_images.sh b/generate_images.sh index 35f9b04..fc94f91 100644 --- a/generate_images.sh +++ b/generate_images.sh @@ -25,7 +25,7 @@ INSTALL_GROUP_FLAG="--no-dev" generate_docker() { docker run --rm repronim/neurodocker:2.1.1 generate docker \ - --base-image debian:bullseye-slim \ + --base-image debian:bookworm-slim \ --pkg-manager apt \ --arg DEBIAN_FRONTEND=noninteractive \ --install $OS_PACKAGES \ @@ -33,6 +33,7 @@ generate_docker() { --run "$UV_INSTALL" \ --env UV_LINK_MODE=copy \ --env UV_PYTHON_INSTALL_DIR=/opt/uv-python \ + --env UV_PYTHON=3.11 \ --env UV_PROJECT_ENVIRONMENT=/home/ffrprep/.venv \ --env PYTHONDONTWRITEBYTECODE=1 \ --copy . /home/ffrprep \ @@ -48,7 +49,7 @@ generate_docker() { generate_singularity() { docker run --rm repronim/neurodocker:2.1.1 generate singularity \ - --base-image debian:bullseye-slim \ + --base-image debian:bookworm-slim \ --pkg-manager apt \ --arg DEBIAN_FRONTEND=noninteractive \ --install $OS_PACKAGES \ @@ -56,6 +57,7 @@ generate_singularity() { --run "$UV_INSTALL" \ --env UV_LINK_MODE=copy \ --env UV_PYTHON_INSTALL_DIR=/opt/uv-python \ + --env UV_PYTHON=3.11 \ --env UV_PROJECT_ENVIRONMENT=/home/ffrprep/.venv \ --env PYTHONDONTWRITEBYTECODE=1 \ --copy . /home/ffrprep \