Skip to content

Commit 8f96483

Browse files
committed
Merge branch 'add-template-type-filtering' of github.com:matusdrobuliak66/osparc-simcore into enh/filter-types
2 parents 74fa7fd + 85e77da commit 8f96483

File tree

8 files changed

+82
-15
lines changed

8 files changed

+82
-15
lines changed

packages/models-library/src/models_library/api_schemas_webserver/projects.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ class ProjectPatch(InputSchema):
261261
),
262262
] = None
263263
quality: dict[str, Any] | None = None
264+
template_type: ProjectTemplateType | None = None
264265

265266
def to_domain_model(self) -> dict[str, Any]:
266267
return self.model_dump(exclude_unset=True, by_alias=False)

services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3962,6 +3962,14 @@ paths:
39623962
schema:
39633963
$ref: '#/components/schemas/ProjectTypeAPI'
39643964
default: all
3965+
- name: template_type
3966+
in: query
3967+
required: false
3968+
schema:
3969+
anyOf:
3970+
- $ref: '#/components/schemas/ProjectTemplateType'
3971+
- type: 'null'
3972+
title: Template Type
39653973
- name: show_hidden
39663974
in: query
39673975
required: false
@@ -14868,6 +14876,10 @@ components:
1486814876
- type: object
1486914877
- type: 'null'
1487014878
title: Quality
14879+
templateType:
14880+
anyOf:
14881+
- $ref: '#/components/schemas/ProjectTemplateType'
14882+
- type: 'null'
1487114883
type: object
1487214884
title: ProjectPatch
1487314885
ProjectPermalink:

services/web/server/src/simcore_service_webserver/projects/_controller/projects_rest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ async def list_projects(request: web.Request):
153153
user_id=req_ctx.user_id,
154154
product_name=req_ctx.product_name,
155155
project_type=query_params.project_type,
156+
template_type=query_params.template_type,
156157
show_hidden=query_params.show_hidden,
157158
trashed=query_params.filters.trashed,
158159
folder_id=query_params.folder_id,

services/web/server/src/simcore_service_webserver/projects/_controller/projects_rest_schemas.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from models_library.basic_types import IDStr
44
from models_library.folders import FolderID
5-
from models_library.projects import ProjectID
5+
from models_library.projects import ProjectID, ProjectTemplateType
66
from models_library.projects_nodes_io import NodeID
77
from models_library.rest_base import RequestParameters
88
from models_library.rest_filters import Filters, FiltersQueryParameters
@@ -136,6 +136,7 @@ class ProjectFilters(Filters):
136136

137137
class ProjectsListExtraQueryParams(RequestParameters):
138138
project_type: Annotated[ProjectTypeAPI, Field(alias="type")] = ProjectTypeAPI.all
139+
template_type: Annotated[ProjectTemplateType | None, Field(...)] = None
139140
show_hidden: Annotated[
140141
bool, Field(description="includes projects marked as hidden in the listing")
141142
] = False
@@ -167,6 +168,22 @@ def _search_check_empty_string(cls, v):
167168
return None
168169
return v
169170

171+
_template_type_null_or_none_str_to_none_validator = field_validator(
172+
"template_type", mode="before"
173+
)(null_or_none_str_to_none_validator)
174+
175+
@model_validator(mode="after")
176+
def check_template_type_compatibility(self):
177+
if (
178+
self.project_type in [ProjectTypeAPI.all, ProjectTypeAPI.user]
179+
and self.template_type is not None
180+
):
181+
msg = (
182+
"When project type is `all` or `user` the template_type should be None"
183+
)
184+
raise ValueError(msg)
185+
return self
186+
170187
_null_or_none_str_to_none_validator = field_validator("folder_id", mode="before")(
171188
null_or_none_str_to_none_validator
172189
)

services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from aiohttp import web
1212
from models_library.folders import FolderID, FolderQuery, FolderScope
13-
from models_library.projects import ProjectID
13+
from models_library.projects import ProjectID, ProjectTemplateType
1414
from models_library.rest_ordering import OrderBy
1515
from models_library.users import UserID
1616
from models_library.workspaces import WorkspaceID, WorkspaceQuery, WorkspaceScope
@@ -91,6 +91,7 @@ async def list_projects( # pylint: disable=too-many-arguments
9191
folder_id: FolderID | None,
9292
# attrs filter
9393
project_type: ProjectTypeAPI,
94+
template_type: ProjectTemplateType | None,
9495
show_hidden: bool, # NOTE: Be careful, this filters only hidden projects
9596
trashed: bool | None,
9697
# search
@@ -148,6 +149,7 @@ async def list_projects( # pylint: disable=too-many-arguments
148149
),
149150
# attrs
150151
filter_by_project_type=ProjectTypeAPI.to_project_type_db(project_type),
152+
filter_by_template_type=template_type,
151153
filter_by_services=user_available_services,
152154
filter_trashed=trashed,
153155
filter_hidden=show_hidden,

services/web/server/src/simcore_service_webserver/projects/_projects_repository_legacy.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ def _create_shared_workspace_query(
583583
def _create_attributes_filters(
584584
*,
585585
filter_by_project_type: ProjectType | None,
586+
filter_by_template_type: ProjectTemplateType | None,
586587
filter_hidden: bool | None,
587588
filter_published: bool | None,
588589
filter_trashed: bool | None,
@@ -595,6 +596,11 @@ def _create_attributes_filters(
595596
if filter_by_project_type is not None:
596597
attributes_filters.append(projects.c.type == filter_by_project_type.value)
597598

599+
if filter_by_template_type is not None:
600+
attributes_filters.append(
601+
projects.c.template_type == filter_by_template_type.value
602+
)
603+
598604
if filter_hidden is not None:
599605
attributes_filters.append(projects.c.hidden.is_(filter_hidden))
600606

@@ -647,6 +653,7 @@ async def list_projects_dicts( # pylint: disable=too-many-arguments,too-many-st
647653
folder_query: FolderQuery,
648654
# attribute filters
649655
filter_by_project_type: ProjectType | None = None,
656+
filter_by_template_type: ProjectTemplateType | None = None,
650657
filter_by_services: list[dict] | None = None,
651658
filter_published: bool | None = None,
652659
filter_hidden: bool | None = False,
@@ -694,6 +701,7 @@ async def list_projects_dicts( # pylint: disable=too-many-arguments,too-many-st
694701

695702
attributes_filters = self._create_attributes_filters(
696703
filter_by_project_type=filter_by_project_type,
704+
filter_by_template_type=filter_by_template_type,
697705
filter_hidden=filter_hidden,
698706
filter_published=filter_published,
699707
filter_trashed=filter_trashed,

services/web/server/src/simcore_service_webserver/projects/_projects_service.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
from .exceptions import (
140140
ClustersKeeperNotAvailableError,
141141
DefaultPricingUnitNotFoundError,
142+
InsufficientRoleForProjectTemplateTypeUpdateError,
142143
InvalidEC2TypeInResourcesSpecsError,
143144
InvalidKeysInResourcesSpecsError,
144145
NodeNotFoundError,
@@ -150,6 +151,7 @@
150151
ProjectOwnerNotFoundInTheProjectAccessRightsError,
151152
ProjectStartsTooManyDynamicNodesError,
152153
ProjectTooManyProjectOpenedError,
154+
ProjectTypeAndTemplateIncompatibilityError,
153155
)
154156
from .models import ProjectDict, ProjectPatchInternalExtended
155157
from .settings import ProjectsSettings, get_plugin_settings
@@ -327,7 +329,27 @@ async def patch_project(
327329
if new_prj_access_rights[_prj_owner_primary_group] != _prj_required_permissions:
328330
raise ProjectOwnerNotFoundInTheProjectAccessRightsError
329331

330-
# 4. Patch the project
332+
# 4. If patching template type
333+
if new_template_type := patch_project_data.get("template_type"):
334+
# 4.1 Check if user is a tester
335+
current_user: dict = await get_user(app, user_id)
336+
if not UserRole(current_user["role"]) >= UserRole.TESTER:
337+
raise InsufficientRoleForProjectTemplateTypeUpdateError
338+
# 4.2 Check the compatibility of the template type with the project
339+
if project_db.type == ProjectType.STANDARD and new_template_type is not None:
340+
raise ProjectTypeAndTemplateIncompatibilityError(
341+
project_uuid=project_uuid,
342+
project_type=project_db.type,
343+
project_template=new_template_type,
344+
)
345+
if project_db.type == ProjectType.TEMPLATE and new_template_type is None:
346+
raise ProjectTypeAndTemplateIncompatibilityError(
347+
project_uuid=project_uuid,
348+
project_type=project_db.type,
349+
project_template=new_template_type,
350+
)
351+
352+
# 5. Patch the project
331353
await _projects_repository.patch_project(
332354
app=app,
333355
project_uuid=project_uuid,

services/web/server/src/simcore_service_webserver/projects/exceptions.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ def debug_message(self):
2323
return f"{self.code}: {self}"
2424

2525

26-
class ProjectInvalidUsageError(BaseProjectError):
27-
...
26+
class ProjectInvalidUsageError(BaseProjectError): ...
2827

2928

3029
class ProjectOwnerNotFoundInTheProjectAccessRightsError(BaseProjectError):
@@ -83,8 +82,17 @@ class ProjectsBatchDeleteError(BaseProjectError):
8382
msg_template = "One or more projects could not be deleted in the batch: {errors}"
8483

8584

86-
class ProjectTrashError(BaseProjectError):
87-
...
85+
class ProjectsPatchError(BaseProjectError): ...
86+
87+
88+
class ProjectTypeAndTemplateIncompatibilityError(ProjectsPatchError):
89+
msg_template = "Patching project '{project_uuid}' type {project_type} and template {project_template} is not allowed"
90+
91+
92+
class InsufficientRoleForProjectTemplateTypeUpdateError(ProjectsPatchError): ...
93+
94+
95+
class ProjectTrashError(BaseProjectError): ...
8896

8997

9098
class ProjectStoppingError(ProjectTrashError):
@@ -146,12 +154,10 @@ def __init__(self, *, max_num_projects: int, **ctx):
146154
self.max_num_projects = max_num_projects
147155

148156

149-
class PermalinkNotAllowedError(BaseProjectError):
150-
...
157+
class PermalinkNotAllowedError(BaseProjectError): ...
151158

152159

153-
class PermalinkFactoryError(BaseProjectError):
154-
...
160+
class PermalinkFactoryError(BaseProjectError): ...
155161

156162

157163
class ProjectNodeResourcesInvalidError(BaseProjectError):
@@ -178,12 +184,10 @@ class InvalidEC2TypeInResourcesSpecsError(ProjectNodeResourcesInvalidError):
178184
)
179185

180186

181-
class ProjectNodeResourcesInsufficientRightsError(BaseProjectError):
182-
...
187+
class ProjectNodeResourcesInsufficientRightsError(BaseProjectError): ...
183188

184189

185-
class ProjectNodeRequiredInputsNotSetError(BaseProjectError):
186-
...
190+
class ProjectNodeRequiredInputsNotSetError(BaseProjectError): ...
187191

188192

189193
class ProjectNodeConnectionsMissingError(ProjectNodeRequiredInputsNotSetError):

0 commit comments

Comments
 (0)