Skip to content

Commit

Permalink
Added search
Browse files Browse the repository at this point in the history
  • Loading branch information
Tsukasa Hiiragi committed Jul 31, 2014
1 parent 63864da commit cb565ef
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 184 deletions.
111 changes: 4 additions & 107 deletions deezer/async.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
from tornado.gen import coroutine, Return
from tornado.httpclient import AsyncHTTPClient
from deezer.client import Client
from deezer.resources import Album, Artist, Comment, Genre
from deezer.resources import Playlist, Radio, Track, User


class AsyncClient(Client):
Expand Down Expand Up @@ -42,109 +40,8 @@ def get_object(self, object_t, object_id=None, relation=None, **kwargs):
response = yield self._async_client.fetch(url)
resp_str = response.body.decode('utf-8')
if self.output is 'json':
raise Return(json.loads(resp_str))
jsn = json.loads(resp_str)
result = self._process_json(jsn)
else:
raise Return(resp_str)

@coroutine
def get_album(self, object_id):
"""
Get the album with the provided id
:returns: an :class:`~deezer.resources.Album` object
"""
jsn = yield self.get_object('album', object_id)
raise Return(Album(self, jsn))

@coroutine
def get_artist(self, object_id):
"""
Get the artist with the provided id
:returns: an :class:`~deezer.resources.Artist` object
"""
jsn = yield self.get_object('artist', object_id)
raise Return(Artist(self, jsn))

@coroutine
def get_comment(self, object_id):
"""
Get the comment with the provided id
:returns: a :class:`~deezer.resources.Comment` object
"""
jsn = yield self.get_object('comment', object_id)
raise Return(Comment(self, jsn))

@coroutine
def get_genre(self, object_id):
"""
Get the genre with the provided id
:returns: a :class:`~deezer.resources.Genre` object
"""
jsn = yield self.get_object('genre', object_id)
raise Return(Genre(self, jsn))

@coroutine
def get_genres(self):
"""
Returns a list of :class:`~deezer.resources.Genre` objects.
"""
jsn = yield self.get_object('genre')
ret = [Genre(self, genre) for genre in jsn['data']]
raise Return(ret)

@coroutine
def get_playlist(self, object_id):
"""
Get the playlist with the provided id
:returns: a :class:`~deezer.resources.Playlist` object
"""
jsn = yield self.get_object('playlist', object_id)
raise Return(Playlist(self, jsn))

@coroutine
def get_radio(self, object_id=None):
"""
Get the radio with the provided id.
:returns: a :class:`~deezer.resources.Radio` object
"""
jsn = yield self.get_object('radio', object_id)
raise Return(Radio(self, jsn))

@coroutine
def get_track(self, object_id):
"""
Get the track with the provided id
:returns: a :class:`~deezer.resources.Track` object
"""
jsn = yield self.get_object('track', object_id)
raise Return(Track(self, jsn))

@coroutine
def get_user(self, object_id):
"""
Get the user with the provided id
:returns: a :class:`~deezer.resources.User` object
"""
jsn = yield self.get_object('user', object_id)
raise Return(User(self, jsn))

@coroutine
def _get_relation(self, object_t, object_id, relation, **kwargs):
"""
Generic method to load the relation from any resource.
Query the client with the object's known parameters
and try to retrieve the provided relation type. This
is not meant to be used directly by a client, it's more
a helper method for the child objects.
:returns: list of resource objects
"""
jsn = yield self.get_object(object_t, object_id, relation, **kwargs)
raise Return(self._process_relation(jsn))
result = resp_str
raise Return(result)
148 changes: 77 additions & 71 deletions deezer/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
`Deezer API <http://developers.deezer.com/api>`_
"""

import json
try:
from urllib import urlencode
from urllib2 import urlopen
except ImportError:
#python 3
from urllib.parse import urlencode
from urllib.request import urlopen
import json
from deezer.resources import Album, Artist, Comment, Genre
from deezer.resources import Playlist, Radio, Track, User
from deezer.resources import Resource
Expand Down Expand Up @@ -42,6 +42,7 @@ class Client(object):
'genre': Genre,
'playlist': Playlist,
'radio': Radio,
'search': None,
'track': Track,
'user': User,
}
Expand All @@ -58,6 +59,31 @@ def __init__(self, **kwargs):
self.app_secret = kwargs.get('app_secret')
self.access_token = kwargs.get('access_token')

def _process_item(self, item):
"""
Recursively convert dictionary
to :class:`~deezer.resources.Resource` object
:returns: instance of :class:`~deezer.resources.Resource`
"""
for key, value in item.items():
if isinstance(value, dict) and 'type' in value:
item[key] = self._process_item(value)
elif isinstance(value, dict) and 'data' in value:
item[key] = [self._process_item(i) for i in value['data']]
object_t = self.objects_types.get(item['type'], Resource)
return object_t(self, item)

def _process_json(self, jsn):
"""
Convert json to a :class:`~deezer.resources.Resource` object,
or list of :class:`~deezer.resources.Resource` objects.
"""
if 'data' in jsn:
return [self._process_item(item) for item in jsn['data']]
else:
return self._process_item(jsn)

def make_str(self, value):
"""
Convert value to str in python2 and python3 compatible way
Expand All @@ -73,8 +99,8 @@ def make_str(self, value):

@property
def scheme(self):
"""Get the http prefix for the address depending on the
use_ssl attribute
"""
Get the http prefix for the address depending on the use_ssl attribute
"""
return self.use_ssl and 'https' or 'http'

Expand Down Expand Up @@ -118,107 +144,87 @@ def get_object(self, object_t, object_id=None, relation=None, **kwargs):
"""
url = self.object_url(object_t, object_id, relation, **kwargs)
response = urlopen(url)
resp_str = response.read().decode('utf-8')
if self.output is "json":
resp_str = response.read()
try:
return json.loads(resp_str)
except TypeError:
#Python 3
encoding = response.headers.get_content_charset()
decoded_str = resp_str.decode(encoding)
return json.loads(decoded_str)
jsn = json.loads(resp_str)
return self._process_json(jsn)
else:
return response.read()
return resp_str

def get_album(self, object_id):
"""Get the album with the provided id
"""
Get the album with the provided id
:returns: an :class:`~deezer.resources.Album` object"""
jsn = self.get_object("album", object_id)
return Album(self, jsn)
:returns: an :class:`~deezer.resources.Album` object
"""
return self.get_object("album", object_id)

def get_artist(self, object_id):
"""Get the artist with the provided id
"""
Get the artist with the provided id
:returns: an :class:`~deezer.resources.Artist` object"""
jsn = self.get_object("artist", object_id)
return Artist(self, jsn)
:returns: an :class:`~deezer.resources.Artist` object
"""
return self.get_object("artist", object_id)

def get_comment(self, object_id):
"""Get the comment with the provided id
"""
Get the comment with the provided id
:returns: a :class:`~deezer.resources.Comment` object"""
jsn = self.get_object("comment", object_id)
return Comment(self, jsn)
:returns: a :class:`~deezer.resources.Comment` object
"""
return self.get_object("comment", object_id)

def get_genre(self, object_id):
"""Get the genre with the provided id
"""
Get the genre with the provided id
:returns: a :class:`~deezer.resources.Genre` object"""
jsn = self.get_object("genre", object_id)
return Genre(self, jsn)
:returns: a :class:`~deezer.resources.Genre` object
"""
return self.get_object("genre", object_id)

def get_genres(self):
"""
Returns a list of :class:`~deezer.resources.Genre` objects.
:returns: a list of :class:`~deezer.resources.Genre` objects.
"""
jsn = self.get_object("genre")
ret = []
for genre in jsn["data"]:
ret.append(Genre(self, genre))
return ret
return self.get_object("genre")

def get_playlist(self, object_id):
"""Get the playlist with the provided id
"""
Get the playlist with the provided id
:returns: a :class:`~deezer.resources.Playlist` object"""
jsn = self.get_object("playlist", object_id)
return Playlist(self, jsn)
:returns: a :class:`~deezer.resources.Playlist` object
"""
return self.get_object("playlist", object_id)

def get_radio(self, object_id=None):
"""Get the radio with the provided id.
"""
Get the radio with the provided id.
:returns: a :class:`~deezer.resources.Radio` object"""
jsn = self.get_object("radio", object_id)
return Radio(self, jsn)
:returns: a :class:`~deezer.resources.Radio` object
"""
return self.get_object("radio", object_id)

def get_track(self, object_id):
"""Get the track with the provided id
"""
Get the track with the provided id
:returns: a :class:`~deezer.resources.Track` object"""
jsn = self.get_object("track", object_id)
return Track(self, jsn)
:returns: a :class:`~deezer.resources.Track` object
"""
return self.get_object("track", object_id)

def get_user(self, object_id):
"""Get the user with the provided id
:returns: a :class:`~deezer.resources.User` object"""
jsn = self.get_object("user", object_id)
return User(self, jsn)

def _process_relation(self, jsn):
"""
Guess resource class and convert json to resource objects
Get the user with the provided id
:returns: list of resource objects
:returns: a :class:`~deezer.resources.User` object
"""
def _process(item):
object_t = self.objects_types.get(item['type'], Resource)
return object_t(self, item)
if 'data' in jsn:
return [_process(item) for item in jsn['data']]
else:
return [_process(jsn)]
return self.get_object("user", object_id)

def _get_relation(self, object_t, object_id, relation, **kwargs):
def search(self, query, relation='track', **kwargs):
"""
Generic method to load the relation from any resource.
Query the client with the object's known parameters
and try to retrieve the provided relation type. This
is not meant to be used directly by a client, it's more
a helper method for the child objects.
Search track, album, artist or user
:returns: list of resource objects
:returns: a list of :class:`~deezer.resources.Resource` objects.
"""
jsn = self.get_object(object_t, object_id, relation, **kwargs)
return self._process_relation(jsn)
return self.get_object('search', relation, q=query, *kwargs)
2 changes: 1 addition & 1 deletion deezer/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def get_relation(self, relation, **kwargs):
"""
# object_t = self.__class__.__name__.lower()
# pylint: disable=E1101
return self.client._get_relation(self.type, self.id, relation, **kwargs)
return self.client.get_object(self.type, self.id, relation, **kwargs)


class Album(Resource):
Expand Down
9 changes: 4 additions & 5 deletions deezer/tests/test_client_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
from mock import patch


def mocked_get_object(dummy_inst, object_t,
object_id=None, dummy_relation=None):
def mocked_get_object(self, object_t, object_id=None, relation=None, **kwargs):
"""
Basic function to mock the get_object Client's method.
Returns a json object, either with the id provided, or
as list of id's in the data field of a json object.
Returns a :class:`~deezer.resources.Resource` object,
or as list of :class:`~deezer.resources.Resource` objects.
"""
if object_id:
item = {
Expand All @@ -35,7 +34,7 @@ def mocked_get_object(dummy_inst, object_t,
}
]
}
return item
return self._process_json(item)


class TestClient(unittest.TestCase):
Expand Down

0 comments on commit cb565ef

Please sign in to comment.