Skip to main content

DocuWare REST-API client

Project description

docuware-client

This is a client library for the REST API of DocuWare DMS. Since DocuWare provides no official developer documentation for the REST API beyond XSD schema files, this client covers only a part of the API's functionality.

Please keep in mind: This is an independent project with no affiliation to DocuWare GmbH. It is a work in progress, may yield unexpected results, and almost certainly contains bugs.

Breaking change in 0.7.0 — OAuth2 only

Starting with version 0.7.0, only OAuth2 authentication is supported. Cookie-based authentication has been removed completely. If you rely on cookie authentication, please stay on version 0.6.x.

OAuth2 requires DocuWare 7.10 or later. Credentials are stored in a .credentials file; the .session file is no longer created or used.

Usage

The recommended way to connect is via docuware.connect(), which resolves credentials from arguments, environment variables, or a .credentials file and handles login automatically:

import docuware

# Credentials from arguments:
dw = docuware.connect(url="http://localhost", username="...", password="...", organization="...")

# Or from environment variables DW_URL, DW_USERNAME, DW_PASSWORD, DW_ORG:
dw = docuware.connect()

# Or from a .credentials file:
dw = docuware.connect(credentials_file="/path/to/.credentials")

Credentials are saved automatically to the file specified by credentials_file after a successfull login.

For more control, use DocuwareClient and login() explicitly:

import docuware

dw = docuware.Client("http://localhost")
dw.login("username", "password", "organization")

Iterate over the organizations and file cabinets and baskets:

for org in dw.organizations:
    print(org)
    for fc in org.file_cabinets:
        print("   ", fc)
    for b in org.baskets:
        print("   ", b)

If you already know the ID or name of the objects, you can also access them directly.

org = dw.organization("1")
fc = org.file_cabinet("Archive")
basket = org.basket("Inbox")
# If you know the ID:
doc = fc.get_document("123456")

Now some examples of how to search for documents. First you need a search dialog:

# Let's use the first one:
dlg = fc.search_dialog()
# Or a specific search dialog:
dlg = fc.search_dialog("Default search dialog")

Each search term consists of a field name and a search pattern. Each search dialog knows its fields:

for field in dlg.fields.values():
    print("Id    =", field.id)
    print("Length=", field.length)
    print("Name  =", field.name)
    print("Type  =", field.type)
    print("-------")

Let's search for some documents:

# Search for DOCNO equal to '123456':
for result in dlg.search("DOCNO=123456"):
    print(result)
# Search for two patterns alternatively:
for result in dlg.search(["DOCNO=123456", "DOCNO=654321"], operation=docuware.Operation.OR):
    print(result)
# Search for documents in a date range (01-31 January 2023):
for result in dlg.search("DWSTOREDATETIME=2023-01-01T00:00:00,2023-02-01T00:00:00"):
    print(result)

DocuWare search values may contain metacharacters such as (, ), *, and ?. When using the dict form, parentheses are automatically escaped by default so that literal values just work:

# Parentheses are escaped automatically — no 422 error:
for result in dlg.search({"DOCTYPE": "Invoice (incoming)"}):
    print(result)

The escaping behaviour is controlled by the quote parameter (QuoteMode.PARTIAL by default):

  • QuoteMode.PARTIAL (default): escapes ( and ), but leaves wildcard characters * and ? intact.
  • QuoteMode.ALL: also escapes * and ? when they must be treated as literals.
  • QuoteMode.NONE: no automatic escaping — use when you need full control over the value syntax.
import docuware

dlg.search({"NAME": "Müller*"})                           # wildcard preserved
dlg.search({"NAME": "50%"}, quote=docuware.QuoteMode.NONE)     # no escaping
dlg.search({"NAME": "a*b"}, quote=docuware.QuoteMode.ALL)      # * escaped

Passing None as a field value searches for documents where that field is empty (EMPTY()).

The string and list forms are raw condition strings — escaping is the caller's responsibility:

# Manual escaping required in string/list form:
for result in dlg.search("DOCTYPE=Invoice \\(incoming\\)"):
    print(result)

Search terms can be as simple as a single string, but can also be more complex. The following two queries are equivalent:

dlg.search(["FIELD1=TERM1,TERM2", "FIELD2=TERM3"])
dlg.search({"FIELD1": ["TERM1", "TERM2"], "FIELD2": ["TERM3"]})

When a field value is a list of two elements, DocuWare interprets it as a range search (TERM1 ≤ field ≤ TERM2). The first value must be less than or equal to the second. This applies to all field types — dates, numbers, and strings alike. date and datetime objects are converted to ISO 8601 automatically:

from datetime import date
# Documents with DOCDATE in January 2023:
dlg.search({"DOCDATE": [date(2023, 1, 1), date(2023, 1, 31)]})
# Documents with CUSTOMERNO between 1000 and 2000:
dlg.search({"CUSTOMERNO": [1000, 2000]})

To match a field against a set of discrete values, use separate conditions with operation=docuware.Operation.OR:

dlg.search(["CUSTOMERNO=1234", "CUSTOMERNO=5678"], operation=docuware.Operation.OR)

The result of a search is always an iterator over the search results, even if no result was obtained. Each individual search result holds a document attribute, which gives access to the document in the archive. The document itself can be downloaded as a whole or only individual attachments.

for result in dlg.search("DOCNO=123456"):
    doc = result.document
    # Download the complete document ...
    data, content_type, filename = doc.download(keep_annotations=True)
    docuware.write_binary_file(data, filename)
    # ... or individual attachments (or sections, as DocuWare calls them)
    for att in doc.attachments:
        data, content_type, filename = att.download()
        docuware.write_binary_file(data, filename)

Create a new document with index fields:

data = {
    "Subject": "My Document",
    "Date": "2023-01-01",
}
# Create document:
doc = fc.create_document(fields=data)
# Add a file as attachment to the new document:
doc.upload_attachment("path/to/file.pdf")

Update index fields of a document:

doc.update({"Status": "Approved", "Amount": 120.0})

Delete documents:

dlg = fc.search_dialog()
for result in dlg.search(["FIELD1=TERM1,TERM2", "FIELD2=TERM3"]):
    document = result.document
    document.delete()

Users and groups of an organisation can be accessed and managed:

# Iterate over the list of users and groups:
for user in org.users:
    print(user)
for group in org.groups:
    print(group)

# Find a specific user:
user = org.users["John Doe"]  # or: org.users.get("John Doe")

# Add a user to a group:
group = org.groups["Managers"]  # or: org.groups.get("Managers")
group.add_user(user)
# or
user.add_to_group(group)

# Deactivate user:
user.active = False # or True to activate user

# Create a new user:
user = docuware.User(first_name="John", last_name="Doe")
org.users.add(user, password="123456")

OAuth2 / PKCE authentication

If your application handles the OAuth2 login itself — for example via the Authorization Code + PKCE flow — you can connect with externally obtained tokens using connect_with_tokens():

import docuware

dw = docuware.connect_with_tokens(
    url="https://acme.docuware.cloud/DocuWare/Platform",
    access_token="...",
    refresh_token="...",
    token_endpoint="https://acme.docuware.cloud/DocuWare/Identity/connect/token",
    client_id="your-client-id",
    on_token_refresh=lambda tokens: save_tokens(tokens),  # optional: persist rotated tokens
)

The docuware.oauth module provides two helpers for the PKCE flow itself: discover_oauth_endpoints() resolves the authorization and token endpoints from a DocuWare instance, and exchange_pkce_code() exchanges the authorization code for tokens.

See examples/oauth2_login.py for a complete reference implementation including browser launch, local callback server, and CSRF state verification.

CLI usage

This package also includes a simple CLI program for collecting information about the archive and searching and downloading documents or attachments.

First you need to log in:

$ dw-client login --url http://localhost/ --username "Doe, John" --password FooBar --organization "Doe Inc."

The credentials are stored in $XDG_CONFIG_HOME/docuware-client/.credentials (or $HOME/.docuware-client.cred if XDG_CONFIG_HOME is not set). Use --credentials-file /path/to/file to specify a different location.

Of course, --help will give you a list of all options:

$ dw-client --help

Some search examples (Bash shell syntax). The --file-cabinet option accepts both file cabinet names and basket names:

$ dw-client search --file-cabinet Archive Customer=Foo\*
$ dw-client search --file-cabinet Archive DocNo=123456 "DocType=Invoice \\(incoming\\)"
$ dw-client search --file-cabinet Archive DocDate=2022-02-14
$ dw-client search --file-cabinet Inbox DocDate=2022-02-14

Downloading documents:

$ dw-client search --file-cabinet Archive Customer=Foo\* --download document --annotations

Note: --annotations forces the download as a PDF with annotations embedded. Without this flag, the document is downloaded in its original format without annotations.

Downloading a specific document by ID (new in v0.6.1):

$ dw-client get --file-cabinet Archive --id 123456

Downloading attachments of a specific document:

# Download document itself (original format) to stdout:
$ dw-client get --file-cabinet Archive --id 123456 --attachment document > output.pdf

# Download specific attachment:
$ dw-client get --file-cabinet Archive --id 123456 --attachment ATTACHMENT_ID --output my_file.pdf

# Download all attachments to a directory:
$ dw-client get --file-cabinet Archive --id 123456 --attachment "*" --output ./downloads/

Creating and updating documents:

# Create a new document with index fields:
$ dw-client create --file-cabinet Archive --file invoice.pdf Subject="New Invoice" Amount=100.50

# Update index fields of an existing document:
$ dw-client update --file-cabinet Archive --id 123456 Status=Approved

# Add an attachment to a document:
$ dw-client attach --file-cabinet Archive --id 123456 --file supplement.pdf

# Remove an attachment:
$ dw-client detach --file-cabinet Archive --id 123456 --attachment-id ATTACHMENT_ID

Downloading attachments (or sections):

$ dw-client search --file-cabinet Archive DocNo=123456 --download attachments

Some information about your DocuWare installation:

$ dw-client info

Listing all organizations, file cabinets, baskets and dialogs at once:

$ dw-client list

A more specific list, only one file cabinet or basket (by name):

$ dw-client list --file-cabinet Archive
$ dw-client list --file-cabinet Inbox

You can also display a (partial) selection of the contents of individual fields:

$ dw-client list --file-cabinet Archive --dialog custom --field DocNo

Further reading

License

This work is released under the BSD 3 license. You may use and redistribute this software as long as the copyright notice is preserved.

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

docuware_client-0.7.9.tar.gz (35.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

docuware_client-0.7.9-py3-none-any.whl (45.7 kB view details)

Uploaded Python 3

File details

Details for the file docuware_client-0.7.9.tar.gz.

File metadata

  • Download URL: docuware_client-0.7.9.tar.gz
  • Upload date:
  • Size: 35.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for docuware_client-0.7.9.tar.gz
Algorithm Hash digest
SHA256 71d77a9a837fa24db9de33f9acb1d1ad48fad8e23d08d66a01aafcd3834c17e7
MD5 26e3f549f3ddcd46a099700ab030a630
BLAKE2b-256 1dd0c9bbb93cfcc1da67be24c04b54e6b7869d96519cc400b581e2fad6d792ba

See more details on using hashes here.

File details

Details for the file docuware_client-0.7.9-py3-none-any.whl.

File metadata

  • Download URL: docuware_client-0.7.9-py3-none-any.whl
  • Upload date:
  • Size: 45.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for docuware_client-0.7.9-py3-none-any.whl
Algorithm Hash digest
SHA256 1890b984ede0415fdb8ade6317647195ef8f9bc13c7b2dc4f985aae9cad2c664
MD5 c93fca3b52e9ddbccd721b5d7f62a4b4
BLAKE2b-256 d2d652f67fca99a03bf0b2fff3e90214ab5cf9e553fe617247d04d96d15b2aa3

See more details on using hashes here.

Supported by

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