Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/kili/adapters/kili_api_gateway/asset/mappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,20 @@ def asset_where_mapper(filters: AssetFilters):
"stepIdNotIn": filters.step_id_not_in,
"stepStatusIn": filters.step_status_in,
"stepStatusNotIn": filters.step_status_not_in,
"stepIdAndStatusIn": (
[
{"stepId": step_id, "status": step_status}
for step_id, step_status in filters.step_id_and_status_in
]
if filters.step_id_and_status_in is not None
else None
),
"stepIdAndStatusNotIn": (
[
{"stepId": step_id, "status": step_status}
for step_id, step_status in filters.step_id_and_status_not_in
]
if filters.step_id_and_status_not_in is not None
else None
),
}
4 changes: 4 additions & 0 deletions src/kili/domain/asset/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class AssetFilters:
issue_status: Optional["IssueStatus"] = None
skipped: Optional[bool] = None
status_in: Optional[ListOrTuple[AssetStatus]] = None
step_id_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None
step_id_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None
step_id_in: Optional[ListOrTuple[str]] = None
step_id_not_in: Optional[ListOrTuple[str]] = None
step_status_in: Optional[ListOrTuple[StatusInStep]] = None
Expand All @@ -73,6 +75,8 @@ class AssetWorkflowFilters(TypedDict, total=False):

skipped: Optional[bool]
status_in: Optional[ListOrTuple[AssetStatus]]
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]]
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]]
step_name_in: Optional[ListOrTuple[str]]
step_name_not_in: Optional[ListOrTuple[str]]
step_status_in: Optional[ListOrTuple[StatusInStep]]
Expand Down
7 changes: 7 additions & 0 deletions src/kili/domain/asset/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ def check_asset_workflow_arguments(
project_workflow_version: WorkflowVersion, asset_workflow_filters: AssetWorkflowFilters
) -> None:
"""Check asset workflow parameters relative to the project workflow version."""
step_name_and_status_in = asset_workflow_filters.get("step_name_and_status_in")
step_name_and_status_not_in = asset_workflow_filters.get("step_name_and_status_not_in")
step_name_in = asset_workflow_filters.get("step_name_in")
step_status_in = asset_workflow_filters.get("step_status_in")
status_in = asset_workflow_filters.get("status_in")
Expand Down Expand Up @@ -64,3 +66,8 @@ def check_asset_workflow_arguments(
raise ValueError(
"Filters step_name_in and/or step_status_in given : use filter status_in for this project."
)
if step_name_and_status_in is not None or step_name_and_status_not_in is not None:
raise ValueError(
"Filters step_name_and_status_in and/or step_name_and_status_not_in given : use filter "
"status_in for this project."
)
4 changes: 3 additions & 1 deletion src/kili/domain_api/assets.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Assets domain namespace for the Kili Python SDK."""
# pylint: disable=too-many-lines,too-many-public-methods
# pylint: disable=too-many-lines

import warnings
from collections.abc import Generator
Expand Down Expand Up @@ -87,6 +87,8 @@ class AssetFilter(TypedDict, total=False):
metadata_where: Optional[dict[str, Any]]
skipped: Optional[bool]
status_in: Optional[list[AssetStatus]]
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]]
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]]
step_name_in: Optional[list[str]]
step_name_not_in: Optional[list[str]]
step_status_in: Optional[list[StatusInStep]]
Expand Down
1 change: 0 additions & 1 deletion src/kili/domain_api/issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
This module provides a comprehensive interface for issue-related operations
including creation, querying, status management, and lifecycle operations.
"""
# pylint: disable=too-many-public-methods

from collections.abc import Generator
from itertools import repeat
Expand Down
2 changes: 1 addition & 1 deletion src/kili/domain_api/labels.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# pylint: disable=too-many-lines,too-many-public-methods
# pylint: disable=too-many-lines
"""Labels domain namespace for the Kili Python SDK.
This module provides a comprehensive interface for label-related operations
Expand Down
1 change: 0 additions & 1 deletion src/kili/domain_api/organizations.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Organizations domain namespace for the Kili Python SDK."""
# pylint: disable=too-many-public-methods

from collections.abc import Generator
from datetime import datetime
Expand Down
1 change: 0 additions & 1 deletion src/kili/domain_api/plugins.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Plugins domain namespace for the Kili Python SDK."""
# pylint: disable=too-many-public-methods

from datetime import datetime
from typing import List, Optional
Expand Down
1 change: 0 additions & 1 deletion src/kili/domain_api/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
This module provides a comprehensive interface for project-related operations
including lifecycle management, user management, workflow configuration, and versioning.
"""
# pylint: disable=too-many-public-methods

from collections.abc import Generator, Iterable, Sequence
from functools import cached_property
Expand Down
1 change: 0 additions & 1 deletion src/kili/domain_api/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
This module provides a comprehensive interface for question-related operations
including creation, querying, status management, and lifecycle operations.
"""
# pylint: disable=too-many-public-methods

from collections.abc import Generator
from itertools import repeat
Expand Down
2 changes: 1 addition & 1 deletion src/kili/domain_api/storages.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Storages domain namespace for the Kili Python SDK."""
# pylint: disable=too-many-lines,too-many-public-methods
# pylint: disable=too-many-lines

from collections.abc import Generator
from functools import cached_property
Expand Down
1 change: 0 additions & 1 deletion src/kili/domain_api/tags.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Tags domain namespace for the Kili Python SDK."""
# pylint: disable=too-many-public-methods

from typing import List, Literal, Optional

Expand Down
1 change: 0 additions & 1 deletion src/kili/domain_api/users.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Users domain namespace for the Kili Python SDK."""
# pylint: disable=too-many-public-methods

import re
from collections.abc import Generator
Expand Down
74 changes: 66 additions & 8 deletions src/kili/presentation/client/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
resolve_disable_tqdm,
)
from kili.presentation.client.helpers.filter_conversion import (
extract_step_id_and_status_filters_from_project_steps,
extract_step_ids_from_project_steps,
)
from kili.use_cases.asset import AssetUseCases
Expand Down Expand Up @@ -157,6 +158,8 @@ def assets(
label_output_format: Literal["dict", "parsed_label"] = "dict",
skipped: Optional[bool] = None,
status_in: Optional[list[AssetStatus]] = None,
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None,
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None,
step_name_in: Optional[list[str]] = None,
step_name_not_in: Optional[list[str]] = None,
step_status_in: Optional[list[StatusInStep]] = None,
Expand Down Expand Up @@ -225,6 +228,8 @@ def assets(
label_output_format: Literal["dict", "parsed_label"] = "dict",
skipped: Optional[bool] = None,
status_in: Optional[list[AssetStatus]] = None,
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None,
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None,
step_name_in: Optional[list[str]] = None,
step_name_not_in: Optional[list[str]] = None,
step_status_in: Optional[list[StatusInStep]] = None,
Expand All @@ -234,6 +239,7 @@ def assets(
) -> list[dict]:
...

# pylint: disable=too-many-branches
@typechecked
def assets(
self,
Expand Down Expand Up @@ -293,6 +299,8 @@ def assets(
label_output_format: Literal["dict", "parsed_label"] = "dict",
skipped: Optional[bool] = None,
status_in: Optional[list[AssetStatus]] = None,
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None,
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None,
step_name_in: Optional[list[str]] = None,
step_name_not_in: Optional[list[str]] = None,
step_status_in: Optional[list[StatusInStep]] = None,
Expand Down Expand Up @@ -366,6 +374,10 @@ def assets(
status_in: Returned assets should have a status that belongs to that list, if given.
Possible choices: `TODO`, `ONGOING`, `LABELED`, `TO_REVIEW` or `REVIEWED`.
Only applicable if the project is in the WorkflowV1 (legacy).
step_name_and_status_in: Returned assets match at least one of the given (step_name, step_status) pairs.
Only applicable if the project is in WorkflowV2.
step_name_and_status_not_in: Returned assets do not match any of the given (step_name, step_status) pairs.
Only applicable if the project is in WorkflowV2.
step_name_in: Returned assets are in the step whose name belong to that list, if given.
Only applicable if the project is in WorkflowV2.
step_name_not_in: Returned assets are in the step whose name does not belong to that list, if given.
Expand Down Expand Up @@ -468,16 +480,21 @@ def assets(
stacklevel=1,
)

step_id_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None
step_id_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None
step_id_in = None
step_id_not_in = None
has_step_filters = step_name_in is not None or step_name_not_in is not None
has_status_filters = (
step_status_in is not None
has_step_or_status_filters = (
step_name_in is not None
or step_name_not_in is not None
or step_name_and_status_in is not None
or step_name_and_status_not_in is not None
or step_status_in is not None
or step_status_not_in is not None
or status_in is not None
or skipped is not None
)
if has_step_filters or has_status_filters:
if has_step_or_status_filters:
check_asset_workflow_arguments(
project_workflow_version=project_workflow_version,
asset_workflow_filters={
Expand All @@ -487,6 +504,8 @@ def assets(
"step_name_not_in": step_name_not_in,
"step_status_in": step_status_in,
"step_status_not_in": step_status_not_in,
"step_name_and_status_in": step_name_and_status_in,
"step_name_and_status_not_in": step_name_and_status_not_in,
},
)
if project_workflow_version == "V2" and step_name_in is not None:
Expand All @@ -499,6 +518,16 @@ def assets(
project_steps=project_steps,
step_name_in=step_name_not_in,
)
if project_workflow_version == "V2" and step_name_and_status_in is not None:
step_id_and_status_in = extract_step_id_and_status_filters_from_project_steps(
project_steps=project_steps,
step_name_and_status_filters=step_name_and_status_in,
)
if project_workflow_version == "V2" and step_name_and_status_not_in is not None:
step_id_and_status_not_in = extract_step_id_and_status_filters_from_project_steps(
project_steps=project_steps,
step_name_and_status_filters=step_name_and_status_not_in,
)

# Resolve disable_tqdm: function parameter > client global setting > function default
disable_tqdm = resolve_disable_tqdm(disable_tqdm, getattr(self, "disable_tqdm", None))
Expand Down Expand Up @@ -550,6 +579,8 @@ def assets(
step_id_not_in=step_id_not_in,
step_status_in=step_status_in,
step_status_not_in=step_status_not_in,
step_id_and_status_in=step_id_and_status_in,
step_id_and_status_not_in=step_id_and_status_not_in,
)
assets_gen = asset_use_cases.list_assets(
filters,
Expand Down Expand Up @@ -625,6 +656,8 @@ def count_assets(
step_name_not_in: Optional[list[str]] = None,
step_status_in: Optional[list[StatusInStep]] = None,
step_status_not_in: Optional[list[StatusInStep]] = None,
step_name_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None,
step_name_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None,
) -> int:
# pylint: disable=line-too-long
"""Count and return the number of assets with the given constraints.
Expand Down Expand Up @@ -691,6 +724,10 @@ def count_assets(
step_status_not_in: Returned assets have the status of their step that does not belong to that list, if given.
Possible choices: `TO_DO`, `DOING`, `PARTIALLY_DONE`, `REDO`, `DONE`, `SKIPPED`.
Only applicable if the project is in WorkflowV2.
step_name_and_status_in: Returned assets match at least one of the given (step_name, step_status) pairs.
Only applicable if the project is in WorkflowV2.
step_name_and_status_not_in: Returned assets do not match any of the given (step_name, step_status) pairs.
Only applicable if the project is in WorkflowV2.

!!! info "Dates format"
Date strings should have format: "YYYY-MM-DD"
Expand Down Expand Up @@ -734,11 +771,18 @@ def count_assets(

step_id_in = None
step_id_not_in = None
has_step_filters = step_name_in is not None or step_name_not_in is not None
has_status_filters = (
status_in is not None or step_status_in is not None or step_status_not_in is not None
step_id_and_status_in: Optional[list[tuple[str, StatusInStep]]] = None
step_id_and_status_not_in: Optional[list[tuple[str, StatusInStep]]] = None
has_step_or_status_filters = (
step_name_in is not None
or step_name_not_in is not None
or step_name_and_status_in is not None
or step_name_and_status_not_in is not None
or status_in is not None
or step_status_in is not None
or step_status_not_in is not None
)
if has_step_filters or has_status_filters:
if has_step_or_status_filters:
project_use_cases = ProjectUseCases(self.kili_api_gateway)
(
project_steps,
Expand All @@ -749,6 +793,8 @@ def count_assets(
asset_workflow_filters={
"skipped": skipped,
"step_name_in": step_name_in,
"step_name_and_status_in": step_name_and_status_in,
"step_name_and_status_not_in": step_name_and_status_not_in,
"step_name_not_in": step_name_not_in,
"step_status_in": step_status_in,
"step_status_not_in": step_status_not_in,
Expand All @@ -766,6 +812,16 @@ def count_assets(
project_steps=project_steps,
step_name_in=step_name_not_in,
)
if project_workflow_version == "V2" and step_name_and_status_in is not None:
step_id_and_status_in = extract_step_id_and_status_filters_from_project_steps(
project_steps=project_steps,
step_name_and_status_filters=step_name_and_status_in,
)
if project_workflow_version == "V2" and step_name_and_status_not_in is not None:
step_id_and_status_not_in = extract_step_id_and_status_filters_from_project_steps(
project_steps=project_steps,
step_name_and_status_filters=step_name_and_status_not_in,
)

filters = AssetFilters(
project_id=ProjectId(project_id),
Expand Down Expand Up @@ -809,6 +865,8 @@ def count_assets(
created_at_lte=created_at_lte,
issue_status=issue_status,
issue_type=issue_type,
step_id_and_status_in=step_id_and_status_in,
step_id_and_status_not_in=step_id_and_status_not_in,
step_id_in=step_id_in,
step_id_not_in=step_id_not_in,
step_status_in=step_status_in,
Expand Down
23 changes: 22 additions & 1 deletion src/kili/presentation/client/helpers/filter_conversion.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Module for common argument validators across client methods."""


from kili.domain.asset.asset import StatusInStep
from kili.domain.project import ProjectStep


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

return [step["id"] for step in matching_steps]


def extract_step_id_and_status_filters_from_project_steps(
project_steps: list[ProjectStep],
step_name_and_status_filters: list[tuple[str, StatusInStep]],
) -> list[tuple[str, StatusInStep]]:
"""Convert a list of (step_name, step_status) tuples to (step_id, step_status) tuples."""
step_name_to_id = {step["name"]: step["id"] for step in project_steps}

unmatched_names = [
step_name
for step_name, _ in step_name_and_status_filters
if step_name not in step_name_to_id
]
if unmatched_names:
raise ValueError(f"The following step names do not match any steps: {unmatched_names}")

return [
(step_name_to_id[step_name], step_status)
for step_name, step_status in step_name_and_status_filters
]
Loading