From 06c3493b1ceb8709e43e646bc8c49e32c0e33bca Mon Sep 17 00:00:00 2001 From: nofel-scale Date: Mon, 8 Feb 2021 23:03:27 -1000 Subject: [PATCH 1/5] update python SDK --- README.md | 256 +++++++++++++++++++++++++++++++++++++++++++ README.rst | 149 ------------------------- scaleapi/__init__.py | 62 ++++++++--- scaleapi/projects.py | 14 +++ tests/test_client.py | 248 ++++++++++++----------------------------- 5 files changed, 385 insertions(+), 344 deletions(-) create mode 100644 README.md delete mode 100644 README.rst create mode 100644 scaleapi/projects.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..ea890e1 --- /dev/null +++ b/README.md @@ -0,0 +1,256 @@ +=================== +Scale AI | Python SDK +=================== + +# Installation + +.. code-block:: bash + + $ pip install --upgrade scaleapi + +Note: We strongly suggest using `scaleapi` with Python version 2.7.9 or greater due to SSL issues with prior versions. + +# Usage + +.. code-block:: python + + import scaleapi + client = scaleapi.ScaleClient('YOUR_API_KEY_HERE') + +# Tasks + +Most of these methods will return a `scaleapi.Task` object, which will contain information +about the json response (task_id, status...). + +Any parameter available in the documentation\_ can be passed as an argument option with the corresponding type. + +.. \_documentation: https://docs.scale.com/reference#task-object + +The following endpoints for tasks are available: + +## Create Task + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#general-image-annotation + +.. code-block:: python + + client.create_task( + task_type = 'imageannotation', + project = 'test_project', + callback_url = "http://www.example.com/callback", + instruction= "Draw a box around each baby cow and big cow.", + attachment_type = "image", + attachment = "http://i.imgur.com/v4cBreD.jpg", + geometries = { + "box": { + "objects_to_annotate": ["Baby Cow", "Big Cow"], + "min_height": 10, + "min_width": 10 + } + } + ) + +## Retrieve task + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#retrieve-tasks + +Retrieve a task given its id. + +.. code-block :: python + + task = client.fetch_task('asdfasdfasdfasdfasdfasdf') + print(task.status) // Task status ('pending', 'completed', 'error', 'canceled') + print(task.response) // If task is complete + +## List Tasks + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#list-multiple-tasks + +Retrieve a list of tasks, with optional filter by stand and end date/type. Paginated with `next_token`. The return value is a `scaleapi.Tasklist`, which acts as a list, but also has fields for the total number of tasks, the limit and offset, and whether or not there's more. + +.. code-block :: python + + next_token = None; + counter = 0 + all_tasks =[] + while True: + tasks = client.tasks( + start_time = "2020-09-08", + end_time = "2021-01-01", + customer_review_status = "accepted", + next_token = next_token, + ) + for task in tasks: + counter += 1 + print('Downloading Task %s | %s' % (counter, task.task_id)) + all_tasks.append(task.__dict__['param_dict']) + next_token = tasks.next_token + if next_token is None: + break + print(all_tasks) + +## Cancel Task + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#cancel-task + +Cancel a task given its id if work has not stared on the task (task status is "que). + +.. code-block :: python + + task = client.cancel_task('asdfasdfasdfasdfasdfasdf') + +# Batches + +## Create Batch + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#batch-creation + +.. code-block:: python + + client.create_batch( + project = 'test_project', + callback = "http://www.example.com/callback", + name = 'batch_name_01_07_2021' + ) + +## Finalize Batch + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#batch-finalization + +.. code-block:: python + + client.create_batch(batch_name = 'batch_name_01_07_2021') + +## Check Batch Status + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#batch-status + +.. code-block:: python + + client.batch_status(batch_name = 'batch_name_01_07_2021') + +## Retrieve Batch + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#batch-retrieval + +.. code-block:: python + + client.get_batch( batch_name = "batch_name_01_07_2021" ) + +## List Batchs + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#batch-list + +Retrieve a list of batches + +.. code-block :: python + + next_token = None; + counter = 0 + all_batchs =[] + while True: + batches = client.batches( + status = "completed" + ) + for batch in batches: + counter += 1 + print('Downloading Batch %s | %s | %s' % (counter, batch.name, batch.param_dict['status'])) + all_batchs.append(batch.__dict__['param_dict']) + next_token = batches.next_token + if next_token is None: + break + print(all_batchs) + +# Projects + +## Create Project + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#project-creation + +.. code-block:: python + + client.create_project( + project_name = 'test_project', + type = 'imageannotation, + params = {'instruction':'Please label the kittens'} + ) + +## Retrieve Project + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#project-retrieval + +.. code-block:: python + + client.get_projet(project_name = 'test_project') + +## List Projects + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#batch-list + +Retrieve a list of batches + +.. code-block :: python + + counter = 0 + projects = client.projects() + for project in projects: + counter += 1 + print('Downloading project %s | %s | %s' % (counter, project['name'], project['type'])) + +## Update Project + +Check `this`\_\_ for further information. + +\_\_ https://docs.scale.com/reference#project-update-parameters + +Retrieve a list of batches + +.. code-block :: python + + data = client.update_project( + project_name='test_project', + pathc = false, + instruction='update: Please label all the stuff', + +) + +# Error handling + +If something went wrong while making API calls, then exceptions will be raised automatically +as a `scaleapi.ScaleException` or `scaleapi.ScaleInvalidRequest` runtime error. For example: + +.. code-block:: python + + try + client.create_categorization_task('Some parameters are missing.') + except scaleapi.ValidationError as e: + print(e.code) # 400 + print(e.message) # missing param X + +# Troubleshooting + +If you notice any problems, please email us at support@scale.com. diff --git a/README.rst b/README.rst deleted file mode 100644 index 2b7314a..0000000 --- a/README.rst +++ /dev/null @@ -1,149 +0,0 @@ -=================== -Scale AI | Python SDK -=================== - - -Installation -============ -.. code-block:: bash - - $ pip install --upgrade scaleapi - -Note: We strongly suggest using `scaleapi` with Python version 2.7.9 or greater due to SSL issues with prior versions. - -Usage -===== -.. code-block:: python - - import scaleapi - client = scaleapi.ScaleClient('YOUR_API_KEY_HERE') - -Tasks -===== - -Most of these methods will return a ``scaleapi.Task`` object, which will contain information -about the json response (task_id, status...). - -Any parameter available in the documentation_ can be passed as an argument option with the corresponding type. - -.. _documentation: https://scale.com/docs - -The following endpoints for tasks are available: - -Create Categorization Task -========================== - -Check `this`__ for further information. - -__ https://scale.com/docs/#create-categorization-task - -.. code-block:: python - - task = client.create_categorization_task( - callback_url = 'http://www.example.com/callback', - instruction = 'Is this company public or private?', - attachment_type = 'website', - attachment = 'http://www.google.com/', - categories = ['public', 'private'] - ) - -Create Image Annotation Task -====================== - -Check `this`__ for further information. - -__ https://docs.scale.com/reference#general-image-annotation - -.. code-block:: python - - client.create_imageannotation_task( - callback_url = 'http://www.example.com/callback', - instruction= 'Draw a box around each baby cow and big cow.', - attachment_type = "image", - attachment = "http://i.imgur.com/v4cBreD.jpg", - geometries = { - "box": { - "objects_to_annotate: ["Baby Cow", "Big Cow"], - "min_height": 10, - "min_width": 10 - } - } - ) - -Retrieve task -============= - -Check `this`__ for further information. - -__ https://docs.scale.com/reference#retrieve-tasks - -Retrieve a task given its id. - -.. code-block :: python - - task = client.fetch_task('asdfasdfasdfasdfasdfasdf') - task.id == 'asdfasdfasdfasdfasdfasdf' # true - -Cancel task -=========== - -Check `this`__ for further information. - -__ https://docs.scale.com/reference#cancel-task - -Cancel a task given its id, only if it's not completed. - -.. code-block :: python - - task = client.cancel_task('asdfasdfasdfasdfasdfasdf') - -List tasks -========== - -Check `this`__ for further information. - -__ https://docs.scale.com/reference#list-multiple-tasks - -Retrieve a list of tasks, with optional filter by date/type. Paginated with limit/offset. -The return value is a ``scaleapi.Tasklist``, which acts as a list, but also has fields -for the total number of tasks, the limit and offset, and whether or not there's more. - -.. code-block :: python - - next_token = None; - counter = 0 - all_tasks =[] - while True: - tasks = client.tasks( - start_time = "2020-09-08", - end_time = "2021-01-01", - customer_review_status = "accepted", - next_token = next_token, - ) - for task in tasks: - counter += 1 - print(f'Downloading Task {counter} | {task.task_id}') - all_tasks.append(task.__dict__['param_dict']) - next_token = tasks.next_token - if next_token is None: - break - print(all_tasks) - -Error handling -============== - -If something went wrong while making API calls, then exceptions will be raised automatically -as a ``scaleapi.ScaleException`` or ``scaleapi.ScaleInvalidRequest`` runtime error. For example: - -.. code-block:: python - - try - client.create_categorization_task('Some parameters are missing.') - except scaleapi.ValidationError as e: - print(e.code) # 400 - print(e.message) # missing param X - -Troubleshooting -=============== - -If you notice any problems, please email us at support@scale.com. diff --git a/scaleapi/__init__.py b/scaleapi/__init__.py index ba18f02..6cd52cc 100644 --- a/scaleapi/__init__.py +++ b/scaleapi/__init__.py @@ -2,24 +2,19 @@ from .tasks import Task from .batches import Batch +from .projects import Project TASK_TYPES = [ - 'annotation', - 'audiotranscription', 'categorization', - 'comparison', - 'cuboidannotation', - 'datacollection', 'imageannotation', - 'lineannotation', 'namedentityrecognition', - 'pointannotation', - 'polygonannotation', 'segmentannotation', - 'transcription', + 'documenttranscription', 'videoannotation', - 'videoboxannotation', - 'videocuboidannotation' + 'videoplaybackannotation', + 'namedentityrecognition', + 'textcollection', + 'documentmodel' ] SCALE_ENDPOINT = 'https://api.scale.com/v1/' DEFAULT_LIMIT = 100 @@ -158,25 +153,58 @@ def create_batch(self, project, batch_name, callback): payload = dict(project=project, name=batch_name, callback=callback) batchdata = self._postrequest('batches', payload) return Batch(batchdata, self) + + def finalize_batch(self, batch_name): + batchdata = self._postrequest('batches/%s/finalize' % batch_name) + return Batch(batchdata, self) + + def batch_status(self, batch_name): + batchdata = self._getrequest('batches/%s/status' % batch_name) + return Batch.get_status(self) - def get_batch(self, batch_name: str): + def get_batch(self, batch_name): batchdata = self._getrequest('batches/%s' % batch_name) return Batch(batchdata, self) - def list_batches(self, **kwargs): + def batches(self, **kwargs): allowed_kwargs = {'start_time', 'end_time', 'status', 'project', - 'batch', 'limit', 'offset', } + 'limit', 'offset', } for key in kwargs: if key not in allowed_kwargs: raise ScaleInvalidRequest('Illegal parameter %s for ScaleClient.tasks()' % key, None) - response = self._getrequest('tasks', params=kwargs) + response = self._getrequest('batches', params=kwargs) + print(response['docs']) docs = [Batch(doc, self) for doc in response['docs']] return Batchlist( - docs, response['total'], response['limit'], response['offset'], - response['has_more'], response.get('next_token'), + docs, response['totalDocs'], response['limit'],response['has_more'], response.get('next_token'), ) + def create_project(self, project_name, type, params): + payload = dict(type=type, name=project_name, params=params) + projectdata = self._postrequest('projects', payload) + return Project(projectdata, self) + + def get_projet(self, project_name): + projectdata = self._getrequest('projects/%s' % project_name) + return Project(projectdata, self) + + def projects(self): + response = self._getrequest('projects') + return response + + def update_project(self,project_name,**kwargs): + allowed_kwargs = {'patch', 'instruction'} + for key in kwargs: + if key not in allowed_kwargs: + raise ScaleInvalidRequest('Illegal parameter %s for ScaleClient.update_project()' + % key, None) + projectdata = self._postrequest('projects/%s/setParams' % project_name) + return projectdata + + + + def _AddTaskTypeCreator(task_type): def create_task_wrapper(self, **kwargs): diff --git a/scaleapi/projects.py b/scaleapi/projects.py new file mode 100644 index 0000000..e619a07 --- /dev/null +++ b/scaleapi/projects.py @@ -0,0 +1,14 @@ +class Project(object): + def __init__(self, param_dict, client): + self.param_dict = param_dict + self.name = param_dict['name'] + self.client = client + + def __hash__(self): + return hash(self.name) + + def __str__(self): + return 'Project(name=%s)' % self.name + + def __repr__(self): + return 'Project(%s)' % self.param_dict diff --git a/tests/test_client.py b/tests/test_client.py index 4d94dce..d436876 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -12,37 +12,41 @@ except KeyError: raise Exception("Please set the environment variable SCALE_TEST_API_KEY to run tests.") - def make_a_task(): - return client.create_comparison_task( - callback_url='http://www.example.com/callback', - instruction='Do the objects in these images have the same pattern?', - attachment_type='image', - attachments=[ - 'http://i.ebayimg.com/00/$T2eC16dHJGwFFZKjy5ZjBRfNyMC4Ig~~_32.JPG', - 'http://images.wisegeek.com/checkered-tablecloth.jpg' - ], - choices=['yes', 'no']) - + return client.create_task( + task_type = 'imageannotation', + callback_url = "http://www.example.com/callback", + instruction = "Draw a box around each baby cow and big cow.", + attachment_type = "image", + attachment = "http://i.imgur.com/v4cBreD.jpg", + geometries = { + "box": { + "objects_to_annotate": ["Baby Cow", "Big Cow"], + "min_height": 10, + "min_width": 10 + } + } + ) def test_categorize_ok(): - task = client.create_categorization_task( + task = client.create_task( + task_type = 'categorization', callback_url='http://www.example.com/callback', instruction='Is this company public or private?', attachment_type='website', attachment='http://www.google.com/', categories=['public', 'private']) - def test_categorize_fail(): with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_categorization_task( + client.create_task( + task_type = 'categorization', callback_url='http://www.example.com/callback', categories=['public', 'private']) - def test_transcription_ok(): - task = client.create_transcription_task( + task = client.create_task( + task_type = 'categorization', callback_url='http://www.example.com/callback', instruction='Transcribe the given fields. Then for each news item on the page, transcribe the information for the row.', attachment_type='website', @@ -56,186 +60,57 @@ def test_transcription_ok(): 'comment_count': 'Number of comments' }) - def test_transcription_fail(): with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_transcription_task( + client.create_task( + task_type='transcription', callback_url='http://www.example.com/callback', attachment_type='website') - -@pytest.mark.skip(reason="Deprecated at the moment") -def test_phonecall_ok(): - task = client.create_phonecall_task( - callback_url='http://www.example.com/callback', - instruction='Call this person and follow the script provided, recording responses', - phone_number='5055006865', - entity_name='Alexandr Wang', - script='Hello ! Are you happy today? (pause) One more thing - what is your email address?', - fields={'email': 'Email Address'}, - choices=['He is happy', 'He is not happy']) - - -@pytest.mark.skip(reason="Deprecated at the moment") -def test_phonecall_fail(): - with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_phonecall_task( - callback_url='http://www.example.com/callback', - instruction='Call this person and follow the script provided, recording responses') - - -def test_comparison_ok(): - task = client.create_comparison_task( - callback_url='http://www.example.com/callback', - instruction='Do the objects in these images have the same pattern?', - attachment_type='image', - attachments=[ - 'http://i.ebayimg.com/00/$T2eC16dHJGwFFZKjy5ZjBRfNyMC4Ig~~_32.JPG', - 'http://images.wisegeek.com/checkered-tablecloth.jpg' - ], - choices=['yes', 'no']) - - -def test_comparison_fail(): - with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_comparison_task( - callback_url='http://www.example.com/callback', - instruction='Do the objects in these images have the same pattern?', - attachment_type='image') - - -def test_annotation_ok(): - task = client.create_annotation_task( - callback_url='http://www.example.com/callback', - instruction='Draw a box around each **baby cow** and **big cow**', - attachment_type='image', - attachment='http://i.imgur.com/v4cBreD.jpg', - min_width='30', - min_height='30', - objects_to_annotate=['baby cow', 'big cow'], - with_labels=True) +def test_imageannotation_ok(): + client.create_task( + task_type = 'imageannotation', + callback_url = "http://www.example.com/callback", + instruction = "Draw a box around each baby cow and big cow.", + attachment_type = "image", + attachment = "http://i.imgur.com/v4cBreD.jpg", + geometries = { + "box": { + "objects_to_annotate": ["Baby Cow", "Big Cow"], + "min_height": 10, + "min_width": 10 + } + } + ) # min_width and min_height should be optional - task2 = client.create_annotation_task( + task2 = client.create_task( + task_type = 'imageannotation', callback_url='http://www.example.com/callback', instruction='Draw a box around each **baby cow** and **big cow**', attachment_type='image', attachment='http://i.imgur.com/v4cBreD.jpg', - objects_to_annotate=['baby cow', 'big cow'], - with_labels=True) - - + geometries = { + "box": { + "objects_to_annotate": ["Baby Cow", "Big Cow"], + "min_height": 10, + "min_width": 10 + } + }) -def test_annotation_fail(): +def test_imageannotation_fail(): with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_annotation_task( + client.create_task( + task_type = 'imageannotation', callback_url='http://www.example.com/callback', instruction='Draw a box around each **baby cow** and **big cow**', attachment_type='image') - -def test_polygonannotation_ok(): - task = client.create_polygonannotation_task( - callback_url='http://www.example.com/callback', - instruction='Draw a tight shape around the big cow', - attachment_type='image', - attachment='http://i.imgur.com/v4cBreD.jpg', - objects_to_annotate=['big cow'], - with_labels=True) - - -def test_polygonannotation_fail(): - with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_polygonannotation_task( - callback_url='http://www.example.com/callback', - instruction='Draw a tight shape around the big cow', - attachment_type='image') - - -def test_lineannotation_ok(): - task = client.create_lineannotation_task( - callback_url='http://www.example.com/callback', - instruction='Draw a tight shape around the big cow', - attachment_type='image', - attachment='http://i.imgur.com/v4cBreD.jpg', - objects_to_annotate=['big cow'], - with_labels=True) - - -def test_lineannotation_fail(): - with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_lineannotation_task( - callback_url='http://www.example.com/callback', - instruction='Draw a tight shape around the big cow', - attachment_type='image') - - -def test_datacollection_ok(): - task = client.create_datacollection_task( - callback_url='http://www.example.com/callback', - instruction='Find the URL for the hiring page for the company with attached website.', - attachment_type='website', - attachment='http://www.google.com/', - fields={ 'hiring_page': 'Hiring Page URL' }) - - -def test_datacollection_fail(): - with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_datacollection_task( - callback_url='http://www.example.com/callback', - attachment_type='website') - - -def test_audiotranscription_ok(): - task = client.create_audiotranscription_task( - callback_url='http://www.example.com/callback', - attachment_type='audio', - attachment='https://storage.googleapis.com/deepmind-media/pixie/knowing-what-to-say/second-list/speaker-3.wav', - verbatim=False, - phrases=['avocado', 'stone'] - ) - - -def test_audiotranscription_fail(): - with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_audiotranscription_task( - callback_url='http://www.example.com/callback', - attachment_type='audio') - - -def test_audiotranscription_fail2(): - with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_audiotranscription_task( - callback_url='http://www.example.com/callback', - attachment='some_non_url') - - -def test_namedentityrecognition_ok(): - return client.create_namedentityrecognition_task( - callback_url='http://www.example.com/callback', - instruction='Do the objects in these images have the same pattern?', - text='Example text to label with NER tool', - labels=[{ - 'name': 'Label_A', - 'description': 'the first label', - }]) - - -def test_unicode_ok(): - task = client.create_categorization_task( - callback_url='http://www.example.com/callback', - instruction='Hello, 世界', - attachment_type='website', - attachment='http://www.google.com/', - categories=['public', 'private']) - - def test_cancel(): task = make_a_task() # raises a scaleexception, because test tasks complete instantly with pytest.raises(scaleapi.ScaleException): task.cancel() - def test_task_retrieval(): task = make_a_task() task2 = client.fetch_task(task.id) @@ -251,7 +126,6 @@ def test_task_retrieval(): assert task2.type == task.type assert task2.created_at == task.created_at - def test_task_retrieval_time(): task = make_a_task() time.sleep(0.5) @@ -261,12 +135,10 @@ def test_task_retrieval_time(): tasks = client.tasks(start_time=start_time, end_time=end_time) assert tasks.docs == [] - def test_task_retrieval_fail(): with pytest.raises(scaleapi.ScaleException): client.fetch_task('fake_id_qwertyuiop') - def test_tasks(): tasks = [] for i in range(3): @@ -275,7 +147,27 @@ def test_tasks(): for task in client.tasks(limit=3): assert task.id in task_ids - def test_tasks_invalid(): with pytest.raises(scaleapi.ScaleException): client.tasks(bogus=0) + +def create_a_batch(): + return client.create_batch( + callback = "http://www.example.com/callback", + batch_name = "kitten_labeling_2020-07", + project = "kitten_labeling" + ) + +def test_finalize_batch(): + batch = create_a_batch() + client.finalize_batch(batch.name) + +def get_batch_status(): + batch = create_a_batch() + client.batch_status(batch.name) + +def get_batch(): + batch = create_a_batch() + client.get_batch(batch.name) + +def \ No newline at end of file From e027133be0d84d63137c3e669baa71281babebc3 Mon Sep 17 00:00:00 2001 From: nofel-scale Date: Tue, 9 Feb 2021 23:19:13 -1000 Subject: [PATCH 2/5] whoops --- scaleapi/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scaleapi/__init__.py b/scaleapi/__init__.py index 6cd52cc..e6330f9 100644 --- a/scaleapi/__init__.py +++ b/scaleapi/__init__.py @@ -174,7 +174,6 @@ def batches(self, **kwargs): raise ScaleInvalidRequest('Illegal parameter %s for ScaleClient.tasks()' % key, None) response = self._getrequest('batches', params=kwargs) - print(response['docs']) docs = [Batch(doc, self) for doc in response['docs']] return Batchlist( docs, response['totalDocs'], response['limit'],response['has_more'], response.get('next_token'), From f396ec6942debd4f8743b2793f3f35bf7f05ad63 Mon Sep 17 00:00:00 2001 From: nofel-scale Date: Fri, 12 Feb 2021 11:47:30 -1000 Subject: [PATCH 3/5] small changes --- README.md => README.rst | 15 +++-- local_test.py | 44 +++++++++++++ scaleapi/__init__.py | 18 ++++-- tests/test_client.py | 140 ++++++++++++++++++++++++++++++++-------- 4 files changed, 178 insertions(+), 39 deletions(-) rename README.md => README.rst (91%) create mode 100644 local_test.py diff --git a/README.md b/README.rst similarity index 91% rename from README.md rename to README.rst index ea890e1..5bcc74f 100644 --- a/README.md +++ b/README.rst @@ -30,14 +30,14 @@ The following endpoints for tasks are available: ## Create Task -Check `this`\_\_ for further information. +This method can be used for any Scale supported task type using the following format: +`client.create_{{Task Type}}_task(...)` and passing the applicable values into the function definition. The applicable fields and further information for each task type can be found in scales API docs `here`\_\_ for further information. -\_\_ https://docs.scale.com/reference#general-image-annotation +\_\_ hhttps://docs.scale.com/reference#general-image-annotation .. code-block:: python - client.create_task( - task_type = 'imageannotation', + client.create_imageannotation_task( project = 'test_project', callback_url = "http://www.example.com/callback", instruction= "Draw a box around each baby cow and big cow.", @@ -123,7 +123,7 @@ Check `this`\_\_ for further information. name = 'batch_name_01_07_2021' ) -## Finalize Batch +## Finalize Batceh Check `this`\_\_ for further information. @@ -153,7 +153,7 @@ Check `this`\_\_ for further information. client.get_batch( batch_name = "batch_name_01_07_2021" ) -## List Batchs +## List Batches Check `this`\_\_ for further information. @@ -167,7 +167,7 @@ Retrieve a list of batches counter = 0 all_batchs =[] while True: - batches = client.batches( + batches = client.list_batches( status = "completed" ) for batch in batches: @@ -207,6 +207,7 @@ Check `this`\_\_ for further information. ## List Projects +This function does not take any arguments. It will return information for every project. Check `this`\_\_ for further information. \_\_ https://docs.scale.com/reference#batch-list diff --git a/local_test.py b/local_test.py new file mode 100644 index 0000000..d2c6139 --- /dev/null +++ b/local_test.py @@ -0,0 +1,44 @@ +import scaleapi + +nofel_key = "live_ecf03a873847470e8ec59f20b09276b4" +# stanford_key = 'live_c23b58e102b044bd9e854436a9c64a18' +client = scaleapi.ScaleClient(nofel_key) +attachment = 'https://p-ZmFjlye.t1.n0.cdn.getcloudapp.com/items/eDuwnDY4/f5fa720d-6c2c-4ef1-8aed-59e7f1021db6.jpeg?source=client&v=1700792a861cc7747f10f1c36e5ba057' + +# task = client.create_task( +# task_type= 'imageannotation', +# callback_url= "http://www.example.com/callback", +# attachment= attachment, +# batch = 'batch_name_01_07_2021', +# # attachment = "s3://scale-sales-uploads/TRI/2d_bbox_sample.mp4", +# # attachment_type = 'video', +# project = 'nofel_test_project', +# ) + +# print(f'attachment: {attachment}') +# print(f'frame_rate: {frame_rate}') + +# data = client.update_project( +# project_name='nofel_test_project', +# instruction='update: Please label all the stuff', +# geometries={'box':{'objects_to_annotate':['update_label']}} +# ) + +# project = client.create_project( +# project_name = 'nofel_test_project', +# type = 'imageannotation', +# params = {'instruction':'Please label the stuff'} +# ) + +batch = client.get_projet( + project_name = 'nofel_test_project', + ) + +print(batch.param_dict['type']) + + +# counter = 0 +# projects = client.projects() +# for project in projects: +# counter += 1 +# print('Downloading project %s | %s | %s' % (counter, project['name'], project['type'])) diff --git a/scaleapi/__init__.py b/scaleapi/__init__.py index e6330f9..c315518 100644 --- a/scaleapi/__init__.py +++ b/scaleapi/__init__.py @@ -5,16 +5,24 @@ from .projects import Project TASK_TYPES = [ + 'annotation', + 'audiotranscription', 'categorization', + 'comparison', + 'cuboidannotation', + 'datacollection', 'imageannotation', + 'lineannotation', 'namedentityrecognition', + 'pointannotation', + 'polygonannotation', 'segmentannotation', + 'transcription', 'documenttranscription', 'videoannotation', + 'videoboxannotation', 'videoplaybackannotation', - 'namedentityrecognition', - 'textcollection', - 'documentmodel' + 'videocuboidannotation' ] SCALE_ENDPOINT = 'https://api.scale.com/v1/' DEFAULT_LIMIT = 100 @@ -166,12 +174,12 @@ def get_batch(self, batch_name): batchdata = self._getrequest('batches/%s' % batch_name) return Batch(batchdata, self) - def batches(self, **kwargs): + def list_batches(self, **kwargs): allowed_kwargs = {'start_time', 'end_time', 'status', 'project', 'limit', 'offset', } for key in kwargs: if key not in allowed_kwargs: - raise ScaleInvalidRequest('Illegal parameter %s for ScaleClient.tasks()' + raise ScaleInvalidRequest('Illegal parameter %s for ScaleClient.list_batches()' % key, None) response = self._getrequest('batches', params=kwargs) docs = [Batch(doc, self) for doc in response['docs']] diff --git a/tests/test_client.py b/tests/test_client.py index d436876..7891ec5 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -13,8 +13,7 @@ raise Exception("Please set the environment variable SCALE_TEST_API_KEY to run tests.") def make_a_task(): - return client.create_task( - task_type = 'imageannotation', + return client.create_imageannotation_task( callback_url = "http://www.example.com/callback", instruction = "Draw a box around each baby cow and big cow.", attachment_type = "image", @@ -29,8 +28,7 @@ def make_a_task(): ) def test_categorize_ok(): - task = client.create_task( - task_type = 'categorization', + task = client.create_categorization_task( callback_url='http://www.example.com/callback', instruction='Is this company public or private?', attachment_type='website', @@ -39,14 +37,12 @@ def test_categorize_ok(): def test_categorize_fail(): with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_task( - task_type = 'categorization', + client.create_categorization_task( callback_url='http://www.example.com/callback', categories=['public', 'private']) def test_transcription_ok(): - task = client.create_task( - task_type = 'categorization', + task = client.create_transcription_task( callback_url='http://www.example.com/callback', instruction='Transcribe the given fields. Then for each news item on the page, transcribe the information for the row.', attachment_type='website', @@ -62,14 +58,12 @@ def test_transcription_ok(): def test_transcription_fail(): with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_task( - task_type='transcription', + client.create_transcription_task( callback_url='http://www.example.com/callback', attachment_type='website') def test_imageannotation_ok(): - client.create_task( - task_type = 'imageannotation', + client.create_imageannotation_task( callback_url = "http://www.example.com/callback", instruction = "Draw a box around each baby cow and big cow.", attachment_type = "image", @@ -82,29 +76,123 @@ def test_imageannotation_ok(): } } ) - # min_width and min_height should be optional - task2 = client.create_task( - task_type = 'imageannotation', + +def test_imageannotation_fail(): + with pytest.raises(scaleapi.ScaleInvalidRequest): + client.create_imageannotation_task( + callback_url='http://www.example.com/callback', + instruction='Draw a box around each **baby cow** and **big cow**', + attachment_type='image') + +def test_documenttranscription_ok(): + client.create_documenttranscription_task( + callback_url= 'http://www.example.com/callback', + instruction= 'Please transcribe this receipt.', + attachment= 'http://document.scale.com/receipt-20200519.jpg', + features= [ + { + 'type': "block", + 'label': "barcode", + } + ] + ) + +def test_documenttranscription_fail(): + with pytest.raises(scaleapi.ScaleInvalidRequest): + client.create_imageannotation_task( + callback_url='http://www.example.com/callback', + instruction='Please transcribe this receipt.', + ) + +def test_annotation_ok(): + task = client.create_annotation_task( callback_url='http://www.example.com/callback', instruction='Draw a box around each **baby cow** and **big cow**', attachment_type='image', attachment='http://i.imgur.com/v4cBreD.jpg', - geometries = { - "box": { - "objects_to_annotate": ["Baby Cow", "Big Cow"], - "min_height": 10, - "min_width": 10 - } - }) + min_width='30', + min_height='30', + objects_to_annotate=['baby cow', 'big cow'], + with_labels=True) -def test_imageannotation_fail(): +def test_annotation_fail(): with pytest.raises(scaleapi.ScaleInvalidRequest): - client.create_task( - task_type = 'imageannotation', + client.create_annotation_task( callback_url='http://www.example.com/callback', instruction='Draw a box around each **baby cow** and **big cow**', attachment_type='image') +def test_polygonannotation_ok(): + task = client.create_polygonannotation_task( + callback_url='http://www.example.com/callback', + instruction='Draw a tight shape around the big cow', + attachment_type='image', + attachment='http://i.imgur.com/v4cBreD.jpg', + objects_to_annotate=['big cow'], + with_labels=True) + +def test_polygonannotation_fail(): + with pytest.raises(scaleapi.ScaleInvalidRequest): + client.create_polygonannotation_task( + callback_url='http://www.example.com/callback', + instruction='Draw a tight shape around the big cow', + attachment_type='image') + +def test_lineannotation_ok(): + task = client.create_lineannotation_task( + callback_url='http://www.example.com/callback', + instruction='Draw a tight shape around the big cow', + attachment_type='image', + attachment='http://i.imgur.com/v4cBreD.jpg', + objects_to_annotate=['big cow'], + with_labels=True) + +def test_lineannotation_fail(): + with pytest.raises(scaleapi.ScaleInvalidRequest): + client.create_lineannotation_task( + callback_url='http://www.example.com/callback', + instruction='Draw a tight shape around the big cow', + attachment_type='image') + +def test_datacollection_ok(): + task = client.create_datacollection_task( + callback_url='http://www.example.com/callback', + instruction='Find the URL for the hiring page for the company with attached website.', + attachment_type='website', + attachment='http://www.google.com/', + fields={ 'hiring_page': 'Hiring Page URL' }) + +def test_datacollection_fail(): + with pytest.raises(scaleapi.ScaleInvalidRequest): + client.create_datacollection_task( + callback_url='http://www.example.com/callback', + attachment_type='website') + +def test_audiotranscription_ok(): + task = client.create_audiotranscription_task( + callback_url='http://www.example.com/callback', + attachment_type='audio', + attachment='https://storage.googleapis.com/deepmind-media/pixie/knowing-what-to-say/second-list/speaker-3.wav', + verbatim=False, + phrases=['avocado', 'stone'] + ) + +def test_audiotranscription_fail(): + with pytest.raises(scaleapi.ScaleInvalidRequest): + client.create_audiotranscription_task( + callback_url='http://www.example.com/callback', + attachment_type='audio') + +def test_namedentityrecognition_ok(): + return client.create_namedentityrecognition_task( + callback_url='http://www.example.com/callback', + instruction='Do the objects in these images have the same pattern?', + text='Example text to label with NER tool', + labels=[{ + 'name': 'Label_A', + 'description': 'the first label', + }]) + def test_cancel(): task = make_a_task() # raises a scaleexception, because test tasks complete instantly @@ -169,5 +257,3 @@ def get_batch_status(): def get_batch(): batch = create_a_batch() client.get_batch(batch.name) - -def \ No newline at end of file From 06c8bad2de6e71075f777e131e5773fd77f8948f Mon Sep 17 00:00:00 2001 From: Nofel Naoman <60404790+nofel-scale@users.noreply.github.com> Date: Tue, 16 Feb 2021 12:29:33 -0800 Subject: [PATCH 4/5] Update README.rst --- README.rst | 64 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/README.rst b/README.rst index 5bcc74f..09af378 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,9 @@ -=================== +===================== Scale AI | Python SDK -=================== +===================== -# Installation +Installation +____________ .. code-block:: bash @@ -10,14 +11,16 @@ Scale AI | Python SDK Note: We strongly suggest using `scaleapi` with Python version 2.7.9 or greater due to SSL issues with prior versions. -# Usage +Usage +_____ .. code-block:: python import scaleapi client = scaleapi.ScaleClient('YOUR_API_KEY_HERE') -# Tasks +Tasks +_____ Most of these methods will return a `scaleapi.Task` object, which will contain information about the json response (task_id, status...). @@ -28,7 +31,8 @@ Any parameter available in the documentation\_ can be passed as an argument opti The following endpoints for tasks are available: -## Create Task +Create Task +^^^^^^^^^^^ This method can be used for any Scale supported task type using the following format: `client.create_{{Task Type}}_task(...)` and passing the applicable values into the function definition. The applicable fields and further information for each task type can be found in scales API docs `here`\_\_ for further information. @@ -52,7 +56,8 @@ This method can be used for any Scale supported task type using the following fo } ) -## Retrieve task +Retrieve task +^^^^^^^^^^^^^ Check `this`\_\_ for further information. @@ -66,7 +71,8 @@ Retrieve a task given its id. print(task.status) // Task status ('pending', 'completed', 'error', 'canceled') print(task.response) // If task is complete -## List Tasks +List Tasks +^^^^^^^^^^ Check `this`\_\_ for further information. @@ -95,7 +101,8 @@ Retrieve a list of tasks, with optional filter by stand and end date/type. Pagin break print(all_tasks) -## Cancel Task +Cancel Task +^^^^^^^^^^^ Check `this`\_\_ for further information. @@ -107,9 +114,11 @@ Cancel a task given its id if work has not stared on the task (task status is "q task = client.cancel_task('asdfasdfasdfasdfasdfasdf') -# Batches +Batches +_______ -## Create Batch +Create Batch +^^^^^^^^^^^^ Check `this`\_\_ for further information. @@ -123,7 +132,8 @@ Check `this`\_\_ for further information. name = 'batch_name_01_07_2021' ) -## Finalize Batceh +Finalize Batceh +^^^^^^^^^^^^^^^ Check `this`\_\_ for further information. @@ -133,7 +143,8 @@ Check `this`\_\_ for further information. client.create_batch(batch_name = 'batch_name_01_07_2021') -## Check Batch Status +Check Batch Status +^^^^^^^^^^^^^^^^^^ Check `this`\_\_ for further information. @@ -143,7 +154,8 @@ Check `this`\_\_ for further information. client.batch_status(batch_name = 'batch_name_01_07_2021') -## Retrieve Batch +Retrieve Batch +^^^^^^^^^^^^^^ Check `this`\_\_ for further information. @@ -153,7 +165,8 @@ Check `this`\_\_ for further information. client.get_batch( batch_name = "batch_name_01_07_2021" ) -## List Batches +List Batches +^^^^^^^^^^^^ Check `this`\_\_ for further information. @@ -179,9 +192,11 @@ Retrieve a list of batches break print(all_batchs) -# Projects +Projects +________ -## Create Project +Create Project +^^^^^^^^^^^^^^ Check `this`\_\_ for further information. @@ -195,7 +210,8 @@ Check `this`\_\_ for further information. params = {'instruction':'Please label the kittens'} ) -## Retrieve Project +Retrieve Project +^^^^^^^^^^^^^^^^ Check `this`\_\_ for further information. @@ -205,7 +221,8 @@ Check `this`\_\_ for further information. client.get_projet(project_name = 'test_project') -## List Projects +List Projects +^^^^^^^^^^^^^ This function does not take any arguments. It will return information for every project. Check `this`\_\_ for further information. @@ -222,7 +239,8 @@ Retrieve a list of batches counter += 1 print('Downloading project %s | %s | %s' % (counter, project['name'], project['type'])) -## Update Project +Update Project +^^^^^^^^^^^^^^ Check `this`\_\_ for further information. @@ -239,7 +257,8 @@ Retrieve a list of batches ) -# Error handling +Error handling +______________ If something went wrong while making API calls, then exceptions will be raised automatically as a `scaleapi.ScaleException` or `scaleapi.ScaleInvalidRequest` runtime error. For example: @@ -252,6 +271,7 @@ as a `scaleapi.ScaleException` or `scaleapi.ScaleInvalidRequest` runtime error. print(e.code) # 400 print(e.message) # missing param X -# Troubleshooting +Troubleshooting +_______________ If you notice any problems, please email us at support@scale.com. From 40b473b1b5739ddc59f0160bb525e2a85eae542c Mon Sep 17 00:00:00 2001 From: Nofel Naoman <60404790+nofel-scale@users.noreply.github.com> Date: Tue, 16 Feb 2021 14:24:54 -0800 Subject: [PATCH 5/5] Delete local_test.py --- local_test.py | 44 -------------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 local_test.py diff --git a/local_test.py b/local_test.py deleted file mode 100644 index d2c6139..0000000 --- a/local_test.py +++ /dev/null @@ -1,44 +0,0 @@ -import scaleapi - -nofel_key = "live_ecf03a873847470e8ec59f20b09276b4" -# stanford_key = 'live_c23b58e102b044bd9e854436a9c64a18' -client = scaleapi.ScaleClient(nofel_key) -attachment = 'https://p-ZmFjlye.t1.n0.cdn.getcloudapp.com/items/eDuwnDY4/f5fa720d-6c2c-4ef1-8aed-59e7f1021db6.jpeg?source=client&v=1700792a861cc7747f10f1c36e5ba057' - -# task = client.create_task( -# task_type= 'imageannotation', -# callback_url= "http://www.example.com/callback", -# attachment= attachment, -# batch = 'batch_name_01_07_2021', -# # attachment = "s3://scale-sales-uploads/TRI/2d_bbox_sample.mp4", -# # attachment_type = 'video', -# project = 'nofel_test_project', -# ) - -# print(f'attachment: {attachment}') -# print(f'frame_rate: {frame_rate}') - -# data = client.update_project( -# project_name='nofel_test_project', -# instruction='update: Please label all the stuff', -# geometries={'box':{'objects_to_annotate':['update_label']}} -# ) - -# project = client.create_project( -# project_name = 'nofel_test_project', -# type = 'imageannotation', -# params = {'instruction':'Please label the stuff'} -# ) - -batch = client.get_projet( - project_name = 'nofel_test_project', - ) - -print(batch.param_dict['type']) - - -# counter = 0 -# projects = client.projects() -# for project in projects: -# counter += 1 -# print('Downloading project %s | %s | %s' % (counter, project['name'], project['type']))