Skip to content

Implemented Task Tags Endpoints #66

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Feb 25, 2023
54 changes: 54 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,60 @@ __ https://docs.scale.com/reference/set-metadata
new_metadata = {'myKey': 'myValue'}
task.set_metadata(new_metadata)

Set A Task's Tags
^^^^^^^^^^^^^^^^^^^^^^^^^

Set a given task's ``tags``. This will replace all existing tags on a task. Check out `Scale's API documentation`__ for more information.

__ https://docs.scale.com/reference/setting-tags

.. code-block :: python

# set a list of tags on a task by specifying task id
new_tags = ["tag1", "tag2", "tag3"]
task = client.set_task_tags('30553edd0b6a93f8f05f0fee', new_tags)

# set a list of tags on a task object
task = client.get_task('30553edd0b6a93f8f05f0fee')
new_tags = ["tag1", "tag2", "tag3"]
task.set_tags(new_tags)

Add Tags to A Task
^^^^^^^^^^^^^^^^^^^^^^^^^

Add ``tags`` to a given task. Check out `Scale's API documentation`__ for more information.

__ https://docs.scale.com/reference/adding-tags

.. code-block :: python

# add a list of tags on a task by specifying task id
tags_to_add = ["tag4", "tag5"]
task = client.add_task_tags('30553edd0b6a93f8f05f0fee', tags_to_add)

# add a list of tags on a task object
task = client.get_task('30553edd0b6a93f8f05f0fee')
tags_to_add = ["tag4", "tag5"]
task.add_tags(tags_to_add)

Delete Tags from A Task
^^^^^^^^^^^^^^^^^^^^^^^^^

Delete ``tags`` from a given task. Check out `Scale's API documentation`__ for more information.

__ https://docs.scale.com/reference/deleting-tags

.. code-block :: python

# delete a list of tags on a task by specifying task id
tags_to_delete = ["tag1", "tag2"]
task = client.delete_task_tags('30553edd0b6a93f8f05f0fee', tags_to_delete)

# delete a list of tags on a task object
task = client.get_task('30553edd0b6a93f8f05f0fee')
tags_to_delete = ["tag1", "tag2"]
task.delete_tags(tags_to_delete)

Batches
_______

Expand Down
103 changes: 90 additions & 13 deletions scaleapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ class ScaleClient:

def __init__(self, api_key, source=None, api_instance_url=None):
self.api = Api(
api_key, user_agent_extension=source, api_instance_url=api_instance_url
api_key,
user_agent_extension=source,
api_instance_url=api_instance_url,
)

def get_task(self, task_id: str) -> Task:
Expand Down Expand Up @@ -148,6 +150,49 @@ def set_task_metadata(self, task_id: str, metadata: Dict) -> Task:
endpoint = f"task/{task_id}/setMetadata"
return Task(self.api.post_request(endpoint, body=metadata), self)

def set_task_tags(self, task_id: str, tags: List[str]) -> Task:
"""Sets completely new list of tags to a task and returns the
associated task.
Args:
task_id (str):
Task id
tags (List[str]):
List of new tags to set
Returns:
Task
"""
endpoint = f"task/{task_id}/tags"
return Task(self.api.post_request(endpoint, body=tags), self)

def add_task_tags(self, task_id: str, tags: List[str]) -> Task:
"""Adds a list of tags to a task and returns the
associated task.
Args:
task_id (str):
Task id
tags (List[str]):
List of tags to add.
Already present tags will be ignored.
Returns:
Task
"""
endpoint = f"task/{task_id}/tags"
return Task(self.api.put_request(endpoint, body=tags), self)

def delete_task_tags(self, task_id: str, tags: List[str]) -> Task:
"""Deletes a list of tags from a task and returns the
associated task.
Args:
task_id (str):
Task id
tags (List[str]):
List of tags to delete. Nonpresent tags will be ignored.
Returns:
Task
"""
endpoint = f"task/{task_id}/tags"
return Task(self.api.delete_request(endpoint, body=tags), self)

def tasks(self, **kwargs) -> Tasklist:
"""Returns a list of your tasks.
Returns up to 100 at a time, to get more, use the
Expand Down Expand Up @@ -949,7 +994,11 @@ def list_teammates(self) -> List[Teammate]:
teammate_list = self.api.get_request(endpoint)
return [Teammate(teammate, self) for teammate in teammate_list]

def invite_teammates(self, emails: List[str], role: TeammateRole) -> List[Teammate]:
def invite_teammates(
self,
emails: List[str],
role: TeammateRole,
) -> List[Teammate]:
"""Invites a list of emails to your team.

Args:
Expand Down Expand Up @@ -989,7 +1038,9 @@ def update_teammates_role(
teammate_list = self.api.post_request(endpoint, payload)
return [Teammate(teammate, self) for teammate in teammate_list]

def list_studio_assignments(self) -> Dict[str, StudioLabelerAssignment]:
def list_studio_assignments(
self,
) -> Dict[str, StudioLabelerAssignment]:
"""Returns a dictionary where the keys are user emails and the
values are projects the user is assigned to.

Expand All @@ -999,8 +1050,12 @@ def list_studio_assignments(self) -> Dict[str, StudioLabelerAssignment]:
endpoint = "studio/assignments"
raw_assignments = self.api.get_request(endpoint)
assignments = {}
for (email, assigned_projects) in raw_assignments.items():
assignments[email] = StudioLabelerAssignment(assigned_projects, email, self)
for email, assigned_projects in raw_assignments.items():
assignments[email] = StudioLabelerAssignment(
assigned_projects,
email,
self,
)
return assignments

def add_studio_assignments(
Expand All @@ -1023,8 +1078,12 @@ def add_studio_assignments(
}
raw_assignments = self.api.post_request(endpoint, payload)
assignments = {}
for (email, assigned_projects) in raw_assignments.items():
assignments[email] = StudioLabelerAssignment(assigned_projects, email, self)
for email, assigned_projects in raw_assignments.items():
assignments[email] = StudioLabelerAssignment(
assigned_projects,
email,
self,
)
return assignments

def remove_studio_assignments(
Expand All @@ -1047,8 +1106,12 @@ def remove_studio_assignments(
}
raw_assignments = self.api.post_request(endpoint, payload)
assignments = {}
for (email, assigned_projects) in raw_assignments.items():
assignments[email] = StudioLabelerAssignment(assigned_projects, email, self)
for email, assigned_projects in raw_assignments.items():
assignments[email] = StudioLabelerAssignment(
assigned_projects,
email,
self,
)
return assignments

def list_project_groups(self, project: str) -> List[StudioProjectGroup]:
Expand Down Expand Up @@ -1083,7 +1146,10 @@ def create_project_group(
"""
endpoint = f"studio/projects/{Api.quote_string(project)}/groups"
payload = {"emails": emails, "name": project_group}
return StudioProjectGroup(self.api.post_request(endpoint, payload), self)
return StudioProjectGroup(
self.api.post_request(endpoint, payload),
self,
)

def update_project_group(
self,
Expand All @@ -1110,8 +1176,14 @@ def update_project_group(
f"studio/projects/{Api.quote_string(project)}"
f"/groups/{Api.quote_string(project_group)}"
)
payload = {"add_emails": add_emails, "remove_emails": remove_emails}
return StudioProjectGroup(self.api.put_request(endpoint, payload), self)
payload = {
"add_emails": add_emails,
"remove_emails": remove_emails,
}
return StudioProjectGroup(
self.api.put_request(endpoint, payload),
self,
)

def list_studio_batches(self) -> List[StudioBatch]:
"""Returns a list with all pending studio batches,
Expand Down Expand Up @@ -1152,7 +1224,12 @@ def set_studio_batches_priorities(
Returns:
List[StudioBatch]
"""
batches_names = list(map(lambda batch_name: {"name": batch_name}, batch_names))
batches_names = list(
map(
lambda batch_name: {"name": batch_name},
batch_names,
)
)
endpoint = "studio/batches/set_priorities"
payload = {"batches": batches_names}
batches = self.api.post_request(endpoint, payload)
Expand Down
2 changes: 1 addition & 1 deletion scaleapi/_version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version__ = "2.12.0"
__version__ = "2.13.0"
__package_name__ = "scaleapi"
11 changes: 7 additions & 4 deletions scaleapi/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ def _http_request(
files=None,
data=None,
) -> Response:

https = requests.Session()
retry_strategy = Retry(
total=HTTP_TOTAL_RETRIES,
Expand Down Expand Up @@ -80,7 +79,6 @@ def _http_request(

@staticmethod
def _raise_on_respose(res: Response):

try:
message = res.json().get("error", res.text)
except ValueError:
Expand Down Expand Up @@ -157,10 +155,15 @@ def post_request(self, endpoint, body=None, files=None, data=None):
data=data,
)

def delete_request(self, endpoint, params=None):
def delete_request(self, endpoint, params=None, body=None):
"""Generic DELETE Request Wrapper"""
return self._api_request(
"DELETE", endpoint, headers=self._headers, auth=self._auth, params=params
"DELETE",
endpoint,
headers=self._headers,
auth=self._auth,
params=params,
body=body,
)

def put_request(self, endpoint, body=None, params=None):
Expand Down
13 changes: 13 additions & 0 deletions scaleapi/tasks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from enum import Enum
from typing import List


class TaskType(Enum):
Expand Down Expand Up @@ -103,3 +104,15 @@ def clear_unique_id(self):
def set_metadata(self, metadata: dict):
"""Sets the metadata of a task"""
self._client.set_task_metadata(self.id, metadata)

def set_tags(self, tags: List[str]):
"""Sets tags of a task"""
self._client.set_task_tags(self.id, tags)

def add_tags(self, tags: List[str]):
"""Adds tags for a task"""
self._client.add_task_tags(self.id, tags)

def delete_tags(self, tags: List[str]):
"""Sets tags for a task"""
self._client.delete_task_tags(self.id, tags)
45 changes: 41 additions & 4 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ def test_invalidkey_fail():


def make_a_task(unique_id: str = None, batch: str = None):

args = {
"callback_url": "http://www.example.com/callback",
"instruction": "Draw a box around each baby cow and big cow.",
Expand Down Expand Up @@ -112,6 +111,37 @@ def test_task_set_metadata():
assert task.metadata == new_metadata


def test_set_task_tags():
unique_id = str(uuid.uuid4())
task = make_a_task(unique_id)
assert not hasattr(task, "tags")
new_tags = ["tag1", "tag2", "tag3"]
task.set_tags(new_tags)
task.refresh()
assert task.tags == new_tags


def test_add_task_tags():
unique_id = str(uuid.uuid4())
task = make_a_task(unique_id)
assert not hasattr(task, "tags")
new_tags = ["tag1", "tag2", "tag3"]
task.add_tags(new_tags)
task.refresh()
assert task.tags == new_tags


def test_delete_task_tags():
unique_id = str(uuid.uuid4())
task = make_a_task(unique_id)
assert not hasattr(task, "tags")
new_tags = ["tag1", "tag2", "tag3"]
task.add_tags(new_tags)
task.delete_tags(["tag1", "tag2"])
task.refresh()
assert task.tags == ["tag3"]


def test_categorize_ok():
client.create_task(
TaskType.Categorization,
Expand All @@ -137,11 +167,15 @@ def test_transcription_ok():
client.create_task(
TaskType.Transcription,
callback_url="http://www.example.com/callback",
instruction="Transcribe the given fields. Then for each news item on the page, "
instruction="Transcribe the given fields. "
"Then for each news item on the page, "
"transcribe the information for the row.",
attachment_type="website",
attachment="http://www.google.com/",
fields={"title": "Title of Webpage", "top_result": "Title of the top result"},
fields={
"title": "Title of Webpage",
"top_result": "Title of the top result",
},
repeatable_fields={
"username": "Username of submitter",
"comment_count": "Number of comments",
Expand Down Expand Up @@ -367,7 +401,10 @@ def test_get_tasks():
for _ in range(3):
tasks.append(make_a_task(batch=batch.name))
task_ids = {task.id for task in tasks}
for task in client.get_tasks(project_name=TEST_PROJECT_NAME, batch_name=batch.name):
for task in client.get_tasks(
project_name=TEST_PROJECT_NAME,
batch_name=batch.name,
):
assert task.id in task_ids


Expand Down