Skip to content

Commit ee8184d

Browse files
authored
Merge pull request #11 from skylab-tech/SCP-8-upload-improvements
Scp 8 upload improvements
2 parents 3b0c0cd + cb0914f commit ee8184d

File tree

6 files changed

+64
-32
lines changed

6 files changed

+64
-32
lines changed

exceptions/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .exceptions import JobNotFoundException, StudioException, PhotoNotFoundException
2+
3+
__all__ = ['JobNotFoundException', 'StudioException', 'PhotoNotFoundException']

skylab_studio/studio_exception.py renamed to exceptions/exceptions.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
class JobNotFoundException(Exception):
2+
def __init__(self, message):
3+
super().__init__(message)
4+
class PhotoNotFoundException(Exception):
5+
def __init__(self, message):
6+
super().__init__(message)
7+
18
class StudioException(Exception):
29
def __init__(self, status_code, message="Studio exception occurred"):
310
self.status_code = status_code
411
self.message = message
512
super().__init__(self.message, self.status_code)
6-

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ pyvips==2.0.2
66
requests>=2.0.0
77
requests_mock>=1.5.2
88
decouple>=0.0.7
9+
sentry-sdk>=2.13.0

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
setup(
1313
name='skylab_studio',
14-
version='0.0.14',
14+
version='0.0.15',
1515
author='skylabtech',
1616
author_email='[email protected]',
1717
packages=find_packages(),

skylab_studio/studio_client.py

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
import base64
1515
import hashlib
1616
import requests
17-
from io import BytesIO
17+
import sentry_sdk
1818

1919
from .version import VERSION
20-
from .studio_exception import StudioException
20+
from exceptions import *
2121

2222
API_HEADER_KEY = 'X-SLT-API-KEY'
2323
API_HEADER_CLIENT = 'X-SLT-API-CLIENT'
@@ -62,7 +62,7 @@ def __init__(self, api_key=None, **kwargs):
6262

6363
if 'debug' in kwargs:
6464
self.debug = kwargs['debug']
65-
65+
6666
if 'max_concurrent_downloads' in kwargs:
6767
self.max_concurrent_downloads = kwargs['max_concurrent_downloads']
6868

@@ -72,6 +72,19 @@ def __init__(self, api_key=None, **kwargs):
7272
LOGGER.debug('Debug enabled')
7373
LOGGER.propagate = True
7474

75+
# initialize sentry
76+
sentry_sdk.init(
77+
dsn="https://[email protected]/4507850876452864",
78+
# Set traces_sample_rate to 1.0 to capture 100%
79+
# of transactions for tracing.
80+
traces_sample_rate=1.0,
81+
# Set profiles_sample_rate to 1.0 to profile 100%
82+
# of sampled transactions.
83+
# We recommend adjusting this value in production.
84+
profiles_sample_rate=1.0,
85+
ignore_errors=[JobNotFoundException, PhotoNotFoundException]
86+
)
87+
7588
def _build_http_auth(self):
7689
return (self.api_key, '')
7790

@@ -302,14 +315,19 @@ def _upload_photo(self, photo_path, id, model='job'):
302315
photo_data = { f"{model}_id": id, "name": photo_name, "use_cache_upload": False }
303316

304317
if model == 'job':
305-
job_type = self.get_job(id)['type']
318+
job = self.get_job(id)
319+
if "type" in job:
320+
job_type = job['type']
321+
if job_type == 'regular':
322+
headers = { 'X-Amz-Tagging': 'job=photo&api=true' }
323+
else:
324+
raise JobNotFoundException(f"Unable to find job with id: {id}")
306325

307-
if job_type == 'regular':
308-
headers = { 'X-Amz-Tagging': 'job=photo&api=true' }
309326

310327
# Ask studio to create the photo record
311328
photo_resp = self._create_photo(photo_data)
312-
if not photo_resp:
329+
330+
if not 'id' in photo_resp:
313331
raise Exception('Unable to create the photo object, if creating profile photo, ensure enable_extract and replace_background is set to: True')
314332

315333
photo_id = photo_resp['id']
@@ -329,28 +347,30 @@ def _upload_photo(self, photo_path, id, model='job'):
329347
# PUT request to presigned url with image data
330348
headers["Content-MD5"] = b64md5
331349

332-
try:
333-
upload_photo_resp = requests.put(upload_url, data, headers=headers)
334-
335-
if not upload_photo_resp:
336-
print('First upload attempt failed, retrying...')
337-
retry = 0
338-
# retry upload
339-
while retry < 3:
340-
upload_photo_resp = requests.put(upload_url, data, headers=headers)
341-
342-
if upload_photo_resp:
343-
break # Upload was successful, exit the loop
344-
elif retry == 2: # Check if retry count is 2 (0-based indexing)
345-
raise Exception('Unable to upload to the bucket after retrying.')
346-
else:
347-
time.sleep(1) # Wait for a moment before retrying
348-
retry += 1
350+
retry = 0
351+
while retry < 3:
352+
try:
353+
# attempt to upload the photo to aws
354+
upload_photo_resp = requests.put(upload_url, data, headers=headers)
349355

350-
except Exception as e:
351-
print(f"An exception of type {type(e).__name__} occurred: {e}")
352-
print('Deleting created, but unuploaded photo...')
353-
self.delete_photo(photo_id)
356+
# Will raise exception for any statuses 4xx-5xx
357+
upload_photo_resp.raise_for_status()
358+
359+
# if raise_for_status didn't throw an exception, then we successfully uploaded, exit the loop
360+
break
361+
362+
# rescue any exceptions in the loop
363+
except Exception as e:
364+
# if we've retried 3 times, delete the photo record and raise exception
365+
if retry == 2:
366+
self.delete_photo(photo_id)
367+
368+
raise Exception(e)
369+
# if we haven't retried 3 times, wait for retry+1 seconds and continue the while loop
370+
else:
371+
print(f"Attempt #{retry + 1} to upload failed, retrying...")
372+
retry += 1
373+
time.sleep(retry+1)
354374

355375
res['upload_response'] = upload_photo_resp.status_code
356376
return res
@@ -488,6 +508,9 @@ async def download_photo(self, photo_id, output_path, profile = None, options =
488508
await semaphore.acquire()
489509

490510
photo = self.get_photo(photo_id)
511+
512+
if not 'job' in photo:
513+
raise PhotoNotFoundException(f"Unable to find photo with id: {photo_id}")
491514
profile_id = photo['job']['profileId']
492515
file_name = photo['name']
493516

@@ -531,4 +554,3 @@ async def download_photo(self, photo_id, output_path, profile = None, options =
531554
finally:
532555
if semaphore != None:
533556
semaphore.release()
534-

skylab_studio/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
For more information, visit https://studio.skylabtech.ai
44
"""
55

6-
VERSION = '0.0.14'
6+
VERSION = '0.0.15'

0 commit comments

Comments
 (0)