Skip to content
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