Skip to content

Paginator

Paginator

Paginator is used to flip through different pages of data that the API returns when searching.

Instead of the user manipulating the URL and parameters, this object handles all of that for them.

When conducting any kind of search the API returns pages of data and each page contains 10 results. This is equivalent to conducting a Google search when Google returns a limited number of links on the first page and all other results are on the next pages.

Using the Paginator object, the user can simply and easily flip through the pages of data the API provides.

Do not create paginator objects

Please note that you are not required or advised to create a paginator object, and instead the Python SDK API object will create a paginator for you, return it, and let you simply use it

Attributes:

Name Type Description
current_page_results List[dict]

List of JSON dictionary results returned from the API

[{result 1}, {result 2}, {result 3}, ...]

Source code in src/cript/api/paginator.py
class Paginator:
    """
    Paginator is used to flip through different pages of data that the API returns when searching.
    > Instead of the user manipulating the URL and parameters, this object handles all of that for them.

    When conducting any kind of search the API returns pages of data and each page contains 10 results.
    This is equivalent to conducting a Google search when Google returns a limited number of links on the first page
    and all other results are on the next pages.

    Using the Paginator object, the user can simply and easily flip through the pages of data the API provides.

    !!! Warning "Do not create paginator objects"
        Please note that you are not required or advised to create a paginator object, and instead the
        Python SDK API object will create a paginator for you, return it, and let you simply use it


    Attributes
    ----------
    current_page_results: List[dict]
        List of JSON dictionary results returned from the API
        ```python
        [{result 1}, {result 2}, {result 3}, ...]
        ```
    """

    _http_headers: dict

    api_endpoint: str

    # if query or page number are None, then it means that api_endpoint does not allow for whatever that is None
    # and that is not added to the URL
    # by default the page_number and query are `None` and they can get filled in
    query: Union[str, None]
    _current_page_number: int

    current_page_results: List[dict]

    @beartype
    def __init__(
        self,
        http_headers: dict,
        api_endpoint: str,
        query: Optional[str] = None,
        current_page_number: int = 0,
    ):
        """
        create a paginator

        1. set all the variables coming into constructor
        1. then prepare any variable as needed e.g. strip extra spaces or url encode query

        Parameters
        ----------
        http_headers: dict
            get already created http headers from API and just use them in paginator
        api_endpoint: str
            api endpoint to send the search requests to
            it already contains what node the user is looking for
        current_page_number: int
            page number to start from. Keep track of current page for user to flip back and forth between pages of data
        query: str
            the value the user is searching for

        Returns
        -------
        None
            instantiate a paginator
        """
        self._http_headers = http_headers
        self.api_endpoint = api_endpoint
        self.query = query
        self._current_page_number = current_page_number

        # check if it is a string and not None to avoid AttributeError
        if api_endpoint is not None:
            # strip the ending slash "/" to make URL uniform and any trailing spaces from either side
            self.api_endpoint = api_endpoint.rstrip("/").strip()

        # check if it is a string and not None to avoid AttributeError
        if query is not None:
            # URL encode query
            self.query = quote(query)

        self.fetch_page_from_api()

    def next_page(self):
        """
        flip to the next page of data.

        Examples
        --------
        ```python
        my_paginator.next_page()
        ```
        """
        self.current_page_number += 1

    def previous_page(self):
        """
        flip to the next page of data.

        Examples
        --------
        ```python
        my_paginator.previous_page()
        ```
        """
        self.current_page_number -= 1

    @property
    @beartype
    def current_page_number(self) -> int:
        """
        get the current page number that you are on.

        Setting the page will take you to that specific page of results

        Examples
        --------
        ```python
        my_paginator.current_page = 10
        ```

        Returns
        -------
        current page number: int
            the current page number of the data
        """
        return self._current_page_number

    @current_page_number.setter
    @beartype
    def current_page_number(self, new_page_number: int) -> None:
        """
        flips to a specific page of data that has been requested

        sets the current_page_number and then sends the request to the API and gets the results of this page number

        Parameters
        ----------
        new_page_number (int): specific page of data that the user wants to go to

        Examples
        --------
        requests.get("https://api.criptapp.org//api?page=2)
        requests.get(f"{self.query}?page={self.current_page_number - 1}")

        Raises
        --------
        InvalidPageRequest, in case the user tries to get a negative page or a page that doesn't exist
        """
        if new_page_number < 0:
            error_message: str = f"Paginator current page number is invalid because it is negative: " f"{self.current_page_number} please set paginator.current_page_number " f"to a positive page number"

            raise RuntimeError(error_message)

        else:
            self._current_page_number = new_page_number
            # when new page number is set, it is then fetched from the API
            self.fetch_page_from_api()

    @beartype
    def fetch_page_from_api(self) -> List[dict]:
        """
        1. builds the URL from the query and page number
        1. makes the request to the API
        1. API responds with a JSON that has data or JSON that has data and result
            1. parses it and correctly sets the current_page_results property

        Raises
        ------
        InvalidSearchRequest
            In case the API responds with an error

        Returns
        -------
        current page results: List[dict]
            makes a request to the API and gets a page of data
        """

        # temporary variable to not overwrite api_endpoint
        temp_api_endpoint: str = self.api_endpoint

        if self.query is not None:
            temp_api_endpoint = f"{temp_api_endpoint}/?q={self.query}"

        elif self.query is None:
            temp_api_endpoint = f"{temp_api_endpoint}/?q="

        temp_api_endpoint = f"{temp_api_endpoint}&page={self.current_page_number}"

        response: requests.Response = requests.get(url=temp_api_endpoint, headers=self._http_headers, timeout=_API_TIMEOUT)

        # it is expected that the response will be JSON
        # try to convert response to JSON
        try:
            api_response: Dict = response.json()

        # if converting API response to JSON gives an error
        # then there must have been an API error, so raise the requests error
        # this is to avoid bad indirect errors and make the errors more direct for users
        except JSONDecodeError:
            response.raise_for_status()

        # handling both cases in case there is result inside of data or just data
        try:
            self.current_page_results = api_response["data"]["result"]
        except KeyError:
            self.current_page_results = api_response["data"]
        except TypeError:
            self.current_page_results = api_response["data"]

        if api_response["code"] == 404 and api_response["error"] == "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.":
            self.current_page_results = []
            return self.current_page_results

        # if API response is not 200 raise error for the user to debug
        if api_response["code"] != 200:
            raise APIError(api_error=str(response.json()), http_method="GET", api_url=temp_api_endpoint)

        return self.current_page_results

current_page_number: int property writable

get the current page number that you are on.

Setting the page will take you to that specific page of results

Examples:

my_paginator.current_page = 10

Returns:

Type Description
current page number: int

the current page number of the data

__init__(http_headers, api_endpoint, query=None, current_page_number=0)

create a paginator

  1. set all the variables coming into constructor
  2. then prepare any variable as needed e.g. strip extra spaces or url encode query

Parameters:

Name Type Description Default
http_headers dict

get already created http headers from API and just use them in paginator

required
api_endpoint str

api endpoint to send the search requests to it already contains what node the user is looking for

required
current_page_number int

page number to start from. Keep track of current page for user to flip back and forth between pages of data

0
query Optional[str]

the value the user is searching for

None

Returns:

Type Description
None

instantiate a paginator

Source code in src/cript/api/paginator.py
@beartype
def __init__(
    self,
    http_headers: dict,
    api_endpoint: str,
    query: Optional[str] = None,
    current_page_number: int = 0,
):
    """
    create a paginator

    1. set all the variables coming into constructor
    1. then prepare any variable as needed e.g. strip extra spaces or url encode query

    Parameters
    ----------
    http_headers: dict
        get already created http headers from API and just use them in paginator
    api_endpoint: str
        api endpoint to send the search requests to
        it already contains what node the user is looking for
    current_page_number: int
        page number to start from. Keep track of current page for user to flip back and forth between pages of data
    query: str
        the value the user is searching for

    Returns
    -------
    None
        instantiate a paginator
    """
    self._http_headers = http_headers
    self.api_endpoint = api_endpoint
    self.query = query
    self._current_page_number = current_page_number

    # check if it is a string and not None to avoid AttributeError
    if api_endpoint is not None:
        # strip the ending slash "/" to make URL uniform and any trailing spaces from either side
        self.api_endpoint = api_endpoint.rstrip("/").strip()

    # check if it is a string and not None to avoid AttributeError
    if query is not None:
        # URL encode query
        self.query = quote(query)

    self.fetch_page_from_api()

fetch_page_from_api()

  1. builds the URL from the query and page number
  2. makes the request to the API
  3. API responds with a JSON that has data or JSON that has data and result
    1. parses it and correctly sets the current_page_results property

Raises:

Type Description
InvalidSearchRequest

In case the API responds with an error

Returns:

Type Description
current page results: List[dict]

makes a request to the API and gets a page of data

Source code in src/cript/api/paginator.py
@beartype
def fetch_page_from_api(self) -> List[dict]:
    """
    1. builds the URL from the query and page number
    1. makes the request to the API
    1. API responds with a JSON that has data or JSON that has data and result
        1. parses it and correctly sets the current_page_results property

    Raises
    ------
    InvalidSearchRequest
        In case the API responds with an error

    Returns
    -------
    current page results: List[dict]
        makes a request to the API and gets a page of data
    """

    # temporary variable to not overwrite api_endpoint
    temp_api_endpoint: str = self.api_endpoint

    if self.query is not None:
        temp_api_endpoint = f"{temp_api_endpoint}/?q={self.query}"

    elif self.query is None:
        temp_api_endpoint = f"{temp_api_endpoint}/?q="

    temp_api_endpoint = f"{temp_api_endpoint}&page={self.current_page_number}"

    response: requests.Response = requests.get(url=temp_api_endpoint, headers=self._http_headers, timeout=_API_TIMEOUT)

    # it is expected that the response will be JSON
    # try to convert response to JSON
    try:
        api_response: Dict = response.json()

    # if converting API response to JSON gives an error
    # then there must have been an API error, so raise the requests error
    # this is to avoid bad indirect errors and make the errors more direct for users
    except JSONDecodeError:
        response.raise_for_status()

    # handling both cases in case there is result inside of data or just data
    try:
        self.current_page_results = api_response["data"]["result"]
    except KeyError:
        self.current_page_results = api_response["data"]
    except TypeError:
        self.current_page_results = api_response["data"]

    if api_response["code"] == 404 and api_response["error"] == "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.":
        self.current_page_results = []
        return self.current_page_results

    # if API response is not 200 raise error for the user to debug
    if api_response["code"] != 200:
        raise APIError(api_error=str(response.json()), http_method="GET", api_url=temp_api_endpoint)

    return self.current_page_results

next_page()

flip to the next page of data.

Examples:

my_paginator.next_page()
Source code in src/cript/api/paginator.py
def next_page(self):
    """
    flip to the next page of data.

    Examples
    --------
    ```python
    my_paginator.next_page()
    ```
    """
    self.current_page_number += 1

previous_page()

flip to the next page of data.

Examples:

my_paginator.previous_page()
Source code in src/cript/api/paginator.py
def previous_page(self):
    """
    flip to the next page of data.

    Examples
    --------
    ```python
    my_paginator.previous_page()
    ```
    """
    self.current_page_number -= 1