Skip to content

Commit

Permalink
Adds create-config entry point
Browse files Browse the repository at this point in the history
Closes #757

Adds a `topostats create-config` entry point and removes reference to `run_topostats`.

As we move towards a new release having a modular interface is desirable and that includes generation of sample
configuration files. This commit brings the "swiss army knife" approach of `topostats <subcommand>`to parity with the
older `run_topostats` by adding `topostats create-config` to generate configuration files for users to edit.

Documentation has been updated to reflect these changes, including the `README.md`. Because we have versioned
documentation users will still be able to refer to the older documentation.

For now the `run_topostats` method remains but raises a deprecation warning if the `--create-config-file` option is used.

**NB** This work was undertaken as a consequence of re-writing the `.github/ISSUE_TEMPLATES/` so that the instructions
could use this new entry point (see separate Pull Request).
  • Loading branch information
ns-rse committed Dec 13, 2023
1 parent 9201cda commit c9926ef
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 76 deletions.
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,41 +51,41 @@ instructions](https://afm-spm.github.io/TopoStats/main/installation.html).
For a full description of usage please refer to the [usage](https://afm-spm.github.io/TopoStats/main/usage.html) documentation.

A default configuration is loaded automatically and so the simplest method of processing images is to run
`run_topostats` in the same directory as your scans _after_ having activated the virtual environment in which you have
`topostats process` in the same directory as your scans _after_ having activated the virtual environment in which you have
installed TopoStats

``` bash
run_topostats
topostats process
```

If you have your own YAML configuration file (see [Usage : Configuring
TopoStats](https://afm-spm.github.io/TopoStats/main/usage.html#configuring_topostats)) then invoke `run_topostats` and use
the argument for `--config <config_file>.yaml` that points to your file.
TopoStats](https://afm-spm.github.io/TopoStats/main/usage.html#configuring_topostats)) then invoke `topostats process`
and use the argument for `--config <config_file>.yaml` that points to your file.

``` bash
# Edit and save my_config.yaml then run TopoStats with this configuration file
run_topostats --config my_config.yaml
topostats process --config my_config.yaml
```

The configuration file is validated before analysis begins and if there are problems you will see errors messages that
are hopefully useful in resolving the error(s) in your modified configuration.

You can generate a sample configuration file using the `--create-config-file` argument which takes a single argument,
the name of the file to save the configuration to (e.g. `config.yaml` or `settings.yaml`). This will _not_ run any
analyses but will instead write the default configuration to the file `config.yaml` in the current directory.
You can generate a sample configuration file using the `topostats create-config` argument which writes the default
configuration to the file `./config.yaml` (i.e. in the current directory). This will _not_ run any analyses.

**NB** - This feature is only available in versions > v2.0.0 as it was introduced after v2.0.0 was released.
**NB** - This feature is only available in versions > v2.0.0 as it was introduced after v2.0.0 was released. In older
version > 2.0.0 and <= 2.1.2 you can use the older `run_topostats --create-config` option.

``` bash
run_topostats --create-config-file config.yaml
```

### Notebooks

Example Jupyter Notebooks are in have been developed that show how to use TopoStats package interactively which is
useful when you are unsure of what parameters are most suited to your scans. Other notebooks exist which show how to
produce plots of the summary grain and tracing statistics or how to generate plots of scans from processed images which
saves having to run the processing again. See the documentation on
Example Jupyter Notebooks have been developed that show how to use TopoStats package interactively which is useful when
you are unsure of what parameters are most suited to your scans. Other notebooks exist which show how to produce plots
of the summary grain and tracing statistics or how to generate plots of scans from processed images which saves having
to run the processing again. See the documentation on
[Notebooks](https://afm-spm.github.io/TopoStats/main/notebooks.html) for further details.

## Contributing
Expand Down
14 changes: 8 additions & 6 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
# Configuration

Configuration for TopoStats is done using a [YAML](https://yaml.org/) configuration file that is specified on the
command line when invoking. The current configuration file is provided in the TopoStats repository at
command line when invoking. If no configuration file is provided this default configuration is loaded automatically and
used.

The current configuration file is provided in the TopoStats repository at
[`topostats/default_config.yaml`](https://github.com/AFM-SPM/TopoStats/blob/main/topostats/default_config.yaml) but
please be aware this may not work with your installed version, particularly if you installed from PyPI.

## Generating a configuration

You can always generate a configuration file appropriate for the version you have installed (bar v2.0.0 as this option
was added afterwards). This writes the default configuration to the specified filename (i.e. it does not have to be
called `config.yaml` it could be called `spm-2023-02-20.yaml`)
called `config.yaml` it could be called `spm-2023-02-20.yaml`). There are a few options available (use `topostats
create-config --help` for further details).

``` bash
run_topostats --create-config-file config.yaml
topostats create-config
```

If no configuration file is provided this default configuration is loaded automatically and used.

## Using a custom configuration

If you have generated a configuration file you can modify and edit a configuration it to change the parameters (see
fields below). Once these changes have been saved, you can run TopoStats with this configuration file as shown below.

``` bash
run_topostats --config my_config.yaml
topostats process --config my_config.yaml
```

On completion a copy of the configuration that was used is written to the output directory so you have a record of the
Expand Down
4 changes: 2 additions & 2 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ As described in [Parameter Configuration](configuration) options are primarily p
have to ensure that the default configuration file (`topostats/default.yaml`) is updated to include your options.

Further the `topostats.validation.validate.config()` function, which checks a valid configuration file with all necessary
fields has been passed when invoking `run_topostats`, will also need updating to include new options in the Schema against
which validation of configuration files is made.
fields has been passed when invoking `topostats` sub-commands, will also need updating to include new options in the
Schema against which validation of configuration files is made.

### IDE Configuration

Expand Down
5 changes: 3 additions & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ After activating your `topostats` Conda environment you can install TopoStats fr
pip install topostats
```

This will install TopoStats under your virtual environment and the command `run_topostats` will be available at the
command line. You can upgrade `topostats` by using the `--upgrade` flag...
This will install TopoStats under your virtual environment and the command `topostats` will be available at the
command line. It has a number of sub-commands which can be displayed by invoking it without any options. You can upgrade
`topostats` by using the `--upgrade` flag...

``` bash
pip install --upgrade topostats
Expand Down
30 changes: 14 additions & 16 deletions docs/usage.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Usage

After having [installed](installation) TopoStats you are ready to run it. For convenience TopoStats provides a command
line interface `run_topostats` that will load a default configuration file and process all images with reasonable
line interface `topostats` that will load a default configuration file and process all images with reasonable
default configuration options.

However, because the location of your image files can not be known in advance you must make a copy of the default
Expand Down Expand Up @@ -80,11 +80,11 @@ shell/terminal you will therefore need to do two things.
1. Navigate to the location of the scans you wish to process using `cd /path/to/where/scans/are/located`.
2. Activate the virtual environment under which you installed TopoStats (refer to [installed](installation) if unsure).

You can now run topostats by invoking `run_topostats` and you should start to see some output similar to that below.
You can now run topostats by invoking `topostats process` and you should start to see some output similar to that below.

``` bash
cd /path/to/where/scans/are/located
run_topostats
topostats process
[Tue, 15 Nov 2022 12:39:48] [INFO ] [topostats] Configuration is valid.
[Tue, 15 Nov 2022 12:39:48] [INFO ] [topostats] Plotting configuration is valid.
[Tue, 15 Nov 2022 12:39:48] [INFO ] [topostats] Configuration file loaded from : None
Expand Down Expand Up @@ -129,7 +129,7 @@ along with information about how to give feedback, report bugs and cite the soft
If you find the output too verbose or of no use you can reduce it by setting the `log_level` to either `error` or
`warning`. This can be done either in the configuration file (see [Configuration](configuration.md) below)
or using the `-l`/`--log-level` flag for example `run_topostats --log_level warning`.
or using the `-l`/`--log-level` flag for example `topostats process --log_level warning`.
## Configuring TopoStats
Expand All @@ -144,14 +144,12 @@ want to make to the default configuration and how to make them.
TopoStats will use some reasonable default parameters by default, but typically you will want to customise the
parameters that are used. This is achieved using a [configuration](configuration) file. This is a
[YAML](https://yaml.org) file that contains parameters for different settings. For convenience you can generate
a sample configuration file in your current working directory using the `--create-config-file` option. It takes a
single argument, the name of the file to save the configuration to (e.g. `config.yaml` or `settings.yaml`), and it will
write the current default configuration to that file.
**NB** - This feature is only available in versions > v2.0.0 as it was introduced after v2.0.0 was released.
a sample configuration file in your current working directory using the `topostats create-config-file` sub-command. It
takes a single argument, the name of the file to save the configuration to (e.g. `config.yaml` or `settings.yaml`), and
it will write the current default configuration to that file.
``` bash
run_topostats --create-config-file my_config.yaml
topostats create-config-file --filename my_config.yaml
ls -l
my_config.yaml
sample_image_scan_2022-12-08-1204.spm
Expand All @@ -169,7 +167,7 @@ You can now start customising the configuration you are going to run TopoStats w
ones you may want to change are....
* `base_dir` (default: `./`) the directory in which to search for scans. By default this is `./` which represents the
directory from which `run_topostats` is called and it is good practice to have one configuration file per batch of
directory from which `topostats process` is called and it is good practice to have one configuration file per batch of
scans that are being processed.
* `output_dir` (default: `output`) the location where the output is saved, by default this is the directory `output`
which will be created if it doesn't exist. If you wish for the output to be somewhere else specify it here. If you
Expand All @@ -192,15 +190,15 @@ the file and return to your terminal.
### Running TopoStats with `my_config.yaml`
To use your new configuration file you need to inform `run_topostats` to use that file rather than the defaults, this is
done using the `--config config.yaml` file.
To use your new configuration file you need to inform `topostats process` to use that file rather than the defaults,
this is done using the `--config config.yaml` file.
**NB** this assumes that you are in the same directory as your scans where you have saved the `my_config.yaml` file that
you edited. That doesn't _have_ to be the case but it makes life easier for if you are not familiar with absolute
and relative paths.
``` bash
run_topostats --config my_config.yaml
topostats process --config my_config.yaml
[Tue, 15 Nov 2022 12:39:48] [INFO ] [topostats] Configuration is valid.
[Tue, 15 Nov 2022 12:39:48] [INFO ] [topostats] Plotting configuration is valid.
[Tue, 15 Nov 2022 12:39:48] [INFO ] [topostats] Configuration file loaded from : None
Expand All @@ -218,8 +216,8 @@ On successful completion you should see the same message noted above.
## Output
The output from running TopoStats is saved in the location defined in the configuration file by `output_dir`. The
default is the directory `output` within the directory from which `run_topostats`. This may differ if you have
used your own customised configuration file.
default is the directory `output` within the directory from which `topostats process`. This may differ if you have
used your own customised configuration file (specifically if you have modified the `output_dir:` option).
At the top level of the output directory are two files `config.yaml` and `all_statistics.csv`
Expand Down
32 changes: 16 additions & 16 deletions tests/test_entry_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
legacy_run_topostats_entry_point,
legacy_toposum_entry_point,
)
from topostats.io import write_config_with_comments
from topostats.plotting import run_toposum
from topostats.run_topostats import run_topostats

Expand Down Expand Up @@ -47,6 +48,8 @@ def test_entry_point_help(capsys, option) -> None:
("dnatracing", "--help"),
("tracingstats", "-h"),
("tracingstats", "--help"),
("create-config", "-h"),
("create-config", "--help"),
],
)
def test_entry_point_subprocess_help(capsys, argument: str, option: str) -> None:
Expand Down Expand Up @@ -76,12 +79,12 @@ def test_entry_point_subprocess_help(capsys, argument: str, option: str) -> None
),
(
[
"process",
"--config",
"create-config",
"--filename",
"dummy/config/dir/config.yaml",
],
run_topostats,
"config_file",
write_config_with_comments,
"filename",
"dummy/config/dir/config.yaml",
),
(
Expand Down Expand Up @@ -112,8 +115,15 @@ def test_entry_point(

def test_entry_point_create_config_file(tmp_path: Path) -> None:
"""Test that the entry point is able to produce a default config file when asked to."""
with pytest.raises(SystemExit):
entry_point(manually_provided_args=["process", "--create_config_file", f"{tmp_path}/test_create_config.yaml"])
entry_point(
manually_provided_args=[
"create-config",
"--filename",
"test_create_config.yaml",
"--output_dir",
f"{tmp_path}",
]
)

assert Path(f"{tmp_path}/test_create_config.yaml").is_file()

Expand Down Expand Up @@ -148,16 +158,6 @@ def test_legacy_run_topostats_entry_point(options: list, expected_arg_name: str,
assert returned_args_dict[expected_arg_name] == expected_arg_value


def test_legacy_run_topostats_entry_point_create_config_file(tmp_path: Path) -> None:
"""Ensure legacy entry point is able to produce a default config file."""
with pytest.raises(SystemExit):
legacy_run_topostats_entry_point(
args=["--create-config-file", f"{tmp_path}/test_legacy_run_topostats_create_config.yaml"]
)

assert Path(f"{tmp_path}/test_legacy_run_topostats_create_config.yaml").is_file()


def test_legacy_toposum_entry_point_create_config_file(tmp_path: Path) -> None:
"""Ensure the toposum legacy entry point is able to produce a default config file."""
with pytest.raises(SystemExit):
Expand Down
50 changes: 42 additions & 8 deletions tests/test_io.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Tests of IO."""
import argparse
from datetime import datetime
from pathlib import Path

Expand Down Expand Up @@ -60,23 +61,56 @@ def test_read_yaml() -> None:
assert sample_config == CONFIG


def test_write_config_with_comments(tmp_path: Path) -> None:
"""Test writing of config file with comments."""
# Read default config with comments
with Path.open(BASE_DIR / "topostats" / "default_config.yaml", encoding="utf-8") as f:
default_config_string = f.read()
@pytest.mark.parametrize(
("filename", "config", "expected_filename"),
[
("test_config_with_comments.yaml", None, "test_config_with_comments.yaml"),
("test_config_with_comments", None, "test_config_with_comments.yaml"),
(None, "default", "config.yaml"),
(None, None, "config.yaml"),
# Example of how to test `dna_config.yaml`
# (None, "dna", "dna_config.yaml")
],
)
def test_write_config_with_comments(tmp_path: Path, filename: str, config: str, expected_filename: str) -> None:
"""Test writing of config file with comments.
If and when specific configurations for different sample types are introduced then the parametrisation can be
extended to allow these adding their names under "config" and introducing specific parameters that may differe
between the configuration files.
"""
# Setup argparse.Namespace with the tests parameters
args = argparse.Namespace()
args.filename = filename
args.output_dir = tmp_path
args.config = config

# Write default config with comments to file
write_config_with_comments(config=default_config_string, output_dir=tmp_path, filename="test_config_with_comments")
write_config_with_comments(args)

# Read the written config
with Path.open(tmp_path / "test_config_with_comments.yaml", encoding="utf-8") as f:
with Path.open(tmp_path / expected_filename, encoding="utf-8") as f:
written_config = f.read()

# Validate that the written config has comments in it
assert default_config_string in written_config
assert "Config file generated" in written_config
assert "For more information on configuration and how to use it" in written_config
# Validate some of the parameters are present
assert "loading:" in written_config
assert "gaussian_mode: nearest" in written_config
assert "style: topostats.mplstyle" in written_config
assert "pixel_interpolation: null" in written_config


def test_write_config_with_comments_user_warning(tmp_path: Path) -> None:
"""Tests a user warning is raised if an attempt is made to request a configuration file type that does not exist."""
args = argparse.Namespace()
args.filename = "config.yaml"
args.output_dir = tmp_path
args.config = "nonsense"

with pytest.raises(UserWarning):
write_config_with_comments(args)


def test_write_yaml(tmp_path: Path) -> None:
Expand Down
Loading

0 comments on commit c9926ef

Please sign in to comment.