Skip to content

Commit b9ba155

Browse files
author
Josselin BUILS
committed
feat(LAB-4339): add step_name_and_status_in / not_in filters
1 parent a089f91 commit b9ba155

6 files changed

Lines changed: 115 additions & 9 deletions

File tree

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,20 @@ def asset_where_mapper(filters: AssetFilters):
6868
"stepIdNotIn": filters.step_id_not_in,
6969
"stepStatusIn": filters.step_status_in,
7070
"stepStatusNotIn": filters.step_status_not_in,
71+
"stepIdAndStatusIn": (
72+
[
73+
{"stepId": step_id, "status": step_status}
74+
for step_id, step_status in filters.step_id_and_status_in
75+
]
76+
if filters.step_id_and_status_in is not None
77+
else None
78+
),
79+
"stepIdAndStatusNotIn": (
80+
[
81+
{"stepId": step_id, "status": step_status}
82+
for step_id, step_status in filters.step_id_and_status_not_in
83+
]
84+
if filters.step_id_and_status_not_in is not None
85+
else None
86+
),
7187
}

src/kili/domain/asset/asset.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ class AssetFilters:
6262
issue_status: Optional["IssueStatus"] = None
6363
skipped: Optional[bool] = None
6464
status_in: Optional[ListOrTuple[AssetStatus]] = None
65+
step_id_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None
66+
step_id_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None
6567
step_id_in: Optional[ListOrTuple[str]] = None
6668
step_id_not_in: Optional[ListOrTuple[str]] = None
6769
step_status_in: Optional[ListOrTuple[StatusInStep]] = None
@@ -73,6 +75,8 @@ class AssetWorkflowFilters(TypedDict, total=False):
7375

7476
skipped: Optional[bool]
7577
status_in: Optional[ListOrTuple[AssetStatus]]
78+
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]]
79+
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]]
7680
step_name_in: Optional[ListOrTuple[str]]
7781
step_name_not_in: Optional[ListOrTuple[str]]
7882
step_status_in: Optional[ListOrTuple[StatusInStep]]

src/kili/domain/asset/helpers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ def check_asset_workflow_arguments(
3333
project_workflow_version: WorkflowVersion, asset_workflow_filters: AssetWorkflowFilters
3434
) -> None:
3535
"""Check asset workflow parameters relative to the project workflow version."""
36+
step_name_and_status_in = asset_workflow_filters.get("step_name_and_status_in")
37+
step_name_and_status_not_in = asset_workflow_filters.get("step_name_and_status_not_in")
3638
step_name_in = asset_workflow_filters.get("step_name_in")
3739
step_status_in = asset_workflow_filters.get("step_status_in")
3840
status_in = asset_workflow_filters.get("status_in")
@@ -64,3 +66,7 @@ def check_asset_workflow_arguments(
6466
raise ValueError(
6567
"Filters step_name_in and/or step_status_in given : use filter status_in for this project."
6668
)
69+
if step_name_and_status_in is not None or step_name_and_status_not_in is not None:
70+
raise ValueError(
71+
"Filters step_name_and_status_in and/or step_name_and_status_not_in given : use filter status_in for this project."
72+
)

src/kili/domain_api/assets.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ class AssetFilter(TypedDict, total=False):
8787
metadata_where: Optional[dict[str, Any]]
8888
skipped: Optional[bool]
8989
status_in: Optional[list[AssetStatus]]
90+
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]]
91+
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]]
9092
step_name_in: Optional[list[str]]
9193
step_name_not_in: Optional[list[str]]
9294
step_status_in: Optional[list[StatusInStep]]

src/kili/presentation/client/asset.py

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
resolve_disable_tqdm,
3333
)
3434
from kili.presentation.client.helpers.filter_conversion import (
35+
extract_step_id_and_status_filters_from_project_steps,
3536
extract_step_ids_from_project_steps,
3637
)
3738
from kili.use_cases.asset import AssetUseCases
@@ -157,6 +158,8 @@ def assets(
157158
label_output_format: Literal["dict", "parsed_label"] = "dict",
158159
skipped: Optional[bool] = None,
159160
status_in: Optional[list[AssetStatus]] = None,
161+
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None,
162+
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None,
160163
step_name_in: Optional[list[str]] = None,
161164
step_name_not_in: Optional[list[str]] = None,
162165
step_status_in: Optional[list[StatusInStep]] = None,
@@ -225,6 +228,8 @@ def assets(
225228
label_output_format: Literal["dict", "parsed_label"] = "dict",
226229
skipped: Optional[bool] = None,
227230
status_in: Optional[list[AssetStatus]] = None,
231+
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None,
232+
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None,
228233
step_name_in: Optional[list[str]] = None,
229234
step_name_not_in: Optional[list[str]] = None,
230235
step_status_in: Optional[list[StatusInStep]] = None,
@@ -293,6 +298,8 @@ def assets(
293298
label_output_format: Literal["dict", "parsed_label"] = "dict",
294299
skipped: Optional[bool] = None,
295300
status_in: Optional[list[AssetStatus]] = None,
301+
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None,
302+
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None,
296303
step_name_in: Optional[list[str]] = None,
297304
step_name_not_in: Optional[list[str]] = None,
298305
step_status_in: Optional[list[StatusInStep]] = None,
@@ -366,6 +373,10 @@ def assets(
366373
status_in: Returned assets should have a status that belongs to that list, if given.
367374
Possible choices: `TODO`, `ONGOING`, `LABELED`, `TO_REVIEW` or `REVIEWED`.
368375
Only applicable if the project is in the WorkflowV1 (legacy).
376+
step_name_and_status_in: Returned assets match at least one of the given (step_name, step_status) pairs.
377+
Only applicable if the project is in WorkflowV2.
378+
step_name_and_status_not_in: Returned assets do not match any of the given (step_name, step_status) pairs.
379+
Only applicable if the project is in WorkflowV2.
369380
step_name_in: Returned assets are in the step whose name belong to that list, if given.
370381
Only applicable if the project is in WorkflowV2.
371382
step_name_not_in: Returned assets are in the step whose name does not belong to that list, if given.
@@ -468,16 +479,21 @@ def assets(
468479
stacklevel=1,
469480
)
470481

482+
step_id_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None
483+
step_id_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None
471484
step_id_in = None
472485
step_id_not_in = None
473-
has_step_filters = step_name_in is not None or step_name_not_in is not None
474-
has_status_filters = (
475-
step_status_in is not None
486+
has_step_or_status_filters = (
487+
step_name_in is not None
488+
or step_name_not_in is not None
489+
or step_name_and_status_in is not None
490+
or step_name_and_status_not_in is not None
491+
or step_status_in is not None
476492
or step_status_not_in is not None
477493
or status_in is not None
478494
or skipped is not None
479495
)
480-
if has_step_filters or has_status_filters:
496+
if has_step_or_status_filters:
481497
check_asset_workflow_arguments(
482498
project_workflow_version=project_workflow_version,
483499
asset_workflow_filters={
@@ -487,6 +503,8 @@ def assets(
487503
"step_name_not_in": step_name_not_in,
488504
"step_status_in": step_status_in,
489505
"step_status_not_in": step_status_not_in,
506+
"step_name_and_status_in": step_name_and_status_in,
507+
"step_name_and_status_not_in": step_name_and_status_not_in,
490508
},
491509
)
492510
if project_workflow_version == "V2" and step_name_in is not None:
@@ -499,6 +517,16 @@ def assets(
499517
project_steps=project_steps,
500518
step_name_in=step_name_not_in,
501519
)
520+
if project_workflow_version == "V2" and step_name_and_status_in is not None:
521+
step_id_and_status_in = extract_step_id_and_status_filters_from_project_steps(
522+
project_steps=project_steps,
523+
step_name_and_status_filters=step_name_and_status_in,
524+
)
525+
if project_workflow_version == "V2" and step_name_and_status_not_in is not None:
526+
step_id_and_status_not_in = extract_step_id_and_status_filters_from_project_steps(
527+
project_steps=project_steps,
528+
step_name_and_status_filters=step_name_and_status_not_in,
529+
)
502530

503531
# Resolve disable_tqdm: function parameter > client global setting > function default
504532
disable_tqdm = resolve_disable_tqdm(disable_tqdm, getattr(self, "disable_tqdm", None))
@@ -550,6 +578,8 @@ def assets(
550578
step_id_not_in=step_id_not_in,
551579
step_status_in=step_status_in,
552580
step_status_not_in=step_status_not_in,
581+
step_id_and_status_in=step_id_and_status_in,
582+
step_id_and_status_not_in=step_id_and_status_not_in,
553583
)
554584
assets_gen = asset_use_cases.list_assets(
555585
filters,
@@ -625,6 +655,8 @@ def count_assets(
625655
step_name_not_in: Optional[list[str]] = None,
626656
step_status_in: Optional[list[StatusInStep]] = None,
627657
step_status_not_in: Optional[list[StatusInStep]] = None,
658+
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None,
659+
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None,
628660
) -> int:
629661
# pylint: disable=line-too-long
630662
"""Count and return the number of assets with the given constraints.
@@ -691,6 +723,10 @@ def count_assets(
691723
step_status_not_in: Returned assets have the status of their step that does not belong to that list, if given.
692724
Possible choices: `TO_DO`, `DOING`, `PARTIALLY_DONE`, `REDO`, `DONE`, `SKIPPED`.
693725
Only applicable if the project is in WorkflowV2.
726+
step_name_and_status_in: Returned assets match at least one of the given (step_name, step_status) pairs.
727+
Only applicable if the project is in WorkflowV2.
728+
step_name_and_status_not_in: Returned assets do not match any of the given (step_name, step_status) pairs.
729+
Only applicable if the project is in WorkflowV2.
694730
695731
!!! info "Dates format"
696732
Date strings should have format: "YYYY-MM-DD"
@@ -734,11 +770,18 @@ def count_assets(
734770

735771
step_id_in = None
736772
step_id_not_in = None
737-
has_step_filters = step_name_in is not None or step_name_not_in is not None
738-
has_status_filters = (
739-
status_in is not None or step_status_in is not None or step_status_not_in is not None
773+
step_id_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None
774+
step_id_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None
775+
has_step_or_status_filters = (
776+
step_name_in is not None
777+
or step_name_not_in is not None
778+
or step_name_and_status_in is not None
779+
or step_name_and_status_not_in is not None
780+
or status_in is not None
781+
or step_status_in is not None
782+
or step_status_not_in is not None
740783
)
741-
if has_step_filters or has_status_filters:
784+
if has_step_or_status_filters:
742785
project_use_cases = ProjectUseCases(self.kili_api_gateway)
743786
(
744787
project_steps,
@@ -749,6 +792,8 @@ def count_assets(
749792
asset_workflow_filters={
750793
"skipped": skipped,
751794
"step_name_in": step_name_in,
795+
"step_name_and_status_in": step_name_and_status_in,
796+
"step_name_and_status_not_in": step_name_and_status_not_in,
752797
"step_name_not_in": step_name_not_in,
753798
"step_status_in": step_status_in,
754799
"step_status_not_in": step_status_not_in,
@@ -766,6 +811,16 @@ def count_assets(
766811
project_steps=project_steps,
767812
step_name_in=step_name_not_in,
768813
)
814+
if project_workflow_version == "V2" and step_name_and_status_in is not None:
815+
step_id_and_status_in = extract_step_id_and_status_filters_from_project_steps(
816+
project_steps=project_steps,
817+
step_name_and_status_filters=step_name_and_status_in,
818+
)
819+
if project_workflow_version == "V2" and step_name_and_status_not_in is not None:
820+
step_id_and_status_not_in = extract_step_id_and_status_filters_from_project_steps(
821+
project_steps=project_steps,
822+
step_name_and_status_filters=step_name_and_status_not_in,
823+
)
769824

770825
filters = AssetFilters(
771826
project_id=ProjectId(project_id),
@@ -809,6 +864,8 @@ def count_assets(
809864
created_at_lte=created_at_lte,
810865
issue_status=issue_status,
811866
issue_type=issue_type,
867+
step_id_and_status_in=step_id_and_status_in,
868+
step_id_and_status_not_in=step_id_and_status_not_in,
812869
step_id_in=step_id_in,
813870
step_id_not_in=step_id_not_in,
814871
step_status_in=step_status_in,

src/kili/presentation/client/helpers/filter_conversion.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Module for common argument validators across client methods."""
22

3-
3+
from kili.domain.asset.asset import StatusInStep
44
from kili.domain.project import ProjectStep
55

66

@@ -19,3 +19,24 @@ def extract_step_ids_from_project_steps(
1919
raise ValueError(f"The following step names do not match any steps: {unmatched_names}")
2020

2121
return [step["id"] for step in matching_steps]
22+
23+
24+
def extract_step_id_and_status_filters_from_project_steps(
25+
project_steps: list[ProjectStep],
26+
step_name_and_status_filters: list[tuple[str, StatusInStep]],
27+
) -> list[tuple[str, StatusInStep]]:
28+
"""Convert a list of (step_name, step_status) tuples to (step_id, step_status) tuples."""
29+
step_name_to_id = {step["name"]: step["id"] for step in project_steps}
30+
31+
unmatched_names = [
32+
step_name
33+
for step_name, _ in step_name_and_status_filters
34+
if step_name not in step_name_to_id
35+
]
36+
if unmatched_names:
37+
raise ValueError(f"The following step names do not match any steps: {unmatched_names}")
38+
39+
return [
40+
(step_name_to_id[step_name], step_status)
41+
for step_name, step_status in step_name_and_status_filters
42+
]

0 commit comments

Comments
 (0)