Package: draftable-compare-api
Draftable Compare API - Python Client LibraryA thin Python client for theDraftable APIwhich wraps all available endpoints and handles authentication and signing.See thefull API documentationfor an introduction to the API, usage notes, and other reference material.RequirementsGetting startedCLI usageAPI referenceInitializing the clientRetrieving comparisonsDeleting comparisonsCreating comparisonsDisplaying comparisonsExporting comparisonsUtility functionsOther informationSelf-signed certificatesLicenseRequirementsOperating system: Any maintained Linux, macOS, or Windows releasePython runtime: Anymaintained version(currently 3.7, 3.8, 3.9, or 3.10)Getting startedCreate a freeAPI accountRetrieve yourcredentialsInstall the librarypipinstalldraftable-compare-apiInstantiate a clientimportdraftableclient=draftable.Client('<yourAccountId>','<yourAuthToken>')comparisons=client.comparisonsStart creating comparisonscomparison=comparisons.create('https://api.draftable.com/static/test-documents/code-of-conduct/left.rtf','https://api.draftable.com/static/test-documents/code-of-conduct/right.pdf')print("Comparison created:{}".format(comparison))# Generate a signed viewer URL to access the private comparison. The expiry# time defaults to 30 minutes if the valid_until parameter is not provided.viewer_url=comparisons.signed_viewer_url(comparison.identifier)print("Viewer URL (expires in 30 mins):{}".format(viewer_url))CLI usageA helper utility,dr-compare, is included for interacting with the API from the command-line. After installing the library rundr-compareto view the built-in help.The CLI tool will only be available in the environment under which the library is installed (e.g. a givenvirtualenv). To ensure it's accessible outside of any given environment install the library user or system-wide (e.g.sudo pip install draftable-compare-api).API referenceThe handling ofdatetimeobjects is as follows:Anynaivedatetimeobjects provided in a method call are assumed to be in UTC time.Returneddatetimeobjects are always "aware" (include timezone information) and use UTC.Initializing the clientThe package provides a module,draftable, which exports a class to create aClientfor your API account.Clientprovides acomparisonsproperty which yields aComparisonsEndpointto manage the comparisons for your API account.Creating aClientdiffers slightly based on the API endpoint being used:importdraftable# Draftable API (default endpoint)account_id='<yourAccountId>'# Replace with your API credentials from:auth_token='<yourAuthToken>'# https://api.draftable.com/account/credentialsclient=draftable.Client(account_id,auth_token)comparisons=client.comparisons# Draftable API regional endpoint or Self-hostedbase_url='https://draftable.example.com/api/v1'# Replace with the endpoint URLaccount_id='<yourAccountId>'# Replace with your API credentials from the regionalauth_token='<yourAuthToken>'# Draftable API endpoint or your Self-hosted containerclient=draftable.Client(account_id,auth_token,base_url)comparisons=client.comparisonsFor API Self-hosted you may need tosuppress TLS certificate validationif the server is using a self-signed certificate (the default).Retrieving comparisonsInstances of theComparisonsEndpointclass provide the following methods for retrieving comparisons:all()Returns alistof all your comparisons, ordered from newest to oldest. This is potentially an expensive operation.get(identifier: str)Returns the specifiedComparisonor raises aNotFoundexception if the specified comparison identifier does not exist.Comparisonobjects have the following properties:identifier: strThe unique identifier of the comparison.left: object/right: objectInformation about each side of the comparison.file_type: strThe file extension.source_url: str(optional)The URL for the file if the original request was specified by URL, otherwiseNone.display_name: str(optional)The display name for the file if given in the original request, otherwiseNone.public: boolIndicates if the comparison is public.creation_time: datetimeTime in UTC when the comparison was created.expiry_time: datetime(optional)The expiry time if the comparison is set to expire, otherwiseNone.ready: boolIndicates if the comparison is ready to display.If aComparisonisready(i.e. it has been processed) it has the following additional properties:ready_time: datetimeTime in UTC the comparison became ready.failed: boolIndicates if comparison processing failed.error_message: str(only present iffailed)Reason processing of the comparison failed.Example usagefromdraftable.endpointsimportexceptionsidentifier='<identifier>'try:comparison=comparisons.get(identifier)print("Comparison '{identifier}' ({visibility}) is{status}.".format(identifier=identifier,visibility='public'ifcomparison.publicelse'private',status='ready'ifcomparison.readyelse'not ready'))ifcomparison.ready:elapsed=comparison.ready_time-comparison.creation_timeprint("The comparison took{}seconds.".format(elapsed.total_seconds()))ifcomparison.failed:print("The comparison failed with error:{}".format(comparison.error_message))exceptexceptions.NotFound:print("Comparison '{}' does not exist.".format(identifier))Deleting comparisonsInstances of theComparisonsEndpointclass provide the following methods for deleting comparisons:delete(identifier: str)Returns nothing on successfully deleting the specified comparison or raises aNotFoundexception if no such comparison exists.Example usageoldest_comparisons=comparisons.all()[-10:]print("Deleting oldest{}comparisons ...".format(len(oldest_comparisons)))forcomparisoninoldest_comparisons:comparisons.delete(comparison.identifier)print("Comparison '{}' deleted.".format(comparison.identifier))Creating comparisonsInstances of theComparisonsEndpointclass provide the following methods for retrieving comparisons:create(left: ComparisonSide, right: ComparisonSide, identifier: str = None, public: bool = False, expires: datetime | timedelta = None)Returns aComparisonrepresenting the newly created comparison.createaccepts the following arguments:left/rightDescribes the left and right files (see following section).identifier(optional)Identifier to use for the comparison:If specified, the identifier must be unique (i.e. not already be in use).If unspecified orNone, the API will automatically generate a unique identifier.public(optional)Specifies the comparison visibility:IfFalseor unspecified authentication is required to view the comparison.IfTruethe comparison can be accessed by anyone with knowledge of the URL.expires(optional)Time at which the comparison will be deleted:Must be specified as adatetimeor atimedelta(UTC if naive).If specified, the provided expiry time must be UTC and in the future.If unspecified orNone, the comparison will never expire (but may be explicitly deleted).Note: The comparision must be retrieved via thecomparisons.get(<identifier>)call to check the 'ready' status for new comparisons. This is an important
step before exporting or accessing consecutive comparisons in any code loops.The following exceptions may be raised:BadRequestThe request could not be processed (e.g.identifieralready in use).InvalidArgumentFailure in parameter validation (e.g.expiresis in the past).Creating comparison sidesThedraftablemodule provides the following static methods for creating comparison sides:draftable.make_side(url_or_file_path: str, file_type: str = None, display_name: str = None)Returns aComparisonSidefor a file or URL by attempting to guess from theurl_or_file_pathparameter.Alternatively, for explicitly creating a file or URL comparison side, the following static methods can be used:draftable.endpoints.comparisons.sides.side_from_file(file: object, file_type: str, display_name: str = None)Returns aComparisonSidefor a locally accessible file.draftable.endpoints.comparisons.sides.side_from_url(url: str, file_type: str, display_name: str = None)Returns aComparisonSidefor a remotely accessible file referenced by URL.These methods accept the following arguments:url_or_file_path(make_sideonly)The file or URL path for a comparison side.file(side_from_fileonly)A file object to be read and uploaded.The file must be opened for reading inbinary mode.url(side_from_urlonly)The URL from which the server will download the file.file_typeThe type of file being submitted:PDF:pdfWord:docx,docm,doc,rtfPowerPoint:pptx,pptm,pptOther:txtdisplay_name(optional)The name of the file shown in the comparison viewer.The following exceptions may be raised:InvalidArgumentFailure in parameter validation (e.g.file_typeis invalid,urlis malformed, orfileis not opened inbinary mode).Themake_sidemethod may additionally raise the following exceptions:InvalidPathThe provided path uses a file URI scheme (i.e.file:) but references a remote host or the file doesn't exist.ValueErrorThe provided path was not a URL or the file doesn't exist.Example usagefromdatetimeimporttimedeltaidentifier=draftable.generate_identifier()comparison=comparisons.create(identifier=identifier,left=draftable.make_side('https://domain.com/left.pdf',file_type='pdf',display_name='Document.pdf'),right=draftable.make_side('path/to/right/file.docx',file_type='docx',display_name='Document (revised).docx'),# Expire this comparison in 2 hours (default is no expiry)expires=timedelta(hours=2))print("Created comparison:{}".format(comparison))Displaying comparisonsInstances of theComparisonsEndpointclass provide the following methods for displaying comparisons:public_viewer_url(identifier: str, wait: bool = False)Generates a public viewer URL for the specified comparison.signed_viewer_url(identifier: str, valid_until: datetime | timedelta = None, wait: bool = False)Generates a signed viewer URL for the specified comparison.Both methods use the following common parameters:identifierIdentifier of the comparison for which to generate aviewer URL.wait(optional)Specifies the behaviour of the viewer if the provided comparison does not exist.IfFalseor unspecified, the viewer will show an error if theidentifierdoes not exist.IfTrue, the viewer will wait for a comparison with the providedidentifierto exist.Note this will result in a perpetual loading animation if theidentifieris never created.Thesigned_viewer_urlmethod also supports the following parameters:valid_until(optional)Time at which the URL will expire (no longer load).Must be specified as adatetimeor atimedelta.If specified, the provided expiry time must be UTC and in the future.If unspecified orNone, the URL will be generated with the default 30 minute expiry.See the displaying comparisons section in theAPI documentationfor additional details.Example usageidentifier='<identifier>'# Retrieve a signed viewer URL which is valid for 1 hour. The viewer will wait# for the comparison to exist in the event processing has not yet completed.viewer_url=comparisons.signed_viewer_url(identifier,timedelta(hours=1),wait=True)print("Viewer URL (expires in 1 hour):{}".format(viewer_url))Exporting comparisonsTo perform comparison exports retrieve anExportsEndpointinstance via theexportsproperty of yourClientinstance:exports=client.exportsCreating comparison exportsInstances of theExportsEndpointclass provide the following methods for exporting comparisons:create(comparison: Union[Comparison, str], kind: str = 'single_page', include_cover_page: bool = False)Returns aExportrepresenting the newly created export.createaccepts the following arguments:comparisonThe comparison to export provided as aComparisoninstance or a comparison identifier.kindThe type of export to perfom.include_cover_pageWhether a cover page should be included (combinedexport kind only).The following export kinds are supported for thekindparameter:single_pageExports only the right-side of the comparison with highlights and deletion markers.combinedExports both the left and right sides.leftExport the left side only.rightExports the right side only.Example usageexports=client.exportsexport=exports.create(comparison,kind='single_page')Retrieving comparison exportsInstances of theExportsEndpointclass provide the following methods for retrieving exports:get(identifier: str)Returns the specifiedExportor raises aNotFoundexception if the specified export identifier does not exist.Exportobjects have the same properties asComparisonobjects.As with comparison requests, export requests need to be processed. Typically processing only takes a few seconds, but this can vary based on numerous factors (e.g. size of the documents being exported, number of changes in the comparison, etc ...). To determine if an export has been processed thereadyproperty of theExportinstance should be inspected and polling can be utilised to wait until the export has been completed.Example usageimporttimeexport=client.exports.create(comparison.identifier)whilenotexport.ready:time.sleep(1)export=client.exports.get(export.identifier)ifexport.ready:print(export.url)Utility functionsThedraftablemodule provides the following static methods for generating comparison identifiers:draftable.generate_identifier()Generates a random unique comparison identifier.Other informationSelf-signed certificatesIf connecting to an API Self-hosted endpoint which is using a self-signed certificate (the default) you will need to suppress certificate validation. This can be done by setting theCURL_CA_BUNDLEenvironment variable to an empty string. On Windows, this must be done from within the Python interpreter due to platform limitations.See the below examples for different operating systems and shell environments. Note that all examples only set the variable for the running shell or Python interpreter and it will not persist. To persist the setting consult the documentation for your shell environment. This should be done with caution as this setting suppresses certificate validation forallconnections made by the Python runtime!Non-Windows environments (inc. WSL)(ba)sh (Linux, macOS, WSL)exportCURL_CA_BUNDLE=0PowerShell:$env:CURL_CA_BUNDLE=0Windows environmentsSetting an environment variable to the empty string is not valid in Windows and is treated as equivalent to removing any existing environment variable of the same name. As such, suppressing certificate validation requires an alternate approach. The most straightforward is to set the environment variable from within Python, instead of before launch.importosos.environ['CURL_CA_BUNDLE']=''If your code spawns Python subprocesses they must separately modify their environment as the change will not be inherited as you'd normally expect.All environmentsDisabling certificate validation in production environments is strongly discouraged as it significantly lowers security. We only recommend setting this environment variable in development environments if configuring a CA signed certificate for API Self-hosted is not possible.LicenseAll content is licensed under the terms ofThe MIT License.