Skip to content

Commit

Permalink
Merge branch 'develop': v 1.1.5. Quota status and delete image
Browse files Browse the repository at this point in the history
  • Loading branch information
mahenzon committed Aug 13, 2018
2 parents cec55ca + f864892 commit 987314e
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 11 deletions.
2 changes: 1 addition & 1 deletion aioalice/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())


__version__ = '1.1.4'
__version__ = '1.1.5'
60 changes: 57 additions & 3 deletions aioalice/dispatcher/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .filters import generate_default_filters, ExceptionsFilter
from ..utils import json, exceptions

from ..types import UploadedImage
from ..types import UploadedImage, Quota
# log = logging.getLogger(__name__)


Expand Down Expand Up @@ -168,11 +168,16 @@ async def get_images(self, skill_id=None, oauth_token=None):
'''
Get uploaded images
:param skill_id: Provide if was not set at the Dispatcher init
:type skill_id: :obj:`str`
:param oauth_token: Provide if was not set at the Dispatcher init
:type oauth_token: :obj:`str`
:return: list of UploadedImage instances
'''
skill_id, oauth_token = self._check_auth(skill_id, oauth_token)
result = await api.request(
self.session, skill_id, oauth_token,
self.session, oauth_token, skill_id,
api.Methods.IMAGES, request_method='GET'
)
if 'images' not in result:
Expand All @@ -183,6 +188,13 @@ async def upload_image(self, image_url_or_bytes, skill_id=None, oauth_token=None
'''
Upload image by either url or bytes
:param image_url_or_bytes: Image URL or bytes
:type image_url_or_bytes: :obj:`str` or `io.BytesIO`
:param skill_id: Provide if was not set at the Dispatcher init
:type skill_id: :obj:`str`
:param oauth_token: Provide if was not set at the Dispatcher init
:type oauth_token: :obj:`str`
:return: UploadedImage
'''
skill_id, oauth_token = self._check_auth(skill_id, oauth_token)
Expand All @@ -193,9 +205,51 @@ async def upload_image(self, image_url_or_bytes, skill_id=None, oauth_token=None
else:
file = image_url_or_bytes
result = await api.request(
self.session, skill_id, oauth_token,
self.session, oauth_token, skill_id,
api.Methods.IMAGES, json, file
)
if 'image' not in result:
raise exceptions.ApiChanged(f'Expected "image" in result, got {result}')
return UploadedImage(**result['image'])

async def get_images_quota(self, oauth_token=None):
'''
Get images storage quota
:param oauth_token: Provide if was not set at the Dispatcher init
:type oauth_token: :obj:`str`
:return: Quota
'''
oauth_token = oauth_token or self.oauth_token
if oauth_token is None:
raise exceptions.AuthRequired('Please provide oauth_token')

result = await api.request(
self.session, oauth_token, request_method='GET',
custom_url=api.BASE_URL + api.Methods.STATUS
)
if 'images' not in result or 'quota' not in result['images']:
raise exceptions.ApiChanged(f'Expected "images" "quota" in result, got {result}')
return Quota(**result['images']['quota'])

async def delete_image(self, image_id, skill_id=None, oauth_token=None):
'''
Delete image by id
:param image_id: Image id to be deleted
:type image_id: :obj:`str`
:param skill_id: Provide if was not set at the Dispatcher init
:type skill_id: :obj:`str`
:param oauth_token: Provide if was not set at the Dispatcher init
:type oauth_token: :obj:`str`
:return: True if result is ok
'''
skill_id, oauth_token = self._check_auth(skill_id, oauth_token)
url = api.Methods.api_url(skill_id, api.Methods.IMAGES) + image_id
result = await api.request(
self.session, oauth_token,
request_method='DELETE', custom_url=url
)
return result['result'] == 'ok' if 'result' in result else False
22 changes: 16 additions & 6 deletions aioalice/dispatcher/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from ..utils import json, exceptions
from ..utils.helper import Helper, HelperMode, Item

API_URL = 'https://dialogs.yandex.net/api/v1/skills/{skill_id}/{method}'
BASE_URL = 'https://dialogs.yandex.net/api/v1/'
API_URL = BASE_URL + 'skills/{skill_id}/{method}/'

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -36,28 +37,36 @@ async def _check_result(response):
exceptions.DialogsAPIError.detect(description)


async def request(session, skill_id, oauth_token, method, json=None, file=None, request_method='POST', **kwargs):
async def request(session, oauth_token, skill_id=None, method=None, json=None,
file=None, request_method='POST', custom_url=None, **kwargs):
"""
Make a request to API
:param session: HTTP Client session
:type session: :obj:`aiohttp.ClientSession`
:param skill_id: skill_id
:type skill_id: :obj:`str`
:param oauth_token: oauth_token
:type oauth_token: :obj:`str`
:param method: API method
:param skill_id: skill_id. Optional. Not used if custom_url is provided
:type skill_id: :obj:`str`
:param method: API method. Optional. Not used if custom_url is provided
:type method: :obj:`str`
:param json: request payload
:type json: :obj: `dict`
:param file: file
:type file: :obj: `io.BytesIO`
:param request_method: API request method
:type request_method: :obj:`str`
:param custom_url: Yandex has very developer UNfriendly API, so some endpoints cannot be achieved by standatd template.
:type custom_url: :obj:`str`
:return: result
:rtype: ::obj:`dict`
"""
log.debug("Making a `%s` request to %r with json `%r` or file `%r`",
request_method, method, json, file)
url = Methods.api_url(skill_id, method)
if custom_url is None:
url = Methods.api_url(skill_id, method)
else:
url = custom_url
headers = {'Authorization': oauth_token}
data = None
if file:
Expand All @@ -75,6 +84,7 @@ class Methods(Helper):
mode = HelperMode.lowerCamelCase

IMAGES = Item() # images
STATUS = Item() # status

@staticmethod
def api_url(skill_id, method):
Expand Down
1 change: 1 addition & 0 deletions aioalice/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .request import Request, RequestType
from .session import BaseSession, Session

from .quota import Quota
from .uploaded_image import UploadedImage
from .media_button import MediaButton
from .image import Image
Expand Down
13 changes: 13 additions & 0 deletions aioalice/types/quota.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from attr import attrs, attrib
from . import AliceObject


@attrs
class Quota(AliceObject):
"""Quota object. Values in bytes"""
total = attrib(type=int)
used = attrib(type=int)
available = attrib(init=False, default=0)

def __attrs_post_init__(self):
self.available = self.total - self.used
4 changes: 4 additions & 0 deletions aioalice/utils/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ class Forbidden(DialogsAPIError, match='Forbidden'):

class ContentNotProvided(DialogsAPIError, match='URL or FILE is needed'):
'''Is thrown when no image is provided within request'''


class InvalidImageID(DialogsAPIError, match='Invalid image ID'):
'''Is thrown if a wrong image_id is provided within request'''
1 change: 1 addition & 0 deletions examples/README-en.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
- [Skip Handlers, log requests](skip_handler_log_everything.py)
- [Get uploaded images](get_images.py)
- [Upload an image](upload_image.py)
- [Get quota status and delete an image](quota_status_and_delete_image.py)
- [Send Big Image Card](card_big_image.py)
- [Send Items List Card](card_items_list.py)
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
- [Пропускаем хэндлеры, логгируем запросы](skip_handler_log_everything.py)
- [Проверить загруженные изображения](get_images.py)
- [Загрузить изображение](upload_image.py)
- [Проверить занятое место и удалить изображение](quota_status_and_delete_image.py)
- [Отправить карточку с одним большим изображением](card_big_image.py)
- [Отправить карточку с альбомом из нескольких изображений](card_items_list.py)
59 changes: 59 additions & 0 deletions examples/quota_status_and_delete_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import asyncio
import logging

from aioalice import Dispatcher

logging.basicConfig(format=u'%(filename)s [LINE:%(lineno)d] #%(levelname)-8s [%(asctime)s] %(message)s',
level=logging.INFO)


# Provide your own skill_id and token!
SKILL_ID = '12a34567-c42d-9876-0e3f-123g55h12345'
OAUTH_TOKEN = 'OAuth AQAAAAABCDEFGHI_jklmnopqrstuvwxyz'

# You can create dp instance without auth: `dp = Dispatcher()`,
# but you'll have to provide it in request
dp = Dispatcher(skill_id=SKILL_ID, oauth_token=OAUTH_TOKEN)


async def check_quota_status_and_delete_image():
try:
# Use `await dp.get_images_quota(OAUTH_TOKEN)`
# If token was not provided on dp's initialisation
quota_before = await dp.get_images_quota()

# Use `await dp.delete_image(image_id, SKILL_ID, OAUTH_TOKEN)`
# If tokens were not provided on dp's initialisation
success = await dp.delete_image('1234567/ff123f70e0e0cf70079a')

# recheck to see the difference
quota_after = await dp.get_images_quota()
except Exception:
logging.exception('Oops!')
else:
print(quota_before)
print('Success?', success)
print(quota_after)
print('Freed up', quota_after.available - quota_before.available, 'bytes')

''' Output:
Quota(total=104857600, used=10423667, available=94433933)
Success? True
Quota(total=104857600, used=10383100, available=94474500)
Freed up 40567 bytes
'''

# You have to close session manually
# if you called any request outside web app
# Session close is added to on_shutdown list
# in webhhok.configure_app
await dp.close()


loop = asyncio.get_event_loop()
tasks = [
loop.create_task(check_quota_status_and_delete_image()),
]
wait_tasks = asyncio.wait(tasks)
loop.run_until_complete(wait_tasks)
loop.close()
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
if sys.version_info < MINIMAL_PY_VERSION:
raise RuntimeError('aioalice works only with Python {}+'.format('.'.join(map(str, MINIMAL_PY_VERSION))))

__version__ = '1.1.4'
__version__ = '1.1.5'


def get_description():
Expand Down

0 comments on commit 987314e

Please sign in to comment.