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 "projects project-list" command #50

Merged
merged 5 commits into from
Oct 30, 2023
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
7 changes: 3 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Install Python
uses: actions/setup-python@v2
with:
python-version: "3.8"
python-version: "3.10"

- name: Install dependencies
run: |
Expand All @@ -52,7 +52,7 @@ jobs:
- name: Install Python
uses: actions/setup-python@v2
with:
python-version: "3.8"
python-version: "3.10"

- name: Install dependencies
run: |
Expand All @@ -70,10 +70,9 @@ jobs:
strategy:
matrix:
python-version:
- '3.8'
- '3.9'
- '3.10'
- '3.11'
- '3.12'
needs: linting
steps:
- uses: actions/checkout@v2
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ exclude =
docs/
varfish_cli/__init__.py
versioneer.py
venv/

ignore = E203, E266, E501, W503
max-line-length = 80
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ def parse_requirements(path):
"Natural Language :: English",
"Topic :: Scientific/Engineering :: Bio-Informatics",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
],
entry_points={"console_scripts": ("varfish-cli = varfish_cli.__main__:main",)},
description="Command line interface client for VarFish Server.",
Expand Down
5 changes: 4 additions & 1 deletion varfish_cli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from varfish_cli.case import run as run_case
from varfish_cli.case import setup_argparse as setup_argparse_case
from varfish_cli.common import CommonConfig, run_nocmd
from varfish_cli.projects import run as run_projects
from varfish_cli.projects import setup_argparse as setup_argparse_projects
from varfish_cli.varannos import run as run_varannos
from varfish_cli.varannos import setup_argparse as setup_argparse_varannos

Expand Down Expand Up @@ -63,6 +65,7 @@ def setup_argparse():
subparsers = parser.add_subparsers(dest="cmd")

setup_argparse_case(subparsers.add_parser("case", help="Work with cases."))
setup_argparse_projects(subparsers.add_parser("projects", help="Work with projects."))
setup_argparse_varannos(subparsers.add_parser("varannos", help="Work with varannos module."))

return parser, subparsers
Expand Down Expand Up @@ -107,7 +110,7 @@ def main(argv=None):
config = CommonConfig.create(args, toml_config)

# Handle the actual command line.
cmds = {None: run_nocmd, "case": run_case, "varannos": run_varannos}
cmds = {None: run_nocmd, "case": run_case, "varannos": run_varannos, "projects": run_projects}

res = cmds[args.cmd](
config, toml_config, args, parser, subparsers.choices[args.cmd] if args.cmd else None
Expand Down
1 change: 1 addition & 0 deletions varfish_cli/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

from varfish_cli.api.case import * # noqa: F403, F401
from varfish_cli.api.models import * # noqa: F403, F401
from varfish_cli.api.project import * # noqa: F403, F401
from varfish_cli.api.varannos import * # noqa: F403, F401
30 changes: 30 additions & 0 deletions varfish_cli/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,36 @@
import dateutil.parser


@unique
class ProjectType(Enum):
"""Enumeration of possible project types."""

#: Container for other categories or projects but not primary data.
CATEGORY = "CATEGORY"
#: Project containing primary data but no other projects.
PROJECT = "PROJECT"


@attr.s(frozen=True, auto_attribs=True)
class Project:
#: The project identifier.
sodar_uuid: uuid.UUID
#: Parent category UUID, if any.
parent: typing.Optional[uuid.UUID]
#: Project title.
title: str
#: Project type.
type: ProjectType
#: Project description.
description: str
#: Markdown README string.
readme: str
#: Whether public guest access is allowed.
public_guest_access: bool
#: Whether the project has been archived.
archive: bool


@attr.s(frozen=True, auto_attribs=True)
class PedigreeMember:
"""Represent a pedigree member as returned by the VarFish API."""
Expand Down
30 changes: 30 additions & 0 deletions varfish_cli/api/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Implementation of API operations on cases."""

import typing

from logzero import logger
import requests

from varfish_cli.api.models import CONVERTER, Project
from varfish_cli.api.varannos import raise_for_status
from varfish_cli.common import strip_trailing_slash

ACCEPT_API_VARFISH = ""

#: End point for listing projects.
ENDPOINT_PROJECT_LIST = "/project/api/list"


def project_list(
server_url: str,
api_token: str,
verify_ssl: bool = True,
) -> typing.List[Project]:
"""Listing of all projects that a user has access to."""
server_url = strip_trailing_slash(server_url)
endpoint = f"{server_url}{ENDPOINT_PROJECT_LIST}"
logger.debug("Sending GET request to end point %s", endpoint)
headers = {"Authorization": "Token %s" % api_token}
result = requests.get(endpoint, headers=headers, verify=verify_ssl)
raise_for_status(result)
return CONVERTER.structure(result.json(), typing.List[Project])
74 changes: 74 additions & 0 deletions varfish_cli/projects/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""Implementation of varfish-cli subcommand 'projects'."""

import argparse
import typing

import attr

from varfish_cli.common import CommonConfig, OutputFormat, run_nocmd
from varfish_cli.projects.project_list import (
setup_argparse as setup_argparse_project_list,
)


@attr.s(frozen=True, auto_attribs=True)
class ProjectsConfig:
"""Configuration for the ``varfish-cli project`` command."""

#: Global configuration
global_config: CommonConfig

#: Path to output file
output_file: str = "-"

#: Output format
output_format: OutputFormat = OutputFormat.TABLE

#: delimiter for CSV output
output_delimiter: str = ","

#: Fields to use for output.
output_fields: typing.Optional[typing.List[str]] = []

@staticmethod
def create(args, global_config, toml_config=None):
_ = toml_config
return ProjectsConfig(
output_format=OutputFormat(args.output_format),
output_file=args.output_file,
output_delimiter=args.output_delimiter,
output_fields=args.output_fields.split(",") if args.output_fields else [],
global_config=global_config,
)


def setup_argparse(parser: argparse.ArgumentParser) -> None:
"""Main entry point for subcommand."""
of_choices = [o.value for o in OutputFormat]
of_default = OutputFormat.TABLE.value
parser.add_argument(
"--output-format",
help=f"Output format, one of {of_choices}, default: {of_default}",
choices=of_choices,
default=of_default,
)
parser.add_argument("--output-fields", help="Output fields if non-default ones")
parser.add_argument(
"--output-delimiter", help="Separator for CSV output, default: ','", default=","
)
parser.add_argument(
"--output-file", help="Path to file to write to, defaults to stdout", default="-"
)

subparsers = parser.add_subparsers(dest="projects_cmd")
setup_argparse_project_list(subparsers.add_parser("project-list", help="List project objects."))


def run(config, toml_config, args, parser, subparser):
"""Main entry point for project command."""
if not args.projects_cmd: # pragma: nocover
return run_nocmd(config, args, parser, subparser)
else:
config = ProjectsConfig.create(args, config, toml_config)
print(args.projects_cmd)
return args.projects_cmd(config, toml_config, args, parser, subparser)
52 changes: 52 additions & 0 deletions varfish_cli/projects/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import typing

import attr

from varfish_cli.common import CommonConfig, OutputFormat


@attr.s(frozen=True, auto_attribs=True)
class ProjectsConfig:
"""Configuration for the ``varfish-cli project`` command."""

#: Global configuration
global_config: CommonConfig

#: Path to output file
output_file: str = "-"

#: Output format
output_format: OutputFormat = OutputFormat.TABLE

#: delimiter for CSV output
output_delimiter: str = ","

#: Fields to use for output.
output_fields: typing.Optional[typing.List[str]] = []

@staticmethod
def create(args, global_config, toml_config=None):
_ = toml_config
return ProjectsConfig(
output_format=OutputFormat(args.output_format),
output_file=args.output_file,
output_delimiter=args.output_delimiter,
output_fields=args.output_fields.split(",") if args.output_fields else [],
global_config=global_config,
)


@attr.s(frozen=True, auto_attribs=True)
class ProjectsListConfig:
"""Configuration for the ``varfish-cli projects project-list`` command."""

#: Project configuration
projects_config: ProjectsConfig

@staticmethod
def create(args, projects_config, toml_config=None):
_ = args
_ = toml_config
return ProjectsListConfig(
projects_config=projects_config,
)
72 changes: 72 additions & 0 deletions varfish_cli/projects/project_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""Implementation of ``varfish-cli projects project-list``"""

import argparse
import sys
import typing

import attrs
from logzero import logger

from varfish_cli import api
from varfish_cli.common import tabular_output, write_output
from varfish_cli.projects.config import OutputFormat, ProjectsListConfig

#: The default fields by output format.
DEFAULT_FIELDS: typing.Dict[OutputFormat, typing.Optional[typing.Tuple[str]]] = {
OutputFormat.TABLE: (
"sodar_uuid",
"type",
"title",
),
OutputFormat.CSV: None,
OutputFormat.JSON: None,
}


def setup_argparse(parser):
parser.add_argument("--hidden-cmd", dest="projects_cmd", default=run, help=argparse.SUPPRESS)


def run(projects_config, toml_config, args, _parser, _subparser, file=sys.stdout):
"""Run Project list command."""
projects_config = attrs.evolve(
projects_config,
output_fields=projects_config.output_fields
or DEFAULT_FIELDS.get(projects_config.output_format),
)
config = ProjectsListConfig.create(args, projects_config, toml_config)
logger.info("Configuration: %s", config)
logger.info("Listing Projects")
base_config = config.projects_config.global_config
res = api.project_list(
server_url=base_config.varfish_server_url,
api_token=base_config.varfish_api_token,
verify_ssl=config.projects_config.global_config.verify_ssl,
)

logger.info("Generating output")
header = (
projects_config.output_fields
if projects_config.output_fields
else [f.name for f in attrs.fields(api.Project)]
)
output = tabular_output(values=res, header=header)

logger.info("Writing output")
logger.info("==============")
if config.projects_config.output_file == "-":
write_output(
output,
sys.stdout,
config.projects_config.output_format,
config.projects_config.output_delimiter,
)
else:
with open(config.projects_config.output_file, "wt") as outputf:
write_output(
output,
outputf,
config.projects_config.output_format,
config.projects_config.output_delimiter,
)
logger.info("All done. Have a nice day!")
Loading