Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update tutorial for new bidsapp #408

Merged
merged 2 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/bids_app/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ A list of analysis levels in the BIDS app. Typically, this will include particip

A mapping from the name of each ``analysis_level`` to the list of rules or files to be run for that analysis level.

(parse-args-config)=
### `parse_args`

A dictionary of command-line parameters to make available as part of the BIDS app. Each item of the mapping is passed to [argparse's `add_argument` function](#argparse.ArgumentParser.add_argument). A number of default entries are present in a new snakebids project's config file that structure the BIDS app's CLI, but additional command-line arguments can be added as necessary.
Expand Down
32 changes: 32 additions & 0 deletions docs/migration/0.11_to_0.12.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(migrate-to-bidsapp)=
# 0.11 to 0.12+

Snakebids 0.12 introduces a [new, more flexible module](#snakebids.bidsapp) for creating bidsapps. This affects the syntax of the `run.py` file. Older versions used the {class}`snakebids.app.SnakeBidsApp` class to initialize the bidsapp, and this method will still work for the forseeable future. Switching to the new syntax will give access to new plugins and integrations and ensure long term support.

If you haven't heavily modified your `run.py` file, you can transition simply by replacing it with the following:

```python
#!/usr/bin/env python3
from pathlib import Path

from snakebids import bidsapp, plugins

app = bidsapp.app(
[
plugins.SnakemakeBidsApp(Path(__file__).resolve().parent),
plugins.BidsValidator(),
plugins.Version(distribution="<app_name_here>"),
]
)


def get_parser():
"""Exposes parser for sphinx doc generation, cwd is the docs dir."""
return app.build_parser().parser


if __name__ == "__main__":
app.run()
```

The snakemake workflow will work in exactly the same way.
6 changes: 5 additions & 1 deletion docs/migration/0.5_to_0.8.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# 0.5 to 0.8
# 0.5 to 0.8+

```{note}
Be sure to also [migrate](0.11_to_0.12.md) your `run.py` file to the new snakebids 0.12 syntax!
```

Starting in version 0.8, {func}`snakebids.generate_inputs()` returns a {class}`BidsInputs <snakebids.BidsDataset>` object instead of a {class}`dict <snakebids.BidsDatasetDict>`. This requires a change in the way info is accessed. The previous {class}`dict <snakebids.BidsDatasetDict>` had top-level keys such as `"input_lists"`. After selecting such a key, you would pass the name of a component to get the information sought:

Expand Down
5 changes: 4 additions & 1 deletion docs/migration/0.7_to_0.8.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 0.7 to 0.8
# 0.7 to 0.8+

````{warning}
If your code still has bits like this:
Expand All @@ -12,6 +12,9 @@ config.update(generate_inputs(
Check out the [pre-0.6 migration guide](/migration/0.5_to_0.8) to a guide on how to upgrade!
````

```{note}
Be sure to also [migrate](0.11_to_0.12.md) your `run.py` file to the new snakebids 0.12 syntax!
```


## Default return of {func}`~snakebids.generate_inputs()`
Expand Down
1 change: 1 addition & 0 deletions docs/migration/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ Snakebids has rapidly evolved over the last few versions, resulting in a number

0.5_to_0.8
0.7_to_0.8
0.11_to_0.12
```
1 change: 1 addition & 0 deletions docs/running_snakebids/overview.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
(running-snakebids)=
# Running Snakebids

Once you've specified a snakebids app with a config file and one or more workflow files, you're ready to invoke your snakebids app with the standard BIDS app CLI.
Expand Down
60 changes: 12 additions & 48 deletions docs/tutorial/step8/config.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
bids_dir: 'data'

fwhm:
- 5
- 10
- 15
- 20

pybids_inputs:
bold:
Expand All @@ -19,52 +14,21 @@ pybids_inputs:
- task
- run

analysis_levels:
- participant

targets_by_analysis_level:
participant:
- '' # if '', then the first rule is run

analysis_levels: &analysis_levels
- participant

parse_args:
bids_dir:
help: |
The directory with the input dataset formatted according to the BIDS
standard.

output_dir:
help: |
The directory where the output files should be stored. If you are running
group level analysis this folder should be prepopulated with the results
of the participant level analysis.

analysis_level:
help: Level of the analysis that will be performed.
choices: *analysis_levels

--participant_label:
help: |
The label(s) of the participant(s) that should be analyzed. The label
corresponds to sub-<participant_label> from the BIDS spec (so it does not
include "sub-"). If this parameter is not provided all subjects should be
analyzed. Multiple participants can be specified with a space separated
list.
nargs: '+'

--exclude_participant_label:
help: |
The label(s) of the participant(s) that should be excluded. The label
corresponds to sub-<participant_label> from the BIDS spec (so it does not
include "sub-"). If this parameter is not provided all subjects should be
analyzed. Multiple participants can be specified with a space separated
list.
nargs: '+'

--derivatives:
help: |
Path(s) to a derivatives dataset, for folder(s) that contains multiple
derivatives datasets.
default: False
type: Path
nargs: '+'
--fwhm:
help: >
Set the full-width-half-maximum values that should be used for smoothing.
type: int
nargs: +
default:
- 5
- 10
- 15
- 20
16 changes: 8 additions & 8 deletions docs/tutorial/step8/run.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#!/usr/bin/env python3
import os
from snakebids.app import SnakeBidsApp
from pathlib import Path

from snakebids import bidsapp, plugins


def main():
app = SnakeBidsApp(os.path.abspath(os.path.dirname(__file__)))
app.run_snakemake()

app = bidsapp.app(
[
plugins.SnakemakeBidsApp(Path(__file__).resolve().parent),
]
)

if __name__ == "__main__":
main()
app.run()
24 changes: 14 additions & 10 deletions docs/tutorial/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,15 +454,23 @@ For reference, here is the updated config file and Snakefile after these changes

Now that we have pybids parsing to dynamically configure our workflow inputs based on our BIDS dataset, we are ready to turn our workflow into a [BIDS App](http://bids-apps.neuroimaging.io/). BIDS Apps are command-line apps with a standardized interface (e.g. three required positional arguments: ``bids_directory``, ``output_directory``, and ``analysis_level``).

We do this in snakebids by creating an executable python script, which uses the {class}`SnakeBidsApp <snakebids.app.SnakeBidsApp>` class from {mod}`snakebids.app` to run snakemake. An example of this `run.py` script is shown below.
We do this in snakebids by creating a python script containing a {func}`bidsapp.app <snakebids.bidsapp.app>` built with the Snakemake integration plugin {class}`SnakemakeBidsApp <snakebids.plugins.SnakemakeBidsApp>`. An example of this `run.py` script is shown below.

```{literalinclude} step8/run.py
:language: python
:linenos:
:caption: run.py
```

However, we will first need to add some additional information to our config file, mainly to define how to parse command-line arguments for this app. This is done with a new `parse_args` dict in the config:
This creates a bidsapp with all the standard arguments. The usage will look something like this:

```bash
./run.py INPUT_DATASET OUTPUT_DATASET [participant|group] [--derivatives] [--participant-label LABEL...]
```

A more complete description of bidsapp usage can be found at <project:#running-snakebids>.

Additional argument can be added using the `config.yml`. For instance, we can turn our `fwhm` setting into a CLI parameter with the following:

```{literalinclude} step8/config.yml
:language: yaml
Expand All @@ -472,21 +480,17 @@ However, we will first need to add some additional information to our config fil
:lineno-match:
```

The above is standard boilerplate for any BIDS app. You can also define any new command-line arguments you wish. Snakebids uses the {mod}`argparse` module, and each entry in this `parse_args` dict thus becomes a call to {meth}`add_argument() <argparse.ArgumentParser.add_argument>` from {class}`argparse.ArgumentParser`. When you run the workflow, snakebids adds the named argument values to the config dict, so your workflow can make use of it as if you had manually added the variable to your configfile.
Snakebids uses the {mod}`argparse` module, and each entry in this `parse_args` dict becomes a call to {meth}`add_argument() <argparse.ArgumentParser.add_argument>` from {class}`argparse.ArgumentParser`. When you run the workflow, snakebids adds the named argument values to the config dict, so your workflow can make use of it as if you had manually added the variable to your configfile. See <project:#parse-args-config> for more details.

Arguments that will receive paths should be given the item `type: Path`, as is done for `--derivatives` in the example above. Without this annotation, paths given to keyword arguments will be interpreted relative to the output directory. Indicating `type: Path` will tell Snakebids to first resolve the path according to your current working directory.

```{note}
`bids_dir` and `output_dir` are always resolved relative to the current directory and do not need any extra annotation.
```
In the above example, snakebids will automatically insert a key called `fwhm` into the snakemake `config` containing the values provided from the command line (or the default, if no values are provided).

BIDS apps also have a required `analysis_level` positional argument, so there are some config variables to set this as well. The analysis levels are in an `analysis_levels` list in the config, and also as keys in a `targets_by_analysis_level` dict, which can be used to map each analysis level to the name of a target rule:
The `analysis_level` positional argument can also be modified in the config. The available levels are in an `analysis_levels` list in the config. Specific snakemake targets can be mapped to these levels in the `targets_by_analysis_level` dict:

```{literalinclude} step8/config.yml
:language: yaml
:caption: config.yml
:linenos:
:start-at: targets_by_analysis_level
:start-at: analysis_levels
:end-before: parse_args
:lineno-match:
```
Expand Down