Skip to content

Commit db9f93b

Browse files
feat(cli, service): support for project image (#3623)
1 parent 3bdfd48 commit db9f93b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+537
-246
lines changed

docs/reference/core.rst

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ Schema classes used to serialize domain models to JSON-LD.
6565
:members:
6666
:show-inheritance:
6767

68+
.. automodule:: renku.command.schema.image
69+
:members:
70+
:show-inheritance:
71+
6872
.. automodule:: renku.command.schema.parameter
6973
:members:
7074
:show-inheritance:
@@ -105,10 +109,6 @@ Datasets
105109
:members:
106110
:show-inheritance:
107111

108-
.. automodule:: renku.core.dataset.request_model
109-
:members:
110-
:show-inheritance:
111-
112112
.. automodule:: renku.core.dataset.tag
113113
:members:
114114
:show-inheritance:
@@ -237,6 +237,17 @@ Errors that can be raised by ``renku.core``.
237237
:members:
238238
:show-inheritance:
239239

240+
Project/Dataset Images
241+
----------------------
242+
243+
.. automodule:: renku.core.image
244+
:members:
245+
:show-inheritance:
246+
247+
.. automodule:: renku.domain_model.image
248+
:members:
249+
250+
240251
Utilities
241252
---------
242253

renku/command/project.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
"""Project management."""
1818

1919
from renku.command.command_builder.command import Command
20-
from renku.core.constant import DATABASE_METADATA_PATH
20+
from renku.core.constant import PROJECT_METADATA_PATH
2121
from renku.core.project import edit_project, show_project
2222

2323

2424
def edit_project_command():
2525
"""Command for editing project metadata."""
2626
command = Command().command(edit_project).lock_project().with_database(write=True)
27-
return command.require_migration().with_commit(commit_only=DATABASE_METADATA_PATH)
27+
return command.require_migration().with_commit(commit_only=PROJECT_METADATA_PATH)
2828

2929

3030
def show_project_command():

renku/command/schema/dataset.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
from renku.command.schema.annotation import AnnotationSchema
2222
from renku.command.schema.calamus import DateTimeList, JsonLDSchema, Nested, Uri, fields, oa, prov, renku, schema
2323
from renku.command.schema.entity import CollectionSchema, EntitySchema
24-
from renku.domain_model.dataset import Dataset, DatasetFile, DatasetTag, ImageObject, Language, RemoteEntity, Url
24+
from renku.command.schema.image import ImageObjectSchema
25+
from renku.domain_model.dataset import Dataset, DatasetFile, DatasetTag, Language, RemoteEntity, Url
2526

2627

2728
def dump_dataset_as_jsonld(dataset: Dataset) -> dict:
@@ -104,21 +105,6 @@ class Meta:
104105
name = fields.String(schema.name)
105106

106107

107-
class ImageObjectSchema(JsonLDSchema):
108-
"""ImageObject schema."""
109-
110-
class Meta:
111-
"""Meta class."""
112-
113-
rdf_type = schema.ImageObject
114-
model = ImageObject
115-
unknown = EXCLUDE
116-
117-
content_url = fields.String(schema.contentUrl)
118-
id = fields.Id(load_default=None)
119-
position = fields.Integer(schema.position)
120-
121-
122108
class RemoteEntitySchema(JsonLDSchema):
123109
"""RemoteEntity schema."""
124110

renku/command/schema/image.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright Swiss Data Science Center (SDSC). A partnership between
2+
# École Polytechnique Fédérale de Lausanne (EPFL) and
3+
# Eidgenössische Technische Hochschule Zürich (ETHZ).
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
"""Image JSON-LD schema."""
17+
18+
from marshmallow import EXCLUDE
19+
20+
from renku.command.schema.calamus import JsonLDSchema, fields, schema
21+
from renku.domain_model.image import ImageObject
22+
23+
24+
class ImageObjectSchema(JsonLDSchema):
25+
"""ImageObject schema."""
26+
27+
class Meta:
28+
"""Meta class."""
29+
30+
rdf_type = schema.ImageObject
31+
model = ImageObject
32+
unknown = EXCLUDE
33+
34+
content_url = fields.String(schema.contentUrl)
35+
id = fields.Id(load_default=None)
36+
position = fields.Integer(schema.position)

renku/command/schema/project.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from renku.command.schema.agent import PersonSchema
2121
from renku.command.schema.annotation import AnnotationSchema
2222
from renku.command.schema.calamus import DateTimeList, JsonLDSchema, Nested, StringList, fields, oa, prov, renku, schema
23+
from renku.command.schema.image import ImageObjectSchema
2324
from renku.domain_model.project import Project
2425

2526

@@ -39,6 +40,7 @@ class Meta:
3940
date_created = DateTimeList(schema.dateCreated, load_default=None, format="iso", extra_formats=("%Y-%m-%d",))
4041
description = fields.String(schema.description, load_default=None)
4142
id = fields.Id(load_default=None)
43+
image = fields.Nested(schema.image, ImageObjectSchema, load_default=None)
4244
immutable_template_files = fields.List(
4345
renku.immutableTemplateFiles,
4446
fields.String(),

renku/core/constant.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
from enum import IntEnum
2020
from pathlib import Path
2121

22+
FILESYSTEM_ROOT = os.path.abspath(os.sep)
23+
"""Path to the root of the filesystem."""
24+
2225
APP_NAME = "Renku"
2326
"""Application name for storing configuration."""
2427

@@ -41,6 +44,9 @@
4144
DATASET_IMAGES = "dataset_images"
4245
"""Directory for dataset images."""
4346

47+
IMAGES = "images"
48+
"""Path for images/icons."""
49+
4450
DEFAULT_DATA_DIR = "data"
4551

4652
DOCKERFILE = "Dockerfile"
@@ -79,6 +85,11 @@
7985
Path(RENKU_HOME) / DATABASE_PATH,
8086
]
8187

88+
PROJECT_METADATA_PATH = [
89+
Path(RENKU_HOME) / DATABASE_PATH,
90+
Path(RENKU_HOME) / IMAGES,
91+
]
92+
8293
DATASET_METADATA_PATHS = [
8394
Path(RENKU_HOME) / DATABASE_PATH,
8495
Path(RENKU_HOME) / DATASET_IMAGES,

renku/core/dataset/dataset.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# limitations under the License.
1616
"""Dataset business logic."""
1717

18+
import imghdr
1819
import os
1920
import shutil
2021
import urllib
@@ -35,8 +36,8 @@
3536
from renku.core.dataset.providers.factory import ProviderFactory
3637
from renku.core.dataset.providers.git import GitProvider
3738
from renku.core.dataset.providers.models import DatasetUpdateAction, ProviderDataset
38-
from renku.core.dataset.request_model import ImageRequestModel
3939
from renku.core.dataset.tag import get_dataset_by_tag, prompt_access_token, prompt_tag_selection
40+
from renku.core.image import ImageObjectRequest
4041
from renku.core.interface.dataset_gateway import IDatasetGateway
4142
from renku.core.storage import check_external_storage, track_paths_in_storage
4243
from renku.core.util import communication
@@ -50,6 +51,7 @@
5051
get_absolute_path,
5152
get_file_size,
5253
get_files,
54+
get_relative_path,
5355
get_safe_relative_path,
5456
hash_file,
5557
is_path_empty,
@@ -109,7 +111,7 @@ def create_dataset(
109111
description: Optional[str] = None,
110112
creators: Optional[List[Person]] = None,
111113
keywords: Optional[List[str]] = None,
112-
images: Optional[List[ImageRequestModel]] = None,
114+
images: Optional[List[ImageObjectRequest]] = None,
113115
update_provenance: bool = True,
114116
custom_metadata: Optional[Dict[str, Any]] = None,
115117
storage: Optional[str] = None,
@@ -123,7 +125,7 @@ def create_dataset(
123125
description(Optional[str], optional): Dataset description (Default value = None).
124126
creators(Optional[List[Person]], optional): Dataset creators (Default value = None).
125127
keywords(Optional[List[str]], optional): Dataset keywords (Default value = None).
126-
images(Optional[List[ImageRequestModel]], optional): Dataset images (Default value = None).
128+
images(Optional[List[ImageObjectRequest]], optional): Dataset images (Default value = None).
127129
update_provenance(bool, optional): Whether to add this dataset to dataset provenance
128130
(Default value = True).
129131
custom_metadata(Optional[Dict[str, Any]], optional): Custom JSON-LD metadata (Default value = None).
@@ -199,7 +201,7 @@ def edit_dataset(
199201
description: Optional[Union[str, NoValueType]],
200202
creators: Optional[Union[List[Person], NoValueType]],
201203
keywords: Optional[Union[List[str], NoValueType]] = NO_VALUE,
202-
images: Optional[Union[List[ImageRequestModel], NoValueType]] = NO_VALUE,
204+
images: Optional[Union[List[ImageObjectRequest], NoValueType]] = NO_VALUE,
203205
custom_metadata: Optional[Union[Dict, List[Dict], NoValueType]] = NO_VALUE,
204206
custom_metadata_source: Optional[Union[str, NoValueType]] = NO_VALUE,
205207
):
@@ -211,7 +213,7 @@ def edit_dataset(
211213
description(Optional[Union[str, NoValueType]]): New description for the dataset.
212214
creators(Optional[Union[List[Person], NoValueType]]): New creators for the dataset.
213215
keywords(Optional[Union[List[str], NoValueType]]): New keywords for dataset (Default value = ``NO_VALUE``).
214-
images(Optional[Union[List[ImageRequestModel], NoValueType]]): New images for dataset
216+
images(Optional[Union[List[ImageObjectRequest], NoValueType]]): New images for dataset
215217
(Default value = ``NO_VALUE``).
216218
custom_metadata(Optional[Union[Dict, List[Dict], NoValueType]]): Custom JSON-LD metadata
217219
(Default value = ``NO_VALUE``).
@@ -248,7 +250,7 @@ def edit_dataset(
248250
if images == NO_VALUE:
249251
images_updated = False
250252
else:
251-
images_updated = set_dataset_images(dataset=dataset, images=cast(Optional[List[ImageRequestModel]], images))
253+
images_updated = set_dataset_images(dataset=dataset, images=cast(Optional[List[ImageObjectRequest]], images))
252254

253255
if images_updated:
254256
updated["images"] = (
@@ -855,12 +857,12 @@ def add_datadir_files_to_dataset(dataset: Dataset) -> None:
855857
dataset.add_or_update_files(dataset_files)
856858

857859

858-
def set_dataset_images(dataset: Dataset, images: Optional[List[ImageRequestModel]]):
860+
def set_dataset_images(dataset: Dataset, images: Optional[List[ImageObjectRequest]]):
859861
"""Set a dataset's images.
860862
861863
Args:
862864
dataset(Dataset): The dataset to set images on.
863-
images(List[ImageRequestModel]): The images to set.
865+
images(List[ImageObjectRequest]): The images to set.
864866
865867
Returns:
866868
True if images were set/modified.
@@ -875,10 +877,30 @@ def set_dataset_images(dataset: Dataset, images: Optional[List[ImageRequestModel
875877
dataset.images = []
876878
images_updated = False
877879
for img in images:
878-
img_object = img.to_image_object(dataset)
880+
image_folder = project_context.dataset_images_path / dataset.initial_identifier
881+
try:
882+
img_object = img.to_image_object(owner_id=dataset.id)
883+
except errors.ImageError as e:
884+
raise errors.DatasetImageError(e) from e
885+
886+
path = img_object.content_url
887+
888+
if not img_object.is_remote:
889+
# NOTE: only copy dataset image if it's not in .renku/datasets/<id>/images/ already
890+
if not path.startswith(str(image_folder)):
891+
image_type = imghdr.what(path)
892+
if image_type:
893+
ext = f".{image_type}"
894+
else:
895+
_, ext = os.path.splitext(path)
896+
target_image_path: Union[Path, str] = image_folder / f"{img_object.position}{ext}"
879897

880-
if not img_object:
881-
continue
898+
image_folder.parent.mkdir(parents=True, exist_ok=True)
899+
shutil.copy(path, target_image_path)
900+
else:
901+
target_image_path = path
902+
903+
img_object.content_url = get_relative_path(target_image_path, base=project_context.path) # type: ignore
882904

883905
if any(i.position == img_object.position for i in dataset.images):
884906
raise errors.DatasetImageError(f"Duplicate dataset image specified for position {img_object.position}")

renku/core/dataset/request_model.py

Lines changed: 0 additions & 102 deletions
This file was deleted.

0 commit comments

Comments
 (0)