Skip to content

Commit 3d33e19

Browse files
committed
Bump Package Version
1 parent 478e533 commit 3d33e19

File tree

8 files changed

+329
-19
lines changed

8 files changed

+329
-19
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
/*.egg-info
44
.tox
55
.cache
6+
/.vscode/

build/lib/scaleapi/__init__.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import requests
2+
3+
from .tasks import Task
4+
from .batches import Batch
5+
6+
TASK_TYPES = [
7+
'annotation',
8+
'audiotranscription',
9+
'categorization',
10+
'comparison',
11+
'cuboidannotation',
12+
'datacollection',
13+
'imageannotation',
14+
'lineannotation',
15+
'namedentityrecognition',
16+
'pointannotation',
17+
'polygonannotation',
18+
'segmentannotation',
19+
'transcription',
20+
'videoannotation',
21+
'videoboxannotation',
22+
'videocuboidannotation'
23+
]
24+
SCALE_ENDPOINT = 'https://api.scale.com/v1/'
25+
DEFAULT_LIMIT = 100
26+
DEFAULT_OFFSET = 0
27+
28+
29+
class ScaleException(Exception):
30+
def __init__(self, message, errcode):
31+
super(ScaleException, self).__init__(
32+
'<Response [{}]> {}'.format(errcode, message))
33+
self.code = errcode
34+
35+
36+
class ScaleInvalidRequest(ScaleException, ValueError):
37+
pass
38+
39+
40+
class Paginator(list):
41+
def __init__(self, docs, total, limit, offset, has_more, next_token=None):
42+
super(Paginator, self).__init__(docs)
43+
self.docs = docs
44+
self.total = total
45+
self.limit = limit
46+
self.offset = offset
47+
self.has_more = has_more
48+
self.next_token = next_token
49+
50+
51+
class Tasklist(Paginator):
52+
pass
53+
54+
55+
class Batchlist(Paginator):
56+
pass
57+
58+
59+
class ScaleClient(object):
60+
def __init__(self, api_key):
61+
self.api_key = api_key
62+
63+
def _getrequest(self, endpoint, params=None):
64+
"""Makes a get request to an endpoint.
65+
66+
If an error occurs, assumes that endpoint returns JSON as:
67+
{ 'status_code': XXX,
68+
'error': 'I failed' }
69+
"""
70+
params = params or {}
71+
r = requests.get(SCALE_ENDPOINT + endpoint,
72+
headers={"Content-Type": "application/json"},
73+
auth=(self.api_key, ''), params=params)
74+
75+
if r.status_code == 200:
76+
return r.json()
77+
else:
78+
try:
79+
error = r.json()['error']
80+
except ValueError:
81+
error = r.text
82+
if r.status_code == 400:
83+
raise ScaleInvalidRequest(error, r.status_code)
84+
else:
85+
raise ScaleException(error, r.status_code)
86+
87+
def _postrequest(self, endpoint, payload=None):
88+
"""Makes a post request to an endpoint.
89+
90+
If an error occurs, assumes that endpoint returns JSON as:
91+
{ 'status_code': XXX,
92+
'error': 'I failed' }
93+
"""
94+
payload = payload or {}
95+
r = requests.post(SCALE_ENDPOINT + endpoint, json=payload,
96+
headers={"Content-Type": "application/json"},
97+
auth=(self.api_key, ''))
98+
99+
if r.status_code == 200:
100+
return r.json()
101+
else:
102+
try:
103+
error = r.json()['error']
104+
except ValueError:
105+
error = r.text
106+
if r.status_code == 400:
107+
raise ScaleInvalidRequest(error, r.status_code)
108+
else:
109+
raise ScaleException(error, r.status_code)
110+
111+
def fetch_task(self, task_id):
112+
"""Fetches a task.
113+
114+
Returns the associated task.
115+
"""
116+
return Task(self._getrequest('task/%s' % task_id), self)
117+
118+
def cancel_task(self, task_id):
119+
"""Cancels a task.
120+
121+
Returns the associated task.
122+
Raises a ScaleException if it has already been canceled.
123+
"""
124+
return Task(self._postrequest('task/%s/cancel' % task_id), self)
125+
126+
def tasks(self, **kwargs):
127+
"""Returns a list of your tasks.
128+
Returns up to 100 at a time, to get more, use the next_token param passed back.
129+
130+
Note that offset is deprecated.
131+
132+
start/end_time are ISO8601 dates, the time range of tasks to fetch.
133+
status can be 'completed', 'pending', or 'canceled'.
134+
type is the task type.
135+
limit is the max number of results to display per page,
136+
next_token can be use to fetch the next page of tasks.
137+
customer_review_status can be 'pending', 'fixed', 'accepted' or 'rejected'.
138+
offset (deprecated) is the number of results to skip (for showing more pages).
139+
"""
140+
allowed_kwargs = {'start_time', 'end_time', 'status', 'type', 'project',
141+
'batch', 'limit', 'offset', 'completed_before', 'completed_after',
142+
'next_token', 'customer_review_status', 'updated_before', 'updated_after'}
143+
for key in kwargs:
144+
if key not in allowed_kwargs:
145+
raise ScaleInvalidRequest('Illegal parameter %s for ScaleClient.tasks()'
146+
% key, None)
147+
response = self._getrequest('tasks', params=kwargs)
148+
docs = [Task(json, self) for json in response['docs']]
149+
return Tasklist(docs, response['total'], response['limit'],
150+
response['offset'], response['has_more'], response.get('next_token'))
151+
152+
def create_task(self, task_type, **kwargs):
153+
endpoint = 'task/' + task_type
154+
taskdata = self._postrequest(endpoint, payload=kwargs)
155+
return Task(taskdata, self)
156+
157+
def create_batch(self, project, batch_name, callback):
158+
payload = dict(project=project, name=batch_name, callback=callback)
159+
batchdata = self._postrequest('batches', payload)
160+
return Batch(batchdata, self)
161+
162+
def get_batch(self, batch_name: str):
163+
batchdata = self._getrequest('batches/%s' % batch_name)
164+
return Batch(batchdata, self)
165+
166+
def list_batches(self, **kwargs):
167+
allowed_kwargs = {'start_time', 'end_time', 'status', 'project',
168+
'batch', 'limit', 'offset', }
169+
for key in kwargs:
170+
if key not in allowed_kwargs:
171+
raise ScaleInvalidRequest('Illegal parameter %s for ScaleClient.tasks()'
172+
% key, None)
173+
response = self._getrequest('tasks', params=kwargs)
174+
docs = [Batch(doc, self) for doc in response['docs']]
175+
return Batchlist(
176+
docs, response['total'], response['limit'], response['offset'],
177+
response['has_more'], response.get('next_token'),
178+
)
179+
180+
181+
def _AddTaskTypeCreator(task_type):
182+
def create_task_wrapper(self, **kwargs):
183+
return self.create_task(task_type, **kwargs)
184+
setattr(ScaleClient, 'create_' + task_type + '_task', create_task_wrapper)
185+
186+
187+
for taskType in TASK_TYPES:
188+
_AddTaskTypeCreator(taskType)

build/lib/scaleapi/batches.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class Batch(object):
2+
def __init__(self, param_dict, client):
3+
self.param_dict = param_dict
4+
self.name = param_dict['name']
5+
self.pending = None
6+
self.completed = None
7+
self.error = None
8+
self.canceled = None
9+
self.client = client
10+
11+
def __hash__(self):
12+
return hash(self.name)
13+
14+
def __str__(self):
15+
return 'Batch(name=%s)' % self.name
16+
17+
def __repr__(self):
18+
return 'Batch(%s)' % self.param_dict
19+
20+
def finalize(self):
21+
return self.client._postrequest("batches/%s/finalize" % self.name)
22+
23+
def get_status(self):
24+
res = self.client._getrequest("batches/%s/status" % self.name)
25+
for stat in ["pending", "completed", "error", "canceled"]:
26+
setattr(self, stat, res.get(stat, 0))

build/lib/scaleapi/tasks.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
class Task(object):
2+
"""Task class, containing task information."""
3+
4+
def __init__(self, param_dict, client):
5+
self.client = client
6+
self.param_dict = param_dict
7+
self.id = param_dict['task_id']
8+
9+
def __getattr__(self, name):
10+
if name in self.param_dict:
11+
return self.param_dict[name]
12+
if name in self.params:
13+
return self.params[name]
14+
raise AttributeError("'%s' object has no attribute %s"
15+
% (type(self).__name__, name))
16+
17+
def __hash__(self):
18+
return hash(self.id)
19+
20+
def __str__(self):
21+
return 'Task(id=%s)' % self.id
22+
23+
def __repr__(self):
24+
return 'Task(%s)' % self.param_dict
25+
26+
def refresh(self):
27+
self.param_dict = self.client._getrequest('task/%s' % self.id)
28+
29+
def cancel(self):
30+
self.client.cancel_task(self.id)

pypi_update_guide.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
_Creating and deploying a new package version is easy_
2+
3+
### Prerequisites
4+
5+
1. Ensure you're on the latest master
6+
7+
2. Ensure you have a PyPI account created and are added as a Collaborator
8+
9+
### Deployment Steps:
10+
11+
**Step 0: Critical - Bump Project Version**
12+
13+
In `setup.py`, you need to specify a new project version.
14+
15+
We use [semantic versioning](https://packaging.python.org/guides/distributing-packages-using-setuptools/#semantic-versioning-preferred). If you are adding a meaningful feature, bump the minor version. If you are fixing a bug, bump the incremental version.
16+
17+
**Step 1: Remove Previous Versions**
18+
19+
Clear out any previously packaged files in the `dist` folder
20+
21+
**Step 2: Create a Source Distribution**
22+
23+
```
24+
python3 setup.py sdist
25+
```
26+
27+
**Step 3: Create `wheel`**
28+
29+
You should also create a wheel for your project. A wheel is a built package that can be installed without needing to go through the “build” process. Installing wheels is substantially faster for the end user than installing from a source distribution
30+
31+
```
32+
python3 setup.py bdist_wheel
33+
```
34+
35+
**Step 4: Install Twine**
36+
37+
Twine is what is used to manage PyPI pacakges
38+
39+
```
40+
pip3 install twine
41+
```
42+
43+
**Step 5: Upload distribution to PyPI**
44+
45+
```
46+
twine upload dist/*
47+
```
48+
49+
**Step 6: Check out the PyPI page to ensure all looks good**
50+
51+
[https://pypi.org/project/scaleapi/](https://pypi.org/project/scaleapi/)

scaleapi/__init__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from collections import namedtuple
21
import requests
32

43
from .tasks import Task
@@ -26,9 +25,11 @@
2625
DEFAULT_LIMIT = 100
2726
DEFAULT_OFFSET = 0
2827

28+
2929
class ScaleException(Exception):
3030
def __init__(self, message, errcode):
31-
super(ScaleException, self).__init__('<Response [{}]> {}'.format(errcode, message))
31+
super(ScaleException, self).__init__(
32+
'<Response [{}]> {}'.format(errcode, message))
3233
self.code = errcode
3334

3435

@@ -147,6 +148,7 @@ def tasks(self, **kwargs):
147148
docs = [Task(json, self) for json in response['docs']]
148149
return Tasklist(docs, response['total'], response['limit'],
149150
response['offset'], response['has_more'], response.get('next_token'))
151+
150152
def create_task(self, task_type, **kwargs):
151153
endpoint = 'task/' + task_type
152154
taskdata = self._postrequest(endpoint, payload=kwargs)
@@ -162,8 +164,8 @@ def get_batch(self, batch_name: str):
162164
return Batch(batchdata, self)
163165

164166
def list_batches(self, **kwargs):
165-
allowed_kwargs = { 'start_time', 'end_time', 'status', 'project',
166-
'batch', 'limit', 'offset', }
167+
allowed_kwargs = {'start_time', 'end_time', 'status', 'project',
168+
'batch', 'limit', 'offset', }
167169
for key in kwargs:
168170
if key not in allowed_kwargs:
169171
raise ScaleInvalidRequest('Illegal parameter %s for ScaleClient.tasks()'
@@ -181,5 +183,6 @@ def create_task_wrapper(self, **kwargs):
181183
return self.create_task(task_type, **kwargs)
182184
setattr(ScaleClient, 'create_' + task_type + '_task', create_task_wrapper)
183185

186+
184187
for taskType in TASK_TYPES:
185188
_AddTaskTypeCreator(taskType)

scaleapi/tasks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
class Task(object):
22
"""Task class, containing task information."""
3+
34
def __init__(self, param_dict, client):
45
self.client = client
56
self.param_dict = param_dict

0 commit comments

Comments
 (0)