1212from models_library .folders import FolderID
1313from models_library .workspaces import WorkspaceID
1414from pydantic import BaseModel , ConfigDict , Field , HttpUrl , field_validator
15- from typing_extensions import TypedDict
1615
1716from .basic_regex import DATE_RE , UUID_RE_BASE
1817from .emails import LowerCaseEmailStr
@@ -54,33 +53,41 @@ class ProjectType(str, Enum):
5453
5554class 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):
95102class 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
@@ -117,85 +127,90 @@ def _convert_sql_alchemy_enum(cls, v):
117127 )
118128
119129
120- class StudyUIDict (TypedDict , total = False ):
121- icon : HttpUrl | None
122- workbench : dict [str , Any ]
123-
124-
125130class Project (BaseProjectModel ):
126131 # NOTE: This is the pydantic pendant of project-v0.0.1.json used in the API of the webserver/webclient
127132 # NOT for usage with DB!!
128133
129134 # Ownership and Access (SEE projects_access.py)
130- prj_owner : LowerCaseEmailStr = Field (
131- ..., description = "user email" , alias = "prjOwner"
132- )
133-
134- # Timestamps
135- creation_date : DateTimeStr = Field ( # type: ignore[assignment]
136- ...,
137- description = "project creation date" ,
138- examples = ["2018-07-01T11:13:43Z" ],
139- alias = "creationDate" ,
140- )
141- last_change_date : DateTimeStr = Field ( # type: ignore[assignment]
142- ...,
143- description = "last save date" ,
144- examples = ["2018-07-01T11:13:43Z" ],
145- alias = "lastChangeDate" ,
146- )
147- access_rights : dict [GroupIDStr , AccessRights ] = Field (
148- ...,
149- description = "object containing the GroupID as key and read/write/execution permissions as value" ,
150- alias = "accessRights" ,
151- )
152-
153- # Classification
154- tags : list [int ] | None = []
155- classifiers : Annotated [
156- list [ClassifierID ] | None ,
135+ prj_owner : Annotated [
136+ LowerCaseEmailStr , Field (description = "user email" , alias = "prjOwner" )
137+ ]
138+ access_rights : Annotated [
139+ dict [GroupIDStr , AccessRights ],
157140 Field (
158- default_factory = list ,
159- description = "Contains the reference to the project classifiers" ,
160- examples = ["some:id:to:a:classifier" ],
141+ description = "object containing the GroupID as key and read/write/execution permissions as value" ,
142+ alias = "accessRights" ,
161143 ),
162- ] = DEFAULT_FACTORY
144+ ]
145+
146+ # Lifecycle
147+ creation_date : Annotated [
148+ DateTimeStr ,
149+ Field ( # type: ignore[assignment]
150+ description = "project creation date" ,
151+ examples = ["2018-07-01T11:13:43Z" ],
152+ alias = "creationDate" ,
153+ ),
154+ ]
155+ last_change_date : Annotated [
156+ DateTimeStr ,
157+ Field ( # type: ignore[assignment]
158+ description = "last save date" ,
159+ examples = ["2018-07-01T11:13:43Z" ],
160+ alias = "lastChangeDate" ,
161+ ),
162+ ]
163163
164164 # Project state (SEE projects_state.py)
165165 state : ProjectState | None = None
166166
167- # UI front-end setup (SEE projects_ui.py)
168- ui : StudyUIDict | None = None
169-
170- # Quality
171- quality : dict [str , Any ] = Field (
172- default_factory = dict ,
173- description = "stores the study quality assessment" ,
174- )
167+ # UI front-end fields (SEE projects_ui.py)
168+ ui : dict [str , Any ] | None = None
169+ dev : dict [str , Any ] | None = None
175170
176- # Dev only
177- dev : dict | None = Field (
178- default = None , description = "object used for development purposes only"
179- )
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
180179
181- workspace_id : WorkspaceID | None = Field (
182- default = None ,
183- description = "To which workspace project belongs. If None, belongs to private user workspace." ,
184- alias = "workspaceId" ,
185- )
186- folder_id : FolderID | None = Field (
187- default = None ,
188- description = "To which folder project belongs. If None, belongs to root folder." ,
189- alias = "folderId" ,
190- )
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
191187
188+ # trash state
192189 trashed : datetime | None = None
193190 trashed_by : Annotated [UserID | None , Field (alias = "trashedBy" )] = None
194191 trashed_by_primary_gid : Annotated [
195192 GroupID | None , Field (alias = "trashedByPrimaryGid" )
196193 ] = None
197194 trashed_explicitly : Annotated [bool , Field (alias = "trashedExplicitly" )] = False
198195
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+
199214 model_config = ConfigDict (
200215 # NOTE: this is a security measure until we get rid of the ProjectDict variants
201216 extra = "forbid" ,
0 commit comments