# Contribrute a stimulus (set)
`stimupy` contains both a wide range of generalized [stimulus-functions](stimupy.stimuli)
that can generate many parameterizated stimulus images,
and several complete sets of stimuli
that all come from the same {py:mod}`source <stimupy.papers>`.
Development, organization and maintenance of these sets can be arduous,
and we are committed to providing a high-quality and well-tested suite of functions.
Thus, `stimupy` is a curated and maintained _library_ of stimuli
(stimulus-functions, to be precise),
not a _repository_ or platform for uploading stimuli.
We welcome contributions to this library of stimuli,
and stimulus sets, from the literature.

## Add a stimulus set
If you wish to contribute a complete set of stimuli from a single paper,
please follow these instructions for organizing and formatting the code.

A {py:mod}`paper <stimupy.papers>` should have the following features:

- Each paper is identified using a `paper_key`: a cite-key like indicator,
  usually some combination of author names/initials and publication date.
  - For {py:mod}`modelfest <stimupy.papers.modelfest>` we made an exception.
- All stimuli belonging a `paper` should be in a single python _module_, i.e., `.py` code-file;
  this file is found under `stimupy/papers/<paper_key>.py`
- Only stimuli that `stimupy` functionality should be included in the `<paper_key>.py`
  - We have made a select few exceptions to this rule
- Each stimulus should be its own function
- Each function should only take a `ppd` argument;
  it should replicate the exact size and geometry of the stimulus,
  allowing only the resolution (i.e., pixels per degree) to be changed.
  This argument should have an actual default value,
  which corresponds to the `ppd` used in the original project.
- Thus, different parameterizations of the "same" stimulus
  (e.g. different geometries, polarities, etc.) should be separate functions.
- As a result, each function should run without any arguments, e.g.
  ```{code-block} python
  stim = stimupy.papers.RHS2007.WE_thick()
  ```
- The output stim-`dict`s must contain at least the `img`,
  but should also contain relevant stimulus parameters.
- The output stim-`dict`s may contain additional information (keys),
  such as experimental results,
  if they do not take up too much space.
- Each function should have a [NumPy-style docstring](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard),
  which at least documents the `ppd` argument and the returned dict as follows:
  ```{code-block} python
    """ <short summary of stimulus function>

    Parameters
    ----------
    ppd : int
        Resolution of stimulus in pixels per degree. (default: 32)

    Returns
    -------
    dict of str
        dict with the stimulus (key: "img")
        and additional keys containing stimulus parameters
    """
  ```
  Ideally it also provides detailed information of which exact stimulus it is,
  e.g., through reference to a specific figure in the original paper.
- The paper-module should also contain an `__all__` attribute,
  which contains a list of all function names of the stimuli in the paper.
  Anything in here is considered a stimulus, anything not in here will not be available
  outside the paper module itself.
- The paper-module should have a NumPy style docstring as well,
  documenting at least the full bibliographical reference for the source of the stimuli.

### An example
```{tip}
This file is included as `stimupy/papers/example.py`
and can be `import`ed as `stimupy.papers.example`
```

```{literalinclude} ../../stimupy/papers/example.py
```

### Testing
For each paper, there is a corresponding `tests/papers/<paper_key>.py` file
containing _regression tests_ code.

These tests generate each stimulus from the paper-module,
and compares the produce `img` (and `masks`s) to a _hash_ of the `img` (and `masks`).
The hashed `img`s for all stimuli is stored in a corresponding
`tests/papers/<paper_key>.json` JSON file.
The JSON files can be generated by running
the `tests/papers.gen_ground_truth.py` script.
See `stimupy/tests/papers/paper_tests_template.py` for a template;
to create this regression test suite for your own paper,
replace `paper_key` with the <paper_key>

### Additional features
The paper-modules already included in `stimupy` have a couple quality-of-life features
which can be copied into a new module.

A function to generate all stimuli in the set:
```{code-block} python
def gen_all(ppd=PPD, skip=False):
    stims = {}  # save the stimulus-dicts in a larger dict, with name as key
    for stim_name in __all__:
        logger.info(f"Generating RHS2007.{stim_name}")

        # Get a reference to the actual function
        func = globals()[stim_name]
        try:
            stim = func(ppd=ppd, pad=pad)

            # Accumulate
            stims[stim_name] = stim
        except NotImplementedError as e:
            if not skip:
                raise e
            # Skip stimuli that aren't implemented
            logger.info("-- not implemented")
            pass

    return stims
```

A code block that gets executed when the `.py` file is run as a script
(rather than imported), and shows an overview of all stimuli in this set:
```{code-block} python
if __name__ == "__main__":
    from stimupy.utils import plot_stimuli

    stims = gen_all(pad=True, skip=True)
    plot_stimuli(stims, mask=True)

```


## Edit or add a stimulus function
- To contribute a stimulus function, add it to the corresponding module


## Contributing back to `stimupy`

0. **Edit** code/documentation
1. **Commit & Push** changes to your fork
    - We use [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) messages.
    For bugfixes, please start your commit message(s) with `fix: ...`.
    For add features (e.g., stimuli, set), please start your commit message(s) with `feat: ...`.
2. **Pull request** from your fork to our repository
    - GitHub Actions will automatically run tests and linters
    - If linters fail, run `black`, `pyupgrade` and `flake8` --
      either separately or all together through `pre-commit`:
      `pre-commit run --all-files`
3. Changes will be reviewed by one of the maintainers