from typing import Callable, Dict, Iterator, List, Union import requests from requests.exceptions import ConnectTimeout, ConnectionError, ReadTimeout from urllib3.exceptions import ReadTimeoutError def iter_chunk(iterable: list, chunk_size: int = 1) -> Iterator: """Split collection of items on chunks. :param iterable: Collection of items. :param chunk_size: Size of chunks. :return: Iterator of chunks. """ count = len(iterable) for i in range(0, count, chunk_size): yield iterable[i : min(i + chunk_size, count)] def request_in_chunks( chunk_size: int = 300, ) -> Callable: """Execute API requests for chunks of items. Executes original request by synchronous chunks. :param chunk_size: A size of a chunk. :return: Wrapped function. """ def wrapper(f: Callable): def wrapped(*args, **kwargs): result = {} items = args[1] for sequential_chunk in iter_chunk(items, chunk_size): args_list = list(args) args_list[1] = sequential_chunk chunk_result = f(*args_list, **kwargs) result.update(chunk_result) return result return wrapped return wrapper class DigitalServiceProvider: def __init__(self, config, logger): self._config = config self._session = None self.logger = logger @property def session(self): """Session which is used for sending requests to the DSP API.""" if not self._session: self._session = requests.Session() self._session.headers.update({'Authorization': self._config.DSP_AUTH}) return self._session @property def dsp_uri(self): """Uri DSP schema.""" return f'https://{self._config.DSP_API_HOSTNAME}/api/' def _send_request(self, request, timeout=30) -> Union[List[dict], dict]: """Send HTTP request to DSP endpoint.""" prepared_request = self.session.prepare_request(request) try: response = self.session.get(prepared_request.url, timeout=timeout) except (ConnectTimeout, ConnectionError, ReadTimeoutError, ReadTimeout,): self.logger.error('Connection error', exc_info=True) return {} if response.status_code != 200: self.logger.warning(f'Response status {response.status_code} -> {response.text}') return {} return response.json() @request_in_chunks(150) def get_isrc_for_spotify_tracks(self, track_ids: list) -> Dict: """Get isrc list for spotify tracks""" request = requests.Request('GET', f'{self.dsp_uri}spotify/tracks') request.params = [('ids', ','.join(track_ids))] response_json = self._send_request(request) return {track.get('id'): track.get('external_ids', {}).get('isrc') for track in response_json.get('tracks', []) if track.get('id')} @request_in_chunks(150) def get_isrc_for_apple_tracks(self, track_ids: list) -> Dict: """Get isrc list for apple tracks""" request = requests.Request('GET', f'{self.dsp_uri}apple/tracks') request.params = [('ids', ','.join(track_ids))] response_json = self._send_request(request) return {track.get('id'): track.get('attributes', {}).get('isrc') for track in response_json.get('data', []) if track.get('id')}