diff --git a/docs/concepts/custom_field_query.md b/docs/concepts/custom_field_query.md index 6b8a6df..0cebb11 100644 --- a/docs/concepts/custom_field_query.md +++ b/docs/concepts/custom_field_query.md @@ -10,7 +10,7 @@ The `custom_field_query` filter lets you search documents by custom field values | ------------------------------------ | ----------------- | ---------------------- | | `CustomFieldQuery(field, op, value)` | — | `[field, op, value]` | | `CustomFieldQueryAnd(q1, q2, …)` | `q1 & q2` | `["AND", [q1, q2, …]]` | -| `CustomFieldQueryOr(q1, q2, …)` | `q1 \| q2` | `["OR", [q1, q2, …]]` | +| `CustomFieldQueryOr(q1, q2, …)` | `q1 | q2` | `["OR", [q1, q2, …]]` | | `CustomFieldQueryNot(q)` | `~q` | `["NOT", q]` | Import from `pypaperless.models.custom_field_query` (or via `pypaperless.models.types`): diff --git a/docs/concepts/custom_fields.md b/docs/concepts/custom_fields.md index 2fe2ca3..8b3c8d8 100644 --- a/docs/concepts/custom_fields.md +++ b/docs/concepts/custom_fields.md @@ -18,7 +18,7 @@ Each custom field has a `data_type` which determines the type of its value: | `INTEGER` | `int` | Integer number | | `FLOAT` | `float` | Floating point number | | `MONETARY` | `str` | Currency amount, e.g. `"EUR12.50"` | -| `SELECT` | `int | Selection from predefined options | +| `SELECT` | `int | str` | Selection from predefined options | | `DOCUMENT_LINK` | `list[int]` | Links to other documents by ID | --- @@ -115,17 +115,17 @@ A `TypeError` is raised if the actual type does not match `expected_type`. When the cache is active, pypaperless instantiates the right subclass automatically: -| Class | `data_type` | `value` type | -| ------------------------------ | -------------------- | ----------------------- | -| `CustomFieldStringValue` | `STRING`, `LONGTEXT` | `str | -| `CustomFieldURLValue` | `URL` | `str | -| `CustomFieldDateValue` | `DATE` | `datetime.date | -| `CustomFieldBooleanValue` | `BOOLEAN` | `bool | -| `CustomFieldIntegerValue` | `INTEGER` | `int | -| `CustomFieldFloatValue` | `FLOAT` | `float | -| `CustomFieldMonetaryValue` | `MONETARY` | `str | -| `CustomFieldSelectValue` | `SELECT` | `int | str | -| `CustomFieldDocumentLinkValue` | `DOCUMENT_LINK` | `list[int] | +| Class | `data_type` | `value` type | +| ------------------------------ | -------------------- | ---------------- | +| `CustomFieldStringValue` | `STRING`, `LONGTEXT` | `str` | +| `CustomFieldURLValue` | `URL` | `str` | +| `CustomFieldDateValue` | `DATE` | `datetime.date` | +| `CustomFieldBooleanValue` | `BOOLEAN` | `bool` | +| `CustomFieldIntegerValue` | `INTEGER` | `int` | +| `CustomFieldFloatValue` | `FLOAT` | `float` | +| `CustomFieldMonetaryValue` | `MONETARY` | `str` | +| `CustomFieldSelectValue` | `SELECT` | `int | str` | +| `CustomFieldDocumentLinkValue` | `DOCUMENT_LINK` | `list[int]` | ### `CustomFieldMonetaryValue` extras diff --git a/docs/concepts/documents.md b/docs/concepts/documents.md index 58ca1e3..1f74406 100644 --- a/docs/concepts/documents.md +++ b/docs/concepts/documents.md @@ -99,6 +99,7 @@ if document.has_search_hit: hit = document.search_hit print(hit.score) print(hit.highlights) + print(hit.note_highlights) print(hit.rank) ``` @@ -149,6 +150,7 @@ print(suggestions.correspondents) # list[int] print(suggestions.document_types) # list[int] print(suggestions.tags) # list[int] print(suggestions.storage_paths) # list[int] +print(suggestions.dates) # list[datetime.date] ``` --- @@ -242,9 +244,9 @@ print(f"Upload queued as task: {task_id}") | `correspondent` | Correspondent ID | | `document_type` | Document type ID | | `storage_path` | Storage path ID | -| `tags` | list[int] | Tag ID(s) | -| `archive_serial_number` | ASN | -| `custom_fields` | DocumentCustomFieldList | Custom field assignments | +| `tags` | Tag IDs | +| `archive_serial_number` | Archive serial number | +| `custom_fields` | Custom field assignments | ### Uploading with custom fields @@ -295,6 +297,21 @@ print(task.status, task.result) --- +## Checking if a document is deleted + +The `is_deleted` property returns `True` when the document is currently in the trash: + +```python +doc = await paperless.documents(42) +print(doc.is_deleted) # False for active documents + +# Documents returned from paperless.trash also have this set +async for doc in paperless.trash: + print(doc.id, doc.is_deleted, doc.deleted_at) +``` + +--- + ## Sending documents by e-mail You can send one or more documents as attachments to one or more e-mail addresses: diff --git a/docs/concepts/permissions.md b/docs/concepts/permissions.md index 31e5e27..608a05c 100644 --- a/docs/concepts/permissions.md +++ b/docs/concepts/permissions.md @@ -149,12 +149,12 @@ class Permissions: Constructed with flat keyword arguments — only specify what you need, omitted keys default to `[]`: -| Keyword argument | Type | Meaning | -| ---------------- | ----------- | -------------------------------- | -| `view_users` | `list[int]` | User IDs with view permission | -| `view_groups` | `list[int]` | Group IDs with view permission | -| `change_users` | `list[int]` | User IDs with change permission | -| `change_groups` | `list[int]` | Group IDs with change permission | +| Keyword argument | Meaning | +| ---------------- | -------------------------------- | +| `view_users` | User IDs with view permission | +| `view_groups` | Group IDs with view permission | +| `change_users` | User IDs with change permission | +| `change_groups` | Group IDs with change permission | ```python from pypaperless.models.types import Permissions diff --git a/docs/migrating-v5-to-v6.md b/docs/migrating-v5-to-v6.md new file mode 100644 index 0000000..fb44e3f --- /dev/null +++ b/docs/migrating-v5-to-v6.md @@ -0,0 +1,326 @@ +# Migrating from v5 to v6 + +v6 is a full rewrite of pypaperless, motivated by three concrete problems with v5: + +- **Tight coupling between models and the HTTP layer.** In v5, every model instance held a reference to the `Paperless` client and called it directly for `update()`, `delete()`, and `save()`. This made models hard to construct in tests and impossible to pass between different client contexts. v6 makes models pure data containers — all I/O goes through service objects. +- **Runtime type safety.** v5 used plain dataclasses with manual dict-to-object conversion. v6 uses Pydantic v2, which validates all incoming API data against the declared field types at parse time. The Pydantic team's own benchmarks show v2 parses 5–17× faster than v1 and catches malformed payloads that would previously have silently produced wrong values. +- **HTTP client ergonomics.** `aiohttp` requires an explicit session lifecycle and has no built-in sync support. `httpx` has a unified sync/async API, ships with a `MockTransport` for testing without a live server, and handles connection pooling transparently. + +The changes are mechanical at call sites but the reasoning is architectural. + +--- + +## Quick checklist + +| # | What to change | Section | +| --- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| 1 | Replace `aiohttp` / `yarl` with `httpx` | [Dependencies](#dependencies) | +| 2 | Update `Paperless(...)` constructor arguments | [Initializing the client](#initializing-the-client) | +| 3 | Replace `reduce()` with `filter()` — different call pattern | [Iteration and filtering](#iteration-and-filtering) | +| 4 | Move `update()`, `delete()`, `save()` from model instances to services | [CRUD](#crud-update-delete-save) | +| 5 | Replace `request_permissions = True` with `with_permissions()` | [Permissions](#permissions) | +| 6 | Replace `doc.get_download()`, `get_metadata()`, etc. | [Document convenience methods removed](#document-convenience-methods-removed) | +| 7 | Note deletion: `note.delete()` → service call | [Document notes](#document-notes) | +| 8 | `generate_api_token()` custom-client argument renamed | [Token generation](#token-generation) | +| 9 | New: `paperless.profile`, `paperless.trash`, `paperless.documents.history` | [New resources](#new-resources) | + +--- + +## Dependencies + +Replace `aiohttp` and `yarl` with `httpx`: + +=== "v5" + ```toml + dependencies = ["aiohttp", "yarl", "pypaperless"] + ``` + +=== "v6" + ```toml + dependencies = ["httpx", "pypaperless"] + ``` + +--- + +## Initializing the client + +The constructor signature changed. `session` was renamed to `client`, and `request_args` was removed. + +=== "v5" + ```python + import aiohttp + paperless = Paperless("http://localhost:8000", "mytoken") + paperless = Paperless("http://localhost:8000", "mytoken", session=my_session) + paperless = Paperless("http://localhost:8000", "mytoken", request_args={"ssl": False}) + ``` + +=== "v6" + ```python + import httpx + paperless = Paperless("http://localhost:8000", "mytoken") + paperless = Paperless("http://localhost:8000", "mytoken", client=my_httpx_client) + + # SSL / TLS customization — pass a pre-configured httpx.AsyncClient + client = httpx.AsyncClient(verify=False) # or verify="/path/to/cert.pem" + paperless = Paperless("http://localhost:8000", "mytoken", client=client) + ``` + +The `url` parameter no longer accepts `yarl.URL` objects — pass a plain string. + +--- + +## Token generation + +`generate_api_token()` is a static method on `Paperless`. The optional custom-client argument was renamed from `session` to `client`: + +=== "v5" + ```python + token = await Paperless.generate_api_token(url, username, password, session=my_session) + ``` + +=== "v6" + ```python + token = await Paperless.generate_api_token(url, username, password, client=my_client) + ``` + +--- + +## Iteration and filtering + +`reduce()` was replaced by `filter()`. The key difference: the context manager now **yields the service object**, so you iterate over `ctx` instead of reusing the outer service name. + +=== "v5" + ```python + # reduce() mutated the helper for the duration of the block + async with paperless.documents.reduce(title__icontains="invoice"): + async for doc in paperless.documents: + print(doc.title) + ``` + +=== "v6" + ```python + async with paperless.documents.filter(title__icontains="invoice") as ctx: + async for doc in ctx: + print(doc.title) + ``` + +The `pages()` method was removed. Use `as_list()` or iterate directly. + +=== "v5" + ```python + async for page in paperless.documents.pages(page_size=25): + for doc in page: + print(doc.title) + ``` + +=== "v6" + ```python + docs = await paperless.documents.as_list() + + async for doc in paperless.documents: + print(doc.title) + ``` + +--- + +## CRUD: update, delete, save + +In v5, CRUD operations lived on model instances. In v6 they live on the service. + +### update() + +=== "v5" + ```python + doc = await paperless.documents(42) + doc.title = "New Title" + await doc.update() + ``` + +=== "v6" + ```python + doc = await paperless.documents(42) + doc.title = "New Title" + await paperless.documents.update(doc) + ``` + +### delete() + +=== "v5" + ```python + doc = await paperless.documents(42) + await doc.delete() + ``` + +=== "v6" + ```python + doc = await paperless.documents(42) + await paperless.documents.delete(doc) + ``` + +This applies to every resource — correspondents, tags, custom fields, etc. + +### save() — creating new resources + +=== "v5" + ```python + draft = paperless.documents.draft(document=raw_bytes, title="Invoice") + pk = await draft.save() + ``` + +=== "v6" + ```python + draft = paperless.documents.draft(document=raw_bytes, title="Invoice") + task_id = await paperless.documents.save(draft) + ``` + +For all other resources (correspondents, tags, …): + +=== "v5" + ```python + draft = paperless.tags.draft(name="urgent") + pk = await draft.save() + ``` + +=== "v6" + ```python + draft = paperless.tags.draft(name="urgent") + pk = await paperless.tags.save(draft) + ``` + +--- + +## Permissions + +The mutable `request_permissions` setter was replaced by a `with_permissions()` context manager. The flag is now automatically reset on exit. + +=== "v5" + ```python + paperless.documents.request_permissions = True + doc = await paperless.documents(42) + print(doc.owner, doc.permissions) + paperless.documents.request_permissions = False # had to reset manually + ``` + +=== "v6" + ```python + async with paperless.documents.with_permissions() as ctx: + doc = await ctx(42) + print(doc.owner, doc.permissions) + async for doc in ctx: + print(doc.owner, doc.permissions) + # flag is reset automatically on exit, even on exceptions + ``` + +--- + +## Document convenience methods removed + +The shortcut methods on `Document` instances that delegated back to the helper were removed. Call the service directly. + +| v5 (on model instance) | v6 (on service) | +| --------------------------------------- | ----------------------------------------------------------- | +| `await doc.get_download()` | `await paperless.documents.download(doc.id)` | +| `await doc.get_download(original=True)` | `await paperless.documents.download(doc.id, original=True)` | +| `await doc.get_metadata()` | `await paperless.documents.metadata(doc.id)` | +| `await doc.get_preview()` | `await paperless.documents.preview(doc.id)` | +| `await doc.get_thumbnail()` | `await paperless.documents.thumbnail(doc.id)` | +| `await doc.get_suggestions()` | `await paperless.documents.suggestions(doc.id)` | + +--- + +## Document notes + +Note deletion moved from the model to the service, consistent with the general CRUD pattern. + +=== "v5" + ```python + notes = await paperless.documents.notes(42) + for note in notes: + await note.delete() + ``` + +=== "v6" + ```python + notes = await paperless.documents.notes(42) + for note in notes: + await paperless.documents.notes.delete(note) + ``` + +Creating a new note: + +=== "v5" + ```python + draft = paperless.documents.notes.draft(note="Checked.", document=42) + note_id, doc_id = await draft.save() + ``` + +=== "v6" + ```python + draft = paperless.documents.notes.draft(note="Checked.", document=42) + note_id, doc_id = await paperless.documents.notes.save(draft) + ``` + +--- + +## New resources + +Two new top-level services were added. + +### `paperless.profile` + +Access the currently authenticated user's own profile: + +```python +profile = await paperless.profile() +print(profile.username, profile.email) +``` + +See [Profile](resources/profile.md) for details. + +### `paperless.trash` + +Browse and manage soft-deleted documents: + +```python +async for doc in paperless.trash: + print(doc.id, doc.title, doc.deleted_at) + +await paperless.trash.restore([42, 43]) +await paperless.trash.empty() # empties entire trash +``` + +The `Document.is_deleted` property returns `True` for documents retrieved from the trash. + +See [Trash](resources/trash.md) for details. + +### `paperless.documents.history` + +A new document audit-log sub-service: + +```python +entries = await paperless.documents.history(42) +for entry in entries: + print(entry.actor, entry.timestamp, entry.action) +``` + +--- + +## Error handling + +`PaperlessConnectionError` is now raised for `httpx.ConnectError` rather than `aiohttp.ClientConnectorError`. If you catch library-specific exceptions, update accordingly: + +=== "v5" + ```python + except aiohttp.ClientConnectorError: + ... + ``` + +=== "v6" + ```python + except httpx.ConnectError: + ... + # or catch the pypaperless wrapper (works in both v5 and v6) + except PaperlessConnectionError: + ... + ``` +``` diff --git a/docs/resources.md b/docs/resources.md index 9fd2f87..515345d 100644 --- a/docs/resources.md +++ b/docs/resources.md @@ -62,7 +62,7 @@ async for correspondent in paperless.correspondents: print(correspondent.id, correspondent.name) async for tag in paperless.tags: - print(tag.name, tag.colour) + print(tag.name, tag.color) ``` ### Getting all primary keys @@ -133,7 +133,7 @@ Use `draft()` to create a new draft model and `save()` to persist it: ```python # Create a new tag -draft = paperless.tags.draft(name="important", colour="#ff0000") +draft = paperless.tags.draft(name="important", color="#ff0000") new_id = await paperless.tags.save(draft) print(f"Created tag with id {new_id}") ``` @@ -158,7 +158,7 @@ Modify fields on a fetched model and call `update()`: ```python tag = await paperless.tags(3) tag.name = "urgent" -tag.colour = "#ff0000" +tag.color = "#ff0000" updated = await paperless.tags.update(tag) print(f"Updated: {updated}") # True if any field changed diff --git a/docs/resources/documents.md b/docs/resources/documents.md index b4cd880..a3f9032 100644 --- a/docs/resources/documents.md +++ b/docs/resources/documents.md @@ -4,35 +4,42 @@ Documents are the core resource in Paperless-ngx. This page shows the essential ## Models -### `Document` *(key fields)* - -| Field | Description | -| --------------- | ---------------------------------- | -| `id` | Primary key | -| `title` | Document title | -| `content` | Full-text content (OCR result) | -| `document_type` | Assigned document type id | -| `correspondent` | Assigned correspondent id | -| `storage_path` | Assigned storage path id | -| `tags` | Assigned tag ids | -| `created` | Creation date | -| `created_date` | Creation date (date-only property) | -| `added` | Date added to Paperless | -| `asn` | Archive serial number | -| `custom_fields` | Custom field values | +### `Document` + +| Field | Description | +| ------------------------ | ------------------------------------- | +| `id` | Primary key | +| `title` | Document title | +| `content` | Full-text content (OCR result) | +| `document_type` | Assigned document type id | +| `correspondent` | Assigned correspondent id | +| `storage_path` | Assigned storage path id | +| `tags` | Assigned tag ids | +| `created` | Creation date (`datetime.date`) | +| `created_date` | Creation date — date-only property | +| `modified` | Last modified timestamp | +| `added` | Date added to Paperless | +| `archive_serial_number` | Archive serial number | +| `original_file_name` | Original uploaded filename | +| `archived_file_name` | Filename of the archived version | +| `page_count` | Number of pages | +| `mime_type` | MIME type of the document | +| `is_shared_by_requester` | Whether the document is shared by you | +| `is_deleted` | `True` when the document is in trash | +| `custom_fields` | Custom field values | ### `DocumentDraft` -| Field | Description | -| --------------- | --------------------- | -| `title` | Document title | -| `document_type` | Document type id | -| `correspondent` | Correspondent id | -| `storage_path` | Storage path id | -| `tags` | Tag ids | -| `created` | Created date | -| `asn` | Archive serial number | -| `custom_fields` | Custom field values | +| Field | Description | +| ----------------------- | --------------------- | +| `title` | Document title | +| `document_type` | Document type id | +| `correspondent` | Correspondent id | +| `storage_path` | Storage path id | +| `tags` | Tag ids | +| `created` | Created date | +| `archive_serial_number` | Archive serial number | +| `custom_fields` | Custom field values | ## Fetch one diff --git a/docs/resources/mail_rules.md b/docs/resources/mail_rules.md index 074d584..f03ec76 100644 --- a/docs/resources/mail_rules.md +++ b/docs/resources/mail_rules.md @@ -23,11 +23,13 @@ Mail rules define how Paperless-ngx processes incoming emails from a mail accoun | `assign_title_from` | How to derive the document title | | `assign_tags` | Tags to assign to imported documents | | `assign_correspondent` | Correspondent id to assign | +| `assign_correspondent_from` | How to derive the correspondent | | `assign_document_type` | Document type id to assign | | `assign_owner_from_rule` | Assign rule owner to the document | | `order` | Processing order | | `attachment_type` | Which attachments to import | | `consumption_scope` | What to consume from the email | +| `pdf_layout` | PDF layout mode for imported email | ## Fetch one diff --git a/docs/resources/status.md b/docs/resources/status.md index ccbe080..d5fcb06 100644 --- a/docs/resources/status.md +++ b/docs/resources/status.md @@ -22,25 +22,40 @@ The `status` resource reports the health of the Paperless-ngx server — databas ### `StatusDatabase` -| Field | Description | -| ------------------ | -------------------------- | -| `type` | Database engine | -| `url` | Connection URL | -| `status` | `"OK"` / `"ERROR"` | -| `error` | Error message if unhealthy | -| `migration_status` | Migration state | +| Field | Description | +| ------------------ | ------------------------------------------------------ | +| `type` | Database engine | +| `url` | Connection URL | +| `status` | `"OK"` / `"ERROR"` | +| `error` | Error message if unhealthy | +| `migration_status` | `StatusDatabaseMigration` object (see below) | + +### `StatusDatabaseMigration` + +| Field | Description | +| ---------------------- | --------------------------------- | +| `latest_migration` | Name of the latest applied migration | +| `unapplied_migrations` | List of pending migration names | ### `StatusTasks` -| Field | Description | -| ------------------------- | ---------------------------------- | -| `redis_url` | Redis connection URL | -| `redis_status` | Redis health | -| `celery_status` | Celery worker health | -| `index_status` | Full-text index health | -| `classifier_status` | ML classifier health | -| `classifier_last_trained` | Last classifier training timestamp | -| `sanity_check_status` | Last sanity check result | +| Field | Description | +| ------------------------- | --------------------------------------------- | +| `redis_url` | Redis connection URL | +| `redis_status` | Redis health (`"OK"` / `"ERROR"`) | +| `redis_error` | Redis error message if unhealthy | +| `celery_url` | Celery broker URL | +| `celery_status` | Celery worker health (`"OK"` / `"ERROR"`) | +| `celery_error` | Celery error message if unhealthy | +| `index_status` | Full-text index health (`"OK"` / `"ERROR"`) | +| `index_last_modified` | Timestamp of last index modification | +| `index_error` | Index error message if unhealthy | +| `classifier_status` | ML classifier health (`"OK"` / `"ERROR"`) | +| `classifier_last_trained` | Last classifier training timestamp | +| `classifier_error` | Classifier error message if unhealthy | +| `sanity_check_status` | Last sanity check result (`"OK"` / `"ERROR"`) | +| `sanity_check_last_run` | Timestamp of last sanity check | +| `sanity_check_error` | Sanity check error message if unhealthy | ## Fetch diff --git a/docs/resources/tasks.md b/docs/resources/tasks.md index ae410a0..a106e1b 100644 --- a/docs/resources/tasks.md +++ b/docs/resources/tasks.md @@ -13,7 +13,7 @@ Tasks represent Celery background jobs — primarily document consumption jobs. | `date_created` | When the task was queued | | `date_done` | When the task finished | | `type` | Task type | -| `status` | `"PENDING"` / `"STARTED"` / `"SUCCESS"` / `"FAILURE"` | +| `status` | `"PENDING"` / `"STARTED"` / `"SUCCESS"` / `"FAILURE"` / `"REVOKED"` | | `result` | Result or error message | | `acknowledged` | Whether the task has been dismissed | | `related_document` | Document id created by this task | @@ -67,3 +67,22 @@ async for task in paperless.tasks: # Collect all pending tasks pending = [t async for t in paperless.tasks if t.status == "PENDING"] ``` + +## Filter + +```python +# Only tasks with a specific status +async for task in paperless.tasks.filter(status="SUCCESS"): + print(task.task_id, task.result) + +# Only unacknowledged consumption tasks +async for task in paperless.tasks.filter(acknowledged=False, type="ConsumptionTask"): + print(task.task_id) +``` + +| Parameter | Type | Description | +| -------------- | ------ | ---------------------------------------- | +| `acknowledged` | `bool` | Filter by whether the task is dismissed | +| `status` | `str` | Filter by status string | +| `task_name` | `str` | Filter by internal task name | +| `type` | `str` | Filter by task type | diff --git a/docs/resources/trash.md b/docs/resources/trash.md index aca0f89..07d082f 100644 --- a/docs/resources/trash.md +++ b/docs/resources/trash.md @@ -23,6 +23,16 @@ trashed = await paperless.trash.as_list() print(f"{len(trashed)} document(s) in trash") ``` +## Filter + +```python +# Trashed documents matching a title substring +async for doc in paperless.trash.filter(title__icontains="invoice"): + print(doc.id, doc.title) +``` + +`filter()` accepts the same parameters as `paperless.documents.filter()`. See [Documents](documents.md#filter) for available parameters. + ## Restore Restore one or more documents back to the document archive: diff --git a/docs/resources/workflows.md b/docs/resources/workflows.md index 448eda8..eede126 100644 --- a/docs/resources/workflows.md +++ b/docs/resources/workflows.md @@ -15,37 +15,69 @@ Workflows automate document processing in Paperless-ngx. Each workflow has one o | `triggers` | Trigger ids / objects | | `actions` | Action ids / objects | -### `WorkflowTrigger` *(key fields)* - -| Field | Description | -| -------------------------- | ---------------------------- | -| `id` | Primary key | -| `type` | Trigger type | -| `sources` | Consumption sources | -| `filter_filename` | Filename pattern filter | -| `filter_path` | Path pattern filter | -| `filter_mailrule` | Mail rule id filter | -| `filter_has_tags` | Required tags | -| `filter_has_correspondent` | Required correspondent | -| `filter_has_document_type` | Required document type | -| `schedule_offset_days` | Scheduled trigger day offset | -| `schedule_is_recurring` | Recurring schedule flag | - -### `WorkflowAction` *(key fields)* - -| Field | Description | -| ---------------------- | --------------------------- | -| `id` | Primary key | -| `type` | Action type | -| `assign_title` | Title template to assign | -| `assign_tags` | Tags to assign | -| `assign_correspondent` | Correspondent id to assign | -| `assign_document_type` | Document type id to assign | -| `assign_storage_path` | Storage path id to assign | -| `assign_owner` | Owner id to assign | -| `remove_all_tags` | Remove all existing tags | -| `email` | Email notification config | -| `webhook` | Webhook notification config | +### `WorkflowTrigger` + +| Field | Description | +| ---------------------------------- | ----------------------------------------------- | +| `id` | Primary key | +| `type` | Trigger type | +| `sources` | Consumption sources | +| `filter_filename` | Filename pattern filter | +| `filter_path` | Path pattern filter | +| `filter_mailrule` | Mail rule id filter | +| `filter_has_tags` | Required tags (document must have all of these) | +| `filter_has_all_tags` | Required tags (all must match) | +| `filter_has_not_tags` | Excluded tags (none may match) | +| `filter_custom_field_query` | Custom field query expression | +| `filter_has_correspondent` | Required correspondent id | +| `filter_has_not_correspondents` | Excluded correspondent ids | +| `filter_has_document_type` | Required document type id | +| `filter_has_not_document_types` | Excluded document type ids | +| `filter_has_storage_path` | Required storage path id | +| `filter_has_not_storage_paths` | Excluded storage path ids | +| `schedule_offset_days` | Scheduled trigger day offset | +| `schedule_is_recurring` | Whether the schedule repeats | +| `schedule_recurring_interval_days` | Interval in days for recurring schedules | +| `schedule_date_field` | Date field used for schedule evaluation | +| `schedule_date_custom_field` | Custom field id used as the schedule date | + +### `WorkflowAction` + +| Field | Description | +| ----------------------------- | ------------------------------------------ | +| `id` | Primary key | +| `type` | Action type | +| `assign_title` | Title template to assign | +| `assign_tags` | Tags to assign | +| `assign_correspondent` | Correspondent id to assign | +| `assign_document_type` | Document type id to assign | +| `assign_storage_path` | Storage path id to assign | +| `assign_owner` | Owner user id to assign | +| `assign_view_users` | User ids to grant view permission | +| `assign_view_groups` | Group ids to grant view permission | +| `assign_change_users` | User ids to grant change permission | +| `assign_change_groups` | Group ids to grant change permission | +| `assign_custom_fields` | Custom field ids to assign | +| `assign_custom_fields_values` | Values for the assigned custom fields | +| `remove_all_tags` | Remove all existing tags | +| `remove_tags` | Tag ids to remove | +| `remove_all_correspondents` | Remove all existing correspondents | +| `remove_correspondents` | Correspondent ids to remove | +| `remove_all_document_types` | Remove all existing document types | +| `remove_document_types` | Document type ids to remove | +| `remove_all_storage_paths` | Remove all existing storage paths | +| `remove_storage_paths` | Storage path ids to remove | +| `remove_all_custom_fields` | Remove all existing custom fields | +| `remove_custom_fields` | Custom field ids to remove | +| `remove_all_owners` | Remove all existing owners | +| `remove_owners` | Owner ids to remove | +| `remove_all_permissions` | Remove all existing permissions | +| `remove_view_users` | User ids to revoke view permission from | +| `remove_view_groups` | Group ids to revoke view permission from | +| `remove_change_users` | User ids to revoke change permission from | +| `remove_change_groups` | Group ids to revoke change permission from | +| `email` | Email notification config | +| `webhook` | Webhook notification config | ## Workflows diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 0000000..0dbc817 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,8 @@ +/* Nav separators: visual border-top on selected top-level items */ +.md-nav--primary > .md-nav__list > .md-nav__item:nth-child(2), +.md-nav--primary > .md-nav__list > .md-nav__item:nth-child(6), +.md-nav--primary > .md-nav__list > .md-nav__item:nth-child(7) { + border-top: 1px solid var(--md-default-fg-color--lighter); + margin-top: 0.6em; + padding-top: 0.6em; +} diff --git a/zensical.toml b/zensical.toml index d453df1..b9940b0 100644 --- a/zensical.toml +++ b/zensical.toml @@ -14,6 +14,8 @@ repo_name = "tb1337/paperless-api" edit_uri = "blob/main/docs/" +extra_css = ["stylesheets/extra.css"] + nav = [ "index.md", { "Getting started" = "quickstart.md" }, @@ -49,6 +51,7 @@ nav = [ { "Workflows" = "resources/workflows.md" }, ]}, { "Reference" = "exceptions.md" }, + { "Migrating v5 → v6" = "migrating-v5-to-v6.md" }, ] # ---------------------------------------------------------------------------- @@ -128,6 +131,9 @@ custom_fences = [ { name = "mermaid", class = "mermaid", format = "pymdownx.superfences.fence_code_format" } ] +[project.markdown_extensions.pymdownx.tabbed] +alternate_style = true + [project.markdown_extensions.pymdownx.emoji] emoji_index = "zensical.extensions.emoji.twemoji" emoji_generator = "zensical.extensions.emoji.to_svg"