From ed3d997bdc0304fdc97d008cf415652a0192c754 Mon Sep 17 00:00:00 2001
From: Maximilian Moser <maximilian.moser@tuwien.ac.at>
Date: Thu, 10 Dec 2020 15:05:30 +0100
Subject: [PATCH] Add further CLI commands for draft management
* add commands for listing/adding/removing files from drafts
* add commands for listing/deleting drafts
---
invenio_utilities_tuw/cli.py | 251 ++++++++++++++++++++++++++++-------
1 file changed, 202 insertions(+), 49 deletions(-)
diff --git a/invenio_utilities_tuw/cli.py b/invenio_utilities_tuw/cli.py
index 1a828e9..bb2e58b 100644
--- a/invenio_utilities_tuw/cli.py
+++ b/invenio_utilities_tuw/cli.py
@@ -2,7 +2,8 @@
import json
import os
-from os.path import basename
+import sys
+from os.path import basename, join, isfile, isdir
import click
from flask.cli import with_appcontext
@@ -11,6 +12,7 @@ from invenio_access import any_user
from invenio_access.utils import get_identity
from invenio_accounts import current_accounts
from invenio_pidstore.models import PersistentIdentifier
+from invenio_rdm_records.records.models import DraftMetadata
from invenio_rdm_records.services.services import (
BibliographicDraftFilesService as DraftFileService,
)
@@ -52,6 +54,44 @@ def get_identity_for_user(user):
return identity
+def convert_to_recid(pid_value, pid_type):
+ if pid_type != "recid":
+ pid_value = (
+ PersistentIdentifier.query.filter_by(pid_value=pid, pid_type=pid_type)
+ .first()
+ .pid_value
+ )
+
+ return pid_value
+
+
+option_as_user = click.option(
+ "--as-user",
+ "-u",
+ "user",
+ metavar="USER",
+ default=None,
+ required=True,
+ help="email address of the user to use for record creation",
+)
+option_pid_type = click.option(
+ "--type",
+ "-t",
+ "pid_type",
+ metavar="PID_TYPE",
+ default="recid",
+ help="pid type (default: 'recid')",
+)
+option_pid_value = click.option(
+ "--pid",
+ "-p",
+ "pid",
+ metavar="PID_VALUE",
+ required=True,
+ help="persistent identifier of the record draft to operate on",
+)
+
+
@click.group()
def utilities():
"""Utility commands for InvenioRDM."""
@@ -64,16 +104,15 @@ def draft():
pass
+@draft.group()
+def files():
+ """Manage files deposited with the draft."""
+ pass
+
+
@draft.command("create")
@click.argument("metadata_path")
-@click.option(
- "--as-user",
- "-u",
- "user",
- default=None,
- metavar="USER",
- help="email address of the user to use for record creation (default: the first user)",
-)
+@option_as_user
@click.option(
"--publish",
"-p",
@@ -87,71 +126,185 @@ def create_draft(metadata_path, publish, user):
recid = None
identity = get_identity_for_user(user)
- if os.path.isfile(metadata_path):
+ if isfile(metadata_path):
draft = create_record_from_metadata(metadata_path, identity)
recid = draft["id"]
- elif os.path.isdir(metadata_path):
- metadata_file_path = os.path.join(metadata_path, "metadata.json")
- deposit_files_path = os.path.join(metadata_path, "files")
- if not os.path.isfile(metadata_file_path):
+ elif isdir(metadata_path):
+ metadata_file_path = join(metadata_path, "metadata.json")
+ deposit_files_path = join(metadata_path, "files")
+ if not isfile(metadata_file_path):
raise Exception("metadata file does not exist: %s" % metadata_file_path)
draft = create_record_from_metadata(metadata_file_path, identity)
recid = draft["id"]
file_names = []
- if os.path.isdir(deposit_files_path):
- file_names = [os.path.basename(fn) for fn in os.listdir(deposit_files_path)]
+ if isdir(deposit_files_path):
+ dir_contents = os.listdir(deposit_files_path)
+ file_names = [basename(fn) for fn in dir_contents if isfile(join(deposit_files_path, fn))]
+ if len(dir_contents) != len(file_names):
+ ignored = [basename(fn) for fn in dir_contents if not isfile(join(deposit_files_path, fn))]
+ click.secho(
+ "ignored in '{}': {}".format(deposit_files_path, ignored),
+ fg="red",
+ err=True,
+ )
service = DraftFileService()
- service.init_files(recid, identity, [{"key": fn} for fn in file_names])
+ service.init_files(
+ id_=recid, identity=identity, data=[{"key": fn} for fn in file_names]
+ )
for fn in file_names:
- file_path = os.path.join(deposit_files_path, fn)
+ file_path = join(deposit_files_path, fn)
with open(file_path, "rb") as deposit_file:
- service.set_file_content(recid, fn, identity, deposit_file)
+ service.set_file_content(
+ id_=recid, file_key=fn, identity=identity, stream=deposit_file
+ )
- service.commit_file(recid, fn, identity)
+ service.commit_file(id_=recid, file_key=fn, identity=identity)
else:
raise Exception("neither a file nor a directory: %s" % metadata_path)
if publish:
service = RecordService()
- service.publish(recid, identity)
+ service.publish(id_=recid, identity=identity)
+
+ click.secho(recid, fg="green")
+
+
+@draft.command("list")
+@option_as_user
+@with_appcontext
+def create_draft(user):
+ """List all drafts accessible to the given user."""
+ identity = get_identity_for_user(user)
+ service = RecordService()
+ recids = [
+ dm.json["id"]
+ for dm in DraftMetadata.query.all()
+ if dm is not None and dm.json is not None
+ ]
+
+ for recid in recids:
+ try:
+ draft = service.read_draft(id_=recid, identity=identity)
+ click.secho(
+ "{} - {}".format(draft.id, draft.data["metadata"]["title"]), fg="green"
+ )
+ except:
+ pass
+
- click.echo(recid)
+@draft.command("delete")
+@option_pid_value
+@option_pid_type
+@option_as_user
+@with_appcontext
+def publish_draft(pid, pid_type, user):
+ """Delete the specified draft."""
+ pid = convert_to_recid(pid, pid_type)
+ identity = get_identity_for_user(user)
+ service = RecordService()
+ service.delete_draft(id_=pid, identity=identity)
+ click.secho(pid, fg="green")
@draft.command("publish")
-@click.argument("pid", metavar="PID_VALUE")
-@click.option(
- "--as-user",
- "-u",
- "user",
- default=None,
- metavar="USER",
- help="email address of the user to use for record creation (default: the first user)",
-)
-@click.option(
- "--type",
- "-t",
- "pid_type",
- default="recid",
- metavar="PID_TYPE",
- help="pid type (default: 'recid')",
-)
+@option_pid_value
+@option_pid_type
+@option_as_user
@with_appcontext
def publish_draft(pid, pid_type, user):
"""Publish the specified draft."""
- if pid_type != "recid":
- pid = (
- PersistentIdentifier.query.filter_by(pid_value=pid, pid_type=pid_type)
- .first()
- .pid_value
- )
- pid_type = "recid"
-
+ pid = convert_to_recid(pid, pid_type)
identity = get_identity_for_user(user)
service = RecordService()
- service.publish(pid, identity)
- click.echo(pid)
+ service.publish(id_=pid, identity=identity)
+ click.secho(pid, fg="green")
+
+
+@files.command("add")
+@click.argument("filepaths", metavar="PATH", type=click.Path(exists=True), nargs=-1)
+@option_pid_value
+@option_pid_type
+@option_as_user
+@with_appcontext
+def add_files(filepaths, pid, pid_type, user):
+ """Add the specified files to the draft."""
+ recid = convert_to_recid(pid, pid_type)
+ identity = get_identity_for_user(user)
+ service = DraftFileService()
+
+ paths = []
+ for file_path in filepaths:
+ if isdir(file_path):
+ # add all files (no recursion into sub-dirs) from the directory
+ dir_contents = os.listdir(file_path)
+ file_names = [
+ basename(fn) for fn in dir_contents if isfile(join(file_path, fn))
+ ]
+ if len(dir_contents) != len(file_names):
+ ignored = [
+ basename(fn)
+ for fn in dir_contents
+ if not isfile(join(file_path, fn))
+ ]
+ click.secho(
+ "ignored in '{}': {}".format(file_path, ignored), fg="red", err=True
+ )
+
+ paths_ = [join(file_path, fn) for fn in file_names]
+ paths.extend(paths_)
+
+ elif isfile(file_path):
+ paths.append(file_path)
+
+ keys = [basename(fp) for fp in paths]
+ if len(set(keys)) != len(keys):
+ click.secho("aborting: duplicates in file names detected", fg="red", err=True)
+ sys.exit(1)
+
+ service.init_files(
+ id_=recid, identity=identity, data=[{"key": basename(fp)} for fp in paths]
+ )
+ for fp in paths:
+ fn = basename(fp)
+ with open(file_path, "rb") as deposit_file:
+ service.set_file_content(
+ id_=recid, file_key=fn, identity=identity, stream=deposit_file
+ )
+ service.commit_file(id_=recid, file_key=fn, identity=identity)
+
+ click.secho(recid, fg="green")
+
+
+@files.command("remove")
+@click.argument("filekeys", metavar="FILE", nargs=-1)
+@option_pid_value
+@option_pid_type
+@option_as_user
+@with_appcontext
+def remove_files(filekeys, pid, pid_type, user):
+ """Remove the deposited files."""
+ recid = convert_to_recid(pid, pid_type)
+ identity = get_identity_for_user(user)
+ service = DraftFileService()
+
+ for file_key in filekeys:
+ service.delete_file(id_=recid, file_key=file_key, identity=identity)
+
+
+@files.command("list")
+@option_pid_value
+@option_pid_type
+@option_as_user
+@with_appcontext
+def list_files(pid, pid_type, user):
+ """Show a list of files deposited with the draft."""
+ recid = convert_to_recid(pid, pid_type)
+ identity = get_identity_for_user(user)
+ service = DraftFileService()
+ file_results = service.list_files(id_=recid, identity=identity)
+ for f in file_results.entries:
+ click.echo(f)
--
GitLab