Skip to content

Commit 613f01a

Browse files
author
Val Brodsky
committed
Add search filters
1 parent e20a774 commit 613f01a

File tree

2 files changed

+259
-0
lines changed

2 files changed

+259
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import datetime
2+
from enum import Enum
3+
from typing import List, Literal, Union
4+
5+
from labelbox.pydantic_compat import BaseModel, Field
6+
7+
8+
class BaseSearchFilter(BaseModel):
9+
10+
class Config:
11+
use_enum_values = True
12+
13+
def dict(self, *args, **kwargs):
14+
res = super().dict(*args, **kwargs)
15+
if 'operation' in res:
16+
res['type'] = res.pop('operation')
17+
18+
# go through all the keys and convert date to string
19+
for key in res:
20+
if isinstance(res[key], datetime.date):
21+
res[key] = res[key].isoformat()
22+
return res
23+
24+
25+
class OperationType(Enum):
26+
Organization = 'organization'
27+
Workspace = 'workspace'
28+
Tag = 'tag'
29+
Stage = 'stage'
30+
WorforceRequestedDate = 'workforce_requested_at'
31+
WorkforceStageUpdatedDate = 'workforce_stage_updated_at'
32+
33+
34+
class IdOperator(Enum):
35+
Is = 'is'
36+
37+
38+
class DateOperator(Enum):
39+
Equals = 'EQUALS'
40+
GreaterThanOrEqual = 'GREATER_THAN_OR_EQUAL'
41+
LessThanOrEqual = 'LESS_THAN_OR_EQUAL'
42+
43+
44+
class DateRangeOperator(Enum):
45+
Between = 'BETWEEN'
46+
47+
48+
class OrganizationFilter(BaseSearchFilter):
49+
operation: Literal[OperationType.Organization]
50+
operator: IdOperator
51+
values: List[str]
52+
53+
54+
class WorkspaceFilter(BaseSearchFilter):
55+
operation: Literal[OperationType.Workspace]
56+
operator: IdOperator
57+
values: List[str]
58+
59+
60+
class TagFilter(BaseSearchFilter):
61+
operation: Literal[OperationType.Tag]
62+
operator: IdOperator
63+
values: List[str]
64+
65+
66+
class ProjectStageFilter(BaseSearchFilter):
67+
operation: Literal[OperationType.Stage]
68+
operator: IdOperator
69+
values: List[str]
70+
71+
72+
class DateValue(BaseSearchFilter):
73+
operator: DateOperator
74+
value: datetime.date
75+
# timezone: TimeZoneName = Field(default=TimeZoneName.UTC) # type: ignore
76+
77+
78+
class WorkforceStageUpdatedFilter(BaseSearchFilter):
79+
operation: Literal[OperationType.WorkforceStageUpdatedDate]
80+
value: DateValue
81+
82+
83+
class WorkforceRequestedDateFilter(BaseSearchFilter):
84+
operation: Literal[OperationType.WorforceRequestedDate]
85+
value: DateValue
86+
87+
88+
class DateRange(BaseSearchFilter):
89+
min: datetime.date
90+
max: datetime.date
91+
92+
93+
class DateRangeValue(BaseSearchFilter):
94+
operator: DateRangeOperator
95+
value: DateRange
96+
97+
98+
class WorkforceRequestedDateRangeFilter(BaseSearchFilter):
99+
operation: Literal[OperationType.WorforceRequestedDate]
100+
# timezone: TimeZoneName = Field(default=TimeZoneName.UTC) # type: ignore
101+
value: DateRangeValue
102+
103+
104+
class WorkforceStageUpdatedRangeFilter(BaseSearchFilter):
105+
operation: Literal[OperationType.WorkforceStageUpdatedDate]
106+
# timezone: TimeZoneName = Field(default=TimeZoneName.UTC) # type: ignore
107+
value: DateRangeValue
108+
109+
110+
SearchFilters = Union[OrganizationFilter, WorkspaceFilter, TagFilter,
111+
ProjectStageFilter, WorkforceRequestedDateFilter,
112+
WorkforceStageUpdatedFilter,
113+
WorkforceRequestedDateRangeFilter,
114+
WorkforceStageUpdatedRangeFilter]
115+
116+
117+
def _build_search_filter(filter: List[SearchFilters]):
118+
operation_types = {f.operation for f in filter}
119+
if len(operation_types) < len(filter):
120+
raise ValueError("Only one filter per operation type is allowed")
121+
122+
return [f.dict() for f in filter]
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import pytest
2+
3+
from labelbox.schema.search_filters import DateOperator, DateRange, DateRangeOperator, DateRangeValue, DateValue, IdOperator, OperationType, OrganizationFilter, ProjectStageFilter, TagFilter, WorkforceRequestedDateFilter, WorkforceRequestedDateRangeFilter, WorkforceStageUpdatedFilter, WorkforceStageUpdatedRangeFilter, WorkspaceFilter, _build_search_filter
4+
from test.test_pdb import pdb
5+
6+
7+
def test_duplicate_filters():
8+
filters = [
9+
OrganizationFilter(operation=OperationType.Organization,
10+
operator=IdOperator.Is,
11+
values=["clphb4vd7000cd2wv1ktu5cwa"]),
12+
WorkspaceFilter(operation=OperationType.Workspace,
13+
operator=IdOperator.Is,
14+
values=["clphb4vd7000cd2wv1ktu5cwa"]),
15+
TagFilter(operation=OperationType.Tag,
16+
operator=IdOperator.Is,
17+
values=["tag"]),
18+
ProjectStageFilter(operation=OperationType.Stage,
19+
operator=IdOperator.Is,
20+
values=["done"]),
21+
WorkforceRequestedDateFilter(
22+
operation=OperationType.WorforceRequestedDate,
23+
operator=DateOperator.Equals,
24+
value="2024-01-01"),
25+
WorkforceStageUpdatedFilter(
26+
operation=OperationType.WorkforceStageUpdatedDate,
27+
operator=DateOperator.Equals,
28+
value="2024-01-01"),
29+
WorkforceRequestedDateRangeFilter(
30+
operation=OperationType.WorforceRequestedDate,
31+
value=DateRangeValue(operator=DateRangeOperator.Between,
32+
value=DateRange(min="2024-01-01",
33+
max="2025-01-01"))),
34+
WorkforceStageUpdatedRangeFilter(
35+
operation=OperationType.WorkforceStageUpdatedDate,
36+
value=DateRangeValue(operator=DateRangeOperator.Between,
37+
value=DateRange(min="2024-01-01",
38+
max="2025-01-01")))
39+
]
40+
41+
with pytest.raises(ValueError):
42+
_build_search_filter(filters)
43+
44+
45+
def test_id_filters():
46+
filters = [
47+
OrganizationFilter(operation=OperationType.Organization,
48+
operator=IdOperator.Is,
49+
values=["clphb4vd7000cd2wv1ktu5cwa"]),
50+
WorkspaceFilter(operation=OperationType.Workspace,
51+
operator=IdOperator.Is,
52+
values=["clphb4vd7000cd2wv1ktu5cwa"]),
53+
TagFilter(operation=OperationType.Tag,
54+
operator=IdOperator.Is,
55+
values=["tag"]),
56+
ProjectStageFilter(operation=OperationType.Stage,
57+
operator=IdOperator.Is,
58+
values=["requested"]),
59+
]
60+
61+
assert _build_search_filter(filters) == [{
62+
"operator": "is",
63+
"values": ["clphb4vd7000cd2wv1ktu5cwa"],
64+
"type": "organization"
65+
}, {
66+
"operator": "is",
67+
"values": ["clphb4vd7000cd2wv1ktu5cwa"],
68+
"type": "workspace"
69+
}, {
70+
"operator": "is",
71+
"values": ["tag"],
72+
"type": "tag"
73+
}, {
74+
"operator": "is",
75+
"values": ["requested"],
76+
"type": "stage"
77+
}]
78+
79+
80+
def test_date_filters():
81+
filters = [
82+
WorkforceRequestedDateFilter(
83+
operation=OperationType.WorforceRequestedDate,
84+
value=DateValue(operator=DateOperator.GreaterThanOrEqual,
85+
value="2024-01-01")),
86+
WorkforceStageUpdatedFilter(
87+
operation=OperationType.WorkforceStageUpdatedDate,
88+
value=DateValue(operator=DateOperator.LessThanOrEqual,
89+
value="2025-01-01")),
90+
]
91+
assert _build_search_filter(filters) == [{
92+
"type": "workforce_requested_at",
93+
"value": {
94+
"operator": "GREATER_THAN_OR_EQUAL",
95+
"value": "2024-01-01",
96+
}
97+
}, {
98+
"type": "workforce_stage_updated_at",
99+
"value": {
100+
"operator": "LESS_THAN_OR_EQUAL",
101+
"value": "2025-01-01",
102+
}
103+
}]
104+
105+
106+
def test_date_range_filters():
107+
filters = [
108+
WorkforceRequestedDateRangeFilter(
109+
operation=OperationType.WorforceRequestedDate,
110+
value=DateRangeValue(operator=DateRangeOperator.Between,
111+
value=DateRange(min="2024-01-01",
112+
max="2025-01-01"))),
113+
WorkforceStageUpdatedRangeFilter(
114+
operation=OperationType.WorkforceStageUpdatedDate,
115+
value=DateRangeValue(operator=DateRangeOperator.Between,
116+
value=DateRange(min="2024-01-01",
117+
max="2025-01-01")))
118+
]
119+
assert _build_search_filter(filters) == [{
120+
"value": {
121+
"operator": "BETWEEN",
122+
"value": {
123+
"min": "2024-01-01",
124+
"max": "2025-01-01"
125+
}
126+
},
127+
"type": "workforce_requested_at"
128+
}, {
129+
"value": {
130+
"operator": "BETWEEN",
131+
"value": {
132+
"min": "2024-01-01",
133+
"max": "2025-01-01"
134+
}
135+
},
136+
"type": "workforce_stage_updated_at"
137+
}]

0 commit comments

Comments
 (0)