2020from  .projects_nodes  import  Node 
2121from  .projects_nodes_io  import  NodeIDStr 
2222from  .projects_state  import  ProjectState 
23- from  .projects_ui  import  StudyUI 
2423from  .users  import  UserID 
2524from  .utils .common_validators  import  (
2625    empty_str_to_none_pre_validator ,
@@ -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  
@@ -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    )
0 commit comments