Skip to content

Commit 638edc2

Browse files
committed
Update docs
1 parent 91c7f46 commit 638edc2

File tree

7 files changed

+95
-107
lines changed

7 files changed

+95
-107
lines changed

src/superannotate/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44

55

6-
__version__ = "4.4.30b2"
6+
__version__ = "4.4.30dev1"
77

88
os.environ.update({"sa_version": __version__})
99
sys.path.append(os.path.split(os.path.realpath(__file__))[0])

src/superannotate/lib/app/interface/sdk_interface.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -406,13 +406,13 @@ def list_users(self, *, include: List[Literal["custom_fields"]] = None, **filter
406406
- email__contains: str
407407
- email__starts: str
408408
- email__ends: str
409-
- state: Literal[“CONFIRMED”, “PENDING”]
410-
- state__in: List[Literal[“CONFIRMED”, “PENDING”]]
409+
- state: Literal[“Confirmed”, “Pending”]
410+
- state__in: List[Literal[“Confirmed”, “Pending”]]
411411
- role: Literal[“admin”, “contributor”]
412412
- role__in: List[Literal[“admin”, “contributor”]]
413413
414414
Custom Fields Filtering:
415-
- Custom fields must be prefixed with custom_field__.
415+
- Custom fields must be prefixed with `custom_field__`.
416416
- Example: custom_field__Due_date__gte="1738281600" (filtering users whose Due date is after the given Unix timestamp).
417417
418418
:type filters: UserFilters, optional
@@ -426,7 +426,7 @@ def list_users(self, *, include: List[Literal["custom_fields"]] = None, **filter
426426
client.list_users(
427427
email__contains="@superannotate.com",
428428
include=["custom_fields"],
429-
state__in=["CONFIRMED"]
429+
state__in=["Confirmed"]
430430
custom_fields__Tag__in=["Tag1", "Tag3"]
431431
)
432432
@@ -3372,7 +3372,7 @@ def list_projects(
33723372
- status__notin: List[Literal[“NotStarted”, “InProgress”, “Completed”, “OnHold”]]
33733373
33743374
Custom Fields Filtering:
3375-
- Custom fields must be prefixed with custom_field__.
3375+
- Custom fields must be prefixed with `custom_field__`.
33763376
- Example: custom_field__Due_date__gte="1738281600" (filtering users whose Due date is after the given Unix timestamp).
33773377
- If include does not include "custom_fields" but filter contains custom_fields, an error will be returned
33783378

src/superannotate/lib/core/entities/filters.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class UserFilters(TypedDict, total=False):
4646
email__contains: Optional[str]
4747
email__starts: Optional[str]
4848
email__ends: Optional[str]
49-
state: Optional[Literal["CONFIRMED", "PENDING"]]
50-
state__in: Optional[List[Literal["CONFIRMED", "PENDING"]]]
51-
role: Optional[Literal["admin", "contributor"]]
52-
role__in: Optional[List[Literal["admin", "contributor"]]]
49+
state: Optional[str]
50+
state__in: Optional[List[str]]
51+
role: Optional[str]
52+
role__in: Optional[List[str]]

src/superannotate/lib/core/entities/work_managament.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Optional
55

66
from lib.core.entities.base import TimedBaseModel
7+
from lib.core.enums import WMUserStateEnum
78
from lib.core.pydantic_v1 import Extra
89
from lib.core.pydantic_v1 import Field
910
from lib.core.pydantic_v1 import parse_datetime
@@ -21,17 +22,6 @@ class ProjectType(str, Enum):
2122
Multimodal = "CUSTOM_LLM"
2223

2324

24-
class WMUserStateEnum(str, Enum):
25-
Pending = "PENDING"
26-
Confirmed = "CONFIRMED"
27-
Video = "PUBLIC_VIDEO"
28-
Document = "PUBLIC_TEXT"
29-
Tiled = "TILED"
30-
Other = "CLASSIFICATION"
31-
PointCloud = "POINT_CLOUD"
32-
Multimodal = "CUSTOM_LLM"
33-
34-
3525
class WMUserTypeEnum(int, Enum):
3626
Contributor = 4
3727
TeamAdmin = 7

src/superannotate/lib/core/enums.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,8 @@ class CustomFieldType(Enum):
199199
SINGLE_SELECT = 3
200200
DATE_PICKER = 4
201201
NUMERIC = 5
202+
203+
204+
class WMUserStateEnum(str, Enum):
205+
Pending = "PENDING"
206+
Confirmed = "CONFIRMED"

src/superannotate/lib/infrastructure/query_builder.py

Lines changed: 74 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from lib.core.enums import CustomFieldType
1515
from lib.core.enums import ProjectStatus
1616
from lib.core.enums import UserRole
17+
from lib.core.enums import WMUserStateEnum
1718
from lib.core.exceptions import AppException
1819
from lib.core.jsx_conditions import EmptyQuery
1920
from lib.core.jsx_conditions import Filter
@@ -95,6 +96,60 @@ def handle(self, filters: Dict[str, Any], query: Query = None) -> Query:
9596
return super().handle(filters, query)
9697

9798

99+
class ItemFilterHandler(AbstractQueryHandler):
100+
def __init__(
101+
self,
102+
project: ProjectEntity,
103+
entity: BaseItemEntity,
104+
service_provider: BaseServiceProvider,
105+
):
106+
self._service_provider = service_provider
107+
self._entity = entity
108+
self._project = project
109+
110+
def handle(self, filters: Dict[str, Any], query: Query = None) -> Query:
111+
if query is None:
112+
query = EmptyQuery()
113+
for key, val in filters.items():
114+
_keys = key.split("__")
115+
val = self._handle_special_fields(_keys, val)
116+
condition, _key = determine_condition_and_key(_keys)
117+
query &= Filter(_key, val, condition)
118+
return super().handle(filters, query)
119+
120+
@staticmethod
121+
def _extract_value_from_mapping(data, extractor=lambda x: x):
122+
if isinstance(data, (list, tuple, set)):
123+
return [extractor(i) for i in data]
124+
return extractor(data)
125+
126+
def _handle_special_fields(self, keys: List[str], val):
127+
"""
128+
Handle special fields like 'approval_status', 'assignments', 'user_role' and 'annotation_status'.
129+
"""
130+
if keys[0] == "approval_status":
131+
val = (
132+
[ApprovalStatus(i).value for i in val]
133+
if isinstance(val, (list, tuple, set))
134+
else ApprovalStatus(val).value
135+
)
136+
elif keys[0] == "annotation_status":
137+
val = self._extract_value_from_mapping(
138+
val,
139+
lambda x: self._service_provider.get_annotation_status_value(
140+
self._project, x
141+
),
142+
)
143+
elif keys[0] == "assignments" and keys[1] == "user_role":
144+
if isinstance(val, list):
145+
val = [
146+
self._service_provider.get_role_id(self._project, i) for i in val
147+
]
148+
else:
149+
val = self._service_provider.get_role_id(self._project, val)
150+
return val
151+
152+
98153
class BaseCustomFieldHandler(AbstractQueryHandler):
99154
def __init__(
100155
self, service_provider: BaseServiceProvider, entity: CustomFieldEntityEnum
@@ -151,8 +206,16 @@ def _determine_condition_and_key(keys: List[str]) -> Tuple[OperatorEnum, str]:
151206
key = ".".join(keys)
152207
return condition, key
153208

154-
@staticmethod
155-
def _handle_special_fields(keys: List[str], val):
209+
def _handle_special_fields(self, keys: List[str], val):
210+
if keys[0] == "custom_field":
211+
component_id = self._service_provider.get_custom_field_component_id(
212+
field_id=int(keys[1]), entity=self._entity
213+
)
214+
if component_id == CustomFieldType.DATE_PICKER.value:
215+
try:
216+
val = val * 1000
217+
except Exception:
218+
raise AppException("Invalid custom field value provided.")
156219
return val
157220

158221
def handle(self, filters: Dict[str, Any], query: Query = None) -> Query:
@@ -168,75 +231,12 @@ def handle(self, filters: Dict[str, Any], query: Query = None) -> Query:
168231
return super().handle(filters, query)
169232

170233

171-
class ItemFilterHandler(AbstractQueryHandler):
172-
def __init__(
173-
self,
174-
project: ProjectEntity,
175-
entity: BaseItemEntity,
176-
service_provider: BaseServiceProvider,
177-
):
178-
self._service_provider = service_provider
179-
self._entity = entity
180-
self._project = project
181-
182-
def handle(self, filters: Dict[str, Any], query: Query = None) -> Query:
183-
if query is None:
184-
query = EmptyQuery()
185-
for key, val in filters.items():
186-
_keys = key.split("__")
187-
val = self._handle_special_fields(_keys, val)
188-
condition, _key = determine_condition_and_key(_keys)
189-
query &= Filter(_key, val, condition)
190-
return super().handle(filters, query)
191-
192-
@staticmethod
193-
def _extract_value_from_mapping(data, extractor=lambda x: x):
194-
if isinstance(data, (list, tuple, set)):
195-
return [extractor(i) for i in data]
196-
return extractor(data)
197-
198-
def _handle_special_fields(self, keys: List[str], val):
199-
"""
200-
Handle special fields like 'approval_status', 'assignments', 'user_role' and 'annotation_status'.
201-
"""
202-
if keys[0] == "approval_status":
203-
val = (
204-
[ApprovalStatus(i).value for i in val]
205-
if isinstance(val, (list, tuple, set))
206-
else ApprovalStatus(val).value
207-
)
208-
elif keys[0] == "annotation_status":
209-
val = self._extract_value_from_mapping(
210-
val,
211-
lambda x: self._service_provider.get_annotation_status_value(
212-
self._project, x
213-
),
214-
)
215-
elif keys[0] == "assignments" and keys[1] == "user_role":
216-
if isinstance(val, list):
217-
val = [
218-
self._service_provider.get_role_id(self._project, i) for i in val
219-
]
220-
else:
221-
val = self._service_provider.get_role_id(self._project, val)
222-
return val
223-
224-
225234
class UserFilterHandler(BaseCustomFieldHandler):
226235
def _handle_special_fields(self, keys: List[str], val):
227236
"""
228237
Handle special fields like 'custom_fields__'.
229238
"""
230-
if keys[0] == "custom_field":
231-
component_id = self._service_provider.get_custom_field_component_id(
232-
field_id=int(keys[1]), entity=self._entity
233-
)
234-
if component_id == CustomFieldType.DATE_PICKER.value:
235-
try:
236-
val = val * 1000
237-
except Exception:
238-
raise AppException("Invalid custom field value provided.")
239-
elif keys[0] == "role":
239+
if keys[0] == "role":
240240
try:
241241
if isinstance(val, list):
242242
val = [UserRole.__getitem__(i.upper()).value for i in val]
@@ -247,14 +247,14 @@ def _handle_special_fields(self, keys: List[str], val):
247247
except (KeyError, AttributeError):
248248
raise AppException("Invalid user role provided.")
249249
elif keys[0] == "state":
250-
valid_states = ["PENDING", "CONFIRMED"]
251-
if isinstance(val, list):
252-
for state in val:
253-
if state not in valid_states:
254-
raise AppException("Invalid user state provided.")
255-
elif val not in valid_states:
250+
try:
251+
if isinstance(val, list):
252+
val = [WMUserStateEnum[i].value for i in val]
253+
else:
254+
val = WMUserStateEnum[val].value
255+
except (TypeError, KeyError):
256256
raise AppException("Invalid user state provided.")
257-
return val
257+
return super()._handle_special_fields(keys, val)
258258

259259

260260
class ProjectFilterHandler(BaseCustomFieldHandler):
@@ -268,16 +268,7 @@ def _handle_special_fields(self, keys: List[str], val):
268268
if isinstance(val, (list, tuple, set))
269269
else ProjectStatus(val).value
270270
)
271-
elif keys[0] == "custom_field":
272-
component_id = self._service_provider.get_custom_field_component_id(
273-
field_id=int(keys[1]), entity=self._entity
274-
)
275-
if component_id == CustomFieldType.DATE_PICKER.value:
276-
try:
277-
val = val * 1000
278-
except Exception:
279-
raise AppException("Invalid custom field value provided.")
280-
return val
271+
return super()._handle_special_fields(keys, val)
281272

282273

283274
class QueryBuilderChain:

tests/integration/work_management/test_user_custom_fields.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ def test_list_users(self):
141141
assert scapegoat["custom_fields"]["SDK_test_date_picker"] == value
142142

143143
# by email__contains
144-
assert len(sa.list_users(email__contains="@superannotate.com")) == len(users)
144+
assert len(sa.list_users(email__contains="@superannotate.com")) == len(
145+
[i for i in users if i["email"].endswith("@superannotate.com")]
146+
)
145147

146148
# by role
147149
assert len(sa.list_users(role="contributor")) == len(all_contributors)
@@ -150,8 +152,8 @@ def test_list_users(self):
150152
sa.list_users(role__in=["invalid_role"])
151153

152154
# by state
153-
assert len(sa.list_users(state="CONFIRMED")) == len(all_confirmed)
154-
assert len(sa.list_users(state__in=["PENDING"])) == len(all_pending)
155+
assert len(sa.list_users(state="Confirmed")) == len(all_confirmed)
156+
assert len(sa.list_users(state__in=["Pending"])) == len(all_pending)
155157
with self.assertRaisesRegexp(AppException, "Invalid user state provided."):
156158
assert len(sa.list_users(state__in=["invalid_state"])) == len(all_pending)
157159

0 commit comments

Comments
 (0)