Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
10 changes: 5 additions & 5 deletions src/hermes/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
# "unused import" errors.
# flake8: noqa

# from hermes.commands.base import HermesHelpCommand
# from hermes.commands.base import HermesVersionCommand
# from hermes.commands.clean.base import HermesCleanCommand
from hermes.commands.base import HermesHelpCommand
from hermes.commands.base import HermesVersionCommand
from hermes.commands.clean.base import HermesCleanCommand
# from hermes.commands.init.base import HermesInitCommand
from hermes.commands.curate.base import HermesCurateCommand
from hermes.commands.harvest.base import HermesHarvestCommand
# from hermes.commands.process.base import HermesProcessCommand
# from hermes.commands.deposit.base import HermesDepositCommand
from hermes.commands.process.base import HermesProcessCommand
from hermes.commands.deposit.base import HermesDepositCommand
# from hermes.commands.postprocess.base import HermesPostprocessCommand
21 changes: 21 additions & 0 deletions src/hermes/commands/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def __call__(self, command: HermesCommand) -> None:


class HermesHelpSettings(BaseModel):
"""Intentionally empty settings class for the help command."""
pass


Expand All @@ -200,3 +201,23 @@ def __call__(self, args: argparse.Namespace) -> None:
# Otherwise, simply show the general help and exit (cleanly).
self.parser.print_help()
self.parser.exit()


class HermesVersionSettings(BaseModel):
"""Intentionally empty settings class for the version command."""
pass


class HermesVersionCommand(HermesCommand):
"""Show HERMES version and exit."""

command_name = "version"
settings_class = HermesVersionSettings

def load_settings(self, args: argparse.Namespace):
"""Pass loading settings as not necessary for this command."""
pass

def __call__(self, args: argparse.Namespace) -> None:
self.log.info(metadata.version("hermes"))
self.parser.exit()
16 changes: 9 additions & 7 deletions src/hermes/commands/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
# from hermes.commands import (HermesHelpCommand, HermesVersionCommand, HermesCleanCommand,
# HermesHarvestCommand, HermesProcessCommand, HermesCurateCommand,
# HermesDepositCommand, HermesPostprocessCommand, HermesInitCommand)
from hermes.commands import HermesCurateCommand, HermesHarvestCommand
from hermes.commands import (HermesCurateCommand, HermesDepositCommand,
HermesHarvestCommand, HermesHelpCommand,
HermesProcessCommand, HermesVersionCommand)
from hermes.commands.base import HermesCommand


Expand All @@ -38,15 +40,15 @@ def main() -> None:
setting_types = {}

for command in (
# HermesHelpCommand(parser),
# HermesVersionCommand(parser),
# HermesInitCommand(parser),
# HermesCleanCommand(parser),
HermesHarvestCommand(parser),
# HermesProcessCommand(parser),
HermesCurateCommand(parser),
# HermesDepositCommand(parser),
HermesDepositCommand(parser),
HermesHarvestCommand(parser),
HermesHelpCommand(parser),
# HermesInitCommand(parser),
# HermesPostprocessCommand(parser),
HermesProcessCommand(parser),
HermesVersionCommand(parser),
):
if command.settings_class is not None:
setting_types[command.command_name] = command.settings_class
Expand Down
57 changes: 26 additions & 31 deletions src/hermes/commands/deposit/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@

import abc
import argparse
import json
import sys

from pydantic import BaseModel

from hermes.commands.base import HermesCommand, HermesPlugin
from hermes.model.context import CodeMetaContext
from hermes.model.path import ContextPath
from hermes.model.errors import HermesValidationError
from hermes.model.context_manager import HermesContext
from hermes.model import SoftwareMetadata
from hermes.model.error import HermesValidationError


class BaseDepositPlugin(HermesPlugin):
Expand All @@ -24,26 +22,35 @@ class BaseDepositPlugin(HermesPlugin):
TODO: describe workflow... needs refactoring to be less stateful!
"""

def __init__(self, command, ctx):
self.command = command
self.ctx = ctx

def __call__(self, command: HermesCommand) -> None:
"""Initiate the deposition process.

This calls a list of additional methods on the class, none of which need to be implemented.
"""
self.command = command
self.ctx = HermesContext()

self.ctx.prepare_step("curate")
self.metadata = SoftwareMetadata.load_from_cache(self.ctx, "result")
self.ctx.finalize_step("curate")

self.prepare()
self.map_metadata()
deposit = self.map_metadata()
self.ctx.prepare_step("deposit")
with self.ctx[command.settings.target] as cache:
cache["deposit"] = deposit
self.ctx.finalize_step("deposit")

if self.is_initial_publication():
self.create_initial_version()
else:
self.create_new_version()

self.update_metadata()
deposit = self.update_metadata()
self.ctx.prepare_step("deposit")
with self.ctx["deposit"] as cache:
cache["result"] = deposit
self.ctx.finalize_step("deposit")
self.delete_artifacts()
self.upload_artifacts()
self.publish()
Expand All @@ -58,8 +65,8 @@ def prepare(self) -> None:
pass

@abc.abstractmethod
def map_metadata(self) -> None:
"""Map the given metadata to the target schema of the deposition platform.
def map_metadata(self) -> dict:
"""Map the given metadata to the target schema of the deposition platform and return it.

When mapping metadata, make sure to add traces to the HERMES software, e.g. via
DataCite's ``relatedIdentifier`` using the ``isCompiledBy`` relation. Ideally, the value
Expand Down Expand Up @@ -88,8 +95,9 @@ def create_new_version(self) -> None:
"""Create a new version of an existing publication on the target platform."""
pass

def update_metadata(self) -> None:
"""Update the metadata of the newly created version."""
@abc.abstractmethod
def update_metadata(self) -> dict:
"""Update the metadata of the newly created version and return it even if it hasn't changed."""
pass

def delete_artifacts(self) -> None:
Expand All @@ -106,7 +114,7 @@ def publish(self) -> None:
pass


class _DepositSettings(BaseModel):
class DepositSettings(BaseModel):
"""Generic deposition settings."""

target: str = ""
Expand All @@ -116,7 +124,7 @@ class HermesDepositCommand(HermesCommand):
""" Deposit the curated metadata to repositories. """

command_name = "deposit"
settings_class = _DepositSettings
settings_class = DepositSettings

def init_command_parser(self, command_parser: argparse.ArgumentParser) -> None:
command_parser.add_argument('--file', '-f', nargs=1, action='append',
Expand All @@ -128,26 +136,13 @@ def __call__(self, args: argparse.Namespace) -> None:
self.args = args
plugin_name = self.settings.target

ctx = CodeMetaContext()
codemeta_file = ctx.get_cache("curate", ctx.hermes_name)
if not codemeta_file.exists():
self.log.error("You must run the 'curate' command before deposit")
sys.exit(1)

codemeta_path = ContextPath("codemeta")
with open(codemeta_file) as codemeta_fh:
ctx.update(codemeta_path, json.load(codemeta_fh))

try:
plugin_func = self.plugins[plugin_name](self, ctx)

plugin_func = self.plugins[plugin_name]()
except KeyError as e:
self.log.error("Plugin '%s' not found.", plugin_name)
self.errors.append(e)

try:
plugin_func(self)

except HermesValidationError as e:
self.log.error("Error while executing %s: %s", plugin_name, e)
self.errors.append(e)
13 changes: 7 additions & 6 deletions src/hermes/commands/deposit/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,23 @@
from pydantic import BaseModel

from hermes.commands.deposit.base import BaseDepositPlugin
from hermes.model.path import ContextPath


class FileDepositSettings(BaseModel):
filename: str = 'hermes.json'
filename: str = 'codemeta.json'


class FileDepositPlugin(BaseDepositPlugin):
settings_class = FileDepositSettings

def map_metadata(self) -> None:
self.ctx.update(ContextPath.parse('deposit.file'), self.ctx['codemeta'])
def map_metadata(self) -> dict:
return self.metadata.compact()

def update_metadata(self) -> dict:
return self.metadata.compact()

def publish(self) -> None:
file_config = self.command.settings.file
output_data = self.ctx['deposit.file']

with open(file_config.filename, 'w') as deposition_file:
json.dump(output_data, deposition_file, indent=2)
json.dump(self.metadata.compact(), deposition_file, indent=2)
Loading
Loading