Source code for cannabis_reports.request_paginator

# -*- coding: utf-8 -*-
# Copyright 2017-TODAY LasLabs Inc.
# License MIT (https://opensource.org/licenses/MIT).

import requests

from ..exceptions import CannabisReportsRemoteException


[docs]class RequestPaginator(object): """ RequestPaginator provides an iterator based upon an initial request. """ # Response attributes that mean things PAGE_TOTAL = 'total_pages' # Total number of pages PAGE_CURRENT = 'current_page' # Current page number PAGE_DATA = 'data' # Attribute if multiple results SSL_VERIFY = True # Verify SSL # HTTP operation constants DELETE = 'delete' GET = 'get' POST = 'post' PUT = 'put' # Starting page ints page_current = 0 page_total = 0 def __init__(self, endpoint, data=None, output_type=dict, request_type=GET, session=None, iteration_limit=None): """Initialize the RequestPaginator object. Args: endpoint (str): URI endpoint to call. session (requests.Session): The authenticated requests session. data (dict, optional): Data to be sent in the query string for the Request. output_type (type, optional): Class type to output. Object will be instantiated using the current row before output. request_type (str, optional): Type of request to send (``GET`` or ``POST``). session (requests.Session, optional): An authenticated requests session to use. iteration_limit (int, optional): Stop after iterating this many pages. Raises: NotImplementedError: In the event that an invalid request type was defined. """ if request_type not in (self.GET, self.POST, self.PUT, self.DELETE): raise NotImplementedError( 'The `%s` request type is not implemented', request_type, ) self.endpoint = endpoint self.data = data self.output_type = output_type self.request_type = request_type self.session = session or requests.Session() self.limit_iter = iteration_limit def __iter__(self, start_page=1): """Provide an iterator for the remote request. The result is returned as an instantiated `self.output_type`. Args: start_page (int, optional): The page number to start on. Raises: StopIteration: To indicate the end of the data set. Yields: mixed: An instantiated object of the type declared in the ``self.output_type``, using the data from each row of the received data. """ self.page_current = start_page - 1 self.page_total = start_page while self.page_current < self.page_total: data = self.data and self.data.copy() or None result = self.call(data) for row in result: yield self.output_type(**row) iteration_count = (self.page_current + 1) - start_page if self.limit_iter and iteration_count >= self.limit_iter: break raise StopIteration()
[docs] def call(self, data=None): """Generic API caller. Return the JSON decoded result. Args: data (dict, optional): Either the request parameters or the JSON data, depending on the request type. Returns: mixed: JSON decoded response. """ if data is None: data = {} data['page'] = self.page_current + 1 return getattr(self, self.request_type)(data)
[docs] def delete(self, json=None): """Send a DELETE request and return the JSON decoded result. Args: json (dict, optional): Object to encode and send in request. Returns: mixed: JSON decoded response data. """ return self._call('delete', url=self.endpoint, json=json)
[docs] def get(self, params=None): """Send a POST request and return the JSON decoded result. Args: params (dict, optional): Mapping of parameters to send in request. Returns: mixed: JSON decoded response data. """ return self._call('get', url=self.endpoint, params=params)
[docs] def post(self, json=None): """Send a POST request and return the JSON decoded result. Args: json (dict, optional): Object to encode and send in request. Returns: mixed: JSON decoded response data. """ return self._call('post', url=self.endpoint, json=json)
[docs] def put(self, json=None): """Send a PUT request and return the JSON decoded result. Args: json (dict, optional): Object to encode and send in request. Returns: mixed: JSON decoded response data. """ return self._call('put', url=self.endpoint, json=json)
def _call(self, method, *args, **kwargs): """Call the remote service and return the response data.""" assert self.session method = getattr(self.session, method) if not kwargs.get('verify'): kwargs['verify'] = self.SSL_VERIFY return self._handle_response( method(*args, **kwargs), ) def _handle_response(self, response): """Parse the response and return the result.""" response_json = response.json() if response.status_code < 200 or response.status_code >= 300: message = response_json.get('error', response_json.get('message')) raise CannabisReportsRemoteException(response.status_code, message) pagination = response_json.get('meta', {}).get('pagination') if pagination: self.page_current = pagination.get(self.PAGE_CURRENT, 1) self.page_total = pagination.get(self.PAGE_TOTAL, 1) else: # Single page. self.page_current = 1 self.page_total = 1 try: return response_json[self.PAGE_DATA] except KeyError: pass # No data was received, but the request was still a success. return True