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

ctapipe-quickstart tool #1774

Merged
merged 14 commits into from
Sep 1, 2021
10 changes: 10 additions & 0 deletions ctapipe/io/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import uuid
import warnings
from collections import OrderedDict
import os
import pwd

from astropy.time import Time
from tables import NaturalNameWarning
Expand Down Expand Up @@ -55,6 +57,14 @@ class Contact(Configurable):
email = Unicode("unknown").tag(config=True)
organization = Unicode("unknown").tag(config=True)

@default("name")
def default_name(self):
""" if no name specified, use the system's user name"""
try:
return pwd.getpwuid(os.getuid()).pw_gecos
except RuntimeError:
return ""

def __repr__(self):
return f"Contact(name={self.name}, email={self.email}, organization={self.organization})"

Expand Down
165 changes: 165 additions & 0 deletions ctapipe/tools/quickstart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
"""
Create a working directory for ctapipe-process containing standard
configuration files.
"""
try:
from importlib.resources import files
except ImportError:
from importlib_resources import files

from pathlib import Path

from ..core import Provenance, Tool, traits
from ..version import __version__ as VERSION

__all__ = ["QuickStartTool"]

CONFIGS_TO_WRITE = ["stage1_config.json", "stage2_config.json", "training_config.json"]

README_TEXT = f"""
ctapipe working directory
-------------------------

This working directory contains some example configuration files that are useful
for processing data with `ctapipe-process`. These include:

- stage1_config.json: generate DL1 data from lower data levels
- stage2_config.json: generate DL2 shower geometry from DL1 or lower levels
- training_config.json: generate both DL1 parameter and DL2 shower geometry data

You can modify these to change the output, and run ctapipe using:

```
ctapipe-process --config <CONFIG> --input <EVENTS FILE> --output <OUTPUT FILE>
```

Where <EVENTS FILE> is any ctapipe-readable event file at a lower or equal data
level to the one requested to be produced, and <CONFIG> is one of the
configuration files generated by `ctapipe-quickstart`.

Details about all configuration options can be found by running:

```
ctapipe-process --help-all
```

This file was generated using ctapipe version {VERSION}
"""


def copy_with_transforms(input_file: Path, output_file: Path, transforms: dict):
"""reads input_file and writes output_file, swapping text listed in the
transformations dict

Parameters
----------
input_file: str
template file to read
output_file: str
file to write
transformations: Dict[str, str]
dict of search and replacement strings
"""

input_file = Path(input_file)
output_file = Path(output_file)

template = input_file.read_text()
for find, replace in transforms.items():
template = template.replace(find, replace)

output_file.write_text(template)


class QuickStartTool(Tool):
"""
Generate quick start files and directory structure.
"""

name = "ctapipe-quickstart"
description = __doc__
examples = """
To be prompted for contact info:

ctapipe-quickstart --workdir MyProduction

Or specify it all in the command-line:

ctapipe-quickstart --name "my name" --contact "[email protected]" --org "My Organization" --workdir Work
kosack marked this conversation as resolved.
Show resolved Hide resolved
"""

workdir = traits.Path(
default_value="./Work",
directory_ok=True,
file_ok=False,
help="working directory where configuration files should be written",
).tag(config=True)

contact_name = traits.Unicode("", help="Contact name").tag(config=True)
contact_email = traits.Unicode("", help="Contact email").tag(config=True)
contact_organization = traits.Unicode("", help="Contact organization").tag(
config=True
)

aliases = {
("d", "workdir"): "QuickStartTool.workdir",
("n", "name"): "QuickStartTool.contact_name",
("e", "email"): "QuickStartTool.contact_email",
("o", "org"): "QuickStartTool.contact_organization",
}

def setup(self):
self.workdir.mkdir(parents=True, exist_ok=True)

if self.contact_name == "":
print("Enter your contact name: ", end="")
self.contact_name = input()

if self.contact_email == "":
print("Enter your contact email: ", end="")
self.contact_email = input()

if self.contact_organization == "":
print("Enter your organization: ", end="")
self.contact_organization = input()

self.transforms = {
"YOUR-NAME-HERE": self.contact_name,
"[email protected]": self.contact_email,
"YOUR-ORGANIZATION": self.contact_organization,
}

def start(self):

for filename in CONFIGS_TO_WRITE:
config = files("ctapipe.tools.tests.resources").joinpath(filename)
destination = self.workdir / filename

if destination.exists():
self.log.warning(
"%s exists, please remove it if you want to generate a new one",
destination,
)
continue

copy_with_transforms(config, destination, transforms=self.transforms)
Provenance().add_output_file(destination, role="ctapipe-process config")

# also generate a README file
readme = self.workdir / "README.md"
if not readme.exists():
readme.write_text(README_TEXT)
Provenance().add_output_file(readme, role="README")

def finish(self):
print(f"Generated examples in {self.workdir}")


def main():
""" run the tool"""
tool = QuickStartTool()
tool.run()


if __name__ == "__main__":
main()
6 changes: 3 additions & 3 deletions ctapipe/tools/tests/resources/stage1_config.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"DataWriter": {
"Contact": {
"name": "YOUR NAME HERE",
"email": "[email protected]",
"organization": "CTA Consortium"
"name": "YOUR-NAME-HERE",
"email": "[email protected]",
"organization": "YOUR-ORGANIZATION"
},
"overwrite": false,
"write_images": true,
Expand Down
5 changes: 5 additions & 0 deletions ctapipe/tools/tests/resources/stage2_config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"DataWriter": {
"Contact": {
"name": "YOUR-NAME-HERE",
"email": "[email protected]",
"organization": "YOUR-ORGANIZATION"
},
"overwrite": false,
"write_images": false,
"write_parameters": false,
Expand Down
5 changes: 5 additions & 0 deletions ctapipe/tools/tests/resources/training_config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"DataWriter": {
"Contact": {
"name": "YOUR-NAME-HERE",
"email": "[email protected]",
"organization": "YOUR-ORGANIZATION"
},
"overwrite": false,
"write_images": false,
"write_parameters": true,
Expand Down
40 changes: 39 additions & 1 deletion ctapipe/tools/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
"""

import pandas as pd
import pytest
import tables
from ctapipe.core import run_tool
from ctapipe.io import DataLevel, EventSource
from ctapipe.tools.process import ProcessorTool
from ctapipe.tools.quickstart import CONFIGS_TO_WRITE, QuickStartTool
from ctapipe.utils import get_dataset_path
from ctapipe.io import DataLevel, EventSource

try:
from importlib.resources import files
Expand Down Expand Up @@ -247,3 +249,39 @@ def test_training_from_simtel(tmp_path):
with tables.open_file(output, mode="r") as testfile:
assert testfile.root.dl1.event.telescope.parameters.tel_002
assert testfile.root.dl2.event.subarray.geometry.HillasReconstructor


@pytest.mark.parametrize("filename", CONFIGS_TO_WRITE)
def test_quickstart_templates(filename):
""" ensure template configs have an appropriate placeholder for the contact info """
config = files("ctapipe.tools.tests.resources").joinpath(filename)
text = config.read_text()

assert "YOUR-NAME-HERE" in text, "Missing expected name placeholder"
assert "[email protected]" in text, "Missing expected email placeholder"
assert "YOUR-ORGANIZATION" in text, "Missing expected org placeholder"


def test_quickstart(tmp_path):
""" ensure quickstart tool generates expected output """

tool = QuickStartTool()
run_tool(
tool,
cwd=tmp_path,
argv=[
"--workdir",
"ProdX",
"--name",
"test",
"--email",
"[email protected]",
"--org",
"CTA",
],
)

assert (tmp_path / "ProdX" / "README.md").exists()

for config in CONFIGS_TO_WRITE:
assert (tmp_path / "ProdX" / config).exists()
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"ctapipe-process = ctapipe.tools.process:main",
"ctapipe-merge = ctapipe.tools.dl1_merge:main",
"ctapipe-fileinfo = ctapipe.tools.fileinfo:main",
"ctapipe-quickstart = ctapipe.tools.quickstart:main",
]
tests_require = ["pytest", "pandas>=0.24.0", "importlib_resources;python_version<'3.9'"]
docs_require = [
Expand Down Expand Up @@ -59,6 +60,7 @@
"zstandard",
"requests",
"setuptools_scm>=3.4",
"importlib_resources;python_version<'3.9'",
],
# here are optional dependencies (as "tag" : "dependency spec")
extras_require={
Expand Down