7373from lib .app .serializers import WMProjectSerializer
7474from lib .core .entities .work_managament import WMUserTypeEnum
7575from lib .core .jsx_conditions import EmptyQuery
76+ from lib .core .jsx_conditions import Join
77+ from lib .core .jsx_conditions import Fields
7678from lib .core .entities .items import ProjectCategoryEntity
7779
7880logger = logging .getLogger ("sa" )
@@ -511,7 +513,7 @@ def set_user_custom_field(
511513 def list_users (
512514 self ,
513515 * ,
514- project : Union [int , str ] = None ,
516+ project : Union [NotEmptyStr , int ] = None ,
515517 include : List [Literal ["custom_fields" , "categories" ]] = None ,
516518 ** filters ,
517519 ):
@@ -1088,7 +1090,12 @@ def search_team_contributors(
10881090 :return: metadata of found users
10891091 :rtype: list of dicts
10901092 """
1091-
1093+ warnings .warn (
1094+ "This function search_team_contributors() will be deprecated and removed in version 4.6.0\n "
1095+ "Recommended replacement: get_user_metadata() or list_users()" ,
1096+ DeprecationWarning ,
1097+ stacklevel = 2 ,
1098+ )
10921099 contributors = self .controller .search_team_contributors (
10931100 email = email , first_name = first_name , last_name = last_name
10941101 ).data
@@ -1124,6 +1131,13 @@ def search_projects(
11241131 :return: project names or metadatas
11251132 :rtype: list of strs or dicts
11261133 """
1134+ warnings .warn (
1135+ "This function search_projects() will be deprecated and removed in version 4.6.0\n "
1136+ "Recommended replacement: get_project_metadata() or list_projects()" ,
1137+ DeprecationWarning ,
1138+ stacklevel = 2 ,
1139+ )
1140+
11271141 statuses = []
11281142 if status :
11291143 if isinstance (status , (list , tuple , set )):
@@ -1574,22 +1588,83 @@ def rename_project(self, project: NotEmptyStr, new_name: NotEmptyStr):
15741588 )
15751589 return ProjectSerializer (response .data ).serialize ()
15761590
1577- def get_folder_metadata (self , project : NotEmptyStr , folder_name : NotEmptyStr ):
1578- """Returns folder metadata
1591+ def get_folder_metadata (
1592+ self ,
1593+ project : NotEmptyStr ,
1594+ folder_name : NotEmptyStr ,
1595+ include_contributors : bool = False ,
1596+ ):
1597+ """
1598+ SAClient.get_folder_metadata(project, folder_name, include_contributors=False)
1599+ Returns folder metadata. Optionally includes a list of contributors that are currently
1600+ assigned to the folder.
15791601
15801602 :param project: project name
15811603 :type project: str
15821604
15831605 :param folder_name: folder's name
15841606 :type folder_name: str
15851607
1586- :return: metadata of folder
1608+ :param include_contributors: If True, includes a list of contributors assigned to the folder in the response.
1609+ Defaults to False.
1610+ :type include_contributors: bool
1611+
1612+ :return: Folder metadata
15871613 :rtype: dict
1614+
1615+ Request Example:
1616+ ::
1617+
1618+ sa_client.get_folder_metadata(
1619+ project="test_project",
1620+ folder_name="test_folder",
1621+ include_contributors=True
1622+ )
1623+
1624+
1625+ Response Example:
1626+ ::
1627+
1628+ {
1629+ "createdAt": "2025-10-27T06:54:09.000Z",
1630+ "updatedAt": "2025-10-27T06:54:09.000Z",
1631+ "id": 1487195,
1632+ "name": "test_folder",
1633+ "status": "NotStarted",
1634+ "project_id": 1203397,
1635+ "team_id": 85922,
1636+ "contributors": [
1637+ {
1638+ "email": "test@superannotate.com",
1639+ "id": 1314658,
1640+ "role": "Annotator",
1641+ "state": "Confirmed"
1642+ }
1643+ ]
1644+ }
1645+
15881646 """
1589- project , folder = self .controller .get_project_folder ((project , folder_name ))
1590- if not folder :
1647+ project = self .controller .get_project (project )
1648+ query = Filter ("name" , folder_name , OperatorEnum .EQ )
1649+ fields = ["id" , "project_id" , "name" , "status" , "team_id" ]
1650+
1651+ if include_contributors :
1652+ query &= Join ("folderUsers" , fields = ["id" ])
1653+ query &= Join (
1654+ "folderUsers.projectUser" , fields = ["id" , "email" , "role" , "state" ]
1655+ )
1656+ fields .append ("folderUsers" )
1657+ query &= Fields (fields )
1658+ response = self .controller .work_management .list_folders (
1659+ project = project , query = query
1660+ )
1661+ response .raise_for_status ()
1662+ if not response .data :
15911663 raise AppException ("Folder not found." )
1592- return BaseSerializer (folder ).serialize (exclude = {"completedCount" , "is_root" })
1664+ folder = response .data [0 ]
1665+ return BaseSerializer (folder ).serialize (
1666+ exclude = {"completedCount" , "is_root" }, by_alias = False
1667+ )
15931668
15941669 def delete_folders (self , project : NotEmptyStr , folder_names : List [NotEmptyStr ]):
15951670 """Delete folder in project.
@@ -2148,7 +2223,7 @@ def assign_folder(
21482223 raise AppException (response .errors )
21492224 project = response .data
21502225 project_contributors = self .controller .work_management .list_users (
2151- project = project
2226+ email__in = users , project = project
21522227 )
21532228 verified_users = [i .email for i in project_contributors ]
21542229 verified_users = set (users ).intersection (set (verified_users ))
@@ -3949,6 +4024,12 @@ def search_items(
39494024 }
39504025 ]
39514026 """
4027+ warnings .warn (
4028+ "This function search_items() will be deprecated and removed in version 4.6.0\n "
4029+ "Recommended replacement: get_item_metadata() or list_items()" ,
4030+ DeprecationWarning ,
4031+ stacklevel = 2 ,
4032+ )
39524033 project , folder = self .controller .get_project_folder (project )
39534034 query_kwargs = {"include" : ["assignments" ]}
39544035 if name_contains :
@@ -4355,7 +4436,7 @@ def attach_items(
43554436 if annotation_status is not None :
43564437 warnings .warn (
43574438 DeprecationWarning (
4358- "The “keep_status” parameter is deprecated. "
4439+ "The “keep_status” parameter is deprecated."
43594440 "Please use the “set_annotation_statuses” function instead."
43604441 )
43614442 )
@@ -4577,7 +4658,7 @@ def move_items(
45774658 def set_items_category (
45784659 self ,
45794660 project : Union [NotEmptyStr , int , Tuple [int , int ], Tuple [str , str ]],
4580- items : List [Union [int , str ]],
4661+ items : List [Union [NotEmptyStr , int ]],
45814662 category : NotEmptyStr ,
45824663 ):
45834664 """
@@ -4615,7 +4696,7 @@ def set_items_category(
46154696 def remove_items_category (
46164697 self ,
46174698 project : Union [NotEmptyStr , int , Tuple [int , int ], Tuple [str , str ]],
4618- items : List [Union [int , str ]],
4699+ items : List [Union [NotEmptyStr , int ]],
46194700 ):
46204701 """
46214702 Remove categories from one or more items.
@@ -5349,7 +5430,7 @@ def remove_users(self, users: Union[List[int], List[str]]):
53495430 Request Example:
53505431 ::
53515432
5352- SAClient .remove_users(member =["example@gmail.com","example1@gmail.com"])
5433+ sa_client .remove_users(users =["example@gmail.com","example1@gmail.com"])
53535434
53545435 """
53555436 success = 0
@@ -5369,7 +5450,7 @@ def remove_users_from_project(
53695450 self , project : Union [NotEmptyStr , int ], users : Union [List [int ], List [str ]]
53705451 ):
53715452 """
5372- Allows removing users from the team .
5453+ Allows removing users from a project .
53735454
53745455 :param project: The name or ID of the project.
53755456 :type project: Union[NotEmptyStr, int]
@@ -5382,7 +5463,7 @@ def remove_users_from_project(
53825463 Request Example:
53835464 ::
53845465
5385- SAClient .remove_users_from_project(project="Test Project", users=["example@gmail.com","example1@gmail.com"])
5466+ sa_client .remove_users_from_project(project="Test Project", users=["example@gmail.com","example1@gmail.com"])
53865467
53875468 """
53885469 project = self .controller .get_project (project )
0 commit comments