"""Beatport API Wrapper module.""" import time import requests from feed_ingestion import logger from feed_ingestion.flows.beatport import config class BeatportAPI(object): """Wrapper class to abstract loading data from Beatport API.""" _token = None _version = 'v4' _oauth_url = 'https://api.beatport.com/{version}/auth/o/token/' _sales_export_url = ( 'https://api.beatport.com/{version}/catalog' '/suppliers/{supplier_id}/sales-export') _sales_export_job_url = ( 'https://api.beatport.com/{version}/jobs' '/sales-export/{guid}') def __init__( self, supplier_id, client_id, client_secret, username, password, version=None ): """Class constructor. Args: supplier_id (int): The supplierId of Beatport. client_id (str): The account name. client_secret (str): The secret password. username (str): The username, for example 'sony'. password(str):password version (str): The api version. """ self.supplier_id = supplier_id self.client_id = client_id self.client_secret = client_secret self.username = username self.password = password self.version = version or self._version @property def token(self): """Beatport API oauth token. Requests oauth token from Beatport and caches it for the future uses. Returns: str: oauth token. """ if self._token is None: self._token = self._request_token() return self._token def _request_token(self): """Request oauth token from Beatport. Returns: str: oauth token. """ post_fields = { 'client_id': self.client_id, 'client_secret': self.client_secret, 'username': self.username, 'password': self.password, 'grant_type': 'password' } res = requests.post( self._oauth_url.format(version=self._version), data=post_fields) if res.status_code != 200: raise requests.RequestException(response=res) return res.json()['access_token'] def _call_sales_export_job(self, data): """Beatport sales-export job by supplier. Requests sales-export from Beatport to trigger the report creation. Args: data (int): basic params with token. Returns: int: GUID from Beatport. """ res = requests.get( self._sales_export_url.format( supplier_id=self.supplier_id, version=self._version ), params=data) if res.status_code != 200: raise requests.RequestException(response=res) return res.json()['id'] def _get_job_status(self, guid, data): """Beatport sales-export job status. Requests sales-export from Beatport to get job status. Args: guid (int): Unique Id from acknowledgement endpoint. data (int): basic params with token. Returns: json: response from Beatport. """ url = self._sales_export_job_url.format( guid=guid, version=self._version) res = requests.get(url, params=data) if res.status_code != 200: raise requests.RequestException(response=res) return res.json() def _get_child_job_id(self, guid, data): """Beatport sales-export acknowledge. Requests sales-export from Beatport to get acknowledgement and child_job_id. Args: guid (int): Unique Id from acknowledgement endpoint. data (int): basic params with token. Returns: str: child_job_id from Beatport. """ max_retry = config.MAX_RETRY_LIMIT while max_retry: res_data = self._get_job_status(guid, data) logger.info('sales-export job status : {status}'.format( status=res_data['state'])) if res_data['state'] == 'DONE': return res_data['child_job_id'] """wait till the job completes.""" time.sleep(5) max_retry -= 1 logger.info('Retrying to get sales-export job status.') def _get_download_job_status(self, guid, data): """Beatport sales-export download job status. Requests download job status from Beatport. Args: guid (int): Unique Id from sales-export acknowledgment job. data (int): basic params with token. Returns: json: response from Beatport. """ url = self._sales_export_job_url.format( guid=guid, version=self._version) res = requests.get(url, params=data) if res.status_code != 200: raise requests.RequestException(response=res) return res.json() def _get_download_url(self, guid, data): """Beatport sales-export download url. Requests sales-export from Beatport to get download url. Args: guid (int): Unique Id from sales-export job. data (int): basic params with token. Returns: str: download url from Beatport. """ max_retry = config.MAX_RETRY_LIMIT while max_retry: res_data = self._get_download_job_status(guid, data) if res_data['state'] == 'DONE': return res_data['download_url'] """wait till the download_url is ready.""" time.sleep(5) max_retry -= 1 def download_file(self, file, date): """Download sales-export report from Beatport. Args: fd (file): File object or writable buffer. date (date or str): date instance or `YYYY-MM-DD` formatted string. """ data = { 'access_token': self.token, 'date_range': '{start_date}:{end_date}'.format(start_date=date, end_date=date) } """trigger sales-export job""" guid = self._call_sales_export_job(data) logger.info('sales-export job triggered : {guid}'.format(guid=guid)) """acknowledgement endpoint and get guid from response""" child_job_id = self._get_child_job_id(guid, data) """get download url""" download_url = self._get_download_url(guid=child_job_id, data=data) """download file and write in filesystem""" s = requests.Session() res = s.get(download_url, stream=True) res.raise_for_status() for chunk in res.iter_content(chunk_size=1024*1024): file.write(chunk)