Skip to main content

Draftable Compare API - Python Client Library

Project description

Draftable Compare API - Python Client Library

This is a thin Python 2/3 client for Draftable’s document comparison API. It wraps the available endpoints, and handles authentication and signing for you. The source code is available on Github.

See the full API documentation for an introduction to the API, usage notes, and other references.

Getting started

  • Sign up for free at api.draftable.com to get your credentials.

  • pip install draftable_compare_api

  • Instantiate the client:

    import draftable
    client = draftable.Client(<your account ID>, <your auth token>)
    comparisons = client.comparisons
  • Start creating comparisons:

    comparison = comparisons.create(
        left = comparisons.side_from_url('https://api.draftable.com/static/api_test_files/grub097.pdf', file_type='pdf'),
        right = comparisons.side_from_url('https://api.draftable.com/static/api_test_files/grub200.pdf', file_type='pdf'),
    )
    
    print("Comparison created:", comparison)
    print("Viewer URL (expires in 30 min):", comparisons.signed_viewer_url(comparison.identifier))

Client API

Dependencies

The only dependency is the pypi requests package.

Design notes

  • This library should be compatible with Python 2 and Python 3. Submit a Github issue if it doesn’t work with your interpreter.

  • This Python library always returns “aware” datetime objects, and assumes that any naive datetime object give it is in UTC time.

  • The API is designed such that requests should always succeed, and comparisons should always succeed in production.

    • Errors in making requests will only occur upon network failure, or when you provide invalid credentials or data.

    • Comparisons will only fail when the files are unreadable, or exceed your account’s size limits.

Initializing the client

The package draftable_compare_api installs a single module, draftable, which exports a single class, draftable.Client.

Client(account_id: str, auth_token: str) will construct an API client. At present, Client has a single property, comparisons, that yields a ComparisonsEndpoint that manages the comparisons for your API account.

So, we’ll assume you set things up as follows:

import draftable
client = draftable.Client(<your account ID>, <your auth token>)
comparisons = client.comparisons

Getting comparisons

ComparisonsEndpoint provides getAll() and get(identifier: str). - all() returns a list of all your comparisons, ordered from newest to oldest. This is a potentially expensive operation. - get(identifier: str) returns a single Comparison object, or raises comparisons.NotFound if there isn’t a comparison with that identifier.

Comparison objects

Comparison objects have the following properties: - identifier: a string giving the identifier. - left, right: objects giving information about each side, with properties: - file_type: the file extension. - source_url (optional): if the file was specified as a URL, this will be a string with the URL. Otherwise, None. - display_name (optional): the display name, if one was given. Otherwise, None. - public: a bool giving whether the comparison is public, or requires authentication to view. - creation_time: a UTC datetime giving when the comparison was created. - expiry_time (optional): if the comparison will expire, a UTC datetime giving the expiry time. Otherwise, None. - ready: bool indicating whether the comparison is ready for display.

If a Comparison is ready (i.e. it has been processed and is ready for display), it will have the following additional properties: - ready_time: UTC datetime giving the time the comparison became ready. - failed: bool indicating whether the comparison succeeded or failed. - error_message (only present if ``failed``): a string providing the developer with the reason the comparison failed.

Example usage

identifier = '.....'

try:
    comparison = comparisons.get('<identifier>')

    print("Comparison '{identifier}' ({publicOrPrivate}) is {readyOrNot}.".format(
        identifier = identifier,
        publicOrPrivate = 'public' if comparison.public else 'private',
        readyOrNot = 'ready' if comparison.ready else 'not ready',
    ))

    if comparison.ready:
        elapsed = comparison.ready_time - comparison.creation_time
        print("The comparison took {} seconds.".format(elapsed.total_seconds()))

        if comparison.failed:
            print("The comparison failed. Error message:", comparison.error_message)

except comparisons.NotFound:
    print("Comparison '{}' doesn't exist.")

Deleting comparisons

ComparisonsEndpoint provides delete(identifier), which attempts to delete the comparison with that identifier.

It has no return value, and raises comparisons.NotFound if there isn’t a comparison with that identifier.

Example usage

oldest_comparisons = comparisons.all()[-10:]

print("Deleting oldest {} comparisons...".format(len(oldest_comparisons)));

for comparison in oldest_comparisons:
    comparisons.delete(comparison.identifier)
    print("Deleted comparison '{}'.".format(comparison.identifier)

Creating comparisons

ComparisonsEndpoint provides create(...), which returns a Comparison object representing the newly created comparison.

Creation options

create accepts the following arguments:

  • left, right: objects describing the left and right files, created using either comparisons.side_from_file or comparisons.side_from_url (see below)

  • identifier (optional): the identifier to use for the comparison.

    • If specified, the identifier can’t clash with an existing comparison.

    • If left unspecified, the API will automatically generate one for you.

  • public (optional): whether the comparison is publicly accessible.

    • Defaults to false. If true, then the comparison viewer can be accessed by anyone, without authentication.

    • See the full API documentation for details.

  • expires (optional): a timedelta or a UTC datetime, specifying when the comparison will be automatically deleted.

    • If given, must be a positive timedelta, or a UTC datetime in the future.

    • Defaults to None, meaning the comparison will never expire.

The function comparisons.side_from_url accepts the following arguments: - url: a fully qualified URL from which Draftable will download the file. - file_type: the type of the file, specified by the file extension. - If you provide the incorrect file type, the comparison will fail. - display_name (optional): a name for the file, to be shown in the comparison.

The function comparisons.side_from_file accepts the following arguments: - file: a file object to be read and uploaded. Please ensure binary mode is used. - file_type: as before. - display_name (optional): as before.

Supported file types

The following file types are supported: - PDF: pdf - Word: docx, docm, doc, rtf - PowerPoint: pptx, pptm, ppt

Exceptions

If you provide comparisons.side_from_file with an invalid file_type, or a file that isn’t opened in binary mode, it will raise comparisons.InvalidArgument.

If you provide comparisons.side_from_url with an invalid file_type or a badly formatted url, it will raise comparisons.InvalidArgument.

Exceptions are raised by create in the following cases: - If a parameter is invalid (e.g. expires is set to a time in the past), it will raise comparisons.InvalidArgument. - If identifier is already in use by another comparison, comparisons.IdentifierNotUnique is raised. - If the API endpoint finds your request invalid for another reason, raises comparisons.BadRequest. - Please see the full API documentation for the other constraints on the data.

Example usage

identifier = comparisons.generate_identifier(); # Generates a unique identifier.

with open('path/to/right/file.docx', 'rb) as right_file:

    comparison = comparisons.create(

        identifier = identifier,

        left = comparisons.side_from_url('https://domain.com/left.pdf', file_type='pdf', display_name='document.pdf'),
        right = comparisons.side_from_file(right_file, file_type='docx', display_name='document (revised).docx'),

        # 'public' is omitted, because we only want to let authenticated users view the comparison.

        # Comparison expires 30 minutes into the future.
        expires: timedelta(minutes=30),

    )

print("Created comparison:", comparison);

# This generates a signed viewer URL that can be used to access the private comparison for the next 30 minutes.
print("Viewer URL (expires in 30 min):", comparisons.signed_viewer_url(identifier));

Displaying comparisons

Comparisons are displayed using a viewer URL. See the section on displaying comparisons in the full API documentation for details.

Viewer URLs are generated with the following methods:

  • comparisons.public_viewer_url(identifier: str, wait: bool = False)

    • Viewer URL for a public comparison with the given identifier.

    • wait is false by default, meaning the viewer will show an error if no such comparison exists.

    • If wait is true, the viewer will wait for a comparison with the given identifier to exist (potentially displaying a loading animation forever).

  • comparisons.signed_vewer_url(identifier: str, valid_until: datetime | timedelta = None, wait: bool = False)

    • Gets a signed viewer URL for a comparison with the given identifier. (The signature is an HMAC based on your credentials.)

    • valid_until gives when the URL will expire. It’s specified as a UTC datetime, or a timedelta.

      • If valid_until is None, the URL defaults to expiring 30 minutes in the future (more than enough time to load the page).

    • Again, if wait is true, the viewer will wait for a comparison with the given identifier to exist.

Example usage

Somewhere in tasks.py:

# Celery task for creating a comparison.
# This will run on a background worker.

@app.task
def upload_comparison_in_background(identifier, left_file_path, right_url):
    with open(left_file_path, 'rb') as left_file:
        comparisons.create(
            identifier = identifier,
            left = comparisons.side_from_file(left_file, ...),
            right = comparisons.side_from_url(right_url, ...),
        )

Then, in compare.py:

from .tasks import upload_comparison_in_background

identifier = comparisons.generate_identifier()

# Upload our request in the background with our Celery task.
upload_comparison_in_background.delay(identifier, ...)

# At some point, we'll have created the comparison. In the mean time, we'll immediately give the user a viewer URL.
viewer_url = comparisons.signed_viewer_url(identifier, wait=true);

# This URL is valid for 30 minutes, the default amount of time.
print("Comparison is being created. View at:", viewer_url)

The comparison viewer will display a loading animation, waiting for the comparison to be created and processed.

Utility methods

  • comparisons.generate_identifier() generates a random unique identifier for you to use.


Other information

Python 2 and 3 compatibility

This package officially supports the latest releases of Python 2 and 3.

At the time of writing, Python 2.7.13, Python 3.5.3, and Python 3.6.0 are known to be supported.

Please report issues you encounter, and we’ll work quickly to resolve them. Contact us at support@draftable.com if you need assistance.

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

draftable_compare_api-1.0.3b1.tar.gz (6.7 kB view hashes)

Uploaded Source

Built Distribution

draftable_compare_api-1.0.3b1-py2.py3-none-any.whl (11.1 kB view hashes)

Uploaded Python 2 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