class Schedule:
    def __init__(self, **connection_params):
        if connection_params:
            self.session = Session(**connection_params)
        else:
            self.session = SessionManager.get_session()

    def get_schedule(self, rec_id=None, output='df'):
        """Get details of all schedules on an account or single schedule if rec_id is passed
        rec_id : int
            Id of your schedule in ClicData
        output : str
            Output format, either df or dict
        """
        suffix = "schedule"
        if type(rec_id) == int:
            schedules = self.session.api_call(suffix=f"{suffix}/{rec_id}",
                                              request_method='get')
        else:
            schedules = self.session.api_call(suffix=suffix,
                                              request_method='get')

        if output == 'df':
            return pd.DataFrame.from_dict(schedules.json().get('schedules'))
        elif output == 'dict':
            return schedules.json().get('schedules')
        else:
            raise Exception("Please enter a valid output: ['df', 'dict'].")

    def trigger_schedule(self, rec_id=None):
        """Trigger a specified schedule by id
        rec_id : int
                Id of your schedule in ClicData
        """
        if type(rec_id) == int:
            suffix = f"schedule/{rec_id}/trigger"
            response = self.session.api_call(suffix=suffix,
                                             request_method='get')
        else:
            raise Exception("Please enter a valid rec_id as an integer.")

        return response
class Account:

    def __init__(self, **connection_params):
        if connection_params:
            self.session = Session(**connection_params)
        else:
            self.session = SessionManager.get_session()

    def get_account(self, output='df'):
        """Get details on account usage and limits
        output : str
            Output format, either df or dict
        """
        suffix = "account"
        account = self.session.api_call(suffix=suffix,
                                        request_method='get')
        if output == 'df':
            return pd.DataFrame.from_dict(account.json())
        elif output == 'dict':
            return account.json()

    def get_account_activity(self, entity='users', output='df'):
        """Retrieve either dashboard or user activity
        entity : str
            Pull activity data for 'dashboards' vs 'users'
        output : str
            Output format, either df or dict
        """
        valid_entities = ['users', 'dashboards']
        if entity not in valid_entities:
            raise Exception("Please enter a valid entity: "+str(valid_entities))
        suffix = "account/activity/" + entity
        activity = self.session.api_call(suffix=suffix,
                                         request_method='get')

        if output == 'df':
            return pd.DataFrame.from_dict(activity.json())
        elif output == 'dict':
            return activity.json()
 def __init__(self, **connection_params):
     if connection_params:
         self.session = Session(**connection_params)
     else:
         self.session = SessionManager.get_session()
class Data:
    def __init__(self, **connection_params):
        if connection_params:
            self.session = Session(**connection_params)
        else:
            self.session = SessionManager.get_session()

    def retrieve_paginated_data(self, suffix=None):
        page = 1
        has_more_data = True
        data = []
        while has_more_data:
            page_response = self.session.api_call(suffix=suffix,
                                                  request_method='get',
                                                  params={"page": page})
            if page_response.status_code == 200:
                has_more_data = page_response.json().get('has_more_data')
                page += 1
                data = data + page_response.json().get('data')
            else:
                has_more_data = False
                print(
                    f"Ran into issues processing your request \nStatus Code: {page_response.status_code}\n"
                    +
                    f"Content: {page_response.text}\nData processed before the error returned"
                )
        return data

    def get_data(self,
                 rec_id=None,
                 name: str = None,
                 unique_key_available: bool = None,
                 refresh: bool = None,
                 output='df'):
        """Retrieve list of data sources or retrieve the contents of a data source
        rec_id : int
            RecId of the data you want to retrieve
        name : str
            data set name based filter to apply
        unique_key_available : bool
            filter data sets based on whether they're using unique keys
        refresh : bool
            filter data sets based on whether they're refresh-able (non-static)
        output : str
            Output format, either df or dict
        """
        if rec_id is None:
            # Add parameter string based on input
            params = {}

            if name:
                params["name"] = str(name)

            if unique_key_available:
                params["uniquekeyavailable"] = unique_key_available

            if refresh:
                params["refresh"] = refresh

            # Use api_call to grab list of data sets
            data = self.session.api_call(suffix='data',
                                         request_method='get',
                                         params=params)

            if output == 'df':
                return pd.DataFrame.from_dict(data.json().get('data'))
            elif output == 'dict':
                return data.json().get('data')

        elif type(rec_id) != int:
            raise Exception("Please enter a valid rec_id as int.")

        else:
            suffix = f"data/{rec_id}"
            data = self.retrieve_paginated_data(suffix=suffix)
            if output == 'df':
                return pd.DataFrame.from_dict(data)
            elif output == 'dict':
                return data

    def get_data_history(self, rec_id=None, ver_id=None, output='df'):
        """Retrieve list of data sources or retrieve the contents of a data source
        rec_id : int
            RecId of the data set you want to check history for
        ver_id : int
            Version ID of the data you want to retrieve from your data set
        output : str
            Output format, either df or dict
        """
        if rec_id is None:
            raise Exception('Please enter a valid data clone RecId.')
        else:
            if ver_id is None:
                suffix = f"data/{rec_id}/versions"
                data = self.session.api_call(suffix=suffix)
                if output == 'df':
                    df = pd.DataFrame.from_dict(data.json().get('versions'))
                    df["data_rec_id"] = rec_id
                    return df
                elif output == 'dict':
                    data_dict = data.json().get('versions')
                    for version in data_dict:
                        version.update({"data_rec_id": rec_id})
                    return data_dict
            else:
                suffix = f"data/{rec_id}/v/{ver_id}"
                data = self.retrieve_paginated_data(suffix=suffix)
                if output == 'df':
                    return pd.DataFrame.from_dict(data)
                elif output == 'dict':
                    return data

    def create_data(self, name=None, description="", cols=None):
        """Creates an empty custom table in ClicData
        name: str
            Name of data table created in ClicData. Must be unique to account.
        desc : str
            Long form description attached to table in ClicData.
        def: dict
            Column name as key, data type as value.
        """
        # List of valid ClicData data types to input
        valid_data_types = [
            'text', 'number', 'datetime', 'date', 'percentage', 'checkbox',
            'dropdown', 'rec_id'
        ]
        # Dictionary containing Pandas data types and the converted type for ClicData
        pandas_convert = {
            'object': 'text',
            'int64': 'number',
            'float64': 'number',
            'bool': 'checkbox',
            'datetime64': 'datetime',
            'timedelta[ns]': 'text',
            'category': 'text'
        }

        if name is None:
            raise Exception('Please enter a name for your data set')
        elif cols is None:
            raise Exception(
                """Please provide a valid dictionary object containing your column names and types.\n
               {"columnName":"type",\n
               "columnName":"type"}""")
        else:
            suffix = "data"
            columns = []
            for column_name in cols.keys():
                data_type = pandas_convert[cols[column_name].name]
                if data_type not in valid_data_types:
                    raise Exception(
                        f'Column [{column_name}] contains an invalid data type ({data_type})'
                        +
                        f', please enter your data with one of the following: {valid_data_types}.'
                    )
                column_def = {"name": column_name, "data_type": data_type}
                columns.append(column_def)

            body = {
                "name": name,
                "description": description,
                "columns": columns
            }

            post = self.session.api_call(suffix=suffix,
                                         body=body,
                                         request_method='post')

            return post

    def append_data(self, rec_id=None, data=None):
        """Append your data to an existing data set
        rec_id : int
            rec_id of your data in ClicData
        data : pandas.Dataframe
            df containing the data you want to append
        """
        if type(rec_id) != int:
            raise Exception('Please enter a valid data clone RecId.')
        elif data is None:
            raise Exception('Please enter a data set to append')
        else:
            suffix = f'data/{rec_id}/row'
            formatted_data = data.to_dict(orient='index')
            data_set = []
            for row_dict in formatted_data.values():
                row = []
                for column, value in row_dict.items():
                    cell = {"column": column, "value": value}
                    row.append(cell)
                data_set.append(row)
            body = {"data": data_set}
            post = self.session.api_call(suffix=suffix,
                                         body=body,
                                         request_method='post')

            return post.text

    def create_and_append(self, name=None, description="", data=None):
        """ Creates a static data set in ClicData using a pandas dataframe
        name : str
            Name of data set to create, must be unique to your account
        desc : str
            Optional, long-form details about your data
        data : pandas.Dataframe
            Data to upload
        """
        if name is None:
            raise Exception('Please enter a name for your data set.')
        elif data is None:
            raise Exception('Please enter a data.')
        else:
            columns = data.dtypes.to_dict()
            rec_id = self.create_data(name=name,
                                      description=description,
                                      cols=columns)

            try:
                rec_id = int(rec_id.text)
            except ValueError as e:
                raise Exception(
                    f'There appears to be an issue with the connection. Creating data set returned:\n{rec_id.text}\n'
                    + f'Error text: {e}')

            status = self.append_data(rec_id=rec_id, data=data)

        return {'rec_id': rec_id, 'status': status.text}

    def rebuild_data(self, rec_id=None, method='reload'):
        """ Rebuild a data set using the specified method
        rec_id : int
            rec_id of your data in ClicData
        method : str
            reload method to refresh the data with
        """
        valid_methods = [
            "reload", "recreate", "update", "updateappend", "append"
        ]

        if type(rec_id) != int:
            raise Exception(
                "Please enter a valid data clone RecId as an integer.")
        if method not in valid_methods:
            raise Exception(f"Please enter a valid method: {valid_methods}")

        suffix = f"data/{rec_id}/{method}"
        response = self.session.api_call(suffix=suffix, request_method='post')

        return response.text

    def delete_data(self, rec_id=None, filters=None, multiple_rows='all'):
        """ Deletes rows from a specified data set.
        name : str
            Name of data set to create, must be unique to your account
        desc : str
            Optional, long-form details about your data
        data : pandas.Dataframe
            Data to upload
        """
        if type(rec_id) != int:
            raise Exception(
                'Please enter a valid data clone RecId as an integer.')
        if filters is None or type(filters) != dict:
            raise Exception(
                "Please enter a dict of column names (keys) and values to filter."
            )
        else:
            suffix = f"data/{rec_id}/row"
            find = []
            for k, v in filters.items:
                cell = {"column": k, "value": v}
                find.append(cell)

            body = {"multiplerows": multiple_rows, "find": find}

            delete = self.session.api_call(request_method='delete',
                                           suffix=suffix,
                                           body=body)

            return delete.text
Exemple #5
0
class Dashboard:
    def __init__(self, **connection_params):
        if connection_params:
            self.session = Session(**connection_params)
        else:
            self.session = SessionManager.get_session()

    def get_dashboard(self, thumbnail=False, name=None, output='df'):
        """Get details of all dashboards on an account
        thumbnail : bool
            Whether to include base64 copies of dashboard thumbnails
        name : str
            Filter dashboards by name
        output : str
            Output format, either df or dict
        """
        suffix = "dashboard"
        if name is not None:
            params = {"includethumbnail": thumbnail, "name": name}
        else:
            params = {"includethumbnail": thumbnail}

        dashboards = self.session.api_call(suffix=suffix,
                                           params=params,
                                           request_method='get')

        if output == 'df':
            return pd.DataFrame.from_dict(dashboards.json().get('dashboards'))
        elif output == 'dict':
            return dashboards.json()

    def get_dashboard_thumbnail(self, rec_id=None, output='base64'):
        """Returns thumbnail either ase base64 encoded string or image
        rec_id : str
            Dashboard rec_id to pull
        output : str
            Whether the function output a string or an image
        """
        if type(rec_id) != int:
            raise Exception("Please enter a valid rec_id integer.")
        suffix = "account/" + rec_id + "/thumbnail"
        thumbnail = self.session.api_call(suffix=suffix, request_method='get')
        if output == 'base64':
            return thumbnail.text
        elif output == 'image':
            image = base64.b64decode(thumbnail.text)
            return image
        else:
            raise Exception(
                "Please enter a valid output type: ['base64', 'image'].")

    def get_dashboard_snapshot(self, rec_id=None, output='base64'):
        """Returns snapshot either ase base64 encoded string or image
        rec_id : str
            Dashboard rec_id to pull
        output : str
            Whether the function output a string or an image
        """
        if type(rec_id) != int:
            raise Exception("Please enter a valid rec_id integer.")
        suffix = "account/" + rec_id + "/snapshot"
        snapshot = self.session.api_call(suffix=suffix, request_method='get')
        if output == 'base64':
            return snapshot.text
        elif output == 'image':
            image = base64.b64decode(snapshot.text)
            return image
        else:
            raise Exception(
                "Please enter a valid output type: ['base64', 'image'].")