Skip to content

Commit 6e105e6

Browse files
feat(LAB-3534): add stepIdIn and stepStatusIn filters
1 parent d749374 commit 6e105e6

6 files changed

Lines changed: 134 additions & 4 deletions

File tree

src/kili/adapters/kili_api_gateway/asset/mappers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,6 @@ def asset_where_mapper(filters: AssetFilters):
6464
"type": filters.issue_type,
6565
"status": filters.issue_status,
6666
},
67+
"stepIdIn": filters.step_id_in,
68+
"stepStatusIn": filters.step_status_in,
6769
}

src/kili/domain/asset/asset.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212

1313
AssetId = NewType("AssetId", str)
1414
AssetExternalId = NewType("AssetExternalId", str)
15-
15+
AssetStatusInStep = NewType("AssetStatusInStep", str)
1616

1717
AssetStatus = Literal["TODO", "ONGOING", "LABELED", "REVIEWED", "TO_REVIEW"]
1818

19+
StatusInStep = Literal["TO_DO", "DOING", "PARTIALLY_DONE", "REDO", "DONE", "SKIPPED"]
20+
1921

2022
@dataclass
2123
class AssetFilters:
@@ -59,3 +61,5 @@ class AssetFilters:
5961
inference_mark_lte: Optional[float] = None
6062
issue_type: Optional["IssueType"] = None
6163
issue_status: Optional["IssueStatus"] = None
64+
step_id_in: Optional[ListOrTuple[str]] = None
65+
step_status_in: Optional[ListOrTuple[StatusInStep]] = None

src/kili/presentation/client/asset.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,23 @@
1717
from typeguard import typechecked
1818

1919
from kili.adapters.kili_api_gateway.helpers.queries import QueryOptions
20-
from kili.domain.asset import AssetExternalId, AssetFilters, AssetId, AssetStatus
20+
from kili.domain.asset.asset import (
21+
AssetExternalId,
22+
AssetFilters,
23+
AssetId,
24+
AssetStatus,
25+
StatusInStep,
26+
)
2127
from kili.domain.issue import IssueStatus, IssueType
2228
from kili.domain.label import LabelType
2329
from kili.domain.project import ProjectId
2430
from kili.domain.types import ListOrTuple
2531
from kili.presentation.client.helpers.common_validators import (
2632
disable_tqdm_if_as_generator,
2733
)
34+
from kili.presentation.client.helpers.filter_conversion import (
35+
convert_step_in_to_step_id_in_filter,
36+
)
2837
from kili.use_cases.asset import AssetUseCases
2938
from kili.utils.logcontext import for_all_methods, log_call
3039

@@ -112,6 +121,8 @@ def assets(
112121
external_id_strictly_in: Optional[List[str]] = None,
113122
external_id_in: Optional[List[str]] = None,
114123
label_output_format: Literal["dict", "parsed_label"] = "dict",
124+
step_in: Optional[List[str]] = None,
125+
step_status_in: Optional[List[StatusInStep]] = None,
115126
*,
116127
as_generator: Literal[True],
117128
) -> Generator[Dict, None, None]:
@@ -190,6 +201,8 @@ def assets(
190201
external_id_strictly_in: Optional[List[str]] = None,
191202
external_id_in: Optional[List[str]] = None,
192203
label_output_format: Literal["dict", "parsed_label"] = "dict",
204+
step_in: Optional[List[str]] = None,
205+
step_status_in: Optional[List[StatusInStep]] = None,
193206
*,
194207
as_generator: Literal[False] = False,
195208
) -> List[Dict]:
@@ -268,6 +281,8 @@ def assets(
268281
external_id_strictly_in: Optional[List[str]] = None,
269282
external_id_in: Optional[List[str]] = None,
270283
label_output_format: Literal["dict", "parsed_label"] = "dict",
284+
step_in: Optional[List[str]] = None,
285+
step_status_in: Optional[List[StatusInStep]] = None,
271286
*,
272287
as_generator: bool = False,
273288
) -> Union[Iterable[Dict], "pd.DataFrame"]:
@@ -291,6 +306,7 @@ def assets(
291306
honeypot_mark_lt: Deprecated. Use `honeypot_mark_lte` instead.
292307
status_in: Returned assets should have a status that belongs to that list, if given.
293308
Possible choices: `TODO`, `ONGOING`, `LABELED`, `TO_REVIEW` or `REVIEWED`.
309+
Only applicable if the project is in the WorkflowV1 (legacy).
294310
label_type_in: Returned assets should have a label whose type belongs to that list, if given.
295311
label_author_in: Returned assets should have a label whose author belongs to that list, if given. An author can be designated by the first name, the last name, or the first name + last name.
296312
label_consensus_mark_gt: Deprecated. Use `label_consensus_mark_gte` instead.
@@ -335,6 +351,10 @@ def assets(
335351
external_id_in: Returned assets should have external ids that partially match the ones in the list.
336352
For example, with `external_id_in=['abc']`, any asset with an external id containing `'abc'` will be returned.
337353
label_output_format: If `parsed_label`, the labels in the assets will be parsed. More information on parsed labels in the [documentation](https://python-sdk-docs.kili-technology.com/latest/sdk/tutorials/label_parsing/).
354+
step_in: Returned assets are in the step whose name belong to that list, if given.
355+
Only applicable if the project is in WorkflowV2.
356+
step_status_in: Returned assets have the status in their step that belongs to that list, if given.
357+
Only applicable if the project is in WorkflowV2.
338358
339359
!!! info "Dates format"
340360
Date strings should have format: "YYYY-MM-DD"
@@ -431,6 +451,20 @@ def assets(
431451

432452
disable_tqdm = disable_tqdm_if_as_generator(as_generator, disable_tqdm)
433453

454+
step_id_in = None
455+
if step_in is not None or step_status_in is not None or status_in is not None:
456+
step_id_in = convert_step_in_to_step_id_in_filter(
457+
self,
458+
project_id=project_id,
459+
fields=fields,
460+
asset_filter_kwargs={
461+
"step_in": step_in,
462+
"step_status_in": step_status_in,
463+
"status_in": status_in,
464+
"skipped": skipped,
465+
},
466+
)
467+
434468
asset_use_cases = AssetUseCases(self.kili_api_gateway)
435469
filters = AssetFilters(
436470
project_id=ProjectId(project_id),
@@ -474,6 +508,8 @@ def assets(
474508
assignee_not_in=assignee_not_in,
475509
issue_status=issue_status,
476510
issue_type=issue_type,
511+
step_id_in=step_id_in,
512+
step_status_in=step_status_in,
477513
)
478514
assets_gen = asset_use_cases.list_assets(
479515
filters,
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""Module for common argument validators across client methods."""
2+
3+
import warnings
4+
from typing import Dict, List, Optional
5+
6+
from kili.adapters.kili_api_gateway.helpers.queries import QueryOptions
7+
from kili.domain.project import ProjectFilters, ProjectId
8+
from kili.domain.types import ListOrTuple
9+
from kili.use_cases.project.project import ProjectUseCases
10+
11+
12+
def convert_step_in_to_step_id_in_filter(
13+
self,
14+
project_id: str,
15+
asset_filter_kwargs: Dict[str, object],
16+
fields: Optional[ListOrTuple[str]] = None,
17+
) -> Optional[List[str]]:
18+
"""If a stepIn filter is given, convert it to a stepIdIn and return it."""
19+
step_in = asset_filter_kwargs.get("step_in")
20+
step_status_in = asset_filter_kwargs.get("step_status_in")
21+
status_in = asset_filter_kwargs.get("status_in")
22+
skipped = asset_filter_kwargs.get("skipped")
23+
24+
project_use_cases = ProjectUseCases(self.kili_api_gateway)
25+
project_filters = ProjectFilters(id=ProjectId(project_id))
26+
options = QueryOptions(disable_tqdm=True, first=1)
27+
project_gen = project_use_cases.list_projects(
28+
project_filters=project_filters, fields=["steps.id", "steps.name"], options=options
29+
)
30+
projects = list(project_gen)
31+
project = projects[0]
32+
33+
if len(project["steps"]) != 0:
34+
if step_status_in is not None and status_in is not None:
35+
raise ValueError(
36+
"Filters step_status_in and status_in both given : only use filter step_status_in for this project."
37+
)
38+
if step_in is not None and status_in is not None:
39+
raise ValueError(
40+
"Filters step_in and status_in both given : use filter step_status_in instead of status_in for this project." # pylint: disable=line-too-long
41+
)
42+
if status_in is not None:
43+
warnings.warn(
44+
"Filter status_in given : use filters step_status_in and step_in instead for this project.",
45+
stacklevel=1,
46+
)
47+
if skipped is not None:
48+
warnings.warn(
49+
"Filter skipped given : use filter step_status_in with the SKIPPED status instead for this project",
50+
stacklevel=1,
51+
)
52+
if fields and "status" in fields:
53+
warnings.warn(
54+
"Field status requested : request fields step and stepStatus instead for this project",
55+
stacklevel=1,
56+
)
57+
58+
if step_in is not None:
59+
# We need to send the step ids to the backend instead of the step names
60+
# for performance reasons (since the step names are not unique)
61+
return [step["id"] for step in project["steps"] if step["name"] in step_in]
62+
return None
63+
64+
if step_in is not None or step_status_in is not None:
65+
raise ValueError(
66+
"Filters step_in and/or step_status_in given : use filter status_in for this project."
67+
)
68+
return None

src/kili/presentation/client/label.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
assert_all_arrays_have_same_size,
3535
disable_tqdm_if_as_generator,
3636
)
37+
from kili.presentation.client.helpers.filter_conversion import (
38+
convert_step_in_to_step_id_in_filter,
39+
)
3740
from kili.services.export import export_labels
3841
from kili.services.export.exceptions import NoCompatibleJobError
3942
from kili.services.export.types import CocoAnnotationModifier, LabelFormat, SplitOption
@@ -1146,8 +1149,8 @@ def export_labels(
11461149
- `label_reviewer_not_in`
11471150
- `assignee_in`
11481151
- `assignee_not_in`
1149-
- `skipped`
1150-
- `status_in`
1152+
- `skipped`: only applicable if the project is in the WorkflowV1 (legacy).
1153+
- `status_in`: only applicable if the project is in the WorkflowV1 (legacy).
11511154
- `label_category_search`
11521155
- `created_at_gte`
11531156
- `created_at_lte`
@@ -1156,6 +1159,8 @@ def export_labels(
11561159
- `inference_mark_gte`
11571160
- `inference_mark_lte`
11581161
- `metadata_where`
1162+
- `step_in`: : only applicable if the project is in the WorkflowV2.
1163+
- `step_status_in`: only applicable if the project is in the WorkflowV2.
11591164
11601165
See the documentation of [`kili.assets()`](https://python-sdk-docs.kili-technology.com/latest/sdk/asset/#kili.queries.asset.__init__.QueriesAsset.assets) for more information.
11611166
normalized_coordinates: This parameter is only effective on the Kili (a.k.a raw) format.
@@ -1204,6 +1209,19 @@ def is_rectangle(coco_annotation, coco_image, kili_annotation):
12041209
else:
12051210
resolved_asset_ids = cast(List[AssetId], asset_ids)
12061211

1212+
if asset_filter_kwargs and (
1213+
asset_filter_kwargs.get("step_in") is not None
1214+
or asset_filter_kwargs.get("step_status_in") is not None
1215+
or asset_filter_kwargs.get("status_in") is not None
1216+
):
1217+
step_id_in = convert_step_in_to_step_id_in_filter(
1218+
self,
1219+
project_id=project_id,
1220+
asset_filter_kwargs=asset_filter_kwargs,
1221+
)
1222+
asset_filter_kwargs.pop("step_in", None)
1223+
asset_filter_kwargs["step_id_in"] = step_id_in
1224+
12071225
try:
12081226
return export_labels(
12091227
self, # pyright: ignore[reportGeneralTypeIssues]

src/kili/services/export/tools.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ def fetch_assets(
140140
"inference_mark_gte": asset_filter_kwargs.pop("inference_mark_gte", None),
141141
"inference_mark_lte": asset_filter_kwargs.pop("inference_mark_lte", None),
142142
"metadata_where": asset_filter_kwargs.pop("metadata_where", None),
143+
"step_id_in": asset_filter_kwargs.pop("step_id_in", None),
144+
"step_status_in": asset_filter_kwargs.pop("step_status_in", None),
143145
}
144146

145147
if asset_filter_kwargs:

0 commit comments

Comments
 (0)