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

feat: Add new command sodar landing-zone-validate #219

Merged
merged 3 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion cubi_tk/sodar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
``landing-zone-create``
Create a landing zone.

``landing-zone-validate`` (planned)
``landing-zone-validate``
Validate a landing zone.

``landing-zone-move``
Expand Down Expand Up @@ -61,6 +61,7 @@
from .lz_create import setup_argparse as setup_argparse_lz_create
from .lz_list import setup_argparse as setup_argparse_lz_list
from .lz_move import setup_argparse as setup_argparse_lz_move
from .lz_validate import setup_argparse as setup_argparse_lz_validate
from .pull_raw_data import setup_argparse as setup_argparse_pull_raw_data
from .upload_sheet import setup_argparse as setup_argparse_upload_sheet

Expand All @@ -86,6 +87,9 @@ def setup_argparse(parser: argparse.ArgumentParser) -> None:
setup_argparse_lz_move(
subparsers.add_parser("landing-zone-move", help="Submit landing zone for moving")
)
setup_argparse_lz_validate(
subparsers.add_parser("landing-zone-validate", help="Submit landing zone for validation")
)
setup_argparse_ingest_fastq(
subparsers.add_parser(
"ingest-fastq", help="Upload external files to SODAR (defaults for fastq)"
Expand Down
105 changes: 105 additions & 0 deletions cubi_tk/sodar/lz_validate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""``cubi-tk sodar landing-zone-validate`` command line program
"""

import argparse
import json
import os
import typing

import cattr
import logzero
from logzero import logger
from sodar_cli import api

from ..common import load_toml_config

# no-frills logger
formatter = logzero.LogFormatter(fmt="%(message)s")
output_logger = logzero.setup_logger(formatter=formatter)

# for testing
output_logger.propagate = True


class ValidateLandingZoneCommand:
"""Implementation of the ``landing-zone-validate`` command."""

def __init__(self, args):
#: Command line arguments.
self.args = args

@classmethod
def setup_argparse(cls, parser: argparse.ArgumentParser) -> None:
"""Setup argument parser."""
parser.add_argument(
"--hidden-cmd", dest="sodar_cmd", default=cls.run, help=argparse.SUPPRESS
)

group_sodar = parser.add_argument_group("SODAR-related")
group_sodar.add_argument(
"--sodar-url",
default=os.environ.get("SODAR_URL", "https://sodar.bihealth.org/"),
help="URL to SODAR, defaults to SODAR_URL environment variable or fallback to https://sodar.bihealth.org/",
)
group_sodar.add_argument(
"--sodar-api-token",
default=os.environ.get("SODAR_API_TOKEN", None),
help="Authentication token when talking to SODAR. Defaults to SODAR_API_TOKEN environment variable.",
)

parser.add_argument(
"--format",
dest="format_string",
default=None,
help="Format string for printing, e.g. %%(uuid)s",
)

parser.add_argument("landing_zone_uuid", help="UUID of landing zone to validate.")

@classmethod
def run(
cls, args, _parser: argparse.ArgumentParser, _subparser: argparse.ArgumentParser
) -> typing.Optional[int]:
"""Entry point into the command."""
return cls(args).execute() # pragma: nocover

def check_args(self, args):
"""Called for checking arguments, override to change behaviour."""
res = 0

toml_config = load_toml_config(args)
args.sodar_url = args.sodar_url or toml_config.get("global", {}).get("sodar_server_url")
args.sodar_api_token = args.sodar_api_token or toml_config.get("global", {}).get(
"sodar_api_token"
)

return res

def execute(self) -> typing.Optional[int]:
"""Execute the landing zone validation."""
res = self.check_args(self.args)
if res: # pragma: nocover
return res

logger.info("Starting cubi-tk sodar landing-zone-validate.")
logger.debug("args: %s", self.args)

landing_zone = api.landingzone.submit_validate(
sodar_url=self.args.sodar_url,
sodar_api_token=self.args.sodar_api_token,
landingzone_uuid=self.args.landing_zone_uuid,
)
values = cattr.unstructure(landing_zone)
if self.args.format_string:
logger.info("Formatted server response:")
output_logger.info(self.args.format_string.replace(r"\t", "\t") % values)
else:
logger.info("Server response:")
output_logger.info(json.dumps(values))

return 0


def setup_argparse(parser: argparse.ArgumentParser) -> None: # pragma: nocover
"""Setup argument parser for ``cubi-tk sodar landing-zone-validate``."""
return ValidateLandingZoneCommand.setup_argparse(parser)
36 changes: 36 additions & 0 deletions tests/test_sodar_lz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""``Tests for sodar_lz functions``"""
from argparse import ArgumentParser
from unittest.mock import patch

from cubi_tk.sodar.lz_validate import ValidateLandingZoneCommand


@patch("cubi_tk.sodar.lz_validate.api.landingzone.submit_validate")
@patch("cubi_tk.sodar.lz_validate.load_toml_config")
def test_validate(mocktoml, mockapi, caplog):
mockapi.return_value = {"a": 1, "b": 2}
argv = [
"--sodar-url",
"sodar_url",
"--sodar-api-token",
"token",
"u-u-i-d",
]

parser = ArgumentParser()
ValidateLandingZoneCommand.setup_argparse(parser)

# No format string
args = parser.parse_args(argv)
ValidateLandingZoneCommand(args).execute()
mockapi.assert_called_with(
sodar_url="sodar_url", sodar_api_token="token", landingzone_uuid="u-u-i-d"
)
assert '{"a": 1, "b": 2}' in caplog.messages

# With format string
argv.insert(-1, "--format")
argv.insert(-1, "%(a)s")
args = parser.parse_args(argv)
ValidateLandingZoneCommand(args).execute()
assert "1" in caplog.messages
Loading