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

feat: support for report plugins #18

Merged
merged 3 commits into from
Feb 16, 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
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,29 @@ cd snakemake-storage-plugin-myfancystorage
# Scaffold the project as a snakemake executor plugin
poetry scaffold-snakemake-storage-plugin

# Next, edit the scaffolded code according to your needs, and publish
# the resulting plugin into a github repository. The scaffold command also
# creates github actions workflows that will immediately start to check and test
# the plugin.
```

## Scaffolding a report plugin

Lets assume that you want to create a snakemake report plugin with the name `snakemake-report-plugin-myfancyreport`.

```bash

# Install poetry plugin via
poetry self add poetry-snakemake-plugin

# Create a new poetry project via
poetry new snakemake-report-plugin-myfancyreport

cd snakemake-storage-plugin-myfancyreport

# Scaffold the project as a snakemake executor plugin
poetry scaffold-snakemake-report-plugin

# Next, edit the scaffolded code according to your needs, and publish
# the resulting plugin into a github repository. The scaffold command also
# creates github actions workflows that will immediately start to check and test
Expand Down
5 changes: 5 additions & 0 deletions poetry_snakemake_plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from poetry_snakemake_plugin.executor_plugins import (
ScaffoldSnakemakeExecutorPluginCommand,
)
from poetry_snakemake_plugin.report_plugins import ScaffoldSnakemakeReportPluginCommand
from poetry_snakemake_plugin.storage_plugins import (
ScaffoldSnakemakeStoragePluginCommand,
)
Expand All @@ -18,3 +19,7 @@ def activate(self, application):
ScaffoldSnakemakeStoragePluginCommand.name,
ScaffoldSnakemakeStoragePluginCommand,
)
application.command_loader.register_factory(
ScaffoldSnakemakeReportPluginCommand.name,
ScaffoldSnakemakeReportPluginCommand,
)
21 changes: 21 additions & 0 deletions poetry_snakemake_plugin/report_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pathlib import Path
from typing import List

from poetry_snakemake_plugin.common import ScaffoldSnakemakePluginCommandBase


class ScaffoldSnakemakeReportPluginCommand(ScaffoldSnakemakePluginCommandBase):
name = "scaffold-snakemake-report-plugin"
description = (
"Scaffolds a snakemake report plugin by adding recommended "
"dependencies and code snippets."
)

def get_templates(self, module_path: Path, tests_path: Path) -> List[str]:
return [
("report-plugins/init.py", module_path / "__init__.py"),
("report-plugins/tests.py", tests_path / "tests.py"),
]

def get_plugin_type(self) -> str:
return "report"
56 changes: 56 additions & 0 deletions poetry_snakemake_plugin/templates/report-plugins/init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from dataclasses import dataclass, field
from typing import Optional

from snakemake_interface_common.exceptions import WorkflowError # noqa: F401
from snakemake_interface_report_plugins.reporter import ReporterBase
from snakemake_interface_report_plugins.settings import ReportSettingsBase


# Optional:
# Define additional settings for your reporter.
# They will occur in the Snakemake CLI as --report-<reporter-name>-<param-name>
# Omit this class if you don't need any.
# Make sure that all defined fields are Optional (or bool) and specify a default value
# of None (or False) or anything else that makes sense in your case.
@dataclass
class ReportSettings(ReportSettingsBase):
myparam: Optional[int] = field(
default=None,
metadata={
"help": "Some help text",
# Optionally request that setting is also available for specification
# via an environment variable. The variable will be named automatically as
# SNAKEMAKE_REPORT_<reporter-name>_<param-name>, all upper case.
# This mechanism should ONLY be used for passwords and usernames.
# For other items, we rather recommend to let people use a profile
# for setting defaults
# (https://snakemake.readthedocs.io/en/stable/executing/cli.html#profiles).
"env_var": False,
# Optionally specify a function that parses the value given by the user.
# This is useful to create complex types from the user input.
"parse_func": ...,
# If a parse_func is specified, you also have to specify an unparse_func
# that converts the parsed value back to a string.
"unparse_func": ...,
# Optionally specify that setting is required when the reporter is in use.
"required": True,
},
)


# Required:
# Implementation of your reporter
class Reporter(ReporterBase):
def __post_init__(self):
# initialize additional attributes
# Do not overwrite the __init__ method as this is kept in control of the base
# class in order to simplify the update process.
# See https://github.com/snakemake/snakemake-interface-report-plugins/snakemake_interface_report_plugins/reporter.py # noqa: E501
# for attributes of the base class.
# In particular, the settings of above ReportSettings class are accessible via
# self.settings.
...

def render(self):
# Render the report, using attributes of the base class.
...
16 changes: 16 additions & 0 deletions poetry_snakemake_plugin/templates/report-plugins/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from typing import Optional
import snakemake.common.tests
from snakemake_interface_report_plugins.settings import ReportSettingsBase


# Check out the base classes found here for all possible options and methods:
# https://github.com/snakemake/snakemake/blob/main/snakemake/common/tests/__init__.py
class TestWorkflowsBase(snakemake.common.tests.TestReportBase):
__test__ = True

def get_reporter(self) -> str:
return "{{plugin_name}}"

def get_report_settings(self) -> Optional[ReportSettingsBase]:
# instantiate ReportSettings of this plugin as appropriate
...
1 change: 1 addition & 0 deletions poetry_snakemake_plugin/templates/storage-plugins/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def get_query(self, tmp_path) -> str:
...

def get_query_not_existing(self, tmp_path) -> str:
# Return a query that is not present in the storage.
...

def get_storage_provider_cls(self) -> Type[StorageProviderBase]:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ build-backend = "poetry.core.masonry.api"


[tool.poetry.plugins."poetry.application.plugin"]
scaffold-snakemake-executor-plugin = "poetry_snakemake_plugin:ScaffoldSnakemakeExecutorPlugin"
scaffold-snakemake-plugin = "poetry_snakemake_plugin:ScaffoldSnakemakeExecutorPlugin"
13 changes: 13 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ def test_new_snakemake_storage_plugin(tmp_path):
os.chdir(orig_dir)


def test_new_snakemake_report_plugin(tmp_path):
orig_dir = os.getcwd()
os.chdir(tmp_path)

run_subcommand("new", "snakemake-report-plugin-test")
os.chdir("snakemake-report-plugin-test")
run_subcommand("scaffold-snakemake-report-plugin")

run_subcommand("run", "black", "--check", "--diff", ".")
run_subcommand("run", "flake8")
os.chdir(orig_dir)


def run_subcommand(*args):
cmd = ["poetry"]
cmd.extend(args)
Expand Down
Loading