Module presalytics.client.api

Expand source code Browse git
import os
import cgi
import webbrowser
import time
import requests
import urllib.parse
import importlib.util
import logging
import json
import environs
import wsgi_microservice_middleware
import functools
import six
import mimetypes
import typing
import io
import time
import presalytics
import presalytics.lib.exceptions
import presalytics.lib.constants as cnst
import presalytics.client.auth
import presalytics.client.oidc
import presalytics.client.presalytics_ooxml_automation.api_client
import presalytics.client.presalytics_story.api_client
import presalytics.client.presalytics_doc_converter.api_client
from uuid import uuid4
from werkzeug.datastructures import FileStorage

logger = logging.getLogger(__name__)

env = environs.Env()


class Client(object):
    """ Class for interacting with Presalytics API endpoints

    The Client class creates a simple interface for user to interactive with the 
    Presalytics API and is the primary building block for user-built automation of stories, 
    dashboards, and interactive presentations.

    A client instance wraps python functions around Presalytics API endpoints and
    manages user authentication. On initialization, he client checks the status of 
    a user authentication the expiry of their refresh an access tokens.  When needed,
    the client will open a browser to prompt the user to login at the presalytics.io
    login page (or raise an `presalytics.lib.exceptions.InvalidTokenException` when 
    `delegate_login` is `True`).

    After authenication, users can call the methods bound to the story, ooxml_automation, 
    and doc_converter attributes to make calls in into the Presalytics API.

    *A note for server-side development*:

    The client class can automatically cache tokens in a file called 
    "token.json", located in the python's current working directory.  This is done so
    users running scripts accross multiple client instances do not have to acquire a new token 
    every time an API call is made. If building a client to operate in a multi-user environment, 
    this behavior should be turned off so that one user cannot not pull one another's tokens.
    To do this, ensure the following parameters are pass to the configuration either 
    via initialization or in a `presalytics.CONFIG` file: 
    
        cache_tokens = False,
        delegate_login = True
    
    When delegate login is True, the client assumes that the application creating 
    instances of the client object will handle user authentication.  The simplest way
    to do this is to pass a token to the client via the "token" keyword argument.

    Parameters
    ----------

    username : str, optional
        Defaults to None.  The user's Presalytics API username.  This keyword will take precedence over a passed to the client
        via `presalytics.CONFIG`.  The username must either be present in `presalytics.CONFIG` or be passed in 
        via keyword, otherwise the client will raise a `presalytics.lib.exceptions.MissingConfigException`.

    password : str, optional
        Defaults to None.  The user's Presalytics API password.  This useful for quickly testing scripts, but in most 
        scenario users should not be passing plaintext into the client via this keyword.  In a secure, single-user 
        environment, passwords are better placed in the `presalytics.CONFIG` object for reuseability.  A more secure
        is to leave passwords out of the configuration, keep `delegate_login` = `False`, and acquire tokens via the browser.
 
    delegate_login : bool, optional
        Defaults to False.  Indicates whether the client would redirect to a browser to 
        acquire an API token. If `DELEGATE_LOGIN` is `True`, when the `presalytics.client.api.Client` does not have 
        access to a valid API token, the client will raise a `presalytics.lib.exceptions.InvalidTokenException`.
        The default operation will automatically open a new browser tab to acquire a new token 
        via website client from the presalytics.io login page.  Putting this setting to True is
        useful for server-side development.

    token : dict, optional
        Defaults to None.  A dictionary contain information about tokens acquire from auth.presalytics.io.  The 
        dictionary must contain an `access_token`, a `refresh_token`, and entries contiaing information about token expiry.  

        Token expiry information can either passed in ISO 8601 formatted string with a UTC offset as dictionary keys
        `access_token_expire_time` and `refresh_token_expire_time` or an integer in seconds with the corresponding 
        dictionary keys`expires_in` and `refresh_expires_in`.
        
        if the `dict` passed in via this keywork does is not have the correct entries, the client will
        raise an `presalytics.lib.exceptions.InvalidTokenException`.

    cache_tokens : bool, optional
        Defaults to False.  Toggles whether or the client should cache its acquired tokens in file called "token.json"
        in the current working directory.  Minimizes the number of times a user is required to login.  Set to False
        in multi-user environments.

    Attributes
    ----------

    direct_grant : bool
        Indicates whether an token will be acquire via the "direct_grant" OpenID Connect flow.  Usually indicates
        whether the user has supplied a passwork to the client either through `presalytics.CONFIG` ro 
        during object initialization.

    doc_converter : presalytics.client.presalytics_doc_converter.api.default_api.DefaultApi
        Interface to the Presalytics API Doc Converter service.  The object contains methods that enable
        the client to make api calls that return deserialized objects from the Presalytics API,
        simplying user and developer interaction with the Presaltytics API.  API calls can be generated
        as follows:

            client = presalytics.Client()
            api_obj = client.doc_converter.{operation_id}(*args)

        where `{operation_id}` is the `operationId` assocated with the endpoint specified the [Doc Converter 
        Service OpenAPI Contract](https://presalytics.io/docs/api-specifications/doc-converter/) , and *args
        are the corresponding arguments that are passed to the method.  A complete list of the avialable
        methods is shown on the `presalytics.client.presalytics_doc_converter.api.default_api.DefaultApi` object.
        
        *Note*:
        This attribute contains automatically generated methods via 
        the [OpenAPI generator](https://github.com/OpenAPITools/openapi-generator).  The 
        `presalytics.client.presalytics_doc_converter.api.default_api.DefaultApi` has been passed an an `api_client`
        keyword argument with an instance of `presalytics.client.api.DocConverterApiClientWithAuth`, which adds 
        an authentication and request processing middleware layer to the default sub package 
        built via code generatation.

    ooxml_automation : presalytics.client.presalytics_ooxml_automation.api.default_api.DefaultApi
        Interface to the Presalytics API Ooxml Automation service.  The object contains methods that enable
        the client to make api calls that return deserialized objects from the Presalytics API,
        simplying user and developer interaction with the Presaltytics API.  API calls can be generated
        as follows:

            client = presalytics.Client()
            api_obj = client.ooxml_automation.{operation_id}(*args)

        where `{operation_id}` is the `operationId` assocated with the endpoint specified the [Ooxml Automation
        Service OpenAPI Contract](https://presalytics.io/docs/api-specifications/ooxml-automation/) , and *args
        are the corresponding arguments that are passed to the method.  A complete list of the avialable
        methods is shown on the `presalytics.client.presalytics_ooxml_automation.api.default_api.DefaultApi` object.
        
        *Note*:
        This attribute contains automatically generated methods via 
        the [OpenAPI generator](https://github.com/OpenAPITools/openapi-generator).  The 
        `presalytics.client.presalytics_ooxml_automation.api.default_api.DefaultApi` has been passed an an `api_client`
        keyword argument with an instance of `presalytics.client.api.OoxmlAutomationApiClientWithAuth`, which adds 
        an authentication and request processing middleware layer to the default sub package 
        built via code generatation.

    story : presalytics.client.presalytics_story.api.default_api.DefaultApi
        Interface to the Presalytics API Ooxml Automation service.  The object contains methods that enable
        the client to make api calls that return deserialized objects from the Presalytics API,
        simplying user and developer interaction with the Presaltytics API.  API calls can be generated
        as follows:

            client = presalytics.Client()
            api_obj = client.story.{operation_id}(*args)

        where `{operation_id}` is the `operationId` assocated with the endpoint specified the [Ooxml Automation
        Service OpenAPI Contract](https://presalytics.io/docs/api-specifications/story/) , and *args
        are the corresponding arguments that are passed to the method.  A complete list of the avialable
        methods is shown on the `presalytics.client.presalytics_story.api.default_api.DefaultApi` object.
        
        *Note*:
        This attribute contains automatically generated methods via 
        the [OpenAPI generator](https://github.com/OpenAPITools/openapi-generator).  The 
        `presalytics.client.presalytics_story.api.default_api.DefaultApi` has been passed an an `api_client`
        keyword argument with an instance of `presalytics.client.api.StoryApiClientWithAuth`, which adds 
        an authentication and request processing middleware layer to the default sub package 
        built via code generatation.

    client_id : str
        The client_id that is used OpenID Connect login.  Defaults to "python-client".  

    client_secret : str, optional
        The client_secret used during OpenID Connect login.  Useful `confidential_client` is True.

    confidential_client : bool
        Indicates whether a this client can obtain tokens from auth.presalytics.io without a user under
        OpenID Connect grant type "confidential_client".  Requires a `client_secret`.  Default is False. 

    oidc : `presalytics.client.oidc.OidcClient`
        A middleware class to help acquire and validate tokens from login.presalytics.io.

    token_util : `presalytics.client.auth.TokenUtil`
        A handler for managing an caching tokens acquired from auth.presalytics.io.

    site_host : str
        The login site host for acquiring tokens.  Set from `presalytics.CONFIG` with keyword `["SITE"]["HOST"]`.
        Defaults to https://presalytics.io.

    redirect_uri : str
        Useful if implementing authorization code flow for and OpenID Connect client.  Redirect URIs must 
        be approved by Presalytics API devops for use in client applications. Set from Set from 
        `presalytics.CONFIG` with keyword `["REDIRECT_URI"]`.  Defaults to https://presalytics.io/user/login-success. 

    login_sleep_interval : int
        The duration (in seconds) between attempts to acquire a token after browser-based authentication. Defaults
        to 5 seconds.        

    login_timeout : int
        Defaults to 60 seconds.  The amount of time the client will attempt to acquire a token after the 
        https://presalytics.io authenicates a user. Raises a `presalytics.lib.exceptions.LoginTimeout`
        if the user has not authenticated by the time the interval has expired.

    """
    def __init__(
            self,
            username=None,
            password=None,
            delegate_login=False,
            token=None,
            cache_tokens=False,
            client_id=None,
            client_secret=None,
            **kwargs):
    
        if username:
            self.username = username
        else:
            try:
                self.username = presalytics.CONFIG['USERNAME']
            except KeyError:
                if token:
                    self.username = None
                else:
                    if not client_secret:
                        raise presalytics.lib.exceptions.MissingConfigException("when not passing tokens directly, a clien must have either a client_secrect or a username")
        try:
            if password:
                self.password = password
            else:
                self.password = presalytics.CONFIG['PASSWORD']
            self.direct_grant = True
        except KeyError:
            self.password = None
            self.direct_grant = False
        try:
            if client_id:
                self.client_id = client_id
            else:
                self.client_id = presalytics.CONFIG['CLIENT_ID']
        except KeyError:
            self.client_id = cnst.DEFAULT_CLIENT_ID
        try:
            if client_secret:
                self.client_secret = client_secret
            else:
                self.client_secret = presalytics.CONFIG['CLIENT_SECRET']
            self.confidential_client = True
        except KeyError:
            self.client_secret = None
            self.confidential_client = False

        try:
            self.site_host = presalytics.CONFIG["HOSTS"]["SITE"]
        except KeyError:
            self.site_host = cnst.SITE_HOST


        try:
            self.redirect_uri = presalytics.CONFIG["REDIRECT_URI"]
        except KeyError:
            self.redirect_uri = cnst.REDIRECT_URI
        if delegate_login or presalytics.CONFIG.get("DELEGATE_LOGIN", False):
            self._delegate_login = True
        else:
            self._delegate_login = False
        self.oidc = presalytics.client.oidc.OidcClient(
            client_id=self.client_id,
            client_secret=self.client_secret
        )
        if presalytics.CONFIG.get("CACHE_TOKENS", None):
            cache_tokens = presalytics.CONFIG.get("CACHE_TOKENS")
        self.token_util = presalytics.client.auth.TokenUtil(token_cache=cache_tokens)
        if token:
            #  Assume if token is passed as string, then it's an access token
            if isinstance(token, str):
                self.token_util.token = {"access_token": token}
            
            # if token is a dictionary with an 'access_token_expire_time' key, it's previous been processed / deserialized
            elif token.get('access_token_expire_time', None):
                self.token_util.token = token

            # if token has an 'expires_in' key, if has not been deserialized
            elif token.get('expires_in', None):
                self.token_util.process_token(token)
            else:
                raise presalytics.lib.exceptions.InvalidTokenException(message="Unknown token format.")
            if self.token_util.token_cache:
                self.token_util._put_token_file()
        if not self._delegate_login:
            self.token_util.token = self.refresh_token()

        doc_converter_api_client = DocConverterApiClientWithAuth(self, **kwargs)
        self.doc_converter = presalytics.client.presalytics_doc_converter.DefaultApi(api_client=doc_converter_api_client)
        ooxml_automation_api_client = OoxmlAutomationApiClientWithAuth(self, **kwargs)
        self.ooxml_automation = presalytics.client.presalytics_ooxml_automation.DefaultApi(api_client=ooxml_automation_api_client)
        story_api_client = StoryApiClientWithAuth(self, **kwargs)
        self.story = presalytics.client.presalytics_story.DefaultApi(api_client=story_api_client)

    def login(self):
        """
        Triggers a an attempt to acquire an API token based on the the client configuration
        """
        if self.direct_grant:
            token = self.oidc.token(username=self.username, password=self.password)
        else:
            token = self.oidc.token(username=self.username)
        self.token_util.process_token(token)
        return self.token_util.token


    def refresh_token(self):
        """
        Obtains a new access token if the access token is expired. if refresh token is expired, 
        this method prompt user to re-authenticate when `delegate_login` is `False` or raise
        an `presalytics.lib.exceptions.InvalidTokenException` when `deletegate_login` is True.
        """
        if self.token_util.is_api_access_token_expired():
            if self.token_util.token.get('refresh_token', None) and self.client_secret:
                refresh_token = self.token_util.token["refresh_token"]
                token = self.oidc.refresh_token(refresh_token)
                self.token_util.process_token(token)
                logger.debug("Refresh token granted successfully.")
            else:
                if self.direct_grant:
                    token = self.oidc.token(username=self.username, password=self.password)
                elif self.confidential_client:
                    token = self.oidc.client_credentials_token()
                elif self._delegate_login:
                    raise presalytics.lib.exceptions.ApiError("Unauthorized. Token has expired", status_code=401)
                else:
                    token = self.oidc.token(username=self.username)
                self.token_util.process_token(token)
            if self.token_util.token_cache:
                self.token_util._put_token_file()
        return self.token_util.token

    def get_auth_header(self):
        """
        Creates a JWT Bearer Authorization token header

        Returns
        ----------
        A `dict` authorization crediential to be attached to an API request
        """
        self.refresh_token()
        auth_header = {
            "Authorization": "Bearer " + self.token_util.token["access_token"]
        }
        return auth_header

    def get_request_id_header(self):
        """
        Creates an 'X-Request-Id' token header for tracing requests through Presalytics API
        services.  If deployed alongside the [WSGI Microservice Middleware](https://github.com/presalytics/WSGI-Microservice-Middleware) 
        package, this method will pull the request id from the call stack.
        
        Returns
        ----------
        A `dict` header representation with an 'X-Request-Id' key to be attached to an API request
        """
        
        current_request_id = wsgi_microservice_middleware.current_request_id()
        if not current_request_id:
            current_request_id = str(uuid4())
        header = {
            "X-Request-Id": current_request_id
        }
        return header

    def download_file(self, story_id, ooxml_automation_id, download_folder=None, filename=None, **kwargs):
        """
        Downloads an updated Ooxml Automation file and places the file in a designated folder

        Parameters
        ---------
        story : str
            The id of the Presalytics Story API object that manages access to document
        
        ooxml_automation_id : str
            The id of the Presalytics API Ooxml Automation service object that you want to download
        
        download_folder : str, optional
            The filepath to the local directory that you want to download the file to. Defaults to the 
            current working directory.
        
        filename: str, optional
            The name of the downloaded file. Defaults to the original filename the the object was created.

        """
        response, status, headers = self.story.story_id_file_ooxmlautomationid_get_with_http_info(story_id, ooxml_automation_id, _preload_content=False)
        if download_folder is None:
            download_folder = os.getcwd()
        if filename is None:
            cd_header = headers.get('Content-Disposition')
            _, params = cgi.parse_header(cd_header)
            filename = params["filename"]
        filepath = os.path.join(download_folder, filename)
        with open(filepath, 'wb') as f:
            f.write(response.data)

    def get_client_info(self):
        """
        Convenience method returning information about this client to pass to downstream objects, e.g.,
        components and new client instances

        Returns
        ----------
        A dictionary containing instances values:
            - token: self.token_util.token
            - client_id: self.client_id
            - cache_tokens: self.token_util.token_cache
            - delegate_login: self.delegate login
        """
        return {
            "token": self.token_util.token,
            "client_id": self.client_id,
            "cache_tokens": self.token_util.token_cache,
            "delegate_login": self._delegate_login
        }

    STATUS_REPOLL_SECONDS = 2
    STATUS_REPOLL_MAX_CYCLES = 20

    def upload_file_and_await_outline(self, 
                                      file: typing.Union[FileStorage, str],
                                      include_relationships=True,
                                      status_repoll_seconds: int = None, 
                                      repoll_max_cycles: int = None):
        """ Useful for testing """
        if type(file) is str:
            content_type = mimetypes.guess_type(file, False)[0] # type: ignore
            with open(file, 'rb') as f:  # type: ignore
                stream = io.BytesIO(f.read())
            file = FileStorage(
                stream=stream,  # type: ignore
                filename=file,  # type: ignore
                content_type=content_type,
                content_length=stream.__sizeof__()             
            )
        if not status_repoll_seconds:
            status_repoll_seconds = self.STATUS_REPOLL_SECONDS
        if not repoll_max_cycles:
            repoll_max_cycles = self.STATUS_REPOLL_MAX_CYCLES
        story = self.story.story_post_file(file=file)
        self.await_outline(story.id)
        return self.story.story_id_get(story.id, include_outline=True, include_relationships=include_relationships)

    def await_outline(self,
                      story_id,
                      status_repoll_seconds: int = None, 
                      repoll_max_cycles: int = None):
        task_running = True
        repoll_cycle_count = 0
        if not status_repoll_seconds:
            status_repoll_seconds = self.STATUS_REPOLL_SECONDS
        if not repoll_max_cycles:
            repoll_max_cycles = self.STATUS_REPOLL_MAX_CYCLES
        while task_running:
            status, status_code, _ = self.story.story_id_status_get_with_http_info(story_id)
            if status_code == 204:
                task_running = False
            elif status_code == 200:
                if status.status == "SUCCESS":
                    task_running = False
                elif repoll_cycle_count >= repoll_max_cycles:
                    raise presalytics.lib.exceptions.ApiError(message="Error occured while uploading file", status_code=500)
                else:
                    logger.info("Story creation task still running.  Rechecking status in {0} seconds".format(status_repoll_seconds))
                    time.sleep(status_repoll_seconds)
                    repoll_cycle_count += 1
        return self.story.story_id_outline_get(story_id)



                
        

class DocConverterApiClientWithAuth(presalytics.client.auth.AuthenticationMixIn, presalytics.client.presalytics_doc_converter.api_client.ApiClient):
    """
    Wraps `presalytics.client.presalytics_doc_converter.api_client.ApiClient` with 
    `presalytics.client.auth.AuthenticationMixIn` middleware
    """
    def __init__(self, parent: Client, **kwargs):
        presalytics.client.auth.AuthenticationMixIn.__init__(self, parent, **kwargs)
        presalytics.client.presalytics_doc_converter.api_client.ApiClient.__init__(self)
        self.update_configuration()

    @property
    def api_name(self):
        return 'doc-converter'


class OoxmlAutomationApiClientWithAuth(presalytics.client.auth.AuthenticationMixIn, presalytics.client.presalytics_ooxml_automation.api_client.ApiClient):
    """
    Wraps `presalytics.client.presalytics_ooxml_automation.api_client.ApiClient` with
    `presalytics.client.auth.AuthenticationMixIn` middleware
    """
    def __init__(self, parent: Client, **kwargs):
        presalytics.client.auth.AuthenticationMixIn.__init__(self, parent, **kwargs)
        presalytics.client.presalytics_ooxml_automation.api_client.ApiClient.__init__(self)
        self.update_configuration()

    @property
    def api_name(self):
        return 'ooxml-automation'


class StoryApiClientWithAuth(presalytics.client.auth.AuthenticationMixIn, presalytics.client.presalytics_story.api_client.ApiClient):
    """
    Wraps `presalytics.client.presalytics_story.api_client.ApiClient` with
    `presalytics.client.auth.AuthenticationMixIn` middleware
    """
    def __init__(self, parent: Client, **kwargs):
        presalytics.client.auth.AuthenticationMixIn.__init__(self, parent, **kwargs)
        presalytics.client.presalytics_story.api_client.ApiClient.__init__(self)
        self.update_configuration()

    @property
    def api_name(self):
        return 'story'


@functools.lru_cache(maxsize=None)
def get_client():
    """
    Caches a client instance for default parameters set in `presalytics.CONFIG`.

    DO NOT use in server-side operation
    """
    client = presalytics.Client()
    return client


        

Functions

def get_client()

Caches a client instance for default parameters set in CONFIG.

DO NOT use in server-side operation

Expand source code Browse git
@functools.lru_cache(maxsize=None)
def get_client():
    """
    Caches a client instance for default parameters set in `presalytics.CONFIG`.

    DO NOT use in server-side operation
    """
    client = presalytics.Client()
    return client

Classes

class Client (username=None, password=None, delegate_login=False, token=None, cache_tokens=False, client_id=None, client_secret=None, **kwargs)

Class for interacting with Presalytics API endpoints

The Client class creates a simple interface for user to interactive with the Presalytics API and is the primary building block for user-built automation of stories, dashboards, and interactive presentations.

A client instance wraps python functions around Presalytics API endpoints and manages user authentication. On initialization, he client checks the status of a user authentication the expiry of their refresh an access tokens. When needed, the client will open a browser to prompt the user to login at the presalytics.io login page (or raise an InvalidTokenException when delegate_login is True).

After authenication, users can call the methods bound to the story, ooxml_automation, and doc_converter attributes to make calls in into the Presalytics API.

A note for server-side development:

The client class can automatically cache tokens in a file called "token.json", located in the python's current working directory. This is done so users running scripts accross multiple client instances do not have to acquire a new token every time an API call is made. If building a client to operate in a multi-user environment, this behavior should be turned off so that one user cannot not pull one another's tokens. To do this, ensure the following parameters are pass to the configuration either via initialization or in a CONFIG file:

cache_tokens = False,
delegate_login = True

When delegate login is True, the client assumes that the application creating instances of the client object will handle user authentication. The simplest way to do this is to pass a token to the client via the "token" keyword argument.

Parameters

username : str, optional
Defaults to None. The user's Presalytics API username. This keyword will take precedence over a passed to the client via CONFIG. The username must either be present in CONFIG or be passed in via keyword, otherwise the client will raise a MissingConfigException.
password : str, optional
Defaults to None. The user's Presalytics API password. This useful for quickly testing scripts, but in most scenario users should not be passing plaintext into the client via this keyword. In a secure, single-user environment, passwords are better placed in the CONFIG object for reuseability. A more secure is to leave passwords out of the configuration, keep delegate_login = False, and acquire tokens via the browser.
delegate_login : bool, optional
Defaults to False. Indicates whether the client would redirect to a browser to acquire an API token. If DELEGATE_LOGIN is True, when the Client does not have access to a valid API token, the client will raise a InvalidTokenException. The default operation will automatically open a new browser tab to acquire a new token via website client from the presalytics.io login page. Putting this setting to True is useful for server-side development.
token : dict, optional

Defaults to None. A dictionary contain information about tokens acquire from auth.presalytics.io. The dictionary must contain an access_token, a refresh_token, and entries contiaing information about token expiry.

Token expiry information can either passed in ISO 8601 formatted string with a UTC offset as dictionary keys access_token_expire_time and refresh_token_expire_time or an integer in seconds with the corresponding dictionary keysexpires_in and refresh_expires_in.

if the dict passed in via this keywork does is not have the correct entries, the client will raise an InvalidTokenException.

cache_tokens : bool, optional
Defaults to False. Toggles whether or the client should cache its acquired tokens in file called "token.json" in the current working directory. Minimizes the number of times a user is required to login. Set to False in multi-user environments.

Attributes

direct_grant : bool
Indicates whether an token will be acquire via the "direct_grant" OpenID Connect flow. Usually indicates whether the user has supplied a passwork to the client either through CONFIG ro during object initialization.
doc_converter : DefaultApi

Interface to the Presalytics API Doc Converter service. The object contains methods that enable the client to make api calls that return deserialized objects from the Presalytics API, simplying user and developer interaction with the Presaltytics API. API calls can be generated as follows:

client = presalytics.Client()
api_obj = client.doc_converter.{operation_id}(*args)

where {operation_id} is the operationId assocated with the endpoint specified the Doc Converter Service OpenAPI Contract , and *args are the corresponding arguments that are passed to the method. A complete list of the avialable methods is shown on the DefaultApi object.

Note: This attribute contains automatically generated methods via the OpenAPI generator. The DefaultApi has been passed an an api_client keyword argument with an instance of DocConverterApiClientWithAuth, which adds an authentication and request processing middleware layer to the default sub package built via code generatation.

ooxml_automation : DefaultApi

Interface to the Presalytics API Ooxml Automation service. The object contains methods that enable the client to make api calls that return deserialized objects from the Presalytics API, simplying user and developer interaction with the Presaltytics API. API calls can be generated as follows:

client = presalytics.Client()
api_obj = client.ooxml_automation.{operation_id}(*args)

where {operation_id} is the operationId assocated with the endpoint specified the Ooxml Automation Service OpenAPI Contract , and *args are the corresponding arguments that are passed to the method. A complete list of the avialable methods is shown on the DefaultApi object.

Note: This attribute contains automatically generated methods via the OpenAPI generator. The DefaultApi has been passed an an api_client keyword argument with an instance of OoxmlAutomationApiClientWithAuth, which adds an authentication and request processing middleware layer to the default sub package built via code generatation.

story : DefaultApi

Interface to the Presalytics API Ooxml Automation service. The object contains methods that enable the client to make api calls that return deserialized objects from the Presalytics API, simplying user and developer interaction with the Presaltytics API. API calls can be generated as follows:

client = presalytics.Client()
api_obj = client.story.{operation_id}(*args)

where {operation_id} is the operationId assocated with the endpoint specified the Ooxml Automation Service OpenAPI Contract , and *args are the corresponding arguments that are passed to the method. A complete list of the avialable methods is shown on the DefaultApi object.

Note: This attribute contains automatically generated methods via the OpenAPI generator. The DefaultApi has been passed an an api_client keyword argument with an instance of StoryApiClientWithAuth, which adds an authentication and request processing middleware layer to the default sub package built via code generatation.

client_id : str
The client_id that is used OpenID Connect login. Defaults to "python-client".
client_secret : str, optional
The client_secret used during OpenID Connect login. Useful confidential_client is True.
confidential_client : bool
Indicates whether a this client can obtain tokens from auth.presalytics.io without a user under OpenID Connect grant type "confidential_client". Requires a client_secret. Default is False.
oidc : presalytics.client.oidc.OidcClient
A middleware class to help acquire and validate tokens from login.presalytics.io.
token_util : presalytics.client.auth.TokenUtil
A handler for managing an caching tokens acquired from auth.presalytics.io.
site_host : str
The login site host for acquiring tokens. Set from CONFIG with keyword ["SITE"]["HOST"]. Defaults to https://presalytics.io.
redirect_uri : str
Useful if implementing authorization code flow for and OpenID Connect client. Redirect URIs must be approved by Presalytics API devops for use in client applications. Set from Set from CONFIG with keyword ["REDIRECT_URI"]. Defaults to https://presalytics.io/user/login-success.
login_sleep_interval : int
The duration (in seconds) between attempts to acquire a token after browser-based authentication. Defaults to 5 seconds.
login_timeout : int
Defaults to 60 seconds. The amount of time the client will attempt to acquire a token after the https://presalytics.io authenicates a user. Raises a LoginTimeout if the user has not authenticated by the time the interval has expired.
Expand source code Browse git
class Client(object):
    """ Class for interacting with Presalytics API endpoints

    The Client class creates a simple interface for user to interactive with the 
    Presalytics API and is the primary building block for user-built automation of stories, 
    dashboards, and interactive presentations.

    A client instance wraps python functions around Presalytics API endpoints and
    manages user authentication. On initialization, he client checks the status of 
    a user authentication the expiry of their refresh an access tokens.  When needed,
    the client will open a browser to prompt the user to login at the presalytics.io
    login page (or raise an `presalytics.lib.exceptions.InvalidTokenException` when 
    `delegate_login` is `True`).

    After authenication, users can call the methods bound to the story, ooxml_automation, 
    and doc_converter attributes to make calls in into the Presalytics API.

    *A note for server-side development*:

    The client class can automatically cache tokens in a file called 
    "token.json", located in the python's current working directory.  This is done so
    users running scripts accross multiple client instances do not have to acquire a new token 
    every time an API call is made. If building a client to operate in a multi-user environment, 
    this behavior should be turned off so that one user cannot not pull one another's tokens.
    To do this, ensure the following parameters are pass to the configuration either 
    via initialization or in a `presalytics.CONFIG` file: 
    
        cache_tokens = False,
        delegate_login = True
    
    When delegate login is True, the client assumes that the application creating 
    instances of the client object will handle user authentication.  The simplest way
    to do this is to pass a token to the client via the "token" keyword argument.

    Parameters
    ----------

    username : str, optional
        Defaults to None.  The user's Presalytics API username.  This keyword will take precedence over a passed to the client
        via `presalytics.CONFIG`.  The username must either be present in `presalytics.CONFIG` or be passed in 
        via keyword, otherwise the client will raise a `presalytics.lib.exceptions.MissingConfigException`.

    password : str, optional
        Defaults to None.  The user's Presalytics API password.  This useful for quickly testing scripts, but in most 
        scenario users should not be passing plaintext into the client via this keyword.  In a secure, single-user 
        environment, passwords are better placed in the `presalytics.CONFIG` object for reuseability.  A more secure
        is to leave passwords out of the configuration, keep `delegate_login` = `False`, and acquire tokens via the browser.
 
    delegate_login : bool, optional
        Defaults to False.  Indicates whether the client would redirect to a browser to 
        acquire an API token. If `DELEGATE_LOGIN` is `True`, when the `presalytics.client.api.Client` does not have 
        access to a valid API token, the client will raise a `presalytics.lib.exceptions.InvalidTokenException`.
        The default operation will automatically open a new browser tab to acquire a new token 
        via website client from the presalytics.io login page.  Putting this setting to True is
        useful for server-side development.

    token : dict, optional
        Defaults to None.  A dictionary contain information about tokens acquire from auth.presalytics.io.  The 
        dictionary must contain an `access_token`, a `refresh_token`, and entries contiaing information about token expiry.  

        Token expiry information can either passed in ISO 8601 formatted string with a UTC offset as dictionary keys
        `access_token_expire_time` and `refresh_token_expire_time` or an integer in seconds with the corresponding 
        dictionary keys`expires_in` and `refresh_expires_in`.
        
        if the `dict` passed in via this keywork does is not have the correct entries, the client will
        raise an `presalytics.lib.exceptions.InvalidTokenException`.

    cache_tokens : bool, optional
        Defaults to False.  Toggles whether or the client should cache its acquired tokens in file called "token.json"
        in the current working directory.  Minimizes the number of times a user is required to login.  Set to False
        in multi-user environments.

    Attributes
    ----------

    direct_grant : bool
        Indicates whether an token will be acquire via the "direct_grant" OpenID Connect flow.  Usually indicates
        whether the user has supplied a passwork to the client either through `presalytics.CONFIG` ro 
        during object initialization.

    doc_converter : presalytics.client.presalytics_doc_converter.api.default_api.DefaultApi
        Interface to the Presalytics API Doc Converter service.  The object contains methods that enable
        the client to make api calls that return deserialized objects from the Presalytics API,
        simplying user and developer interaction with the Presaltytics API.  API calls can be generated
        as follows:

            client = presalytics.Client()
            api_obj = client.doc_converter.{operation_id}(*args)

        where `{operation_id}` is the `operationId` assocated with the endpoint specified the [Doc Converter 
        Service OpenAPI Contract](https://presalytics.io/docs/api-specifications/doc-converter/) , and *args
        are the corresponding arguments that are passed to the method.  A complete list of the avialable
        methods is shown on the `presalytics.client.presalytics_doc_converter.api.default_api.DefaultApi` object.
        
        *Note*:
        This attribute contains automatically generated methods via 
        the [OpenAPI generator](https://github.com/OpenAPITools/openapi-generator).  The 
        `presalytics.client.presalytics_doc_converter.api.default_api.DefaultApi` has been passed an an `api_client`
        keyword argument with an instance of `presalytics.client.api.DocConverterApiClientWithAuth`, which adds 
        an authentication and request processing middleware layer to the default sub package 
        built via code generatation.

    ooxml_automation : presalytics.client.presalytics_ooxml_automation.api.default_api.DefaultApi
        Interface to the Presalytics API Ooxml Automation service.  The object contains methods that enable
        the client to make api calls that return deserialized objects from the Presalytics API,
        simplying user and developer interaction with the Presaltytics API.  API calls can be generated
        as follows:

            client = presalytics.Client()
            api_obj = client.ooxml_automation.{operation_id}(*args)

        where `{operation_id}` is the `operationId` assocated with the endpoint specified the [Ooxml Automation
        Service OpenAPI Contract](https://presalytics.io/docs/api-specifications/ooxml-automation/) , and *args
        are the corresponding arguments that are passed to the method.  A complete list of the avialable
        methods is shown on the `presalytics.client.presalytics_ooxml_automation.api.default_api.DefaultApi` object.
        
        *Note*:
        This attribute contains automatically generated methods via 
        the [OpenAPI generator](https://github.com/OpenAPITools/openapi-generator).  The 
        `presalytics.client.presalytics_ooxml_automation.api.default_api.DefaultApi` has been passed an an `api_client`
        keyword argument with an instance of `presalytics.client.api.OoxmlAutomationApiClientWithAuth`, which adds 
        an authentication and request processing middleware layer to the default sub package 
        built via code generatation.

    story : presalytics.client.presalytics_story.api.default_api.DefaultApi
        Interface to the Presalytics API Ooxml Automation service.  The object contains methods that enable
        the client to make api calls that return deserialized objects from the Presalytics API,
        simplying user and developer interaction with the Presaltytics API.  API calls can be generated
        as follows:

            client = presalytics.Client()
            api_obj = client.story.{operation_id}(*args)

        where `{operation_id}` is the `operationId` assocated with the endpoint specified the [Ooxml Automation
        Service OpenAPI Contract](https://presalytics.io/docs/api-specifications/story/) , and *args
        are the corresponding arguments that are passed to the method.  A complete list of the avialable
        methods is shown on the `presalytics.client.presalytics_story.api.default_api.DefaultApi` object.
        
        *Note*:
        This attribute contains automatically generated methods via 
        the [OpenAPI generator](https://github.com/OpenAPITools/openapi-generator).  The 
        `presalytics.client.presalytics_story.api.default_api.DefaultApi` has been passed an an `api_client`
        keyword argument with an instance of `presalytics.client.api.StoryApiClientWithAuth`, which adds 
        an authentication and request processing middleware layer to the default sub package 
        built via code generatation.

    client_id : str
        The client_id that is used OpenID Connect login.  Defaults to "python-client".  

    client_secret : str, optional
        The client_secret used during OpenID Connect login.  Useful `confidential_client` is True.

    confidential_client : bool
        Indicates whether a this client can obtain tokens from auth.presalytics.io without a user under
        OpenID Connect grant type "confidential_client".  Requires a `client_secret`.  Default is False. 

    oidc : `presalytics.client.oidc.OidcClient`
        A middleware class to help acquire and validate tokens from login.presalytics.io.

    token_util : `presalytics.client.auth.TokenUtil`
        A handler for managing an caching tokens acquired from auth.presalytics.io.

    site_host : str
        The login site host for acquiring tokens.  Set from `presalytics.CONFIG` with keyword `["SITE"]["HOST"]`.
        Defaults to https://presalytics.io.

    redirect_uri : str
        Useful if implementing authorization code flow for and OpenID Connect client.  Redirect URIs must 
        be approved by Presalytics API devops for use in client applications. Set from Set from 
        `presalytics.CONFIG` with keyword `["REDIRECT_URI"]`.  Defaults to https://presalytics.io/user/login-success. 

    login_sleep_interval : int
        The duration (in seconds) between attempts to acquire a token after browser-based authentication. Defaults
        to 5 seconds.        

    login_timeout : int
        Defaults to 60 seconds.  The amount of time the client will attempt to acquire a token after the 
        https://presalytics.io authenicates a user. Raises a `presalytics.lib.exceptions.LoginTimeout`
        if the user has not authenticated by the time the interval has expired.

    """
    def __init__(
            self,
            username=None,
            password=None,
            delegate_login=False,
            token=None,
            cache_tokens=False,
            client_id=None,
            client_secret=None,
            **kwargs):
    
        if username:
            self.username = username
        else:
            try:
                self.username = presalytics.CONFIG['USERNAME']
            except KeyError:
                if token:
                    self.username = None
                else:
                    if not client_secret:
                        raise presalytics.lib.exceptions.MissingConfigException("when not passing tokens directly, a clien must have either a client_secrect or a username")
        try:
            if password:
                self.password = password
            else:
                self.password = presalytics.CONFIG['PASSWORD']
            self.direct_grant = True
        except KeyError:
            self.password = None
            self.direct_grant = False
        try:
            if client_id:
                self.client_id = client_id
            else:
                self.client_id = presalytics.CONFIG['CLIENT_ID']
        except KeyError:
            self.client_id = cnst.DEFAULT_CLIENT_ID
        try:
            if client_secret:
                self.client_secret = client_secret
            else:
                self.client_secret = presalytics.CONFIG['CLIENT_SECRET']
            self.confidential_client = True
        except KeyError:
            self.client_secret = None
            self.confidential_client = False

        try:
            self.site_host = presalytics.CONFIG["HOSTS"]["SITE"]
        except KeyError:
            self.site_host = cnst.SITE_HOST


        try:
            self.redirect_uri = presalytics.CONFIG["REDIRECT_URI"]
        except KeyError:
            self.redirect_uri = cnst.REDIRECT_URI
        if delegate_login or presalytics.CONFIG.get("DELEGATE_LOGIN", False):
            self._delegate_login = True
        else:
            self._delegate_login = False
        self.oidc = presalytics.client.oidc.OidcClient(
            client_id=self.client_id,
            client_secret=self.client_secret
        )
        if presalytics.CONFIG.get("CACHE_TOKENS", None):
            cache_tokens = presalytics.CONFIG.get("CACHE_TOKENS")
        self.token_util = presalytics.client.auth.TokenUtil(token_cache=cache_tokens)
        if token:
            #  Assume if token is passed as string, then it's an access token
            if isinstance(token, str):
                self.token_util.token = {"access_token": token}
            
            # if token is a dictionary with an 'access_token_expire_time' key, it's previous been processed / deserialized
            elif token.get('access_token_expire_time', None):
                self.token_util.token = token

            # if token has an 'expires_in' key, if has not been deserialized
            elif token.get('expires_in', None):
                self.token_util.process_token(token)
            else:
                raise presalytics.lib.exceptions.InvalidTokenException(message="Unknown token format.")
            if self.token_util.token_cache:
                self.token_util._put_token_file()
        if not self._delegate_login:
            self.token_util.token = self.refresh_token()

        doc_converter_api_client = DocConverterApiClientWithAuth(self, **kwargs)
        self.doc_converter = presalytics.client.presalytics_doc_converter.DefaultApi(api_client=doc_converter_api_client)
        ooxml_automation_api_client = OoxmlAutomationApiClientWithAuth(self, **kwargs)
        self.ooxml_automation = presalytics.client.presalytics_ooxml_automation.DefaultApi(api_client=ooxml_automation_api_client)
        story_api_client = StoryApiClientWithAuth(self, **kwargs)
        self.story = presalytics.client.presalytics_story.DefaultApi(api_client=story_api_client)

    def login(self):
        """
        Triggers a an attempt to acquire an API token based on the the client configuration
        """
        if self.direct_grant:
            token = self.oidc.token(username=self.username, password=self.password)
        else:
            token = self.oidc.token(username=self.username)
        self.token_util.process_token(token)
        return self.token_util.token


    def refresh_token(self):
        """
        Obtains a new access token if the access token is expired. if refresh token is expired, 
        this method prompt user to re-authenticate when `delegate_login` is `False` or raise
        an `presalytics.lib.exceptions.InvalidTokenException` when `deletegate_login` is True.
        """
        if self.token_util.is_api_access_token_expired():
            if self.token_util.token.get('refresh_token', None) and self.client_secret:
                refresh_token = self.token_util.token["refresh_token"]
                token = self.oidc.refresh_token(refresh_token)
                self.token_util.process_token(token)
                logger.debug("Refresh token granted successfully.")
            else:
                if self.direct_grant:
                    token = self.oidc.token(username=self.username, password=self.password)
                elif self.confidential_client:
                    token = self.oidc.client_credentials_token()
                elif self._delegate_login:
                    raise presalytics.lib.exceptions.ApiError("Unauthorized. Token has expired", status_code=401)
                else:
                    token = self.oidc.token(username=self.username)
                self.token_util.process_token(token)
            if self.token_util.token_cache:
                self.token_util._put_token_file()
        return self.token_util.token

    def get_auth_header(self):
        """
        Creates a JWT Bearer Authorization token header

        Returns
        ----------
        A `dict` authorization crediential to be attached to an API request
        """
        self.refresh_token()
        auth_header = {
            "Authorization": "Bearer " + self.token_util.token["access_token"]
        }
        return auth_header

    def get_request_id_header(self):
        """
        Creates an 'X-Request-Id' token header for tracing requests through Presalytics API
        services.  If deployed alongside the [WSGI Microservice Middleware](https://github.com/presalytics/WSGI-Microservice-Middleware) 
        package, this method will pull the request id from the call stack.
        
        Returns
        ----------
        A `dict` header representation with an 'X-Request-Id' key to be attached to an API request
        """
        
        current_request_id = wsgi_microservice_middleware.current_request_id()
        if not current_request_id:
            current_request_id = str(uuid4())
        header = {
            "X-Request-Id": current_request_id
        }
        return header

    def download_file(self, story_id, ooxml_automation_id, download_folder=None, filename=None, **kwargs):
        """
        Downloads an updated Ooxml Automation file and places the file in a designated folder

        Parameters
        ---------
        story : str
            The id of the Presalytics Story API object that manages access to document
        
        ooxml_automation_id : str
            The id of the Presalytics API Ooxml Automation service object that you want to download
        
        download_folder : str, optional
            The filepath to the local directory that you want to download the file to. Defaults to the 
            current working directory.
        
        filename: str, optional
            The name of the downloaded file. Defaults to the original filename the the object was created.

        """
        response, status, headers = self.story.story_id_file_ooxmlautomationid_get_with_http_info(story_id, ooxml_automation_id, _preload_content=False)
        if download_folder is None:
            download_folder = os.getcwd()
        if filename is None:
            cd_header = headers.get('Content-Disposition')
            _, params = cgi.parse_header(cd_header)
            filename = params["filename"]
        filepath = os.path.join(download_folder, filename)
        with open(filepath, 'wb') as f:
            f.write(response.data)

    def get_client_info(self):
        """
        Convenience method returning information about this client to pass to downstream objects, e.g.,
        components and new client instances

        Returns
        ----------
        A dictionary containing instances values:
            - token: self.token_util.token
            - client_id: self.client_id
            - cache_tokens: self.token_util.token_cache
            - delegate_login: self.delegate login
        """
        return {
            "token": self.token_util.token,
            "client_id": self.client_id,
            "cache_tokens": self.token_util.token_cache,
            "delegate_login": self._delegate_login
        }

    STATUS_REPOLL_SECONDS = 2
    STATUS_REPOLL_MAX_CYCLES = 20

    def upload_file_and_await_outline(self, 
                                      file: typing.Union[FileStorage, str],
                                      include_relationships=True,
                                      status_repoll_seconds: int = None, 
                                      repoll_max_cycles: int = None):
        """ Useful for testing """
        if type(file) is str:
            content_type = mimetypes.guess_type(file, False)[0] # type: ignore
            with open(file, 'rb') as f:  # type: ignore
                stream = io.BytesIO(f.read())
            file = FileStorage(
                stream=stream,  # type: ignore
                filename=file,  # type: ignore
                content_type=content_type,
                content_length=stream.__sizeof__()             
            )
        if not status_repoll_seconds:
            status_repoll_seconds = self.STATUS_REPOLL_SECONDS
        if not repoll_max_cycles:
            repoll_max_cycles = self.STATUS_REPOLL_MAX_CYCLES
        story = self.story.story_post_file(file=file)
        self.await_outline(story.id)
        return self.story.story_id_get(story.id, include_outline=True, include_relationships=include_relationships)

    def await_outline(self,
                      story_id,
                      status_repoll_seconds: int = None, 
                      repoll_max_cycles: int = None):
        task_running = True
        repoll_cycle_count = 0
        if not status_repoll_seconds:
            status_repoll_seconds = self.STATUS_REPOLL_SECONDS
        if not repoll_max_cycles:
            repoll_max_cycles = self.STATUS_REPOLL_MAX_CYCLES
        while task_running:
            status, status_code, _ = self.story.story_id_status_get_with_http_info(story_id)
            if status_code == 204:
                task_running = False
            elif status_code == 200:
                if status.status == "SUCCESS":
                    task_running = False
                elif repoll_cycle_count >= repoll_max_cycles:
                    raise presalytics.lib.exceptions.ApiError(message="Error occured while uploading file", status_code=500)
                else:
                    logger.info("Story creation task still running.  Rechecking status in {0} seconds".format(status_repoll_seconds))
                    time.sleep(status_repoll_seconds)
                    repoll_cycle_count += 1
        return self.story.story_id_outline_get(story_id)

Class variables

var STATUS_REPOLL_SECONDS

int(x=0) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.

>>> int('0b100', base=0)
4
var STATUS_REPOLL_MAX_CYCLES

int(x=0) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.

>>> int('0b100', base=0)
4

Methods

def login(self)

Triggers a an attempt to acquire an API token based on the the client configuration

Expand source code Browse git
def login(self):
    """
    Triggers a an attempt to acquire an API token based on the the client configuration
    """
    if self.direct_grant:
        token = self.oidc.token(username=self.username, password=self.password)
    else:
        token = self.oidc.token(username=self.username)
    self.token_util.process_token(token)
    return self.token_util.token
def refresh_token(self)

Obtains a new access token if the access token is expired. if refresh token is expired, this method prompt user to re-authenticate when delegate_login is False or raise an InvalidTokenException when deletegate_login is True.

Expand source code Browse git
def refresh_token(self):
    """
    Obtains a new access token if the access token is expired. if refresh token is expired, 
    this method prompt user to re-authenticate when `delegate_login` is `False` or raise
    an `presalytics.lib.exceptions.InvalidTokenException` when `deletegate_login` is True.
    """
    if self.token_util.is_api_access_token_expired():
        if self.token_util.token.get('refresh_token', None) and self.client_secret:
            refresh_token = self.token_util.token["refresh_token"]
            token = self.oidc.refresh_token(refresh_token)
            self.token_util.process_token(token)
            logger.debug("Refresh token granted successfully.")
        else:
            if self.direct_grant:
                token = self.oidc.token(username=self.username, password=self.password)
            elif self.confidential_client:
                token = self.oidc.client_credentials_token()
            elif self._delegate_login:
                raise presalytics.lib.exceptions.ApiError("Unauthorized. Token has expired", status_code=401)
            else:
                token = self.oidc.token(username=self.username)
            self.token_util.process_token(token)
        if self.token_util.token_cache:
            self.token_util._put_token_file()
    return self.token_util.token
def get_auth_header(self)

Creates a JWT Bearer Authorization token header

Returns

A dict authorization crediential to be attached to an API request

Expand source code Browse git
def get_auth_header(self):
    """
    Creates a JWT Bearer Authorization token header

    Returns
    ----------
    A `dict` authorization crediential to be attached to an API request
    """
    self.refresh_token()
    auth_header = {
        "Authorization": "Bearer " + self.token_util.token["access_token"]
    }
    return auth_header
def get_request_id_header(self)

Creates an 'X-Request-Id' token header for tracing requests through Presalytics API services. If deployed alongside the WSGI Microservice Middleware package, this method will pull the request id from the call stack.

Returns

A dict header representation with an 'X-Request-Id' key to be attached to an API request

Expand source code Browse git
def get_request_id_header(self):
    """
    Creates an 'X-Request-Id' token header for tracing requests through Presalytics API
    services.  If deployed alongside the [WSGI Microservice Middleware](https://github.com/presalytics/WSGI-Microservice-Middleware) 
    package, this method will pull the request id from the call stack.
    
    Returns
    ----------
    A `dict` header representation with an 'X-Request-Id' key to be attached to an API request
    """
    
    current_request_id = wsgi_microservice_middleware.current_request_id()
    if not current_request_id:
        current_request_id = str(uuid4())
    header = {
        "X-Request-Id": current_request_id
    }
    return header
def download_file(self, story_id, ooxml_automation_id, download_folder=None, filename=None, **kwargs)

Downloads an updated Ooxml Automation file and places the file in a designated folder

Parameters

story : str
The id of the Presalytics Story API object that manages access to document
ooxml_automation_id : str
The id of the Presalytics API Ooxml Automation service object that you want to download
download_folder : str, optional
The filepath to the local directory that you want to download the file to. Defaults to the current working directory.
filename : str, optional
The name of the downloaded file. Defaults to the original filename the the object was created.
Expand source code Browse git
def download_file(self, story_id, ooxml_automation_id, download_folder=None, filename=None, **kwargs):
    """
    Downloads an updated Ooxml Automation file and places the file in a designated folder

    Parameters
    ---------
    story : str
        The id of the Presalytics Story API object that manages access to document
    
    ooxml_automation_id : str
        The id of the Presalytics API Ooxml Automation service object that you want to download
    
    download_folder : str, optional
        The filepath to the local directory that you want to download the file to. Defaults to the 
        current working directory.
    
    filename: str, optional
        The name of the downloaded file. Defaults to the original filename the the object was created.

    """
    response, status, headers = self.story.story_id_file_ooxmlautomationid_get_with_http_info(story_id, ooxml_automation_id, _preload_content=False)
    if download_folder is None:
        download_folder = os.getcwd()
    if filename is None:
        cd_header = headers.get('Content-Disposition')
        _, params = cgi.parse_header(cd_header)
        filename = params["filename"]
    filepath = os.path.join(download_folder, filename)
    with open(filepath, 'wb') as f:
        f.write(response.data)
def get_client_info(self)

Convenience method returning information about this client to pass to downstream objects, e.g., components and new client instances

Returns

A dictionary containing instances values:
  • token: self.token_util.token
  • client_id: self.client_id
  • cache_tokens: self.token_util.token_cache
  • delegate_login: self.delegate login
Expand source code Browse git
def get_client_info(self):
    """
    Convenience method returning information about this client to pass to downstream objects, e.g.,
    components and new client instances

    Returns
    ----------
    A dictionary containing instances values:
        - token: self.token_util.token
        - client_id: self.client_id
        - cache_tokens: self.token_util.token_cache
        - delegate_login: self.delegate login
    """
    return {
        "token": self.token_util.token,
        "client_id": self.client_id,
        "cache_tokens": self.token_util.token_cache,
        "delegate_login": self._delegate_login
    }
def upload_file_and_await_outline(self, file, include_relationships=True, status_repoll_seconds=None, repoll_max_cycles=None)

Useful for testing

Expand source code Browse git
def upload_file_and_await_outline(self, 
                                  file: typing.Union[FileStorage, str],
                                  include_relationships=True,
                                  status_repoll_seconds: int = None, 
                                  repoll_max_cycles: int = None):
    """ Useful for testing """
    if type(file) is str:
        content_type = mimetypes.guess_type(file, False)[0] # type: ignore
        with open(file, 'rb') as f:  # type: ignore
            stream = io.BytesIO(f.read())
        file = FileStorage(
            stream=stream,  # type: ignore
            filename=file,  # type: ignore
            content_type=content_type,
            content_length=stream.__sizeof__()             
        )
    if not status_repoll_seconds:
        status_repoll_seconds = self.STATUS_REPOLL_SECONDS
    if not repoll_max_cycles:
        repoll_max_cycles = self.STATUS_REPOLL_MAX_CYCLES
    story = self.story.story_post_file(file=file)
    self.await_outline(story.id)
    return self.story.story_id_get(story.id, include_outline=True, include_relationships=include_relationships)
def await_outline(self, story_id, status_repoll_seconds=None, repoll_max_cycles=None)
Expand source code Browse git
def await_outline(self,
                  story_id,
                  status_repoll_seconds: int = None, 
                  repoll_max_cycles: int = None):
    task_running = True
    repoll_cycle_count = 0
    if not status_repoll_seconds:
        status_repoll_seconds = self.STATUS_REPOLL_SECONDS
    if not repoll_max_cycles:
        repoll_max_cycles = self.STATUS_REPOLL_MAX_CYCLES
    while task_running:
        status, status_code, _ = self.story.story_id_status_get_with_http_info(story_id)
        if status_code == 204:
            task_running = False
        elif status_code == 200:
            if status.status == "SUCCESS":
                task_running = False
            elif repoll_cycle_count >= repoll_max_cycles:
                raise presalytics.lib.exceptions.ApiError(message="Error occured while uploading file", status_code=500)
            else:
                logger.info("Story creation task still running.  Rechecking status in {0} seconds".format(status_repoll_seconds))
                time.sleep(status_repoll_seconds)
                repoll_cycle_count += 1
    return self.story.story_id_outline_get(story_id)
class DocConverterApiClientWithAuth (parent, **kwargs)

Wraps ApiClient with AuthenticationMixIn middleware

Expand source code Browse git
class DocConverterApiClientWithAuth(presalytics.client.auth.AuthenticationMixIn, presalytics.client.presalytics_doc_converter.api_client.ApiClient):
    """
    Wraps `presalytics.client.presalytics_doc_converter.api_client.ApiClient` with 
    `presalytics.client.auth.AuthenticationMixIn` middleware
    """
    def __init__(self, parent: Client, **kwargs):
        presalytics.client.auth.AuthenticationMixIn.__init__(self, parent, **kwargs)
        presalytics.client.presalytics_doc_converter.api_client.ApiClient.__init__(self)
        self.update_configuration()

    @property
    def api_name(self):
        return 'doc-converter'

Ancestors

Instance variables

var api_name
Expand source code Browse git
@property
def api_name(self):
    return 'doc-converter'

Inherited members

class OoxmlAutomationApiClientWithAuth (parent, **kwargs)

Wraps ApiClient with AuthenticationMixIn middleware

Expand source code Browse git
class OoxmlAutomationApiClientWithAuth(presalytics.client.auth.AuthenticationMixIn, presalytics.client.presalytics_ooxml_automation.api_client.ApiClient):
    """
    Wraps `presalytics.client.presalytics_ooxml_automation.api_client.ApiClient` with
    `presalytics.client.auth.AuthenticationMixIn` middleware
    """
    def __init__(self, parent: Client, **kwargs):
        presalytics.client.auth.AuthenticationMixIn.__init__(self, parent, **kwargs)
        presalytics.client.presalytics_ooxml_automation.api_client.ApiClient.__init__(self)
        self.update_configuration()

    @property
    def api_name(self):
        return 'ooxml-automation'

Ancestors

Instance variables

var api_name
Expand source code Browse git
@property
def api_name(self):
    return 'ooxml-automation'

Inherited members

class StoryApiClientWithAuth (parent, **kwargs)

Wraps ApiClient with AuthenticationMixIn middleware

Expand source code Browse git
class StoryApiClientWithAuth(presalytics.client.auth.AuthenticationMixIn, presalytics.client.presalytics_story.api_client.ApiClient):
    """
    Wraps `presalytics.client.presalytics_story.api_client.ApiClient` with
    `presalytics.client.auth.AuthenticationMixIn` middleware
    """
    def __init__(self, parent: Client, **kwargs):
        presalytics.client.auth.AuthenticationMixIn.__init__(self, parent, **kwargs)
        presalytics.client.presalytics_story.api_client.ApiClient.__init__(self)
        self.update_configuration()

    @property
    def api_name(self):
        return 'story'

Ancestors

Instance variables

var api_name
Expand source code Browse git
@property
def api_name(self):
    return 'story'

Inherited members