Skip to content

Commit dd342a3

Browse files
authored
♻️ refactors web-api project's ui schemas (#7210)
1 parent c04a389 commit dd342a3

File tree

15 files changed

+152
-128
lines changed

15 files changed

+152
-128
lines changed

api/specs/web-server/_projects.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class _ProjectCreateHeaderParams(BaseModel):
7474
)
7575
async def create_project(
7676
_h: Annotated[_ProjectCreateHeaderParams, Depends()],
77-
_path: Annotated[ProjectCreateQueryParams, Depends()],
77+
_query: Annotated[ProjectCreateQueryParams, Depends()],
7878
_body: ProjectCreateNew | ProjectCopyOverride,
7979
):
8080
...

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
from ..projects import ClassifierID, DateTimeStr, NodesDict, ProjectID
2929
from ..projects_access import AccessRights, GroupIDStr
3030
from ..projects_state import ProjectState
31-
from ..projects_ui import StudyUI
3231
from ..utils._original_fastapi_encoders import jsonable_encoder
3332
from ..utils.common_validators import (
3433
empty_str_to_none_pre_validator,
@@ -38,6 +37,7 @@
3837
from ..workspaces import WorkspaceID
3938
from ._base import EmptyModel, InputSchema, OutputSchema
4039
from .permalinks import ProjectPermalink
40+
from .projects_ui import StudyUI
4141

4242

4343
class ProjectCreateNew(InputSchema):
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from typing import Annotated, TypeAlias
2+
3+
from pydantic import BaseModel, ConfigDict, Field, PlainSerializer
4+
from pydantic_extra_types.color import Color
5+
6+
from ..projects_nodes_layout import Position
7+
8+
PositionUI: TypeAlias = Position
9+
10+
11+
class MarkerUI(BaseModel):
12+
color: Annotated[Color, PlainSerializer(Color.as_hex), Field(...)]
13+
14+
model_config = ConfigDict(extra="forbid")

packages/models-library/src/models_library/projects_ui.py renamed to packages/models-library/src/models_library/api_schemas_webserver/projects_ui.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
TypedDict,
1919
)
2020

21-
from .projects_nodes_io import NodeID, NodeIDStr
21+
from ..projects_nodes_io import NodeID, NodeIDStr
22+
from ..utils.common_validators import empty_str_to_none_pre_validator
2223
from .projects_nodes_ui import MarkerUI, PositionUI
23-
from .utils.common_validators import empty_str_to_none_pre_validator
2424

2525

2626
class WorkbenchUI(BaseModel):

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

Lines changed: 104 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
from .projects_nodes import Node
2121
from .projects_nodes_io import NodeIDStr
2222
from .projects_state import ProjectState
23-
from .projects_ui import StudyUI
2423
from .users import UserID
2524
from .utils.common_validators import (
2625
empty_str_to_none_pre_validator,
@@ -54,33 +53,41 @@ class ProjectType(str, Enum):
5453

5554
class BaseProjectModel(BaseModel):
5655
# Description of the project
57-
uuid: ProjectID = Field(
58-
...,
59-
description="project unique identifier",
60-
examples=[
61-
"07640335-a91f-468c-ab69-a374fa82078d",
62-
"9bcf8feb-c1b1-41b6-b201-639cd6ccdba8",
63-
],
64-
)
65-
name: str = Field(
66-
..., description="project name", examples=["Temporal Distortion Simulator"]
67-
)
68-
description: str = Field(
69-
...,
70-
description="longer one-line description about the project",
71-
examples=["Dabbling in temporal transitions ..."],
72-
)
73-
thumbnail: HttpUrl | None = Field(
74-
...,
75-
description="url of the project thumbnail",
76-
examples=["https://placeimg.com/171/96/tech/grayscale/?0.jpg"],
77-
)
56+
uuid: Annotated[
57+
ProjectID,
58+
Field(
59+
description="project unique identifier",
60+
examples=[
61+
"07640335-a91f-468c-ab69-a374fa82078d",
62+
"9bcf8feb-c1b1-41b6-b201-639cd6ccdba8",
63+
],
64+
),
65+
]
66+
67+
name: Annotated[
68+
str,
69+
Field(description="project name", examples=["Temporal Distortion Simulator"]),
70+
]
71+
description: Annotated[
72+
str,
73+
Field(
74+
description="longer one-line description about the project",
75+
examples=["Dabbling in temporal transitions ..."],
76+
),
77+
]
78+
thumbnail: Annotated[
79+
HttpUrl | None,
80+
Field(
81+
description="url of the project thumbnail",
82+
examples=["https://placeimg.com/171/96/tech/grayscale/?0.jpg"],
83+
),
84+
]
7885

79-
creation_date: datetime = Field(...)
80-
last_change_date: datetime = Field(...)
86+
creation_date: datetime
87+
last_change_date: datetime
8188

8289
# Pipeline of nodes (SEE projects_nodes.py)
83-
workbench: Annotated[NodesDict, Field(..., description="Project's pipeline")]
90+
workbench: Annotated[NodesDict, Field(description="Project's pipeline")]
8491

8592
# validators
8693
_empty_thumbnail_is_none = field_validator("thumbnail", mode="before")(
@@ -95,15 +102,18 @@ class BaseProjectModel(BaseModel):
95102
class ProjectAtDB(BaseProjectModel):
96103
# Model used to READ from database
97104

98-
id: int = Field(..., description="The table primary index")
105+
id: Annotated[int, Field(description="The table primary index")]
99106

100-
project_type: ProjectType = Field(..., alias="type", description="The project type")
107+
project_type: Annotated[
108+
ProjectType, Field(alias="type", description="The project type")
109+
]
101110

102-
prj_owner: int | None = Field(..., description="The project owner id")
111+
prj_owner: Annotated[int | None, Field(description="The project owner id")]
103112

104-
published: bool | None = Field(
105-
default=False, description="Defines if a study is available publicly"
106-
)
113+
published: Annotated[
114+
bool | None,
115+
Field(default=False, description="Defines if a study is available publicly"),
116+
]
107117

108118
@field_validator("project_type", mode="before")
109119
@classmethod
@@ -122,76 +132,87 @@ class Project(BaseProjectModel):
122132
# NOT for usage with DB!!
123133

124134
# Ownership and Access (SEE projects_access.py)
125-
prj_owner: LowerCaseEmailStr = Field(
126-
..., description="user email", alias="prjOwner"
127-
)
128-
129-
# Timestamps
130-
creation_date: DateTimeStr = Field( # type: ignore[assignment]
131-
...,
132-
description="project creation date",
133-
examples=["2018-07-01T11:13:43Z"],
134-
alias="creationDate",
135-
)
136-
last_change_date: DateTimeStr = Field( # type: ignore[assignment]
137-
...,
138-
description="last save date",
139-
examples=["2018-07-01T11:13:43Z"],
140-
alias="lastChangeDate",
141-
)
142-
access_rights: dict[GroupIDStr, AccessRights] = Field(
143-
...,
144-
description="object containing the GroupID as key and read/write/execution permissions as value",
145-
alias="accessRights",
146-
)
135+
prj_owner: Annotated[
136+
LowerCaseEmailStr, Field(description="user email", alias="prjOwner")
137+
]
138+
access_rights: Annotated[
139+
dict[GroupIDStr, AccessRights],
140+
Field(
141+
description="object containing the GroupID as key and read/write/execution permissions as value",
142+
alias="accessRights",
143+
),
144+
]
147145

148-
# Classification
149-
tags: list[int] | None = []
150-
classifiers: Annotated[
151-
list[ClassifierID] | None,
146+
# Lifecycle
147+
creation_date: Annotated[ # type: ignore[assignment]
148+
DateTimeStr,
152149
Field(
153-
default_factory=list,
154-
description="Contains the reference to the project classifiers",
155-
examples=["some:id:to:a:classifier"],
150+
description="project creation date",
151+
examples=["2018-07-01T11:13:43Z"],
152+
alias="creationDate",
156153
),
157-
] = DEFAULT_FACTORY
154+
]
155+
last_change_date: Annotated[ # type: ignore[assignment]
156+
DateTimeStr,
157+
Field(
158+
description="last save date",
159+
examples=["2018-07-01T11:13:43Z"],
160+
alias="lastChangeDate",
161+
),
162+
]
158163

159164
# Project state (SEE projects_state.py)
160165
state: ProjectState | None = None
161166

162-
# UI front-end setup (SEE projects_ui.py)
163-
ui: StudyUI | None = None
164-
165-
# Quality
166-
quality: dict[str, Any] = Field(
167-
default_factory=dict,
168-
description="stores the study quality assessment",
169-
)
167+
# UI front-end fields (SEE projects_ui.py)
168+
ui: dict[str, Any] | None = None
169+
dev: dict[str, Any] | None = None
170170

171-
# Dev only
172-
dev: dict | None = Field(
173-
default=None, description="object used for development purposes only"
174-
)
171+
# Parenthood
172+
workspace_id: Annotated[
173+
WorkspaceID | None,
174+
Field(
175+
description="To which workspace project belongs. If None, belongs to private user workspace.",
176+
alias="workspaceId",
177+
),
178+
] = None
175179

176-
workspace_id: WorkspaceID | None = Field(
177-
default=None,
178-
description="To which workspace project belongs. If None, belongs to private user workspace.",
179-
alias="workspaceId",
180-
)
181-
folder_id: FolderID | None = Field(
182-
default=None,
183-
description="To which folder project belongs. If None, belongs to root folder.",
184-
alias="folderId",
185-
)
180+
folder_id: Annotated[
181+
FolderID | None,
182+
Field(
183+
description="To which folder project belongs. If None, belongs to root folder.",
184+
alias="folderId",
185+
),
186+
] = None
186187

188+
# trash state
187189
trashed: datetime | None = None
188190
trashed_by: Annotated[UserID | None, Field(alias="trashedBy")] = None
189191
trashed_by_primary_gid: Annotated[
190192
GroupID | None, Field(alias="trashedByPrimaryGid")
191193
] = None
192194
trashed_explicitly: Annotated[bool, Field(alias="trashedExplicitly")] = False
193195

196+
# Labeling
197+
tags: Annotated[list[int] | None, Field(default_factory=list)] = DEFAULT_FACTORY
198+
classifiers: Annotated[
199+
list[ClassifierID] | None,
200+
Field(
201+
default_factory=list,
202+
description="Contains the reference to the project classifiers",
203+
examples=["some:id:to:a:classifier"],
204+
),
205+
] = DEFAULT_FACTORY
206+
quality: Annotated[
207+
dict[str, Any],
208+
Field(
209+
default_factory=dict,
210+
description="stores the study quality assessment",
211+
),
212+
] = DEFAULT_FACTORY
213+
194214
model_config = ConfigDict(
195215
# NOTE: this is a security measure until we get rid of the ProjectDict variants
196216
extra="forbid",
217+
populate_by_name=True,
197218
)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
PortLink,
2828
SimCoreFileLink,
2929
)
30-
from .projects_nodes_ui import PositionUI
30+
from .projects_nodes_layout import Position
3131
from .projects_state import RunningState
3232
from .services import ServiceKey, ServiceVersion
3333

@@ -224,7 +224,7 @@ class Node(BaseModel):
224224
] = None
225225

226226
position: Annotated[
227-
PositionUI | None,
227+
Position | None,
228228
Field(
229229
deprecated=True,
230230
description="Use projects_ui.WorkbenchUI.position instead",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from pydantic import BaseModel, ConfigDict, Field
2+
3+
4+
class Position(BaseModel):
5+
x: int = Field(..., description="The x position", examples=[["12"]])
6+
y: int = Field(..., description="The y position", examples=[["15"]])
7+
8+
model_config = ConfigDict(extra="forbid")

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

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

packages/models-library/tests/test_projects_nodes_ui.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pytest
2-
from models_library.projects_nodes_ui import MarkerUI
2+
from models_library.api_schemas_webserver.projects_nodes_ui import MarkerUI
33
from pydantic_extra_types.color import Color
44

55

packages/models-library/tests/test_projects_ui.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pytest
2-
from models_library.projects_ui import AnnotationUI
2+
from models_library.api_schemas_webserver.projects_ui import AnnotationUI
33
from pydantic_extra_types.color import Color
44

55

0 commit comments

Comments
 (0)