Skip to content
Open
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
2 changes: 1 addition & 1 deletion recipes/frame_dicom_data.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, let's import the volumes using `appendManyToDataset` (see [link](https://staging.cloud.kili-technology.com/docs/python-graphql-api/python-api/#append_many_to_dataset)). The key argument is `json_content_array`, which is a list of list of strings. Each element is the list of urls or paths pointing to images of the volume considered."
"Finally, let's import the volumes using `appendManyToDataset`. The key argument is `json_content_array`, which is a list of list of strings. Each element is the list of urls or paths pointing to images of the volume considered."
]
},
{
Expand Down
72 changes: 71 additions & 1 deletion src/kili/adapters/kili_api_gateway/project_workflow/mappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

from kili.domain.project import WorkflowStepCreate, WorkflowStepUpdate

from .types import ProjectWorkflowDataKiliAPIGatewayInput
from .types import (
AddReviewStepInput,
DeleteStepInput,
ProjectWorkflowDataKiliAPIGatewayInput,
RenameStepInput,
UpdateLabelingStepPropertiesInput,
UpdateReviewStepPropertiesInput,
)


def project_input_mapper(data: ProjectWorkflowDataKiliAPIGatewayInput) -> dict:
Expand Down Expand Up @@ -46,3 +53,66 @@ def update_step_mapper(
for gql_key, py_key in mapping.items()
if py_key in data and (data[py_key] is not None or gql_key in null_fields)
}


def add_review_step_input_mapper(data: AddReviewStepInput) -> dict:
"""Build the GraphQL AddReviewStepInput variable."""
result: dict = {
"projectId": data.project_id,
"name": data.name,
"assignees": data.assignees,
}
if data.step_coverage is not None:
result["stepCoverage"] = data.step_coverage
if data.use_honeypot is not None:
result["useHoneypot"] = data.use_honeypot
if data.send_back_to_step is not None:
result["sendBackStepId"] = data.send_back_to_step
return result


def update_labeling_step_properties_input_mapper(data: UpdateLabelingStepPropertiesInput) -> dict:
"""Build the GraphQL UpdateLabelingStepPropertiesInput variable."""
result: dict = {
"projectId": data.project_id,
"stepId": data.step_id,
}
if data.consensus_coverage is not None:
result["consensusCoverage"] = data.consensus_coverage
if data.number_of_expected_labels_for_consensus is not None:
result["numberOfExpectedLabelsForConsensus"] = data.number_of_expected_labels_for_consensus
if data.use_honeypot is not None:
result["useHoneypot"] = data.use_honeypot
return result


def update_review_step_properties_input_mapper(data: UpdateReviewStepPropertiesInput) -> dict:
"""Build the GraphQL UpdateReviewStepPropertiesInput variable."""
result: dict = {
"projectId": data.project_id,
"stepId": data.step_id,
}
if data.step_coverage is not None:
result["stepCoverage"] = data.step_coverage
if data.send_back_to_step is not None:
result["sendBackStepId"] = data.send_back_to_step
if data.use_honeypot is not None:
result["useHoneypot"] = data.use_honeypot
return result


def delete_step_input_mapper(data: DeleteStepInput) -> dict:
"""Build the GraphQL DeleteStepInput variable."""
return {
"projectId": data.project_id,
"stepId": data.step_id,
}


def rename_step_input_mapper(data: RenameStepInput) -> dict:
"""Build the GraphQL RenameStepInput variable."""
return {
"projectId": data.project_id,
"stepId": data.step_id,
"newName": data.new_name,
}
55 changes: 55 additions & 0 deletions src/kili/adapters/kili_api_gateway/project_workflow/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,58 @@ def get_steps_query(fragment: str) -> str:
}}
}}
"""


def get_add_review_step_mutation() -> str:
"""Return the GraphQL addReviewStep mutation."""
return """
mutation addReviewStep($input: AddReviewStepInput!) {
data: addReviewStep(input: $input) {
steps{id,name}
}
}
"""


def get_update_labeling_step_properties_mutation() -> str:
"""Return the GraphQL updateLabelingStepProperties mutation."""
return """
mutation updateLabelingStepProperties($input: UpdateLabelingStepPropertiesInput!) {
data: updateLabelingStepProperties(input: $input) {
steps{id,name}
}
}
"""


def get_update_review_step_properties_mutation() -> str:
"""Return the GraphQL updateReviewStepProperties mutation."""
return """
mutation updateReviewStepProperties($input: UpdateReviewStepPropertiesInput!) {
data: updateReviewStepProperties(input: $input) {
steps{id,name}
}
}
"""


def get_delete_step_mutation() -> str:
"""Return the GraphQL deleteStep mutation."""
return """
mutation deleteStep($input: DeleteStepInput!) {
data: deleteStep(input: $input) {
steps{id,name}
}
}
"""


def get_rename_step_mutation() -> str:
"""Return the GraphQL renameStep mutation."""
return """
mutation renameStep($input: RenameStepInput!) {
data: renameStep(input: $input) {
steps{id,name}
}
}
"""
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,31 @@
from kili.domain.types import ListOrTuple
from kili.exceptions import NotFound

from .mappers import project_input_mapper
from .mappers import (
add_review_step_input_mapper,
delete_step_input_mapper,
project_input_mapper,
rename_step_input_mapper,
update_labeling_step_properties_input_mapper,
update_review_step_properties_input_mapper,
)
from .operations import (
get_add_review_step_mutation,
get_delete_step_mutation,
get_rename_step_mutation,
get_steps_query,
get_update_labeling_step_properties_mutation,
get_update_project_workflow_mutation,
get_update_review_step_properties_mutation,
)
from .types import (
AddReviewStepInput,
DeleteStepInput,
ProjectWorkflowDataKiliAPIGatewayInput,
RenameStepInput,
UpdateLabelingStepPropertiesInput,
UpdateReviewStepPropertiesInput,
)
from .types import ProjectWorkflowDataKiliAPIGatewayInput


class ProjectWorkflowOperationMixin(BaseOperationMixin):
Expand Down Expand Up @@ -177,3 +196,65 @@ def remove_reviewers_from_step(
)

return removed_emails

def add_review_step(self, data: AddReviewStepInput) -> dict:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got two strange behaviors during my tests :

  • add a review step "Test"
  • remove this step in the UI
  • add a review step "Test" -> got an error (step already exists), but step is here again

-> we should not raise an error when reactivating a step IMO

  • add a review step "Test"
  • remove this step in the UI
  • add a review step "Test 2" -> Test is present again

"""Add a review step to a project workflow."""
existing_members = ProjectUserQuery(self.graphql_client, self.http_client)(
where=ProjectUserWhere(project_id=data.project_id, status="ACTIVATED", deleted=False),
fields=["role", "user.email", "user.id", "activated"],
options=QueryOptions(None),
)
members_by_email = {m["user"]["email"]: m for m in (existing_members or [])}
assignees_to_add = []
assignees_added = []
assignees_not_added = []
for email in data.assignees:
member = members_by_email.get(email)
if member and member.get("role") != "LABELER":
user_id = member["user"]["id"]
assignees_to_add.append(user_id)
assignees_added.append(email)
else:
assignees_not_added.append(email)
if assignees_not_added:
warnings.warn(
"These emails were not added (not found or can not review): "
+ ", ".join(assignees_not_added)
)
data.assignees = assignees_to_add
Comment on lines +202 to +224
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it would be cleaner to use existing method (like list_activated_project_users) instead of doing the graphQL query from scratch

variables = {"input": add_review_step_input_mapper(data)}
mutation = get_add_review_step_mutation()
result = self.graphql_client.execute(mutation, variables)
steps = result.get("data", {}).get("steps", [])
return next(step for step in steps if step.get("name") == data.name)

def update_labeling_step_properties(self, data: UpdateLabelingStepPropertiesInput) -> dict:
"""Update properties of a labeling step."""
variables = {"input": update_labeling_step_properties_input_mapper(data)}
mutation = get_update_labeling_step_properties_mutation()
result = self.graphql_client.execute(mutation, variables)
steps = result.get("data", {}).get("steps", [])
return next(step for step in steps if step.get("id") == data.step_id)

def update_review_step_properties(self, data: UpdateReviewStepPropertiesInput) -> dict:
"""Update properties of a review step."""
variables = {"input": update_review_step_properties_input_mapper(data)}
mutation = get_update_review_step_properties_mutation()
result = self.graphql_client.execute(mutation, variables)
steps = result.get("data", {}).get("steps", [])
return next(step for step in steps if step.get("id") == data.step_id)

def delete_step(self, data: DeleteStepInput) -> dict:
"""Delete a step from a project workflow."""
variables = {"input": delete_step_input_mapper(data)}
mutation = get_delete_step_mutation()
result = self.graphql_client.execute(mutation, variables)
return result["data"]

def rename_step(self, data: RenameStepInput) -> dict:
"""Rename a step in a project workflow."""
variables = {"input": rename_step_input_mapper(data)}
mutation = get_rename_step_mutation()
result = self.graphql_client.execute(mutation, variables)
steps = result.get("data", {}).get("steps", [])
return next(step for step in steps if step.get("id") == data.step_id)
53 changes: 53 additions & 0 deletions src/kili/adapters/kili_api_gateway/project_workflow/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,56 @@ class ProjectWorkflowDataKiliAPIGatewayInput:
update_steps: Optional[list[WorkflowStepUpdate]]
delete_steps: Optional[list[str]]
null_fields: frozenset[str] = frozenset()


@dataclass
class AddReviewStepInput:
"""Input data for adding a review step to a project workflow."""

project_id: str
name: str
assignees: list[str]
step_coverage: int | None = None
use_honeypot: bool | None = None
send_back_to_step: str | None = None


@dataclass
class UpdateLabelingStepPropertiesInput:
"""Input data for updating labeling step properties."""

project_id: str
step_id: str
assignees: list[str] | None = None
consensus_coverage: int | None = None
number_of_expected_labels_for_consensus: int | None = None
use_honeypot: bool | None = None


@dataclass
class UpdateReviewStepPropertiesInput:
"""Input data for updating review step properties."""

project_id: str
step_id: str
assignees: list[str] | None = None
step_coverage: int | None = None
send_back_to_step: str | None = None
use_honeypot: bool | None = None


@dataclass
class DeleteStepInput:
"""Input data for deleting a step from a project workflow."""

project_id: str
step_id: str


@dataclass
class RenameStepInput:
"""Input data for renaming a step in a project workflow."""

project_id: str
step_id: str
new_name: str
Loading
Loading