Skip to content

Commit 5083273

Browse files
authored
Merge branch 'master' into add_wbserver_functions_rest
2 parents 45c28b6 + b86f633 commit 5083273

File tree

90 files changed

+3612
-2564
lines changed

Some content is hidden

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

90 files changed

+3612
-2564
lines changed

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@
2626
from ..emails import LowerCaseEmailStr
2727
from ..folders import FolderID
2828
from ..groups import GroupID
29-
from ..projects import ClassifierID, DateTimeStr, NodesDict, ProjectID
29+
from ..projects import (
30+
ClassifierID,
31+
DateTimeStr,
32+
NodesDict,
33+
ProjectID,
34+
ProjectTemplateType,
35+
ProjectType,
36+
)
3037
from ..projects_access import AccessRights, GroupIDStr
3138
from ..projects_state import ProjectState
3239
from ..utils._original_fastapi_encoders import jsonable_encoder
@@ -106,6 +113,9 @@ class ProjectGet(OutputSchema):
106113
description: str
107114
thumbnail: HttpUrl | Literal[""]
108115

116+
type: ProjectType
117+
template_type: ProjectTemplateType | None
118+
109119
workbench: NodesDict
110120

111121
prj_owner: LowerCaseEmailStr
@@ -153,6 +163,8 @@ def _update_json_schema_extra(schema: JsonDict) -> None:
153163
"name": "My Project",
154164
"description": "This is a sample project",
155165
"thumbnail": "https://example.com/thumbnail.png",
166+
"type": "STANDARD",
167+
"template_type": None,
156168
"workbench": {},
157169
"prj_owner": "[email protected]",
158170
"access_rights": {},

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
from datetime import datetime
6-
from enum import Enum
6+
from enum import Enum, auto
77
from typing import Annotated, Any, Final, TypeAlias
88
from uuid import UUID
99

@@ -25,6 +25,7 @@
2525
empty_str_to_none_pre_validator,
2626
none_to_empty_str_pre_validator,
2727
)
28+
from .utils.enums import StrAutoEnum
2829

2930
ProjectID: TypeAlias = UUID
3031
CommitID: TypeAlias = int
@@ -52,6 +53,12 @@ class ProjectType(str, Enum):
5253
STANDARD = "STANDARD"
5354

5455

56+
class ProjectTemplateType(StrAutoEnum):
57+
TEMPLATE = auto()
58+
TUTORIAL = auto()
59+
HYPERTOOL = auto()
60+
61+
5562
class BaseProjectModel(BaseModel):
5663
# Description of the project
5764
uuid: Annotated[
@@ -108,6 +115,12 @@ class ProjectAtDB(BaseProjectModel):
108115
project_type: Annotated[
109116
ProjectType, Field(alias="type", description="The project type")
110117
]
118+
template_type: Annotated[
119+
ProjectTemplateType | None,
120+
Field(
121+
examples=["TEMPLATE", "TUTORIAL", "HYPERTOOL", None],
122+
),
123+
]
111124

112125
prj_owner: Annotated[int | None, Field(description="The project owner id")]
113126

@@ -165,6 +178,22 @@ class Project(BaseProjectModel):
165178
# Project state (SEE projects_state.py)
166179
state: ProjectState | None = None
167180

181+
# Type of project
182+
type: Annotated[
183+
ProjectType,
184+
Field(
185+
description="The project type",
186+
examples=["TEMPLATE", "STANDARD"],
187+
),
188+
]
189+
template_type: Annotated[
190+
ProjectTemplateType | None,
191+
Field(
192+
alias="templateType",
193+
examples=["TEMPLATE", "TUTORIAL", "HYPERTOOL", None],
194+
),
195+
]
196+
168197
# UI front-end fields (SEE projects_ui.py)
169198
ui: dict[str, Any] | None = None
170199
dev: dict[str, Any] | None = None

packages/models-library/src/models_library/rpc/webserver/projects.py

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,68 @@
22
from typing import Annotated, TypeAlias
33
from uuid import uuid4
44

5-
from models_library.projects import NodesDict, ProjectID
6-
from models_library.projects_nodes import Node
7-
from models_library.rpc_pagination import PageRpc
85
from pydantic import BaseModel, ConfigDict, Field
96
from pydantic.config import JsonDict
107

8+
from ...projects import NodesDict, ProjectID
9+
from ...projects_nodes import Node
10+
from ...rpc_pagination import PageRpc
11+
12+
13+
class MetadataFilterItem(BaseModel):
14+
name: str
15+
pattern: str
16+
17+
18+
class ListProjectsMarkedAsJobRpcFilters(BaseModel):
19+
"""Filters model for the list_projects_marked_as_jobs RPC.
20+
21+
NOTE: Filters models are used to validate all possible filters in an API early on,
22+
particularly to ensure compatibility and prevent conflicts between different filters.
23+
"""
24+
25+
job_parent_resource_name_prefix: str | None = None
26+
27+
any_custom_metadata: Annotated[
28+
list[MetadataFilterItem] | None,
29+
Field(description="Searchs for matches of any of the custom metadata fields"),
30+
] = None
31+
32+
@staticmethod
33+
def _update_json_schema_extra(schema: JsonDict) -> None:
34+
schema.update(
35+
{
36+
"examples": [
37+
{
38+
"job_parent_resource_name_prefix": "solvers/solver123",
39+
"any_custom_metadata": [
40+
{
41+
"name": "solver_type",
42+
"pattern": "FEM",
43+
},
44+
{
45+
"name": "mesh_cells",
46+
"pattern": "1*",
47+
},
48+
],
49+
},
50+
{
51+
"any_custom_metadata": [
52+
{
53+
"name": "solver_type",
54+
"pattern": "*CFD*",
55+
}
56+
],
57+
},
58+
{"job_parent_resource_name_prefix": "solvers/solver123"},
59+
]
60+
}
61+
)
62+
63+
model_config = ConfigDict(
64+
json_schema_extra=_update_json_schema_extra,
65+
)
66+
1167

1268
class ProjectJobRpcGet(BaseModel):
1369
"""

packages/models-library/tests/test_projects.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ def minimal_project(faker: Faker) -> dict[str, Any]:
2424
"creationDate": "2019-05-24T10:36:57.813Z",
2525
"lastChangeDate": "2019-05-24T10:36:57.813Z",
2626
"workbench": {},
27+
"type": "STANDARD",
28+
"templateType": None,
2729
}
2830

2931

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""add templateType to projects
2+
3+
Revision ID: b39f2dc87ccd
4+
Revises: fc1701bb7e93
5+
Create Date: 2025-05-14 11:59:27.033449+00:00
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "b39f2dc87ccd"
14+
down_revision = "fc1701bb7e93"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
# Create enum type first
22+
project_template_type = sa.Enum(
23+
"TEMPLATE", "TUTORIAL", "HYPERTOOL", name="projecttemplatetype"
24+
)
25+
project_template_type.create(op.get_bind())
26+
27+
op.add_column(
28+
"projects",
29+
sa.Column(
30+
"template_type",
31+
project_template_type,
32+
nullable=True,
33+
default=None,
34+
),
35+
)
36+
# ### end Alembic commands ###
37+
op.execute("UPDATE projects SET template_type='TEMPLATE' WHERE type='TEMPLATE'")
38+
39+
40+
def downgrade():
41+
# ### commands auto generated by Alembic - please adjust! ###
42+
op.drop_column("projects", "template_type")
43+
# ### end Alembic commands ###
44+
sa.Enum(name="projecttemplatetype").drop(op.get_bind())

packages/postgres-database/src/simcore_postgres_database/models/projects.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ class ProjectType(enum.Enum):
1616
STANDARD = "STANDARD"
1717

1818

19+
class ProjectTemplateType(str, enum.Enum):
20+
TEMPLATE = "TEMPLATE"
21+
TUTORIAL = "TUTORIAL"
22+
HYPERTOOL = "HYPERTOOL"
23+
24+
1925
projects = sa.Table(
2026
"projects",
2127
metadata,
@@ -29,6 +35,13 @@ class ProjectType(enum.Enum):
2935
default=ProjectType.STANDARD,
3036
doc="Either standard or template types",
3137
),
38+
sa.Column(
39+
"template_type",
40+
sa.Enum(ProjectTemplateType),
41+
nullable=True,
42+
default=None,
43+
doc="None if type is STANDARD, otherwise it is one of the ProjectTemplateType",
44+
),
3245
sa.Column(
3346
"uuid",
3447
sa.String,

packages/pytest-simcore/src/pytest_simcore/helpers/webserver_rpc_server.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from models_library.projects import ProjectID
1010
from models_library.rest_pagination import PageOffsetInt
1111
from models_library.rpc.webserver.projects import (
12+
ListProjectsMarkedAsJobRpcFilters,
1213
PageRpcProjectJobRpcGet,
1314
ProjectJobRpcGet,
1415
)
@@ -56,24 +57,24 @@ async def list_projects_marked_as_jobs(
5657
# pagination
5758
offset: PageOffsetInt = 0,
5859
limit: PageLimitInt = DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
59-
# filters
60-
job_parent_resource_name_prefix: str | None = None,
60+
filters: ListProjectsMarkedAsJobRpcFilters | None = None,
6161
) -> PageRpcProjectJobRpcGet:
6262
assert rpc_client
6363
assert product_name
6464
assert user_id
6565

66-
if job_parent_resource_name_prefix:
67-
assert not job_parent_resource_name_prefix.startswith("/")
68-
assert not job_parent_resource_name_prefix.endswith("%")
69-
assert not job_parent_resource_name_prefix.startswith("%")
66+
if filters and filters.job_parent_resource_name_prefix:
67+
assert not filters.job_parent_resource_name_prefix.startswith("/")
68+
assert not filters.job_parent_resource_name_prefix.endswith("%")
69+
assert not filters.job_parent_resource_name_prefix.startswith("%")
7070

7171
items = [
7272
item
7373
for item in ProjectJobRpcGet.model_json_schema()["examples"]
74-
if job_parent_resource_name_prefix is None
74+
if filters is None
75+
or filters.job_parent_resource_name_prefix is None
7576
or item.get("job_parent_resource_name").startswith(
76-
job_parent_resource_name_prefix
77+
filters.job_parent_resource_name_prefix
7778
)
7879
]
7980

packages/pytest-simcore/src/pytest_simcore/simcore_webserver_projects_rest_api.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ def request_desc(self) -> str:
7575
},
7676
"dev": None,
7777
"workspace_id": None,
78+
"type": "STANDARD",
79+
"templateType": None,
7880
"folder_id": None,
7981
"trashedAt": None,
8082
"trashedBy": None,
@@ -102,6 +104,8 @@ def request_desc(self) -> str:
102104
"lastChangeDate": "2021-12-06T10:13:03.100Z",
103105
"workbench": {},
104106
"workspaceId": 123,
107+
"type": "STANDARD",
108+
"templateType": None,
105109
"folderId": 2,
106110
"accessRights": {"2": {"read": True, "write": True, "delete": True}},
107111
"dev": {},
@@ -157,6 +161,8 @@ def request_desc(self) -> str:
157161
"state": {"value": "NOT_STARTED"},
158162
},
159163
"workspace_id": None,
164+
"type": "STANDARD",
165+
"templateType": None,
160166
"folder_id": None,
161167
"trashedAt": None,
162168
"trashedBy": None,
@@ -232,6 +238,8 @@ def request_desc(self) -> str:
232238
"creationDate": "2021-12-06T10:13:03.100Z",
233239
"lastChangeDate": "2021-12-06T10:13:07.347Z",
234240
"workbench": {},
241+
"type": "STANDARD",
242+
"templateType": None,
235243
"accessRights": {"2": {"read": True, "write": True, "delete": True}},
236244
"dev": {},
237245
"classifiers": [],
@@ -482,6 +490,8 @@ def request_desc(self) -> str:
482490
"accessRights": {"2": {"read": True, "write": True, "delete": True}},
483491
"dev": {},
484492
"workspace_id": None,
493+
"type": "STANDARD",
494+
"templateType": None,
485495
"folder_id": None,
486496
"trashedAt": None,
487497
"trashedBy": None,
@@ -684,6 +694,8 @@ def request_desc(self) -> str:
684694
"classifiers": [],
685695
"dev": {},
686696
"workspace_id": None,
697+
"type": "STANDARD",
698+
"templateType": None,
687699
"folder_id": None,
688700
"trashedAt": None,
689701
"trashed_by": None,
@@ -935,6 +947,8 @@ def request_desc(self) -> str:
935947
"classifiers": [],
936948
"dev": {},
937949
"workspace_id": None,
950+
"type": "STANDARD",
951+
"templateType": None,
938952
"folder_id": None,
939953
"trashedAt": None,
940954
"trashedBy": None,

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/projects.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
from models_library.projects import ProjectID
77
from models_library.rabbitmq_basic_types import RPCMethodName
88
from models_library.rest_pagination import PageOffsetInt
9-
from models_library.rpc.webserver.projects import PageRpcProjectJobRpcGet
9+
from models_library.rpc.webserver.projects import (
10+
ListProjectsMarkedAsJobRpcFilters,
11+
PageRpcProjectJobRpcGet,
12+
)
1013
from models_library.rpc_pagination import (
1114
DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
1215
PageLimitInt,
@@ -51,8 +54,7 @@ async def list_projects_marked_as_jobs(
5154
# pagination
5255
offset: PageOffsetInt = 0,
5356
limit: PageLimitInt = DEFAULT_NUMBER_OF_ITEMS_PER_PAGE,
54-
# filters
55-
job_parent_resource_name_prefix: str | None = None,
57+
filters: ListProjectsMarkedAsJobRpcFilters | None = None,
5658
) -> PageRpcProjectJobRpcGet:
5759
result = await rpc_client.request(
5860
WEBSERVER_RPC_NAMESPACE,
@@ -61,7 +63,7 @@ async def list_projects_marked_as_jobs(
6163
user_id=user_id,
6264
offset=offset,
6365
limit=limit,
64-
job_parent_resource_name_prefix=job_parent_resource_name_prefix,
66+
filters=filters,
6567
)
6668
assert TypeAdapter(PageRpcProjectJobRpcGet).validate_python(result) # nosec
6769
return cast(PageRpcProjectJobRpcGet, result)

0 commit comments

Comments
 (0)