Skip to content

Commit

Permalink
feat(cli,service): add project show command, add keywords to project (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Panaetius committed Nov 24, 2021
1 parent a3556c4 commit 5943f5f
Show file tree
Hide file tree
Showing 16 changed files with 362 additions and 17 deletions.
2 changes: 1 addition & 1 deletion renku/cli/dataset.py
Expand Up @@ -474,7 +474,7 @@ def list_dataset(format, columns):
type=click.Path(exists=True, dir_okay=False),
help="Custom metadata to be associated with the dataset.",
)
@click.option("-k", "--keyword", default=None, multiple=True, type=click.STRING, help="List of keywords or tags.")
@click.option("-k", "--keyword", default=None, multiple=True, type=click.STRING, help="List of keywords.")
def create(name, title, description, creators, metadata, keyword):
"""Create an empty dataset in the current repo."""
from renku.core.commands.dataset import create_dataset
Expand Down
3 changes: 3 additions & 0 deletions renku/cli/init.py
Expand Up @@ -248,6 +248,7 @@ def resolve_data_directory(data_dir, path):
@click.argument("path", default=".", type=click.Path(writable=True, file_okay=False, resolve_path=True))
@click.option("-n", "--name", callback=validate_name, help="Provide a custom project name.")
@click.option("--description", help="Provide a description for the project.")
@click.option("-k", "--keyword", default=None, multiple=True, type=click.STRING, help="List of keywords.")
@click.option(
"--data-dir",
default=None,
Expand Down Expand Up @@ -292,6 +293,7 @@ def init(
path,
name,
description,
keyword,
template_id,
template_index,
template_source,
Expand Down Expand Up @@ -327,6 +329,7 @@ def init(
path=path,
name=name,
description=description,
keywords=keyword,
template_id=template_id,
template_index=template_index,
template_source=template_source,
Expand Down
49 changes: 47 additions & 2 deletions renku/cli/project.py
Expand Up @@ -17,6 +17,23 @@
# limitations under the License.
r"""Renku CLI commands for handling of projects.
Showing project metadata
~~~~~~~~~~~~~~~~~~~~~~~~
You can see the metadata of the current project by using ``renku project show``:
.. code-block:: console
$ renku project show
Id: /projects/john.doe/flights-tutorial
Name: flights-tutorial
Description: Flight tutorial project
Creator: John Doe <John Doe@datascience.ch>
Created: 2021-11-05T10:32:57+01:00
Keywords: keyword1, keyword2
Renku Version: 1.0.0
Project Template: python-minimal (1.0.0)
Editing projects
~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -55,6 +72,7 @@ def project():

@project.command()
@click.option("-d", "--description", default=None, type=click.STRING, help="Project's description.")
@click.option("-k", "--keyword", default=None, multiple=True, type=click.STRING, help="List of keywords.")
@click.option(
"-c",
"--creator",
Expand All @@ -69,7 +87,7 @@ def project():
type=click.Path(exists=True, dir_okay=False),
help="Custom metadata to be associated with the project.",
)
def edit(description, creator, metadata):
def edit(description, keyword, creator, metadata):
"""Edit project metadata."""
from renku.core.commands.project import edit_project_command

Expand All @@ -81,7 +99,7 @@ def edit(description, creator, metadata):
result = (
edit_project_command()
.build()
.execute(description=description, creator=creator, custom_metadata=custom_metadata)
.execute(description=description, creator=creator, keywords=keyword, custom_metadata=custom_metadata)
)

updated, no_email_warning = result.output
Expand All @@ -92,3 +110,30 @@ def edit(description, creator, metadata):
click.echo("Successfully updated: {}.".format(", ".join(updated.keys())))
if no_email_warning:
click.echo(ClickCallback.WARNING + f"No email or wrong format for: {no_email_warning}")


def _print_project(project):
"""Print project metadata."""
click.echo(click.style("Id: ", bold=True, fg="magenta") + click.style(project.id, bold=True))
click.echo(click.style("Name: ", bold=True, fg="magenta") + click.style(project.name, bold=True))
click.echo(click.style("Description: ", bold=True, fg="magenta") + click.style(project.description, bold=True))
click.echo(click.style("Creator: ", bold=True, fg="magenta") + click.style(project.creator_str, bold=True))
click.echo(click.style("Created: ", bold=True, fg="magenta") + click.style(project.created_str, bold=True))
click.echo(click.style("Keywords: ", bold=True, fg="magenta") + click.style(project.keywords_str, bold=True))
click.echo(click.style("Renku Version: ", bold=True, fg="magenta") + click.style(project.agent, bold=True))
click.echo(
click.style("Project Template: ", bold=True, fg="magenta") + click.style(project.template_info, bold=True)
)

if project.annotations:
click.echo(click.style("Annotations: ", bold=True, fg="magenta") + click.style(project.annotations, bold=True))


@project.command()
def show():
"""Show details for the project."""
from renku.core.commands.project import show_project_command

project = show_project_command().build().execute().output

_print_project(project)
9 changes: 8 additions & 1 deletion renku/core/commands/init.py
Expand Up @@ -237,6 +237,7 @@ def _init(
path,
name,
description,
keywords,
template_id,
template_index,
template_source,
Expand Down Expand Up @@ -351,6 +352,7 @@ def _init(
force=force,
data_dir=data_dir,
description=description,
keywords=keywords,
)
except FileExistsError as e:
raise errors.InvalidFileOperation(e)
Expand Down Expand Up @@ -503,6 +505,7 @@ def create_from_template(
user=None,
commit_message=None,
description=None,
keywords=None,
):
"""Initialize a new project from a template."""

Expand All @@ -521,7 +524,9 @@ def create_from_template(
metadata["name"] = name

with client.commit(commit_message=commit_message, commit_only=commit_only, skip_dirty_checks=True):
with client.with_metadata(name=name, description=description, custom_metadata=custom_metadata) as project:
with client.with_metadata(
name=name, description=description, custom_metadata=custom_metadata, keywords=keywords
) as project:
project.template_source = metadata["__template_source__"]
project.template_ref = metadata["__template_ref__"]
project.template_id = metadata["__template_id__"]
Expand Down Expand Up @@ -554,6 +559,7 @@ def _create_from_template_local(
initial_branch=None,
commit_message=None,
description=None,
keywords=None,
):
"""Initialize a new project from a template."""

Expand All @@ -580,6 +586,7 @@ def _create_from_template_local(
user=user,
commit_message=commit_message,
description=description,
keywords=keywords,
)


Expand Down
26 changes: 23 additions & 3 deletions renku/core/commands/project.py
Expand Up @@ -17,25 +17,34 @@
# limitations under the License.
"""Project management."""

from renku.core.commands.view_model.project import ProjectViewModel
from renku.core.management.command_builder import inject
from renku.core.management.command_builder.command import Command
from renku.core.management.interface.client_dispatcher import IClientDispatcher
from renku.core.management.interface.project_gateway import IProjectGateway
from renku.core.management.repository import DATABASE_METADATA_PATH
from renku.core.utils.metadata import construct_creator


@inject.autoparams()
def _edit_project(description, creator, custom_metadata, project_gateway: IProjectGateway):
def _edit_project(description, creator, keywords, custom_metadata, project_gateway: IProjectGateway):
"""Edit dataset metadata."""
possible_updates = {"creator": creator, "description": description, "custom_metadata": custom_metadata}
possible_updates = {
"creator": creator,
"description": description,
"keywords": keywords,
"custom_metadata": custom_metadata,
}

creator, no_email_warnings = construct_creator(creator, ignore_email=True)

updated = {k: v for k, v in possible_updates.items() if v}

if updated:
project = project_gateway.get_project()
project.update_metadata(creator=creator, description=description, custom_metadata=custom_metadata)
project.update_metadata(
creator=creator, description=description, keywords=keywords, custom_metadata=custom_metadata
)
project_gateway.update_project(project)

return updated, no_email_warnings
Expand All @@ -45,3 +54,14 @@ def edit_project_command():
"""Command for editing project metadata."""
command = Command().command(_edit_project).lock_project().with_database(write=True)
return command.require_migration().with_commit(commit_only=DATABASE_METADATA_PATH)


@inject.autoparams()
def _show_project(client_dispatcher: IClientDispatcher):
"""Show project metadata."""
return ProjectViewModel.from_project(client_dispatcher.current_client.project)


def show_project_command():
"""Command for showing project metadata."""
return Command().command(_show_project).lock_project().with_database().require_migration()
79 changes: 79 additions & 0 deletions renku/core/commands/view_model/project.py
@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
#
# Copyright 2017-2021 - Swiss Data Science Center (SDSC)
# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
# Eidgenössische Technische Hochschule Zürich (ETHZ).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Project view model."""

import json
from datetime import datetime
from typing import List, Optional

from renku.core.models.project import Project
from renku.core.models.provenance.agent import Person


class ProjectViewModel:
"""A view model for a ``Project``."""

def __init__(
self,
id: str,
name: str,
creator: Person,
created: datetime,
description: str,
agent: str,
annotations: Optional[str],
template_info: str,
keywords: Optional[List[str]],
):
self.id = id
self.name = name
self.creator = creator
self.creator_str = creator.full_identity
self.created = created
self.created_str = created.isoformat()
self.description = description
self.agent = agent
self.annotations = annotations
self.template_info = template_info
self.keywords = keywords
self.keywords_str = ", ".join(keywords)

@classmethod
def from_project(cls, project: Project):
"""Create view model from ``Project``."""
template_info = ""

if project.template_source:
if project.template_source == "renku":
template_info = f"{project.template_id} ({project.template_version})"
else:
template_info = f"{project.template_source}@{project.template_ref}: {project.template_id}"

return cls(
id=project.id,
name=project.name,
creator=project.creator,
created=project.date_created,
description=project.description,
agent=project.agent_version,
annotations=json.dumps([{"id": a.id, "body": a.body, "source": a.source} for a in project.annotations])
if project.annotations
else None,
template_info=template_info,
keywords=project.keywords,
)
3 changes: 2 additions & 1 deletion renku/core/management/repository.py
Expand Up @@ -249,6 +249,7 @@ def with_metadata(
read_only=False,
name=None,
description=None,
keywords=None,
custom_metadata=None,
):
"""Yield an editable metadata object."""
Expand All @@ -257,7 +258,7 @@ def with_metadata(
project = project_gateway.get_project()
except ValueError:
project = Project.from_client(
name=name, description=description, custom_metadata=custom_metadata, client=self
name=name, description=description, keywords=keywords, custom_metadata=custom_metadata, client=self
)

yield project
Expand Down
12 changes: 10 additions & 2 deletions renku/core/models/project.py
Expand Up @@ -36,6 +36,8 @@
class Project(persistent.Persistent):
"""Represent a project."""

keywords = None

def __init__(
self,
*,
Expand All @@ -54,6 +56,7 @@ def __init__(
template_source: str = None,
template_version: str = None,
version: str = None,
keywords: List[str] = None,
):
from renku.core.management.migrate import SUPPORTED_PROJECT_VERSION

Expand All @@ -79,13 +82,15 @@ def __init__(
self.template_source: str = template_source
self.template_version: str = template_version
self.version: str = version
self.keywords: List[str] = keywords or []

@classmethod
def from_client(
cls,
client,
name: str = None,
description: str = None,
keywords: List[str] = None,
custom_metadata: Dict = None,
creator: Person = None,
) -> "Project":
Expand All @@ -101,7 +106,9 @@ def from_client(
raise ValueError("Project Creator not set")

id = cls.generate_id(namespace=namespace, name=name)
return cls(creator=creator, id=id, name=name, description=description, annotations=annotations)
return cls(
creator=creator, id=id, name=name, description=description, keywords=keywords, annotations=annotations
)

@staticmethod
def get_namespace_and_name(*, client=None, name: str = None, creator: Person = None):
Expand Down Expand Up @@ -134,7 +141,7 @@ def generate_id(namespace: str, name: str):

def update_metadata(self, custom_metadata=None, **kwargs):
"""Updates metadata."""
editable_attributes = ["creator", "description"]
editable_attributes = ["creator", "description", "keywords"]
for name, value in kwargs.items():
if name not in editable_attributes:
raise errors.ParameterError(f"Cannot edit field: '{name}'")
Expand Down Expand Up @@ -174,3 +181,4 @@ class Meta:
template_source = fields.String(renku.templateSource, missing=None)
template_version = fields.String(renku.templateVersion, missing=None)
version = StringList(schema.schemaVersion, missing="1")
keywords = fields.List(schema.keywords, fields.String(), missing=None)
7 changes: 7 additions & 0 deletions renku/data/shacl_shape.json
Expand Up @@ -214,6 +214,13 @@
"sh:class": {
"@id": "prov:Activity"
}
},
{
"nodeKind": "sh:Literal",
"path": "schema:keywords",
"datatype": {
"@id": "xsd:string"
}
}
]
},
Expand Down
1 change: 1 addition & 0 deletions renku/service/controllers/project_edit.py
Expand Up @@ -54,6 +54,7 @@ def renku_op(self):
description=self.ctx.get("description"),
creator=self.ctx.get("creator"),
custom_metadata=self.ctx.get("custom_metadata"),
keywords=self.ctx.get("keywords"),
)
)

Expand Down

0 comments on commit 5943f5f

Please sign in to comment.