Skip to content

Commit 4105182

Browse files
Implemented Task Tags Endpoints (#66)
* task tags * add formating * add formating flake8 * add formating flake8 black combined * line length 72 * isort * fix readme * Update README.rst --------- Co-authored-by: Fatih Kurtoglu <fatih.kurtoglu@scale.com>
1 parent 04def7c commit 4105182

File tree

6 files changed

+206
-22
lines changed

6 files changed

+206
-22
lines changed

README.rst

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,60 @@ __ https://docs.scale.com/reference/set-metadata
310310
new_metadata = {'myKey': 'myValue'}
311311
task.set_metadata(new_metadata)
312312
313+
Set A Task's Tags
314+
^^^^^^^^^^^^^^^^^^^^^^^^^
315+
316+
Set a given task's ``tags``. This will replace all existing tags on a task. Check out `Scale's API documentation`__ for more information.
317+
318+
__ https://docs.scale.com/reference/setting-tags
319+
320+
.. code-block :: python
321+
322+
# set a list of tags on a task by specifying task id
323+
new_tags = ["tag1", "tag2", "tag3"]
324+
task = client.set_task_tags('30553edd0b6a93f8f05f0fee', new_tags)
325+
326+
# set a list of tags on a task object
327+
task = client.get_task('30553edd0b6a93f8f05f0fee')
328+
new_tags = ["tag1", "tag2", "tag3"]
329+
task.set_tags(new_tags)
330+
331+
Add Tags to A Task
332+
^^^^^^^^^^^^^^^^^^^^^^^^^
333+
334+
Add ``tags`` to a given task. Check out `Scale's API documentation`__ for more information.
335+
336+
__ https://docs.scale.com/reference/adding-tags
337+
338+
.. code-block :: python
339+
340+
# add a list of tags on a task by specifying task id
341+
tags_to_add = ["tag4", "tag5"]
342+
task = client.add_task_tags('30553edd0b6a93f8f05f0fee', tags_to_add)
343+
344+
# add a list of tags on a task object
345+
task = client.get_task('30553edd0b6a93f8f05f0fee')
346+
tags_to_add = ["tag4", "tag5"]
347+
task.add_tags(tags_to_add)
348+
349+
Delete Tags from A Task
350+
^^^^^^^^^^^^^^^^^^^^^^^^^
351+
352+
Delete ``tags`` from a given task. Check out `Scale's API documentation`__ for more information.
353+
354+
__ https://docs.scale.com/reference/deleting-tags
355+
356+
.. code-block :: python
357+
358+
# delete a list of tags on a task by specifying task id
359+
tags_to_delete = ["tag1", "tag2"]
360+
task = client.delete_task_tags('30553edd0b6a93f8f05f0fee', tags_to_delete)
361+
362+
# delete a list of tags on a task object
363+
task = client.get_task('30553edd0b6a93f8f05f0fee')
364+
tags_to_delete = ["tag1", "tag2"]
365+
task.delete_tags(tags_to_delete)
366+
313367
Batches
314368
_______
315369

scaleapi/__init__.py

Lines changed: 90 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ class ScaleClient:
5050

5151
def __init__(self, api_key, source=None, api_instance_url=None):
5252
self.api = Api(
53-
api_key, user_agent_extension=source, api_instance_url=api_instance_url
53+
api_key,
54+
user_agent_extension=source,
55+
api_instance_url=api_instance_url,
5456
)
5557

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

153+
def set_task_tags(self, task_id: str, tags: List[str]) -> Task:
154+
"""Sets completely new list of tags to a task and returns the
155+
associated task.
156+
Args:
157+
task_id (str):
158+
Task id
159+
tags (List[str]):
160+
List of new tags to set
161+
Returns:
162+
Task
163+
"""
164+
endpoint = f"task/{task_id}/tags"
165+
return Task(self.api.post_request(endpoint, body=tags), self)
166+
167+
def add_task_tags(self, task_id: str, tags: List[str]) -> Task:
168+
"""Adds a list of tags to a task and returns the
169+
associated task.
170+
Args:
171+
task_id (str):
172+
Task id
173+
tags (List[str]):
174+
List of tags to add.
175+
Already present tags will be ignored.
176+
Returns:
177+
Task
178+
"""
179+
endpoint = f"task/{task_id}/tags"
180+
return Task(self.api.put_request(endpoint, body=tags), self)
181+
182+
def delete_task_tags(self, task_id: str, tags: List[str]) -> Task:
183+
"""Deletes a list of tags from a task and returns the
184+
associated task.
185+
Args:
186+
task_id (str):
187+
Task id
188+
tags (List[str]):
189+
List of tags to delete. Nonpresent tags will be ignored.
190+
Returns:
191+
Task
192+
"""
193+
endpoint = f"task/{task_id}/tags"
194+
return Task(self.api.delete_request(endpoint, body=tags), self)
195+
151196
def tasks(self, **kwargs) -> Tasklist:
152197
"""Returns a list of your tasks.
153198
Returns up to 100 at a time, to get more, use the
@@ -949,7 +994,11 @@ def list_teammates(self) -> List[Teammate]:
949994
teammate_list = self.api.get_request(endpoint)
950995
return [Teammate(teammate, self) for teammate in teammate_list]
951996

952-
def invite_teammates(self, emails: List[str], role: TeammateRole) -> List[Teammate]:
997+
def invite_teammates(
998+
self,
999+
emails: List[str],
1000+
role: TeammateRole,
1001+
) -> List[Teammate]:
9531002
"""Invites a list of emails to your team.
9541003
9551004
Args:
@@ -989,7 +1038,9 @@ def update_teammates_role(
9891038
teammate_list = self.api.post_request(endpoint, payload)
9901039
return [Teammate(teammate, self) for teammate in teammate_list]
9911040

992-
def list_studio_assignments(self) -> Dict[str, StudioLabelerAssignment]:
1041+
def list_studio_assignments(
1042+
self,
1043+
) -> Dict[str, StudioLabelerAssignment]:
9931044
"""Returns a dictionary where the keys are user emails and the
9941045
values are projects the user is assigned to.
9951046
@@ -999,8 +1050,12 @@ def list_studio_assignments(self) -> Dict[str, StudioLabelerAssignment]:
9991050
endpoint = "studio/assignments"
10001051
raw_assignments = self.api.get_request(endpoint)
10011052
assignments = {}
1002-
for (email, assigned_projects) in raw_assignments.items():
1003-
assignments[email] = StudioLabelerAssignment(assigned_projects, email, self)
1053+
for email, assigned_projects in raw_assignments.items():
1054+
assignments[email] = StudioLabelerAssignment(
1055+
assigned_projects,
1056+
email,
1057+
self,
1058+
)
10041059
return assignments
10051060

10061061
def add_studio_assignments(
@@ -1023,8 +1078,12 @@ def add_studio_assignments(
10231078
}
10241079
raw_assignments = self.api.post_request(endpoint, payload)
10251080
assignments = {}
1026-
for (email, assigned_projects) in raw_assignments.items():
1027-
assignments[email] = StudioLabelerAssignment(assigned_projects, email, self)
1081+
for email, assigned_projects in raw_assignments.items():
1082+
assignments[email] = StudioLabelerAssignment(
1083+
assigned_projects,
1084+
email,
1085+
self,
1086+
)
10281087
return assignments
10291088

10301089
def remove_studio_assignments(
@@ -1047,8 +1106,12 @@ def remove_studio_assignments(
10471106
}
10481107
raw_assignments = self.api.post_request(endpoint, payload)
10491108
assignments = {}
1050-
for (email, assigned_projects) in raw_assignments.items():
1051-
assignments[email] = StudioLabelerAssignment(assigned_projects, email, self)
1109+
for email, assigned_projects in raw_assignments.items():
1110+
assignments[email] = StudioLabelerAssignment(
1111+
assigned_projects,
1112+
email,
1113+
self,
1114+
)
10521115
return assignments
10531116

10541117
def list_project_groups(self, project: str) -> List[StudioProjectGroup]:
@@ -1083,7 +1146,10 @@ def create_project_group(
10831146
"""
10841147
endpoint = f"studio/projects/{Api.quote_string(project)}/groups"
10851148
payload = {"emails": emails, "name": project_group}
1086-
return StudioProjectGroup(self.api.post_request(endpoint, payload), self)
1149+
return StudioProjectGroup(
1150+
self.api.post_request(endpoint, payload),
1151+
self,
1152+
)
10871153

10881154
def update_project_group(
10891155
self,
@@ -1110,8 +1176,14 @@ def update_project_group(
11101176
f"studio/projects/{Api.quote_string(project)}"
11111177
f"/groups/{Api.quote_string(project_group)}"
11121178
)
1113-
payload = {"add_emails": add_emails, "remove_emails": remove_emails}
1114-
return StudioProjectGroup(self.api.put_request(endpoint, payload), self)
1179+
payload = {
1180+
"add_emails": add_emails,
1181+
"remove_emails": remove_emails,
1182+
}
1183+
return StudioProjectGroup(
1184+
self.api.put_request(endpoint, payload),
1185+
self,
1186+
)
11151187

11161188
def list_studio_batches(self) -> List[StudioBatch]:
11171189
"""Returns a list with all pending studio batches,
@@ -1152,7 +1224,12 @@ def set_studio_batches_priorities(
11521224
Returns:
11531225
List[StudioBatch]
11541226
"""
1155-
batches_names = list(map(lambda batch_name: {"name": batch_name}, batch_names))
1227+
batches_names = list(
1228+
map(
1229+
lambda batch_name: {"name": batch_name},
1230+
batch_names,
1231+
)
1232+
)
11561233
endpoint = "studio/batches/set_priorities"
11571234
payload = {"batches": batches_names}
11581235
batches = self.api.post_request(endpoint, payload)

scaleapi/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = "2.12.0"
1+
__version__ = "2.13.0"
22
__package_name__ = "scaleapi"

scaleapi/api.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ def _http_request(
4646
files=None,
4747
data=None,
4848
) -> Response:
49-
5049
https = requests.Session()
5150
retry_strategy = Retry(
5251
total=HTTP_TOTAL_RETRIES,
@@ -80,7 +79,6 @@ def _http_request(
8079

8180
@staticmethod
8281
def _raise_on_respose(res: Response):
83-
8482
try:
8583
message = res.json().get("error", res.text)
8684
except ValueError:
@@ -157,10 +155,15 @@ def post_request(self, endpoint, body=None, files=None, data=None):
157155
data=data,
158156
)
159157

160-
def delete_request(self, endpoint, params=None):
158+
def delete_request(self, endpoint, params=None, body=None):
161159
"""Generic DELETE Request Wrapper"""
162160
return self._api_request(
163-
"DELETE", endpoint, headers=self._headers, auth=self._auth, params=params
161+
"DELETE",
162+
endpoint,
163+
headers=self._headers,
164+
auth=self._auth,
165+
params=params,
166+
body=body,
164167
)
165168

166169
def put_request(self, endpoint, body=None, params=None):

scaleapi/tasks.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from enum import Enum
2+
from typing import List
23

34

45
class TaskType(Enum):
@@ -103,3 +104,15 @@ def clear_unique_id(self):
103104
def set_metadata(self, metadata: dict):
104105
"""Sets the metadata of a task"""
105106
self._client.set_task_metadata(self.id, metadata)
107+
108+
def set_tags(self, tags: List[str]):
109+
"""Sets tags of a task"""
110+
self._client.set_task_tags(self.id, tags)
111+
112+
def add_tags(self, tags: List[str]):
113+
"""Adds tags for a task"""
114+
self._client.add_task_tags(self.id, tags)
115+
116+
def delete_tags(self, tags: List[str]):
117+
"""Sets tags for a task"""
118+
self._client.delete_task_tags(self.id, tags)

tests/test_client.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ def test_invalidkey_fail():
4747

4848

4949
def make_a_task(unique_id: str = None, batch: str = None):
50-
5150
args = {
5251
"callback_url": "http://www.example.com/callback",
5352
"instruction": "Draw a box around each baby cow and big cow.",
@@ -112,6 +111,37 @@ def test_task_set_metadata():
112111
assert task.metadata == new_metadata
113112

114113

114+
def test_set_task_tags():
115+
unique_id = str(uuid.uuid4())
116+
task = make_a_task(unique_id)
117+
assert not hasattr(task, "tags")
118+
new_tags = ["tag1", "tag2", "tag3"]
119+
task.set_tags(new_tags)
120+
task.refresh()
121+
assert task.tags == new_tags
122+
123+
124+
def test_add_task_tags():
125+
unique_id = str(uuid.uuid4())
126+
task = make_a_task(unique_id)
127+
assert not hasattr(task, "tags")
128+
new_tags = ["tag1", "tag2", "tag3"]
129+
task.add_tags(new_tags)
130+
task.refresh()
131+
assert task.tags == new_tags
132+
133+
134+
def test_delete_task_tags():
135+
unique_id = str(uuid.uuid4())
136+
task = make_a_task(unique_id)
137+
assert not hasattr(task, "tags")
138+
new_tags = ["tag1", "tag2", "tag3"]
139+
task.add_tags(new_tags)
140+
task.delete_tags(["tag1", "tag2"])
141+
task.refresh()
142+
assert task.tags == ["tag3"]
143+
144+
115145
def test_categorize_ok():
116146
client.create_task(
117147
TaskType.Categorization,
@@ -137,11 +167,15 @@ def test_transcription_ok():
137167
client.create_task(
138168
TaskType.Transcription,
139169
callback_url="http://www.example.com/callback",
140-
instruction="Transcribe the given fields. Then for each news item on the page, "
170+
instruction="Transcribe the given fields. "
171+
"Then for each news item on the page, "
141172
"transcribe the information for the row.",
142173
attachment_type="website",
143174
attachment="http://www.google.com/",
144-
fields={"title": "Title of Webpage", "top_result": "Title of the top result"},
175+
fields={
176+
"title": "Title of Webpage",
177+
"top_result": "Title of the top result",
178+
},
145179
repeatable_fields={
146180
"username": "Username of submitter",
147181
"comment_count": "Number of comments",
@@ -367,7 +401,10 @@ def test_get_tasks():
367401
for _ in range(3):
368402
tasks.append(make_a_task(batch=batch.name))
369403
task_ids = {task.id for task in tasks}
370-
for task in client.get_tasks(project_name=TEST_PROJECT_NAME, batch_name=batch.name):
404+
for task in client.get_tasks(
405+
project_name=TEST_PROJECT_NAME,
406+
batch_name=batch.name,
407+
):
371408
assert task.id in task_ids
372409

373410

0 commit comments

Comments
 (0)