From 4c764a8331e1fc5a099a6c8b86ddda07bf3ba4fc Mon Sep 17 00:00:00 2001 From: Manuel Holtgrewe Date: Mon, 30 Oct 2023 12:46:19 +0100 Subject: [PATCH 1/5] feat: add "projects project-list" command --- setup.cfg | 1 + varfish_cli/__main__.py | 5 +- varfish_cli/api/__init__.py | 1 + varfish_cli/api/models.py | 30 +++++++++++ varfish_cli/api/project.py | 30 +++++++++++ varfish_cli/projects/__init__.py | 74 ++++++++++++++++++++++++++++ varfish_cli/projects/config.py | 52 +++++++++++++++++++ varfish_cli/projects/project_list.py | 72 +++++++++++++++++++++++++++ 8 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 varfish_cli/api/project.py create mode 100644 varfish_cli/projects/__init__.py create mode 100644 varfish_cli/projects/config.py create mode 100644 varfish_cli/projects/project_list.py diff --git a/setup.cfg b/setup.cfg index 2ba6ad0..800b066 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,6 +11,7 @@ exclude = docs/ varfish_cli/__init__.py versioneer.py + venv/ ignore = E203, E266, E501, W503 max-line-length = 80 diff --git a/varfish_cli/__main__.py b/varfish_cli/__main__.py index af9ac44..add4891 100644 --- a/varfish_cli/__main__.py +++ b/varfish_cli/__main__.py @@ -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 @@ -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 @@ -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 diff --git a/varfish_cli/api/__init__.py b/varfish_cli/api/__init__.py index 3101aae..a249e36 100644 --- a/varfish_cli/api/__init__.py +++ b/varfish_cli/api/__init__.py @@ -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 diff --git a/varfish_cli/api/models.py b/varfish_cli/api/models.py index da74e67..6939163 100644 --- a/varfish_cli/api/models.py +++ b/varfish_cli/api/models.py @@ -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.""" diff --git a/varfish_cli/api/project.py b/varfish_cli/api/project.py new file mode 100644 index 0000000..c5b2fdc --- /dev/null +++ b/varfish_cli/api/project.py @@ -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]) diff --git a/varfish_cli/projects/__init__.py b/varfish_cli/projects/__init__.py new file mode 100644 index 0000000..58c3fdf --- /dev/null +++ b/varfish_cli/projects/__init__.py @@ -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) diff --git a/varfish_cli/projects/config.py b/varfish_cli/projects/config.py new file mode 100644 index 0000000..82f697f --- /dev/null +++ b/varfish_cli/projects/config.py @@ -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, + ) diff --git a/varfish_cli/projects/project_list.py b/varfish_cli/projects/project_list.py new file mode 100644 index 0000000..e16e0f0 --- /dev/null +++ b/varfish_cli/projects/project_list.py @@ -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!") From b5b47a36d6ceb54a20e6cd04ca4b6c76bf582419 Mon Sep 17 00:00:00 2001 From: Manuel Holtgrewe Date: Mon, 30 Oct 2023 12:48:33 +0100 Subject: [PATCH 2/5] add py 3.12 to test matrix --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b5797e3..a5a033c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -74,6 +74,7 @@ jobs: - '3.9' - '3.10' - '3.11' + - '3.12' needs: linting steps: - uses: actions/checkout@v2 From a6a1624fe0515e924da76cfd31f6d209ebea9090 Mon Sep 17 00:00:00 2001 From: Manuel Holtgrewe Date: Mon, 30 Oct 2023 12:55:15 +0100 Subject: [PATCH 3/5] wip --- .github/workflows/main.yml | 6 ++---- setup.py | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a5a033c..4009104 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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: | @@ -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: | @@ -70,8 +70,6 @@ jobs: strategy: matrix: python-version: - - '3.8' - - '3.9' - '3.10' - '3.11' - '3.12' diff --git a/setup.py b/setup.py index 888878f..89da577 100644 --- a/setup.py +++ b/setup.py @@ -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.", From 174cd91d334db1900cc755b2bcfaae29f5d8b925 Mon Sep 17 00:00:00 2001 From: Manuel Holtgrewe Date: Mon, 30 Oct 2023 12:55:57 +0100 Subject: [PATCH 4/5] adding deps report --- .github/workflows/deps-report.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/deps-report.yml diff --git a/.github/workflows/deps-report.yml b/.github/workflows/deps-report.yml new file mode 100644 index 0000000..e83c266 --- /dev/null +++ b/.github/workflows/deps-report.yml @@ -0,0 +1,22 @@ + +name: Dependencies report + +on: + pull_request: + +jobs: + build: + name: Dependencies report + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + lfs: true + + - name: deps-report + uses: MeilleursAgents/deps-report@master + with: + file: Pipfile.lock + github_token: ${{ secrets.GITHUB_TOKEN }} From 3d7519da1541dbf2d33080001345f401f2f49cda Mon Sep 17 00:00:00 2001 From: Manuel Holtgrewe Date: Mon, 30 Oct 2023 12:59:22 +0100 Subject: [PATCH 5/5] wip --- .github/workflows/deps-report.yml | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .github/workflows/deps-report.yml diff --git a/.github/workflows/deps-report.yml b/.github/workflows/deps-report.yml deleted file mode 100644 index e83c266..0000000 --- a/.github/workflows/deps-report.yml +++ /dev/null @@ -1,22 +0,0 @@ - -name: Dependencies report - -on: - pull_request: - -jobs: - build: - name: Dependencies report - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - lfs: true - - - name: deps-report - uses: MeilleursAgents/deps-report@master - with: - file: Pipfile.lock - github_token: ${{ secrets.GITHUB_TOKEN }}