Skip to main content

A Python API client for interacting with Knack applications.

Project description

Knackpy

Build Coverage Python

Knackpy v1.0 is under development. Documented methods should work, but check the status badge ^^

Table of Contents

Installation

Knackpy requires Python v3.6+. To use the development version Knackpy v1.0, install with:

$ pip install knackpy-dev

Quick Start

>>> import knackpy
>>> app = knackpy.App(app_id="myappid",  api_key="myverysecretapikey")
>>> records = app.records("object_1")
>>> records_formatted = [record.format() for record in records]

Modules

App

Knackpy is designed around the App class. It provides helpers for querying and manipulating Knack application data. You should use the App class because:

  • It allows you to query obejcts and views by key or name
  • It takes care of localization issues
  • It let's you download and upload files from your app.
  • It does other things, too
Args
  • app_id (str): Knack application ID string.

  • api_key (str, optional, default=None): Knack API key.

  • metadata (dict, optional, default=None): The Knack app's metadata as a dict. If None it will be fetched on init. You can find your apps metadata at here.

  • tzinfo (pytz.Timezone, optional): A pytz.Timezone object. Defaults to None. When None, is set automatically based on the app's metadadata.

  • max_attempts (int): The maximum number of attempts to make if a request times out. Default values that are set in knackpy.api.request.

  • timeout (int, optional, default=30): Number of seconds to wait before a Knack API request times out. Further reading: Requests docs.

Usage

To create an App instance, the bare minimum you need to provide is your application ID.

If you construct an App instance without providing an API key, you will only be able to fetch records from publicly-availble views.

Note that fetching data from public views is a smart way to avoid hitting your API limit.

# basic app construction with api key
>>> import knackpy
>>> my_app = knackpy.App(app_id="myappid", api_key="myverysecretapikey")

App.records()

Fetch records from a Knack application.

Args
  • name_or_key (str): An object or view key or name string that exists in the app.
  • refresh (bool, optional, default=None): If true, will re-fetch the records from the Knack API, regardless of if they have already been downloaded.
  • record_limit (int, optional, default=None): The maximum number of records to retrieve. If None, all records will be downloaded.
  • filters (dict or list, optional): A dict or list of query filters to be applied, per Knack's specification.
Returns

A generator function which yields knackpy.Record objects.

Usage
# fetch all records from object_1
>>> records = my_app.records("object_1")
# fetch all records from view named "My Exciting View"
>>> records = my_app.records("My Exciting View")

Container identifiers can be supplied as an object or view key (object_1, view_1) or name (my_exciting_object, My Exciting View).

Note that namespace conflicts are highly likely when fetching by name, because Knack uses object names as the default name for views. If you attempt to query your application by a name that exists as both an object and a view, Knackpy will raise a ValueError.

App.records() returns a generator. You'll need to re-intialize it with App.records(<container:str>) each time you iterate on your records.

>>> records_formatted = [
...    record.format() for record in my_app.records("my_exciting_object")
... ]
# re-intialize the records generator
>>> records_raw = [record.raw for record in my_app.recordss("my_exciting_object")]

If you've only fetched one container, you can omit the container name when accessing your records. This is helpful during development, but for readability we suggest you avoid this practice in production code.

>>> my_app = knackpy.App(app_id="myappid")
>>> records = [record for record in my_app.records("My Exciting View")]
# you can omit the container name if you want to access your records again
>>> same_records_without_accessor = [record for record in my_app.records()]

Once you've constructed an App instance, you can resuse it to fetch records from other objects and views. This cuts down on calls to Knack's metadata API.

>>> my_app.records("my_exciting_object")
>>> my_app.records("my_boring_object")
>>> my_app.records("view_1")

Raw record data is available at App.data. You can also use this to cehck the readily available data in your App instance, like so:

>>> app.data.keys()
["object_1", "object_2", "view_1"]

References to all available endpoints are stored at App.containers. This is handy if you want to check the name of a container, or its key:

>>> app.containers
[
    Container(obj='object_1', view=None, scene=None, name='my_boring_object'),
    Container(obj='object_2', view=None, scene=None, name='my_exciting_object'),
    Container(obj=None, view='view_1', scene='scene_1', name='My Exciting View'),
]

You can cut down on API calls by providing your own Knack metadata when creating an App instance:

>>> import json
# get your app's metadata here: https://loader.knack.com/v1/applications/<app_id:str>"
>>> with open("my_metadata.json", "r") as fin:
...     metadata = json.loads(fin.read())
>> app = knackpy.App(app_id,  metadata=metadata)

You can side-load record data into your your app as well. Note that you must assign your data to a valid key that exists in your app:

>>> with open("my_knack_data.json", "r") as fin:
...     data = { "object_3": json.loads(fin.read()) }
>> app.data = data
>> records = [record.format() for record in app.records("object_3")]

You can use knackpy.records() to fetch "raw" data from your Knack app. Be aware that raw Knack timestamps are problematic. See the Records documentation.

App.info()

Display summary metrics about the app.

>>> app.info()
{'objects': 10, 'scenes': 4, 'records': 6786, 'size': '25.47mb'}

App.to_csv()

Write formatted Knack records to CSV. Be aware that destination will be overwritten, if they exist.

Args
  • name_or_key (str): an object or view key or name string that exists in the app.

  • out_dir (str, optional): Relative path to the directory to which files will be written. Defaults to "_csv"

  • delimiter (str, optional): The delimiter string. Defaults to comma (,).

Api

# This is equivalent to exporting records in JSON format from the Knack Builder
>>> import knackpy
>>> data = knackpy.get(
...     app_id="myappid",
...     api_key="myverysecretapikey",
...     obj="object_1",
...     record_limit=None,
...     timeout=30
... )

What's New in v1.0

  • The Knack class is now App.
  • Fetch records using object/view names
  • No more rows-per-page or page count limiting; just set a record_limit.
  • App summary stats:
>>> app.info()
{'objects': 10, 'scenes': 4, 'records': 6786, 'size': '25.47mb'}
  • Pythonic use of exceptions, warnings, and logging.
  • Automatic localization (no need to set TZ info)
  • "Raw" data is available with timestamp corrections
  • Reduce API calls with metadata and/or record side-loading
  • Null values are consistently returned as NoneType's

Issues and Contributions

Issues and pull requests are welcome. Know that your contributions are donated to the public domain.

Timestamps and Localization

Although the Knack API returns timestamp values as Unix timestamps in millesconds, these raw values represent millisecond timestamps in your localized timezone. For example a Knack timestamp value of 1578254700000 represents Sunday, January 5, 2020 8:05:00 PM local time.

To address this, the App class converts Knack timestamps into real (UTC-based) unix timestamps. Timestamps are corrected by referencing the timezone info in your apps metadata. You can manually override your app's timezone information by passing an IANA-compliant timezone string to your App instance, like so:

>>> app = knackpy.App(app_id="myappid",  api_key="myverysecretapikey", tzinfo="US/Eastern")

If you'd like to access your raw, uncorrected records, they can be found at App.data[<container_name:str>].

Note also that Record objects return corrected timestamps via Record.raw and Record.format(). So,

>>> my_app = knackpy.App(app_id="myappid",  api_key="myverysecretapikey", tzinfo="US/Eastern")
# yields raw records with corrected millisecond timestamps
>>> records_raw = [record.raw for record in app.records("object_3")]
# yields corrected timestamps as ISO-8601 strings
>>> records_formatted = [record.format() for record in app.records("object_3")]

Exceptions

Knackpy uses Python's built-in exceptions, as well as Requests's exceptions when interacting with the Knack API.

If you need to inspect an API exception (for example to see the text content of the response), you can access the Response object by handling the exception like so:

>>> my_app = knackpy.App(api_key="myappid")
# raises HTTPError. You cannot request object records without supplying an API key
>>> try:
...       records = my_app.records("object_1")
... except HTTPError as e:
...     print(e.response.text)
...     raise e
# 'Unauthorized Object Access'

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

knackpy-dev-1.0.3.tar.gz (23.7 kB view hashes)

Uploaded Source

Built Distribution

knackpy_dev-1.0.3-py3-none-any.whl (21.9 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page