Changeset View
Changeset View
Standalone View
Standalone View
swh/deposit/cli/deposit.py
- This file was moved from swh/deposit/cli.py.
# Copyright (C) 2017-2019 The Software Heritage developers | # Copyright (C) 2017-2019 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU General Public License version 3, or any later version | # License: GNU General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
import click | |||||
import os | |||||
import logging | import logging | ||||
import os | |||||
import uuid | import uuid | ||||
from swh.deposit.config import setup_django_for | import click | ||||
try: | |||||
from swh.deposit.client import PublicApiDepositClient | |||||
except ImportError: | |||||
logging.warn("Optional client subcommand unavailable. " | |||||
"Install swh.deposit.client to be able to use it.") | |||||
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) | from swh.deposit.client import PublicApiDepositClient | ||||
from swh.deposit.cli import cli | |||||
@click.group(context_settings=CONTEXT_SETTINGS) | |||||
@click.option('--config-file', '-C', default=None, | |||||
type=click.Path(exists=True, dir_okay=False,), | |||||
help="Optional extra configuration file.") | |||||
@click.option('--platform', default='development', | |||||
type=click.Choice(['development', 'production']), | |||||
help='development or production platform') | |||||
@click.option('--verbose/--no-verbose', default=False, | |||||
help='Verbose mode') | |||||
@click.pass_context | |||||
def cli(ctx, config_file, platform, verbose): | |||||
logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||
logger.addHandler(logging.StreamHandler()) | |||||
_loglevel = logging.DEBUG if verbose else logging.INFO | |||||
logger.setLevel(_loglevel) | |||||
ctx.ensure_object(dict) | |||||
# configuration happens here | |||||
setup_django_for(platform, config_file=config_file) | |||||
ctx.obj = {'loglevel': _loglevel} | |||||
@cli.group('user') | |||||
@click.pass_context | |||||
def user(ctx): | |||||
"""Manipulate user.""" | |||||
pass | |||||
def _create_collection(name): | |||||
"""Create the collection with name if it does not exist. | |||||
Args: | |||||
name (str): collection's name | |||||
Returns: | |||||
collection (DepositCollection): the existing collection object | |||||
(created or not) | |||||
""" | |||||
# to avoid loading too early django namespaces | |||||
from swh.deposit.models import DepositCollection | |||||
try: | |||||
collection = DepositCollection.objects.get(name=name) | |||||
click.echo('Collection %s exists, nothing to do.' % name) | |||||
except DepositCollection.DoesNotExist: | |||||
click.echo('Create new collection %s' % name) | |||||
collection = DepositCollection.objects.create(name=name) | |||||
click.echo('Collection %s created' % name) | |||||
return collection | |||||
@user.command('create') | |||||
@click.option('--username', required=True, help="User's name") | |||||
@click.option('--password', required=True, | |||||
help="Desired user's password (plain).") | |||||
@click.option('--firstname', default='', help="User's first name") | |||||
@click.option('--lastname', default='', help="User's last name") | |||||
@click.option('--email', default='', help="User's email") | |||||
@click.option('--collection', help="User's collection") | |||||
@click.pass_context | |||||
def user_create(ctx, username, password, firstname, lastname, email, | |||||
collection): | |||||
"""Create a user with some needed information (password, collection) | |||||
If the collection does not exist, the collection is then created | |||||
alongside. | |||||
The password is stored encrypted using django's utilies. | |||||
""" | |||||
# to avoid loading too early django namespaces | |||||
from swh.deposit.models import DepositClient | |||||
click.echo('collection: %s' % collection) | |||||
# create the collection if it does not exist | |||||
collection = _create_collection(collection) | |||||
# user create/update | |||||
try: | |||||
user = DepositClient.objects.get(username=username) | |||||
click.echo('User %s exists, updating information.' % user) | |||||
user.set_password(password) | |||||
except DepositClient.DoesNotExist: | |||||
click.echo('Create new user %s' % username) | |||||
user = DepositClient.objects.create_user( | |||||
username=username, | |||||
password=password) | |||||
user.collections = [collection.id] | |||||
user.first_name = firstname | |||||
user.last_name = lastname | |||||
user.email = email | |||||
user.is_active = True | |||||
user.save() | |||||
click.echo('Information registered for user %s' % user) | |||||
@user.command('list') | |||||
@click.pass_context | |||||
def user_list(ctx): | |||||
"""List existing users. | |||||
This entrypoint is not paginated yet as there is not a lot of | |||||
entry. | |||||
""" | |||||
# to avoid loading too early django namespaces | |||||
from swh.deposit.models import DepositClient | |||||
users = DepositClient.objects.all() | |||||
if not users: | |||||
output = 'Empty user list' | |||||
else: | |||||
output = '\n'.join((user.username for user in users)) | |||||
click.echo(output) | |||||
@cli.group('collection') | |||||
@click.pass_context | |||||
def collection(ctx): | |||||
"""Manipulate collection.""" | |||||
pass | |||||
@collection.command('create') | |||||
@click.option('--name', required=True, help="Collection's name") | |||||
@click.pass_context | |||||
def collection_create(ctx, name): | |||||
_create_collection(name) | |||||
@collection.command('list') | |||||
@click.pass_context | |||||
def collection_list(ctx): | |||||
"""List existing collections. | |||||
This entrypoint is not paginated yet as there is not a lot of | |||||
entry. | |||||
""" | |||||
# to avoid loading too early django namespaces | |||||
from swh.deposit.models import DepositCollection | |||||
collections = DepositCollection.objects.all() | |||||
if not collections: | |||||
output = 'Empty collection list' | |||||
else: | |||||
output = '\n'.join((col.name for col in collections)) | |||||
click.echo(output) | |||||
class InputError(ValueError): | class InputError(ValueError): | ||||
"""Input script error | """Input script error | ||||
""" | """ | ||||
pass | pass | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | def client_command_parse_input( | ||||
}) | }) | ||||
if not collection: | if not collection: | ||||
# retrieve user's collection | # retrieve user's collection | ||||
sd_content = client.service_document() | sd_content = client.service_document() | ||||
if 'error' in sd_content: | if 'error' in sd_content: | ||||
raise InputError('Service document retrieval: %s' % ( | raise InputError('Service document retrieval: %s' % ( | ||||
sd_content['error'], )) | sd_content['error'], )) | ||||
collection = sd_content['collection'] | collection = sd_content[ | ||||
'service']['workspace']['collection']['sword:name'] | |||||
if not slug: | if not slug: | ||||
# generate slug | # generate slug | ||||
slug = generate_slug() | slug = generate_slug() | ||||
return { | return { | ||||
'archive': archive, | 'archive': archive, | ||||
'username': username, | 'username': username, | ||||
'password': password, | 'password': password, | ||||
'metadata': metadata, | 'metadata': metadata, | ||||
'collection': collection, | 'collection': collection, | ||||
'slug': slug, | 'slug': slug, | ||||
'partial': partial, | 'in_progress': partial, | ||||
'client': client, | 'client': client, | ||||
'url': url, | 'url': url, | ||||
'deposit_id': deposit_id, | 'deposit_id': deposit_id, | ||||
'replace': replace, | 'replace': replace, | ||||
} | } | ||||
def deposit_status(config, dry_run, logger): | def _subdict(d, keys): | ||||
'return a dict from d with only given keys' | |||||
return {k: v for k, v in d.items() if k in keys} | |||||
def deposit_status(config, logger): | |||||
logger.debug('Status deposit') | logger.debug('Status deposit') | ||||
keys = ('collection', 'deposit_id') | |||||
client = config['client'] | client = config['client'] | ||||
collection = config['collection'] | return client.deposit_status( | ||||
deposit_id = config['deposit_id'] | **_subdict(config, keys)) | ||||
if not dry_run: | |||||
r = client.deposit_status(collection, deposit_id, logger) | |||||
return r | |||||
return {} | |||||
def deposit_create(config, dry_run, logger): | def deposit_create(config, logger): | ||||
"""Delegate the actual deposit to the deposit client. | """Delegate the actual deposit to the deposit client. | ||||
""" | """ | ||||
logger.debug('Create deposit') | logger.debug('Create deposit') | ||||
client = config['client'] | client = config['client'] | ||||
collection = config['collection'] | keys = ('collection', 'archive', 'metadata', 'slug', 'in_progress') | ||||
archive_path = config['archive'] | return client.deposit_create( | ||||
metadata_path = config['metadata'] | **_subdict(config, keys)) | ||||
slug = config['slug'] | |||||
in_progress = config['partial'] | |||||
if not dry_run: | |||||
r = client.deposit_create(collection, slug, archive_path, | |||||
metadata_path, in_progress, logger) | |||||
return r | |||||
return {} | |||||
def deposit_update(config, dry_run, logger): | def deposit_update(config, logger): | ||||
"""Delegate the actual deposit to the deposit client. | """Delegate the actual deposit to the deposit client. | ||||
""" | """ | ||||
logger.debug('Update deposit') | logger.debug('Update deposit') | ||||
client = config['client'] | client = config['client'] | ||||
collection = config['collection'] | keys = ('collection', 'deposit_id', 'archive', 'metadata', | ||||
deposit_id = config['deposit_id'] | 'slug', 'in_progress', 'replace') | ||||
archive_path = config['archive'] | return client.deposit_update( | ||||
metadata_path = config['metadata'] | **_subdict(config, keys)) | ||||
slug = config['slug'] | |||||
in_progress = config['partial'] | |||||
replace = config['replace'] | |||||
if not dry_run: | |||||
r = client.deposit_update(collection, deposit_id, slug, archive_path, | |||||
metadata_path, in_progress, replace, logger) | |||||
return r | |||||
return {} | |||||
@cli.command() | @cli.command() | ||||
@click.option('--username', required=1, | @click.option('--username', required=1, | ||||
help="(Mandatory) User's name") | help="(Mandatory) User's name") | ||||
@click.option('--password', required=1, | @click.option('--password', required=1, | ||||
help="(Mandatory) User's associated password") | help="(Mandatory) User's associated password") | ||||
@click.option('--archive', | @click.option('--archive', | ||||
Show All 13 Lines | |||||
@click.option('--deposit-id', default=None, | @click.option('--deposit-id', default=None, | ||||
help='(Optional) Update an existing partial deposit with its identifier') # noqa | help='(Optional) Update an existing partial deposit with its identifier') # noqa | ||||
@click.option('--replace/--no-replace', default=False, | @click.option('--replace/--no-replace', default=False, | ||||
help='(Optional) Update by replacing existing metadata to a deposit') # noqa | help='(Optional) Update by replacing existing metadata to a deposit') # noqa | ||||
@click.option('--url', default='https://deposit.softwareheritage.org/1', | @click.option('--url', default='https://deposit.softwareheritage.org/1', | ||||
help="(Optional) Deposit server api endpoint. By default, https://deposit.softwareheritage.org/1") # noqa | help="(Optional) Deposit server api endpoint. By default, https://deposit.softwareheritage.org/1") # noqa | ||||
@click.option('--status/--no-status', default=False, | @click.option('--status/--no-status', default=False, | ||||
help="(Optional) Deposit's status") | help="(Optional) Deposit's status") | ||||
@click.option('--dry-run/--no-dry-run', default=False, | |||||
help='(Optional) No-op deposit') | |||||
@click.option('--verbose/--no-verbose', default=False, | @click.option('--verbose/--no-verbose', default=False, | ||||
help='Verbose mode') | help='Verbose mode') | ||||
@click.pass_context | @click.pass_context | ||||
def deposit(ctx, | def deposit(ctx, | ||||
username, password, archive=None, metadata=None, | username, password, archive=None, metadata=None, | ||||
archive_deposit=False, metadata_deposit=False, | archive_deposit=False, metadata_deposit=False, | ||||
collection=None, slug=None, partial=False, deposit_id=None, | collection=None, slug=None, partial=False, deposit_id=None, | ||||
replace=False, status=False, | replace=False, status=False, | ||||
url='https://deposit.softwareheritage.org/1', dry_run=True, | url='https://deposit.softwareheritage.org/1', | ||||
verbose=False): | verbose=False): | ||||
"""Software Heritage Public Deposit Client | """Software Heritage Public Deposit Client | ||||
Create/Update deposit through the command line or access its | Create/Update deposit through the command line or access its | ||||
status. | status. | ||||
More documentation can be found at | More documentation can be found at | ||||
https://docs.softwareheritage.org/devel/swh-deposit/getting-started.html. | https://docs.softwareheritage.org/devel/swh-deposit/getting-started.html. | ||||
""" | """ | ||||
logger = logging.getLogger(__name__) | |||||
if dry_run: | |||||
logger.info("**DRY RUN**") | |||||
config = {} | config = {} | ||||
try: | try: | ||||
logger.debug('Parsing cli options') | logger.debug('Parsing cli options') | ||||
config = client_command_parse_input( | config = client_command_parse_input( | ||||
username, password, archive, metadata, archive_deposit, | username, password, archive, metadata, archive_deposit, | ||||
metadata_deposit, collection, slug, partial, deposit_id, | metadata_deposit, collection, slug, partial, deposit_id, | ||||
replace, url, status) | replace, url, status) | ||||
except InputError as e: | except InputError as e: | ||||
msg = 'Problem during parsing options: %s' % e | msg = 'Problem during parsing options: %s' % e | ||||
r = { | r = { | ||||
'error': msg, | 'error': msg, | ||||
} | } | ||||
logger.info(r) | logger.info(r) | ||||
return 1 | return 1 | ||||
if verbose: | if verbose: | ||||
logger.info("Parsed configuration: %s" % ( | logger.info("Parsed configuration: %s" % ( | ||||
config, )) | config, )) | ||||
deposit_id = config['deposit_id'] | deposit_id = config['deposit_id'] | ||||
if status and deposit_id: | if status and deposit_id: | ||||
r = deposit_status(config, dry_run, logger) | r = deposit_status(config, logger) | ||||
elif not status and deposit_id: | elif not status and deposit_id: | ||||
r = deposit_update(config, dry_run, logger) | r = deposit_update(config, logger) | ||||
elif not status and not deposit_id: | elif not status and not deposit_id: | ||||
r = deposit_create(config, dry_run, logger) | r = deposit_create(config, logger) | ||||
logger.info(r) | logger.info(r) | ||||
def main(): | |||||
return cli(auto_envvar_prefix='SWH_DEPOSIT') | |||||
if __name__ == '__main__': | |||||
main() |