Skip to content
Draft
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
24 changes: 24 additions & 0 deletions src/kili/adapters/kili_api_gateway/asset/operations_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
)
from kili.adapters.kili_api_gateway.label.common import get_annotation_fragment
from kili.adapters.kili_api_gateway.project.common import get_project
from kili.core.graphql.operations.asset.mutations import GQL_SET_ASSET_CONSENSUS
from kili.domain.asset import AssetFilters
from kili.domain.types import ListOrTuple

Expand Down Expand Up @@ -166,3 +167,26 @@ def count_assets_annotations(self, filters: AssetFilters) -> int:
count_result = self.graphql_client.execute(GQL_COUNT_ASSET_ANNOTATIONS, payload)
count: int = count_result["data"]
return count

def update_asset_consensus(
self,
project_id: str,
is_consensus: bool,
asset_id: str | None = None,
external_id: str | None = None,
) -> bool:
"""Update consensus on an asset."""
if asset_id is None and external_id is None:
raise ValueError("At least one of asset_id or external_id must be provided")

payload = {
"projectId": project_id,
"isConsensus": is_consensus,
}
if asset_id is not None:
payload["assetId"] = asset_id
if external_id is not None:
payload["externalId"] = external_id

result = self.graphql_client.execute(GQL_SET_ASSET_CONSENSUS, payload)
return result["data"]
16 changes: 16 additions & 0 deletions src/kili/core/graphql/operations/asset/mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,19 @@
}
}
"""

GQL_SET_ASSET_CONSENSUS = """
mutation setAssetConsensus(
$assetId: ID,
$externalId: String,
$projectId: ID!,
$isConsensus: Boolean!
) {
data: setAssetConsensus(
assetId: $assetId,
externalId: $externalId,
projectId: $projectId,
isConsensus: $isConsensus
)
}
"""
78 changes: 77 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
# pylint: disable=too-many-lines,too-many-public-methods

import warnings
from collections.abc import Generator
Expand Down Expand Up @@ -264,6 +264,7 @@ class AssetsNamespace(DomainNamespace):
- move_to_next_step(): Move assets to the next workflow step
- assign(): Assign assets to labelers
- update_priority(): Update asset priorities
- update_consensus(): Activate or deactivate consensus on an asset

Examples:
>>> kili = Kili()
Expand Down Expand Up @@ -2173,3 +2174,78 @@ def update_priority(
priorities=priorities if priorities is not None else [],
**kwargs,
)

@overload
def update_consensus(
self,
*,
asset_id: str,
project_id: str,
is_consensus: bool,
) -> bool:
...

@overload
def update_consensus(
self,
*,
external_id: str,
project_id: str,
is_consensus: bool,
) -> bool:
...

@typechecked
def update_consensus(
self,
*,
project_id: str,
is_consensus: bool,
asset_id: Optional[str] = None,
external_id: Optional[str] = None,
) -> bool:
"""Activate or deactivate consensus on an asset.

Args:
project_id: The project ID.
is_consensus: Whether to activate (True) or deactivate (False) consensus on the asset.
asset_id: The internal asset ID to modify. Either asset_id or external_id must be provided.
external_id: The external ID of the asset to modify. Either asset_id or external_id must be provided.

Returns:
The consensus value that was set (True if consensus was activated, False if deactivated).

Raises:
ValueError: If neither asset_id nor external_id is provided.

Examples:
>>> # Activate consensus on an asset using asset_id
>>> result = kili.assets.update_consensus(
... project_id="my_project",
... is_consensus=True,
... asset_id="ckg22d81r0jrg0885unmuswj8"
... )
>>> # result is True

>>> # Activate consensus on an asset using external_id
>>> result = kili.assets.update_consensus(
... project_id="my_project",
... is_consensus=True,
... external_id="my_asset_001"
... )
>>> # result is True

>>> # Deactivate consensus on an asset
>>> result = kili.assets.update_consensus(
... project_id="my_project",
... is_consensus=False,
... asset_id="ckg22d81r0jrg0885unmuswj8"
... )
>>> # result is False
"""
return self._client.update_asset_consensus(
project_id=project_id,
is_consensus=is_consensus,
asset_id=asset_id,
external_id=external_id,
)
1 change: 1 addition & 0 deletions src/kili/domain_api/exports.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Tags domain namespace for the Kili Python SDK."""
# pylint: disable=too-many-public-methods

from typing import TYPE_CHECKING, Any, Optional, TypedDict, Union

Expand Down
1 change: 1 addition & 0 deletions src/kili/domain_api/issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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
# pylint: disable=too-many-lines,too-many-public-methods
"""Labels domain namespace for the Kili Python SDK.

This module provides a comprehensive interface for label-related operations
Expand Down
1 change: 1 addition & 0 deletions src/kili/domain_api/organizations.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""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: 1 addition & 0 deletions src/kili/domain_api/plugins.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""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: 1 addition & 0 deletions src/kili/domain_api/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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: 1 addition & 0 deletions src/kili/domain_api/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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
# pylint: disable=too-many-lines,too-many-public-methods

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

from typing import List, Literal, Optional

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

import re
from collections.abc import Generator
Expand Down
59 changes: 59 additions & 0 deletions src/kili/presentation/client/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,3 +763,62 @@ def count_assets(
)
asset_use_cases = AssetUseCases(self.kili_api_gateway)
return asset_use_cases.count_assets(filters)

@typechecked
def update_asset_consensus(
self,
project_id: str,
is_consensus: bool,
asset_id: Optional[str] = None,
external_id: Optional[str] = None,
) -> bool:
"""Activate or deactivate consensus on an asset.

Args:
project_id: The project ID.
is_consensus: Whether to activate (True) or deactivate (False) consensus on the asset.
asset_id: The internal asset ID to modify. Either asset_id or external_id must be provided.
external_id: The external ID of the asset to modify. Either asset_id or external_id must be provided.

Returns:
The consensus value that was set (True if consensus was activated, False if deactivated).

Raises:
ValueError: If neither asset_id nor external_id is provided.

Examples:
>>> # Activate consensus on an asset using asset_id
>>> result = kili.update_asset_consensus(
... project_id="my_project",
... is_consensus=True,
... asset_id="ckg22d81r0jrg0885unmuswj8"
... )
>>> # result is True

>>> # Activate consensus on an asset using external_id
>>> result = kili.update_asset_consensus(
... project_id="my_project",
... is_consensus=True,
... external_id="my_asset_001"
... )
>>> # result is True

>>> # Deactivate consensus on an asset
>>> result = kili.update_asset_consensus(
... project_id="my_project",
... is_consensus=False,
... asset_id="ckg22d81r0jrg0885unmuswj8"
... )
>>> # result is False
"""
if asset_id is None and external_id is None:
raise ValueError(
"At least one of asset_id or external_id must be provided to update_asset_consensus"
)

return self.kili_api_gateway.update_asset_consensus(
project_id=project_id,
is_consensus=is_consensus,
asset_id=asset_id,
external_id=external_id,
)
55 changes: 55 additions & 0 deletions tests/unit/domain_api/test_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def mock_client(self):
client.change_asset_external_ids = MagicMock()
client.add_metadata = MagicMock()
client.set_metadata = MagicMock()
client.update_asset_consensus = MagicMock()
return client

@pytest.fixture()
Expand Down Expand Up @@ -232,6 +233,60 @@ def test_delete_assets(self, assets_namespace, mock_client):
asset_ids=["asset1", "asset2"], external_ids=None, project_id=""
)

def test_update_consensus_with_asset_id(self, assets_namespace, mock_client):
"""Test update_consensus method with asset_id."""
mock_client.update_asset_consensus.return_value = True

result = assets_namespace.update_consensus(
project_id="project_123",
asset_id="asset1",
is_consensus=True,
)

assert result is True
mock_client.update_asset_consensus.assert_called_once_with(
project_id="project_123",
is_consensus=True,
asset_id="asset1",
external_id=None,
)

def test_update_consensus_with_external_id(self, assets_namespace, mock_client):
"""Test update_consensus method with external_id."""
mock_client.update_asset_consensus.return_value = True

result = assets_namespace.update_consensus(
project_id="project_123",
external_id="ext_asset1",
is_consensus=True,
)

assert result is True
mock_client.update_asset_consensus.assert_called_once_with(
project_id="project_123",
is_consensus=True,
asset_id=None,
external_id="ext_asset1",
)

def test_update_consensus_deactivate(self, assets_namespace, mock_client):
"""Test update_consensus method to deactivate consensus."""
mock_client.update_asset_consensus.return_value = False

result = assets_namespace.update_consensus(
project_id="project_123",
asset_id="asset1",
is_consensus=False,
)

assert result is False
mock_client.update_asset_consensus.assert_called_once_with(
project_id="project_123",
is_consensus=False,
asset_id="asset1",
external_id=None,
)


class TestAssetsNamespaceContractCompatibility:
"""Contract tests to ensure domain API matches legacy API behavior."""
Expand Down