Skip to content

Commit

Permalink
Merge pull request #70 from zooniverse/update-help
Browse files Browse the repository at this point in the history
Documentation and usability updates
  • Loading branch information
adammcmaster authored Nov 16, 2017
2 parents beb532f + d71d43b commit 69b42da
Show file tree
Hide file tree
Showing 8 changed files with 357 additions and 43 deletions.
101 changes: 93 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,23 @@ the API behind [the Zooniverse](https://www.zooniverse.org/).

## Installation

In most cases, you can just use `pip` to install the latest release:
The Panoptes CLI is written in Python, so in order to install it you will need
to install either Python 2 or Python 3, along with `pip`. macOS and Linux
already come with Python installed, so run this to see if you already have
everything you need:

```
$ python --version && pip --version
```

If you see an error like `python: command not found` or `pip: command not found`
then you will need to install this:

- [Python installation](https://wiki.python.org/moin/BeginnersGuide/Download)
- [Pip installation](https://pip.pypa.io/en/stable/installing/)

Once these are installed you can just use `pip` to install the latest release of
the CLI:

```
$ pip install panoptescli
Expand All @@ -25,6 +41,81 @@ To upgrade an existing installation to the latest version:
pip install -U panoptescli
```

## Built-in help

Every command comes with a built in `--help` option, which explains how to use
it.

```
$ panoptes --help
Usage: panoptes [OPTIONS] COMMAND [ARGS]...
Options:
-e, --endpoint TEXT Overides the default API endpoint
-a, --admin Enables admin mode. Ignored if you're not logged in as
an administrator.
--version Show the version and exit.
--help Show this message and exit.
Commands:
configure Sets default values for configuration...
info Displays version and environment information...
project Contains commands for managing projects.
subject Contains commands for retrieving information...
subject-set Contains commands for managing subject sets.
workflow Contains commands for managing workflows.
```

```
$ panoptes project --help
Usage: panoptes project [OPTIONS] COMMAND [ARGS]...
Contains commands for managing projects.
Options:
--help Show this message and exit.
Commands:
create Creates a new project.
download Downloads project-level data exports.
ls Lists project IDs and names.
modify Changes the attributes of an existing...
```

```
$ panoptes subject-set upload-subjects --help
Usage: panoptes subject-set upload-subjects [OPTIONS] SUBJECT_SET_ID MANIFEST_FILES...
Uploads subjects from each of the given MANIFEST_FILES.
Example with only local files:
$ panoptes subject-set upload-subjects 4667 manifest.csv
Local filenames will be automatically detected in the manifest and
uploaded.
If you are hosting your media yourself, you can put the URLs in the
manifest and specify the column number(s):
$ panoptes subject-set upload-subjects -r 1 4667 manifest.csv
$ panoptes subject-set upload-subjects -r 1 -r 2 4667 manifest.csv
Any local files will still be detected and uploaded.
Options:
-M, --allow-missing Do not abort when creating subjects with no
media files.
-r, --remote-location INTEGER Specify a field (by column number) in the
manifest which contains a URL to a remote
media location. Can be used more than once.
-m, --mime-type TEXT MIME type for remote media. Defaults to
image/png. Has no effect without --remote-
location.
--help Show this message and exit.
```

## Uploading non-image media types

If you wish to upload subjects with non-image media (e.g. audio or video),
Expand All @@ -44,13 +135,7 @@ If you see `libmagic: False` in the output then it isn't installed.
## Command Line Examples

This readme does not list everything that the CLI can do. For a full list of
commands and their options, use the built in help. E.g.:

```
$ panoptes --help
$ panoptes project --help
$ panoptes subject-set upload-subjects --help
```
commands and their options, use the built in help as described above.

### Log in and optionally set the API endpoint

Expand Down
2 changes: 2 additions & 0 deletions panoptes_cli/commands/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
@cli.command()
@click.pass_context
def configure(ctx):
"""Sets default values for configuration options."""

if not os.path.isdir(ctx.parent.config_dir):
os.mkdir(ctx.parent.config_dir)

Expand Down
2 changes: 2 additions & 0 deletions panoptes_cli/commands/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
@cli.command()
@click.pass_context
def info(ctx):
"""Displays version and environment information for debugging."""

info = {
'Panoptes CLI version': (
pkg_resources.require("panoptescli")[0].version
Expand Down
133 changes: 117 additions & 16 deletions panoptes_cli/commands/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,53 @@

@cli.group()
def project():
"""Contains commands for managing projects."""
pass

@project.command()
@click.option('--project-id', '-p', required=False, type=int)
@click.option('--display-name', '-n')
@click.option('--launch-approved', '-a', is_flag=True)
@click.option('--slug', '-s')
@click.option(
'--project-id',
'-p',
help="Show the project with the given ID.",
required=False,
type=int,
)
@click.option(
'--display-name',
'-n',
help="Show projects whose display name exactly matches the given string.",
)
@click.option(
'--launch-approved',
'-a',
help="Only show projects which have been approved by the Zooniverse.",
is_flag=True
)
@click.option(
'--slug',
'-s',
help="Show the project whose slug exactly matches the given string.",
)
@click.option(
'--quiet',
'-q',
is_flag=True,
help='Only print project IDs',
help='Only print project IDs (omit project names).',
)
@click.argument(
'search',
required=False,
nargs=-1
)
@click.argument('search', required=False, nargs=-1)
def ls(project_id, display_name, launch_approved, slug, quiet, search):
"""
Lists project IDs and names.
Any given SEARCH terms are used for a full-text search of project titles.
A "*" before the project ID indicates that the project is private.
"""

if not launch_approved:
launch_approved = None

Expand All @@ -40,9 +72,25 @@ def ls(project_id, display_name, launch_approved, slug, quiet, search):
@project.command()
@click.argument('display-name', required=True)
@click.argument('description', required=True)
@click.option('--primary-language', '-l', default='en')
@click.option('--public', '-p', is_flag=True)
@click.option(
'--primary-language',
'-l',
help="Sets the language code for the project's primary language.",
default='en'
)
@click.option(
'--public',
'-p',
help="Makes the project publically accessible.",
is_flag=True
)
def create(display_name, description, primary_language, public):
"""
Creates a new project.
Prints the project ID and name of the new project.
"""

project = Project()
project.display_name = display_name
project.description = description
Expand All @@ -53,30 +101,65 @@ def create(display_name, description, primary_language, public):

@project.command()
@click.argument('project-id', required=True, type=int)
@click.option('--display-name', '-n', required=False)
@click.option('--description', '-d', required=False)
@click.option('--primary-language', '-l', default='en')
@click.option('--public', '-p', is_flag=True)
@click.option(
'--display-name',
'-n',
help="Sets the project's public display name.",
required=False,
)
@click.option(
'--description',
'-d',
help="Sets the full-text description of the project.",
required=False,
)
@click.option(
'--primary-language',
'-l',
help="Sets the language code for the project's primary language.",
default='en',
)
@click.option(
'--public/--private',
'-p/-P',
help="Sets the project to be public or private.",
is_flag=True
)
def modify(project_id, display_name, description, primary_language, public):
"""
Changes the attributes of an existing project.
Any attributes which are not specified are left unchanged.
"""

project = Project.find(project_id)
if display_name:
project.display_name = display_name
if description:
project.description = description
if primary_language:
project.primary_language = primary_language
if private is not None:
if public is not None:
project.private = not public
project.save()
echo_project(project)

@project.command()
@click.argument('project-id', required=True, type=int)
@click.argument('output-file', required=True, type=click.File('wb'))
@click.option('--generate', '-g', is_flag=True)
@click.option(
'--generate',
'-g',
help="Generates a new export before downloading.",
is_flag=True
)
@click.option(
'--generate-timeout',
'-T',
help=(
"Time in seconds to wait for new export to be ready. Defaults to "
"unlimited. Has no effect unless --generate is given."
),
required=False,
type=int,
)
Expand All @@ -93,14 +176,32 @@ def modify(project_id, display_name, description, primary_language, public):
default='classifications'
)
def download(project_id, output_file, generate, generate_timeout, data_type):
"""
Downloads project-level data exports.
OUTPUT_FILE will be overwritten if it already exists. Set OUTPUT_FILE to -
to output to stdout.
"""

project = Project.find(project_id)

if generate:
click.echo("Generating new export...", err=True)

export = project.get_export(
data_type,
generate=generate,
wait_timeout=generate_timeout
)
for chunk in export.iter_content():
output_file.write(chunk)

with click.progressbar(
export.iter_content(chunk_size=1024),
label='Downloading',
length=(int(export.headers.get('content-length')) / 1024 + 1),
file=click.get_text_stream('stderr'),
) as chunks:
for chunk in chunks:
output_file.write(chunk)

def echo_project(project):
click.echo(
Expand Down
9 changes: 8 additions & 1 deletion panoptes_cli/commands/subject.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,30 @@

@cli.group()
def subject():
"""Contains commands for retrieving information about subjects."""

pass

@subject.command()
@click.option(
'--subject-set-id',
'-s',
help="List subjects from the given subject set.",
type=int,
required=False
)
@click.option(
'--quiet',
'-q',
is_flag=True,
help='Only print subject IDs',
help='Only print subject IDs (omit media URLs).',
)
@click.argument('subject-ids', type=int, required=False, nargs=-1)
def ls(subject_set_id, quiet, subject_ids):
"""
Lists subject IDs and their media URLs.
"""

if subject_ids:
for subject_id in subject_ids:
subject = Subject.find(subject_id)
Expand Down
Loading

0 comments on commit 69b42da

Please sign in to comment.