digitalhub 0.9.2__py3-none-any.whl → 0.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of digitalhub might be problematic. Click here for more details.
- digitalhub/__init__.py +2 -3
- digitalhub/client/_base/api_builder.py +1 -1
- digitalhub/client/_base/client.py +25 -2
- digitalhub/client/_base/params_builder.py +16 -0
- digitalhub/client/dhcore/api_builder.py +9 -3
- digitalhub/client/dhcore/client.py +30 -398
- digitalhub/client/dhcore/configurator.py +361 -0
- digitalhub/client/dhcore/error_parser.py +107 -0
- digitalhub/client/dhcore/models.py +13 -23
- digitalhub/client/dhcore/params_builder.py +178 -0
- digitalhub/client/dhcore/utils.py +4 -44
- digitalhub/client/local/api_builder.py +13 -18
- digitalhub/client/local/client.py +18 -2
- digitalhub/client/local/enums.py +11 -0
- digitalhub/client/local/params_builder.py +116 -0
- digitalhub/configurator/api.py +31 -0
- digitalhub/configurator/configurator.py +195 -0
- digitalhub/configurator/credentials_store.py +65 -0
- digitalhub/configurator/ini_module.py +74 -0
- digitalhub/entities/_base/_base/entity.py +2 -2
- digitalhub/entities/_base/context/entity.py +4 -4
- digitalhub/entities/_base/entity/builder.py +5 -5
- digitalhub/entities/_base/executable/entity.py +2 -2
- digitalhub/entities/_base/material/entity.py +12 -12
- digitalhub/entities/_base/material/status.py +1 -1
- digitalhub/entities/_base/material/utils.py +2 -2
- digitalhub/entities/_base/unversioned/entity.py +2 -2
- digitalhub/entities/_base/versioned/entity.py +2 -2
- digitalhub/entities/_commons/enums.py +2 -0
- digitalhub/entities/_commons/metrics.py +164 -0
- digitalhub/entities/_commons/types.py +5 -0
- digitalhub/entities/_commons/utils.py +2 -2
- digitalhub/entities/_processors/base.py +527 -0
- digitalhub/entities/{_operations/processor.py → _processors/context.py} +212 -837
- digitalhub/entities/_processors/utils.py +158 -0
- digitalhub/entities/artifact/artifact/spec.py +3 -1
- digitalhub/entities/artifact/crud.py +13 -12
- digitalhub/entities/artifact/utils.py +1 -1
- digitalhub/entities/builders.py +6 -18
- digitalhub/entities/dataitem/_base/entity.py +0 -41
- digitalhub/entities/dataitem/crud.py +27 -15
- digitalhub/entities/dataitem/table/entity.py +49 -35
- digitalhub/entities/dataitem/table/models.py +4 -3
- digitalhub/{utils/data_utils.py → entities/dataitem/table/utils.py} +46 -54
- digitalhub/entities/dataitem/utils.py +58 -10
- digitalhub/entities/function/crud.py +9 -9
- digitalhub/entities/model/_base/entity.py +120 -0
- digitalhub/entities/model/_base/spec.py +6 -17
- digitalhub/entities/model/_base/status.py +10 -0
- digitalhub/entities/model/crud.py +13 -12
- digitalhub/entities/model/huggingface/spec.py +9 -4
- digitalhub/entities/model/mlflow/models.py +2 -2
- digitalhub/entities/model/mlflow/spec.py +7 -7
- digitalhub/entities/model/mlflow/utils.py +44 -5
- digitalhub/entities/project/_base/entity.py +317 -9
- digitalhub/entities/project/_base/spec.py +8 -6
- digitalhub/entities/project/crud.py +12 -11
- digitalhub/entities/run/_base/entity.py +103 -6
- digitalhub/entities/run/_base/spec.py +4 -2
- digitalhub/entities/run/_base/status.py +12 -0
- digitalhub/entities/run/crud.py +8 -8
- digitalhub/entities/secret/_base/entity.py +3 -3
- digitalhub/entities/secret/_base/spec.py +4 -2
- digitalhub/entities/secret/crud.py +11 -9
- digitalhub/entities/task/_base/entity.py +4 -4
- digitalhub/entities/task/_base/models.py +51 -40
- digitalhub/entities/task/_base/spec.py +2 -0
- digitalhub/entities/task/_base/utils.py +2 -2
- digitalhub/entities/task/crud.py +12 -8
- digitalhub/entities/workflow/crud.py +9 -9
- digitalhub/factory/utils.py +9 -9
- digitalhub/readers/{_base → data/_base}/builder.py +1 -1
- digitalhub/readers/{_base → data/_base}/reader.py +16 -4
- digitalhub/readers/{api.py → data/api.py} +2 -2
- digitalhub/readers/{factory.py → data/factory.py} +3 -3
- digitalhub/readers/{pandas → data/pandas}/builder.py +2 -2
- digitalhub/readers/{pandas → data/pandas}/reader.py +110 -30
- digitalhub/readers/query/__init__.py +0 -0
- digitalhub/stores/_base/store.py +59 -69
- digitalhub/stores/api.py +8 -33
- digitalhub/stores/builder.py +44 -161
- digitalhub/stores/local/store.py +106 -89
- digitalhub/stores/remote/store.py +86 -11
- digitalhub/stores/s3/configurator.py +108 -0
- digitalhub/stores/s3/enums.py +17 -0
- digitalhub/stores/s3/models.py +21 -0
- digitalhub/stores/s3/store.py +154 -70
- digitalhub/{utils/s3_utils.py → stores/s3/utils.py} +7 -3
- digitalhub/stores/sql/configurator.py +88 -0
- digitalhub/stores/sql/enums.py +16 -0
- digitalhub/stores/sql/models.py +24 -0
- digitalhub/stores/sql/store.py +106 -85
- digitalhub/{readers/_commons → utils}/enums.py +5 -1
- digitalhub/utils/exceptions.py +6 -0
- digitalhub/utils/file_utils.py +8 -7
- digitalhub/utils/generic_utils.py +28 -15
- digitalhub/utils/git_utils.py +16 -9
- digitalhub/utils/types.py +5 -0
- digitalhub/utils/uri_utils.py +2 -2
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info}/METADATA +25 -31
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info}/RECORD +108 -99
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info}/WHEEL +1 -2
- digitalhub/client/dhcore/env.py +0 -23
- digitalhub/entities/_base/project/entity.py +0 -341
- digitalhub-0.9.2.dist-info/top_level.txt +0 -2
- test/local/CRUD/test_artifacts.py +0 -96
- test/local/CRUD/test_dataitems.py +0 -96
- test/local/CRUD/test_models.py +0 -95
- test/local/imports/test_imports.py +0 -66
- test/local/instances/test_validate.py +0 -55
- test/test_crud_functions.py +0 -109
- test/test_crud_runs.py +0 -86
- test/test_crud_tasks.py +0 -81
- test/testkfp.py +0 -37
- test/testkfp_pipeline.py +0 -22
- /digitalhub/{entities/_base/project → configurator}/__init__.py +0 -0
- /digitalhub/entities/{_operations → _processors}/__init__.py +0 -0
- /digitalhub/readers/{_base → data}/__init__.py +0 -0
- /digitalhub/readers/{_commons → data/_base}/__init__.py +0 -0
- /digitalhub/readers/{pandas → data/pandas}/__init__.py +0 -0
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info/licenses}/LICENSE.txt +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
3
5
|
from pydantic import Field
|
|
4
6
|
|
|
5
7
|
from digitalhub.entities.model._base.spec import ModelSpec, ModelValidator
|
|
@@ -15,13 +17,13 @@ class ModelSpecHuggingface(ModelSpec):
|
|
|
15
17
|
path: str,
|
|
16
18
|
framework: str | None = None,
|
|
17
19
|
algorithm: str | None = None,
|
|
18
|
-
base_model: str | None = None,
|
|
19
20
|
parameters: dict | None = None,
|
|
20
|
-
|
|
21
|
+
base_model: str | None = None,
|
|
21
22
|
model_id: str | None = None,
|
|
22
|
-
model_revision: str = None,
|
|
23
|
+
model_revision: str | None = None,
|
|
23
24
|
) -> None:
|
|
24
|
-
super().__init__(path, framework, algorithm,
|
|
25
|
+
super().__init__(path, framework, algorithm, parameters)
|
|
26
|
+
self.base_model = base_model
|
|
25
27
|
self.model_id = model_id
|
|
26
28
|
self.model_revision = model_revision
|
|
27
29
|
|
|
@@ -31,6 +33,9 @@ class ModelValidatorHuggingface(ModelValidator):
|
|
|
31
33
|
ModelValidatorHuggingface validator.
|
|
32
34
|
"""
|
|
33
35
|
|
|
36
|
+
base_model: Optional[str] = None
|
|
37
|
+
"""Base model."""
|
|
38
|
+
|
|
34
39
|
placeholder_model_id: str = Field(default=None, alias="model_id")
|
|
35
40
|
"""Huggingface model id. If not specified, the model is loaded from the model path."""
|
|
36
41
|
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel
|
|
5
|
+
from pydantic import BaseModel
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Signature(BaseModel):
|
|
@@ -26,7 +26,7 @@ class Dataset(BaseModel):
|
|
|
26
26
|
name: Optional[str] = None
|
|
27
27
|
digest: Optional[str] = None
|
|
28
28
|
profile: Optional[str] = None
|
|
29
|
-
|
|
29
|
+
dataset_schema: Optional[str] = None
|
|
30
30
|
source: Optional[str] = None
|
|
31
31
|
source_type: Optional[str] = None
|
|
32
32
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
3
5
|
from pydantic import Field
|
|
4
6
|
|
|
5
7
|
from digitalhub.entities.model._base.spec import ModelSpec, ModelValidator
|
|
@@ -16,15 +18,13 @@ class ModelSpecMlflow(ModelSpec):
|
|
|
16
18
|
path: str,
|
|
17
19
|
framework: str | None = None,
|
|
18
20
|
algorithm: str | None = None,
|
|
19
|
-
base_model: str | None = None,
|
|
20
21
|
parameters: dict | None = None,
|
|
21
|
-
metrics: dict | None = None,
|
|
22
22
|
flavor: str | None = None,
|
|
23
23
|
model_config: dict | None = None,
|
|
24
24
|
input_datasets: list[Dataset] | None = None,
|
|
25
|
-
signature: Signature = None,
|
|
25
|
+
signature: Signature | None = None,
|
|
26
26
|
) -> None:
|
|
27
|
-
super().__init__(path, framework, algorithm,
|
|
27
|
+
super().__init__(path, framework, algorithm, parameters)
|
|
28
28
|
self.flavor = flavor
|
|
29
29
|
self.model_config = model_config
|
|
30
30
|
self.input_datasets = input_datasets
|
|
@@ -36,14 +36,14 @@ class ModelValidatorMlflow(ModelValidator):
|
|
|
36
36
|
ModelValidatorMlflow validator.
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
-
flavor: str = None
|
|
39
|
+
flavor: Optional[str] = None
|
|
40
40
|
"""Mlflow model flavor."""
|
|
41
41
|
|
|
42
42
|
placeholder_cfg_: dict = Field(default=None, alias="model_config")
|
|
43
43
|
"""Mlflow model config."""
|
|
44
44
|
|
|
45
|
-
input_datasets: list[Dataset] = None
|
|
45
|
+
input_datasets: Optional[list[Dataset]] = None
|
|
46
46
|
"""Mlflow input datasets."""
|
|
47
47
|
|
|
48
|
-
signature: Signature = None
|
|
48
|
+
signature: Optional[Signature] = None
|
|
49
49
|
"""Mlflow model signature."""
|
|
@@ -1,11 +1,32 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import typing
|
|
3
4
|
from urllib.parse import urlparse
|
|
4
5
|
|
|
5
6
|
import mlflow
|
|
6
7
|
|
|
7
8
|
from digitalhub.entities.model.mlflow.models import Dataset, Signature
|
|
8
9
|
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from mlflow.entities import Run
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_mlflow_run(run_id: str) -> Run:
|
|
15
|
+
"""
|
|
16
|
+
Get MLFlow run.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
run_id : str
|
|
21
|
+
The id of the mlflow run.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
Run
|
|
26
|
+
The extracted run.
|
|
27
|
+
"""
|
|
28
|
+
return mlflow.MlflowClient().get_run(run_id)
|
|
29
|
+
|
|
9
30
|
|
|
10
31
|
def from_mlflow_run(run_id: str) -> dict:
|
|
11
32
|
"""
|
|
@@ -23,13 +44,11 @@ def from_mlflow_run(run_id: str) -> dict:
|
|
|
23
44
|
"""
|
|
24
45
|
|
|
25
46
|
# Get MLFlow run
|
|
26
|
-
|
|
27
|
-
run = client.get_run(run_id)
|
|
47
|
+
run = get_mlflow_run(run_id)
|
|
28
48
|
|
|
29
49
|
# Extract spec
|
|
30
50
|
data = run.data
|
|
31
51
|
parameters = data.params
|
|
32
|
-
metrics = data.metrics
|
|
33
52
|
source_path = urlparse(run.info.artifact_uri).path
|
|
34
53
|
model_uri = f"runs:/{run_id}/model"
|
|
35
54
|
model = mlflow.pyfunc.load_model(model_uri=model_uri)
|
|
@@ -56,7 +75,7 @@ def from_mlflow_run(run_id: str) -> dict:
|
|
|
56
75
|
name=d.dataset.name,
|
|
57
76
|
digest=d.dataset.digest,
|
|
58
77
|
profile=d.dataset.profile,
|
|
59
|
-
|
|
78
|
+
dataset_schema=d.dataset.schema,
|
|
60
79
|
source=d.dataset.source,
|
|
61
80
|
source_type=d.dataset.source_type,
|
|
62
81
|
).to_dict()
|
|
@@ -72,7 +91,6 @@ def from_mlflow_run(run_id: str) -> dict:
|
|
|
72
91
|
# common properties
|
|
73
92
|
model_params["framework"] = flavor
|
|
74
93
|
model_params["parameters"] = parameters
|
|
75
|
-
model_params["metrics"] = metrics
|
|
76
94
|
|
|
77
95
|
# specific to MLFlow
|
|
78
96
|
model_params["flavor"] = flavor
|
|
@@ -81,3 +99,24 @@ def from_mlflow_run(run_id: str) -> dict:
|
|
|
81
99
|
model_params["signature"] = signature
|
|
82
100
|
|
|
83
101
|
return model_params
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_mlflow_model_metrics(run_id: str) -> dict:
|
|
105
|
+
"""
|
|
106
|
+
Get MLFlow model metrics for a given run id.
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
run_id : str
|
|
111
|
+
The id of the mlflow run.
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
dict
|
|
116
|
+
The extracted metrics.
|
|
117
|
+
"""
|
|
118
|
+
# Get MLFlow run
|
|
119
|
+
run = get_mlflow_run(run_id)
|
|
120
|
+
|
|
121
|
+
# Extract metrics
|
|
122
|
+
return run.data.metrics
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import typing
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
from typing import Any
|
|
5
6
|
|
|
6
|
-
from digitalhub.
|
|
7
|
+
from digitalhub.client.api import get_client
|
|
8
|
+
from digitalhub.context.api import build_context
|
|
9
|
+
from digitalhub.entities._base.entity.entity import Entity
|
|
7
10
|
from digitalhub.entities._commons.enums import EntityTypes
|
|
8
|
-
from digitalhub.entities.
|
|
11
|
+
from digitalhub.entities._processors.base import base_processor
|
|
12
|
+
from digitalhub.entities._processors.context import context_processor
|
|
9
13
|
from digitalhub.entities.artifact.crud import (
|
|
10
14
|
delete_artifact,
|
|
11
15
|
get_artifact,
|
|
@@ -64,9 +68,14 @@ from digitalhub.entities.workflow.crud import (
|
|
|
64
68
|
new_workflow,
|
|
65
69
|
update_workflow,
|
|
66
70
|
)
|
|
67
|
-
from digitalhub.
|
|
71
|
+
from digitalhub.factory.api import build_entity_from_dict
|
|
72
|
+
from digitalhub.utils.exceptions import BackendError, EntityAlreadyExistsError, EntityError
|
|
73
|
+
from digitalhub.utils.generic_utils import get_timestamp
|
|
74
|
+
from digitalhub.utils.io_utils import write_yaml
|
|
75
|
+
from digitalhub.utils.uri_utils import has_local_scheme
|
|
68
76
|
|
|
69
77
|
if typing.TYPE_CHECKING:
|
|
78
|
+
from digitalhub.entities._base.context.entity import ContextEntity
|
|
70
79
|
from digitalhub.entities._base.entity.metadata import Metadata
|
|
71
80
|
from digitalhub.entities.artifact._base.entity import Artifact
|
|
72
81
|
from digitalhub.entities.dataitem._base.entity import Dataitem
|
|
@@ -79,7 +88,7 @@ if typing.TYPE_CHECKING:
|
|
|
79
88
|
from digitalhub.entities.workflow._base.entity import Workflow
|
|
80
89
|
|
|
81
90
|
|
|
82
|
-
class Project(
|
|
91
|
+
class Project(Entity):
|
|
83
92
|
"""
|
|
84
93
|
A class representing a project.
|
|
85
94
|
"""
|
|
@@ -96,9 +105,308 @@ class Project(ProjectEntity):
|
|
|
96
105
|
user: str | None = None,
|
|
97
106
|
local: bool = False,
|
|
98
107
|
) -> None:
|
|
99
|
-
super().__init__(
|
|
100
|
-
self.
|
|
101
|
-
self.
|
|
108
|
+
super().__init__(kind, metadata, spec, status, user)
|
|
109
|
+
self.id = name
|
|
110
|
+
self.name = name
|
|
111
|
+
self.key = base_processor.build_project_key(self.name, local=local)
|
|
112
|
+
|
|
113
|
+
self._obj_attr.extend(["id", "name"])
|
|
114
|
+
|
|
115
|
+
# Set client
|
|
116
|
+
self._client = get_client(local)
|
|
117
|
+
|
|
118
|
+
# Set context
|
|
119
|
+
build_context(self)
|
|
120
|
+
|
|
121
|
+
##############################
|
|
122
|
+
# Save / Refresh / Export
|
|
123
|
+
##############################
|
|
124
|
+
|
|
125
|
+
def save(self, update: bool = False) -> Project:
|
|
126
|
+
"""
|
|
127
|
+
Save entity into backend.
|
|
128
|
+
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
update : bool
|
|
132
|
+
If True, the object will be updated.
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
Project
|
|
137
|
+
Entity saved.
|
|
138
|
+
"""
|
|
139
|
+
if update:
|
|
140
|
+
if self._client.is_local():
|
|
141
|
+
self.metadata.updated = get_timestamp()
|
|
142
|
+
new_obj = base_processor.update_project_entity(
|
|
143
|
+
entity_type=self.ENTITY_TYPE,
|
|
144
|
+
entity_name=self.name,
|
|
145
|
+
entity_dict=self.to_dict(),
|
|
146
|
+
local=self._client.is_local(),
|
|
147
|
+
)
|
|
148
|
+
else:
|
|
149
|
+
new_obj = base_processor.create_project_entity(_entity=self)
|
|
150
|
+
self._update_attributes(new_obj)
|
|
151
|
+
return self
|
|
152
|
+
|
|
153
|
+
def refresh(self) -> Project:
|
|
154
|
+
"""
|
|
155
|
+
Refresh object from backend.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
Project
|
|
160
|
+
Project object.
|
|
161
|
+
"""
|
|
162
|
+
new_obj = base_processor.read_project_entity(
|
|
163
|
+
entity_type=self.ENTITY_TYPE,
|
|
164
|
+
entity_name=self.name,
|
|
165
|
+
local=self._client.is_local(),
|
|
166
|
+
)
|
|
167
|
+
self._update_attributes(new_obj)
|
|
168
|
+
return self
|
|
169
|
+
|
|
170
|
+
def search_entity(
|
|
171
|
+
self,
|
|
172
|
+
query: str | None = None,
|
|
173
|
+
entity_types: list[str] | None = None,
|
|
174
|
+
name: str | None = None,
|
|
175
|
+
kind: str | None = None,
|
|
176
|
+
created: str | None = None,
|
|
177
|
+
updated: str | None = None,
|
|
178
|
+
description: str | None = None,
|
|
179
|
+
labels: list[str] | None = None,
|
|
180
|
+
**kwargs,
|
|
181
|
+
) -> list[ContextEntity]:
|
|
182
|
+
"""
|
|
183
|
+
Search objects from backend.
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
query : str
|
|
188
|
+
Search query.
|
|
189
|
+
entity_types : list[str]
|
|
190
|
+
Entity types.
|
|
191
|
+
name : str
|
|
192
|
+
Entity name.
|
|
193
|
+
kind : str
|
|
194
|
+
Entity kind.
|
|
195
|
+
created : str
|
|
196
|
+
Entity creation date.
|
|
197
|
+
updated : str
|
|
198
|
+
Entity update date.
|
|
199
|
+
description : str
|
|
200
|
+
Entity description.
|
|
201
|
+
labels : list[str]
|
|
202
|
+
Entity labels.
|
|
203
|
+
**kwargs : dict
|
|
204
|
+
Parameters to pass to the API call.
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
list[ContextEntity]
|
|
209
|
+
List of object instances.
|
|
210
|
+
"""
|
|
211
|
+
objs = context_processor.search_entity(
|
|
212
|
+
self.name,
|
|
213
|
+
query=query,
|
|
214
|
+
entity_types=entity_types,
|
|
215
|
+
name=name,
|
|
216
|
+
kind=kind,
|
|
217
|
+
created=created,
|
|
218
|
+
updated=updated,
|
|
219
|
+
description=description,
|
|
220
|
+
labels=labels,
|
|
221
|
+
**kwargs,
|
|
222
|
+
)
|
|
223
|
+
self.refresh()
|
|
224
|
+
return objs
|
|
225
|
+
|
|
226
|
+
def export(self) -> str:
|
|
227
|
+
"""
|
|
228
|
+
Export object as a YAML file in the context folder.
|
|
229
|
+
If the objects are not embedded, the objects are exported as a YAML file.
|
|
230
|
+
|
|
231
|
+
Returns
|
|
232
|
+
-------
|
|
233
|
+
str
|
|
234
|
+
Exported filepath.
|
|
235
|
+
"""
|
|
236
|
+
obj = self._refresh_to_dict()
|
|
237
|
+
pth = Path(self.spec.context) / f"{self.ENTITY_TYPE}s-{self.name}.yaml"
|
|
238
|
+
obj = self._export_not_embedded(obj)
|
|
239
|
+
write_yaml(pth, obj)
|
|
240
|
+
return str(pth)
|
|
241
|
+
|
|
242
|
+
def _refresh_to_dict(self) -> dict:
|
|
243
|
+
"""
|
|
244
|
+
Try to refresh object to collect entities related to project.
|
|
245
|
+
|
|
246
|
+
Returns
|
|
247
|
+
-------
|
|
248
|
+
dict
|
|
249
|
+
Entity object in dictionary format.
|
|
250
|
+
"""
|
|
251
|
+
try:
|
|
252
|
+
return self.refresh().to_dict()
|
|
253
|
+
except BackendError:
|
|
254
|
+
return self.to_dict()
|
|
255
|
+
|
|
256
|
+
def _export_not_embedded(self, obj: dict) -> dict:
|
|
257
|
+
"""
|
|
258
|
+
Export project objects if not embedded.
|
|
259
|
+
|
|
260
|
+
Parameters
|
|
261
|
+
----------
|
|
262
|
+
obj : dict
|
|
263
|
+
Project object in dictionary format.
|
|
264
|
+
|
|
265
|
+
Returns
|
|
266
|
+
-------
|
|
267
|
+
dict
|
|
268
|
+
Updatated project object in dictionary format with referenced entities.
|
|
269
|
+
"""
|
|
270
|
+
# Cycle over entity types
|
|
271
|
+
for entity_type in self._get_entity_types():
|
|
272
|
+
# Entity types are stored as a list of entities
|
|
273
|
+
for idx, entity in enumerate(obj.get("spec", {}).get(entity_type, [])):
|
|
274
|
+
# Export entity if not embedded is in metadata, else do nothing
|
|
275
|
+
if not self._is_embedded(entity):
|
|
276
|
+
# Get entity object from backend
|
|
277
|
+
ent = context_processor.read_context_entity(entity["key"])
|
|
278
|
+
|
|
279
|
+
# Export and store ref in object metadata inside project
|
|
280
|
+
pth = ent.export()
|
|
281
|
+
obj["spec"][entity_type][idx]["metadata"]["ref"] = pth
|
|
282
|
+
|
|
283
|
+
# Return updated object
|
|
284
|
+
return obj
|
|
285
|
+
|
|
286
|
+
def _import_entities(self, obj: dict) -> None:
|
|
287
|
+
"""
|
|
288
|
+
Import project entities.
|
|
289
|
+
|
|
290
|
+
Parameters
|
|
291
|
+
----------
|
|
292
|
+
obj : dict
|
|
293
|
+
Project object in dictionary format.
|
|
294
|
+
|
|
295
|
+
Returns
|
|
296
|
+
-------
|
|
297
|
+
None
|
|
298
|
+
"""
|
|
299
|
+
entity_types = self._get_entity_types()
|
|
300
|
+
|
|
301
|
+
# Cycle over entity types
|
|
302
|
+
for entity_type in entity_types:
|
|
303
|
+
# Entity types are stored as a list of entities
|
|
304
|
+
for entity in obj.get("spec", {}).get(entity_type, []):
|
|
305
|
+
embedded = self._is_embedded(entity)
|
|
306
|
+
ref = entity["metadata"].get("ref")
|
|
307
|
+
|
|
308
|
+
# Import entity if not embedded and there is a ref
|
|
309
|
+
if not embedded and ref is not None:
|
|
310
|
+
# Import entity from local ref
|
|
311
|
+
if has_local_scheme(ref):
|
|
312
|
+
try:
|
|
313
|
+
# Artifacts, Dataitems and Models
|
|
314
|
+
if entity_type in entity_types[:3]:
|
|
315
|
+
context_processor.import_context_entity(ref)
|
|
316
|
+
|
|
317
|
+
# Functions and Workflows
|
|
318
|
+
elif entity_type in entity_types[3:]:
|
|
319
|
+
context_processor.import_executable_entity(ref)
|
|
320
|
+
|
|
321
|
+
except FileNotFoundError:
|
|
322
|
+
msg = f"File not found: {ref}."
|
|
323
|
+
raise EntityError(msg)
|
|
324
|
+
|
|
325
|
+
# If entity is embedded, create it and try to save
|
|
326
|
+
elif embedded:
|
|
327
|
+
# It's possible that embedded field in metadata is not shown
|
|
328
|
+
if entity["metadata"].get("embedded") is None:
|
|
329
|
+
entity["metadata"]["embedded"] = True
|
|
330
|
+
|
|
331
|
+
try:
|
|
332
|
+
build_entity_from_dict(entity).save()
|
|
333
|
+
except EntityAlreadyExistsError:
|
|
334
|
+
pass
|
|
335
|
+
|
|
336
|
+
def _load_entities(self, obj: dict) -> None:
|
|
337
|
+
"""
|
|
338
|
+
Load project entities.
|
|
339
|
+
|
|
340
|
+
Parameters
|
|
341
|
+
----------
|
|
342
|
+
obj : dict
|
|
343
|
+
Project object in dictionary format.
|
|
344
|
+
|
|
345
|
+
Returns
|
|
346
|
+
-------
|
|
347
|
+
None
|
|
348
|
+
"""
|
|
349
|
+
entity_types = self._get_entity_types()
|
|
350
|
+
|
|
351
|
+
# Cycle over entity types
|
|
352
|
+
for entity_type in entity_types:
|
|
353
|
+
# Entity types are stored as a list of entities
|
|
354
|
+
for entity in obj.get("spec", {}).get(entity_type, []):
|
|
355
|
+
embedded = self._is_embedded(entity)
|
|
356
|
+
ref = entity["metadata"].get("ref")
|
|
357
|
+
|
|
358
|
+
# Load entity if not embedded and there is a ref
|
|
359
|
+
if not embedded and ref is not None:
|
|
360
|
+
# Load entity from local ref
|
|
361
|
+
if has_local_scheme(ref):
|
|
362
|
+
try:
|
|
363
|
+
# Artifacts, Dataitems and Models
|
|
364
|
+
if entity_type in entity_types[:3]:
|
|
365
|
+
context_processor.load_context_entity(ref)
|
|
366
|
+
|
|
367
|
+
# Functions and Workflows
|
|
368
|
+
elif entity_type in entity_types[3:]:
|
|
369
|
+
context_processor.load_executable_entity(ref)
|
|
370
|
+
|
|
371
|
+
except FileNotFoundError:
|
|
372
|
+
msg = f"File not found: {ref}."
|
|
373
|
+
raise EntityError(msg)
|
|
374
|
+
|
|
375
|
+
def _is_embedded(self, entity: dict) -> bool:
|
|
376
|
+
"""
|
|
377
|
+
Check if entity is embedded.
|
|
378
|
+
|
|
379
|
+
Parameters
|
|
380
|
+
----------
|
|
381
|
+
entity : dict
|
|
382
|
+
Entity in dictionary format.
|
|
383
|
+
|
|
384
|
+
Returns
|
|
385
|
+
-------
|
|
386
|
+
bool
|
|
387
|
+
True if entity is embedded.
|
|
388
|
+
"""
|
|
389
|
+
metadata_embedded = entity["metadata"].get("embedded", False)
|
|
390
|
+
no_status = entity.get("status", None) is None
|
|
391
|
+
no_spec = entity.get("spec", None) is None
|
|
392
|
+
return metadata_embedded or not (no_status and no_spec)
|
|
393
|
+
|
|
394
|
+
def _get_entity_types(self) -> list[str]:
|
|
395
|
+
"""
|
|
396
|
+
Get entity types.
|
|
397
|
+
|
|
398
|
+
Returns
|
|
399
|
+
-------
|
|
400
|
+
list
|
|
401
|
+
Entity types.
|
|
402
|
+
"""
|
|
403
|
+
return [
|
|
404
|
+
f"{EntityTypes.ARTIFACT.value}s",
|
|
405
|
+
f"{EntityTypes.DATAITEM.value}s",
|
|
406
|
+
f"{EntityTypes.MODEL.value}s",
|
|
407
|
+
f"{EntityTypes.FUNCTION.value}s",
|
|
408
|
+
f"{EntityTypes.WORKFLOW.value}s",
|
|
409
|
+
]
|
|
102
410
|
|
|
103
411
|
##############################
|
|
104
412
|
# Artifacts
|
|
@@ -1859,7 +2167,7 @@ class Project(ProjectEntity):
|
|
|
1859
2167
|
-------
|
|
1860
2168
|
None
|
|
1861
2169
|
"""
|
|
1862
|
-
return
|
|
2170
|
+
return base_processor.share_project_entity(
|
|
1863
2171
|
entity_type=self.ENTITY_TYPE,
|
|
1864
2172
|
entity_name=self.name,
|
|
1865
2173
|
user=user,
|
|
@@ -1879,7 +2187,7 @@ class Project(ProjectEntity):
|
|
|
1879
2187
|
-------
|
|
1880
2188
|
None
|
|
1881
2189
|
"""
|
|
1882
|
-
return
|
|
2190
|
+
return base_processor.share_project_entity(
|
|
1883
2191
|
entity_type=self.ENTITY_TYPE,
|
|
1884
2192
|
entity_name=self.name,
|
|
1885
2193
|
user=user,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
3
5
|
from digitalhub.entities._base.entity.spec import Spec, SpecValidator
|
|
4
6
|
|
|
5
7
|
|
|
@@ -31,20 +33,20 @@ class ProjectValidator(SpecValidator):
|
|
|
31
33
|
ProjectValidator validator.
|
|
32
34
|
"""
|
|
33
35
|
|
|
34
|
-
context: str = None
|
|
36
|
+
context: Optional[str] = None
|
|
35
37
|
"""The project's context."""
|
|
36
38
|
|
|
37
|
-
functions: list = None
|
|
39
|
+
functions: Optional[list] = None
|
|
38
40
|
"""List of project's functions."""
|
|
39
41
|
|
|
40
|
-
artifacts: list = None
|
|
42
|
+
artifacts: Optional[list] = None
|
|
41
43
|
"""List of project's artifacts."""
|
|
42
44
|
|
|
43
|
-
workflows: list = None
|
|
45
|
+
workflows: Optional[list] = None
|
|
44
46
|
"""List of project's workflows."""
|
|
45
47
|
|
|
46
|
-
dataitems: list = None
|
|
48
|
+
dataitems: Optional[list] = None
|
|
47
49
|
"""List of project's dataitems."""
|
|
48
50
|
|
|
49
|
-
models: list = None
|
|
51
|
+
models: Optional[list] = None
|
|
50
52
|
"""List of project's models."""
|
|
@@ -3,7 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import typing
|
|
4
4
|
|
|
5
5
|
from digitalhub.entities._commons.enums import EntityTypes
|
|
6
|
-
from digitalhub.entities.
|
|
6
|
+
from digitalhub.entities._processors.base import base_processor
|
|
7
|
+
from digitalhub.entities._processors.context import context_processor
|
|
7
8
|
from digitalhub.entities.project.utils import setup_project
|
|
8
9
|
from digitalhub.utils.exceptions import BackendError
|
|
9
10
|
|
|
@@ -58,7 +59,7 @@ def new_project(
|
|
|
58
59
|
"""
|
|
59
60
|
if context is None:
|
|
60
61
|
context = "./"
|
|
61
|
-
obj =
|
|
62
|
+
obj = base_processor.create_project_entity(
|
|
62
63
|
name=name,
|
|
63
64
|
kind="project",
|
|
64
65
|
description=description,
|
|
@@ -103,7 +104,7 @@ def get_project(
|
|
|
103
104
|
--------
|
|
104
105
|
>>> obj = get_project("my-project")
|
|
105
106
|
"""
|
|
106
|
-
obj =
|
|
107
|
+
obj = base_processor.read_project_entity(
|
|
107
108
|
entity_type=ENTITY_TYPE,
|
|
108
109
|
entity_name=name,
|
|
109
110
|
local=local,
|
|
@@ -142,7 +143,7 @@ def import_project(
|
|
|
142
143
|
--------
|
|
143
144
|
>>> obj = import_project("my-project.yaml")
|
|
144
145
|
"""
|
|
145
|
-
obj =
|
|
146
|
+
obj = base_processor.import_project_entity(file=file, local=local, config=config)
|
|
146
147
|
return setup_project(obj, setup_kwargs)
|
|
147
148
|
|
|
148
149
|
|
|
@@ -175,7 +176,7 @@ def load_project(
|
|
|
175
176
|
--------
|
|
176
177
|
>>> obj = load_project("my-project.yaml")
|
|
177
178
|
"""
|
|
178
|
-
obj =
|
|
179
|
+
obj = base_processor.load_project_entity(file=file, local=local, config=config)
|
|
179
180
|
return setup_project(obj, setup_kwargs)
|
|
180
181
|
|
|
181
182
|
|
|
@@ -195,7 +196,7 @@ def list_projects(local: bool = False, **kwargs) -> list[Project]:
|
|
|
195
196
|
list
|
|
196
197
|
List of objects.
|
|
197
198
|
"""
|
|
198
|
-
return
|
|
199
|
+
return base_processor.list_project_entities(local=local, **kwargs)
|
|
199
200
|
|
|
200
201
|
|
|
201
202
|
def get_or_create_project(
|
|
@@ -268,11 +269,11 @@ def update_project(entity: Project, **kwargs) -> Project:
|
|
|
268
269
|
--------
|
|
269
270
|
>>> obj = update_project(obj)
|
|
270
271
|
"""
|
|
271
|
-
return
|
|
272
|
+
return base_processor.update_project_entity(
|
|
272
273
|
entity_type=entity.ENTITY_TYPE,
|
|
273
274
|
entity_name=entity.name,
|
|
274
275
|
entity_dict=entity.to_dict(),
|
|
275
|
-
local=entity.
|
|
276
|
+
local=entity._client.is_local(),
|
|
276
277
|
**kwargs,
|
|
277
278
|
)
|
|
278
279
|
|
|
@@ -283,7 +284,7 @@ def delete_project(
|
|
|
283
284
|
clean_context: bool = True,
|
|
284
285
|
local: bool = False,
|
|
285
286
|
**kwargs,
|
|
286
|
-
) ->
|
|
287
|
+
) -> dict:
|
|
287
288
|
"""
|
|
288
289
|
Delete a project.
|
|
289
290
|
|
|
@@ -309,7 +310,7 @@ def delete_project(
|
|
|
309
310
|
--------
|
|
310
311
|
>>> delete_project("my-project")
|
|
311
312
|
"""
|
|
312
|
-
return
|
|
313
|
+
return base_processor.delete_project_entity(
|
|
313
314
|
entity_type=ENTITY_TYPE,
|
|
314
315
|
entity_name=name,
|
|
315
316
|
local=local,
|
|
@@ -362,7 +363,7 @@ def search_entity(
|
|
|
362
363
|
list[ContextEntity]
|
|
363
364
|
List of object instances.
|
|
364
365
|
"""
|
|
365
|
-
return
|
|
366
|
+
return context_processor.search_entity(
|
|
366
367
|
project_name,
|
|
367
368
|
query=query,
|
|
368
369
|
entity_types=entity_types,
|