@@ -155,35 +155,112 @@ class Projects:
155155 def __init__ (self , parent : "Hub" ):
156156 self ._parent = parent
157157
158- def get (self , fields : Optional [list [str ]] = None , all : Optional [bool ] = False , project_id : Optional [str ] = None ) -> list [dict [str , Any ]]:
158+ def get (
159+ self ,
160+ fields : Optional [list [str ]] = None ,
161+ all : Optional [bool ] = False ,
162+ project_id : Optional [str ] = None ,
163+ limit : Optional [int ] = None ,
164+ deleted : Optional [bool ] = None ,
165+ hidden : Optional [bool ] = None ,
166+ state : Optional [str ] = None ,
167+ account_id_for_hidden : Optional [str ] = None ,
168+ ) -> list [dict [str , Any ]]:
159169 """
160170 Get data about projects that you are a collaborator on. Only gets
161171 recent projects by default; set all=True to get all projects.
162172
163173 Args:
164174 fields (Optional[list[str]]): The fields about the project to get.
165- Default: ['project_id', 'title', 'last_edited', 'state'], but see
175+ Default: ['project_id', 'title', 'last_edited', 'created', ' state', 'deleted', 'users '], but see
166176 https://github.com/sagemathinc/cocalc/blob/master/src/packages/util/db-schema/projects.ts
167177 all (Optional[bool]): If True, return ALL your projects,
168178 not just the recent ones. False by default.
169179 project_id (Optional[str]): If given, gets just this
170180 one project (as a list of length 1).
181+ limit (Optional[int]): Maximum number of projects to return after filtering. None means no limit.
182+ deleted (Optional[bool]): If set, filter deleted status (True -> only deleted, False -> only not deleted).
183+ hidden (Optional[bool]): If set, filter by collaborator-specific hidden flag. Default None (no filter).
184+ state (Optional[str]): If set, only return projects whose state matches (e.g., 'opened', 'running').
185+ account_id_for_hidden (Optional[str]): Account ID used to evaluate the hidden flag in the users map.
171186
172187 Returns:
173188 list[dict[str, Any]]: List of projects.
174189 """
190+ from datetime import datetime
191+
192+ def _parse_ts (value : Any ) -> float :
193+ if value is None :
194+ return 0.0
195+ if isinstance (value , (int , float )):
196+ return float (value )
197+ if isinstance (value , str ):
198+ try :
199+ return datetime .fromisoformat (value .replace ("Z" , "+00:00" )).timestamp ()
200+ except ValueError :
201+ try :
202+ return float (value )
203+ except Exception :
204+ return 0.0
205+ return 0.0
206+
207+ def _state_str (val : Any ) -> str :
208+ if isinstance (val , dict ):
209+ return str (val .get ("state" ) or val .get ("status" ) or "" )
210+ if val is None :
211+ return ""
212+ return str (val )
213+
175214 if fields is None :
176- fields = ['project_id' , 'title' , 'last_edited' , 'state' ]
215+ fields = ['project_id' , 'title' , 'last_edited' , 'created' , ' state' , 'deleted' , 'users ' ]
177216 v : list [dict [str , Any ]] = [{}]
178217 for field in fields :
179218 v [0 ][field ] = None
180219 if project_id :
181220 v [0 ]['project_id' ] = project_id
182- query : dict [str , list [dict [str , None ]]] = {}
221+ query : dict [str , list [dict [str , Any ]]] = {}
183222 table = 'projects_all' if all else 'projects'
184223 query [table ] = v
185224 result = self ._parent .db .query (query )
186- return result [table ]
225+ projects : list [dict [str , Any ]] = result [table ]
226+
227+ filtered : list [dict [str , Any ]] = []
228+ for project in projects :
229+ if deleted is not None :
230+ if bool (project .get ("deleted" )) != deleted :
231+ continue
232+
233+ if state :
234+ project_state = _state_str (project .get ("state" )).lower ()
235+ if project_state != state .lower ():
236+ continue
237+
238+ if hidden is not None and account_id_for_hidden :
239+ users = project .get ("users" ) or {}
240+ if isinstance (users , dict ):
241+ user_info = users .get (account_id_for_hidden , {})
242+ is_hidden = False
243+ if isinstance (user_info , dict ):
244+ is_hidden = bool (user_info .get ("hide" ))
245+ if is_hidden != hidden :
246+ continue
247+
248+ filtered .append (project )
249+
250+ filtered .sort (
251+ key = lambda p : (
252+ _parse_ts (p .get ("last_edited" )),
253+ _parse_ts (p .get ("created" )),
254+ (p .get ("title" ) or "" ).lower (),
255+ p .get ("project_id" ) or "" ,
256+ ),
257+ reverse = True ,
258+ )
259+
260+ if limit is not None and limit >= 0 :
261+ filtered = filtered [:limit ]
262+
263+ return filtered
187264
188265 @api_method ("projects.copyPathBetweenProjects" )
189266 def copy_path_between_projects (
0 commit comments