]> kaliko git repositories - mpd-sima.git/blobdiff - sima/lib/simaecho.py
http client/cache controller refactoring
[mpd-sima.git] / sima / lib / simaecho.py
index 27bbce2d0f122f8952438aafbf2979538a616f37..3f649a6575faf13849006403dd7e1493eb997641 100644 (file)
 Consume EchoNest web service
 """
 
 Consume EchoNest web service
 """
 
-__version__ = '0.0.2'
+__version__ = '0.0.3'
 __author__ = 'Jack Kaliko'
 
 
 __author__ = 'Jack Kaliko'
 
 
-from datetime import timedelta
-
-from requests import Session, Request, Timeout, ConnectionError
 
 from sima import ECH
 from sima.lib.meta import Artist
 from sima.lib.track import Track
 
 from sima import ECH
 from sima.lib.meta import Artist
 from sima.lib.track import Track
-from sima.lib.http import CacheController
-from sima.utils.utils import WSError, WSNotFound, WSTimeout, WSHTTPError
-from sima.utils.utils import getws, Throttle
+from sima.lib.http import HttpClient
+from sima.utils.utils import WSError, WSNotFound
+from sima.utils.utils import getws
 if len(ECH.get('apikey')) == 23:  # simple hack allowing imp.reload
     getws(ECH)
 
 if len(ECH.get('apikey')) == 23:  # simple hack allowing imp.reload
     getws(ECH)
 
-# Some definitions
-WAIT_BETWEEN_REQUESTS = timedelta(0, 2)
-SOCKET_TIMEOUT = 6
-
 
 class SimaEch:
     """EchoNest http client
 
 class SimaEch:
     """EchoNest http client
@@ -50,49 +43,13 @@ class SimaEch:
     ratelimit = None
     name = 'EchoNest'
     cache = False
     ratelimit = None
     name = 'EchoNest'
     cache = False
-    stats = {'304':0, 'cached':0, 'minrl':'120'}
+    stats = {'etag':0,
+             'ccontrol':0,
+             'minrl':120,
+             'total':0}
 
     def __init__(self):
 
     def __init__(self):
-        self.controller = CacheController(self.cache)
-
-    def _fetch(self, ressource, payload):
-        """
-        Prepare http request
-        Use cached elements or proceed http request
-        """
-        req = Request('GET', ressource, params=payload,
-                      ).prepare()
-        if self.cache:
-            cached_response = self.controller.cached_request(req.url, req.headers)
-            if cached_response:
-                SimaEch.stat.update(cached=SimaEch.stat.get('cached')+1)
-                return cached_response.json()
-        try:
-            return self._fetch_ws(req)
-        except Timeout:
-            raise WSTimeout('Failed to reach server within {0}s'.format(
-                               SOCKET_TIMEOUT))
-        except ConnectionError as err:
-            raise WSError(err)
-
-    @Throttle(WAIT_BETWEEN_REQUESTS)
-    def _fetch_ws(self, prepreq):
-        """fetch from web service"""
-        sess = Session()
-        resp = sess.send(prepreq, timeout=SOCKET_TIMEOUT)
-        if resp.status_code == 304:
-            SimaEch.stats.update({'304':SimaEch.stats.get('304')+1})
-            resp = self.controller.update_cached_response(prepreq, resp)
-        elif resp.status_code != 200:
-            raise WSHTTPError('{0.status_code}: {0.reason}'.format(resp))
-        ans = resp.json()
-        self._controls_answer(ans)
-        SimaEch.ratelimit = resp.headers.get('x-ratelimit-remaining', None)
-        minrl = min(SimaEch.ratelimit, SimaEch.stats.get('minrl'))
-        SimaEch.stats.update(minrl=minrl)
-        if self.cache:
-            self.controller.cache_response(resp.request, resp)
-        return ans
+        self.http = HttpClient(cache=self.cache, stats=self.stats)
 
     def _controls_answer(self, ans):
         """Controls answer.
 
     def _controls_answer(self, ans):
         """Controls answer.
@@ -127,7 +84,9 @@ class SimaEch:
                 payload.update(artist=name)
             payload.update(results=100)
             payload.update(sort='song_hotttnesss-desc')
                 payload.update(artist=name)
             payload.update(results=100)
             payload.update(sort='song_hotttnesss-desc')
-        return payload
+        # > hashing the URL into a cache key
+        # return a sorted list of 2-tuple to have consistent cache
+        return sorted(payload.items(), key=lambda param: param[0])
 
     def get_similar(self, artist=None):
         """Fetch similar artists
 
     def get_similar(self, artist=None):
         """Fetch similar artists
@@ -135,8 +94,9 @@ class SimaEch:
         payload = self._forge_payload(artist)
         # Construct URL
         ressource = '{0}/artist/similar'.format(SimaEch.root_url)
         payload = self._forge_payload(artist)
         # Construct URL
         ressource = '{0}/artist/similar'.format(SimaEch.root_url)
-        ans = self._fetch(ressource, payload)
-        for art in ans.get('response').get('artists'):
+        ans = self.http(ressource, payload)
+        self._controls_answer(ans.json())
+        for art in ans.json().get('response').get('artists'):
             mbid = None
             if 'foreign_ids' in art:
                 for frgnid in art.get('foreign_ids'):
             mbid = None
             if 'foreign_ids' in art:
                 for frgnid in art.get('foreign_ids'):
@@ -151,13 +111,14 @@ class SimaEch:
         payload = self._forge_payload(artist, top=True)
         # Construct URL
         ressource = '{0}/song/search'.format(SimaEch.root_url)
         payload = self._forge_payload(artist, top=True)
         # Construct URL
         ressource = '{0}/song/search'.format(SimaEch.root_url)
-        ans = self._fetch(ressource, payload)
+        ans = self.http(ressource, payload)
+        self._controls_answer(ans.json())
         titles = list()
         art = {
                 'artist': artist.name,
                 'musicbrainz_artistid': artist.mbid,
                 }
         titles = list()
         art = {
                 'artist': artist.name,
                 'musicbrainz_artistid': artist.mbid,
                 }
-        for song in ans.get('response').get('songs'):
+        for song in ans.json().get('response').get('songs'):
             title = song.get('title')
             if title not in titles:
                 titles.append(title)
             title = song.get('title')
             if title not in titles:
                 titles.append(title)