easypaperless 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- easypaperless-0.1.0/.env.EXAMPLE +5 -0
- easypaperless-0.1.0/.github/workflows/publish.yml +31 -0
- easypaperless-0.1.0/.github/workflows/publish_testpypi.yml +25 -0
- easypaperless-0.1.0/.gitignore +42 -0
- easypaperless-0.1.0/CHANGELOG.md +6 -0
- easypaperless-0.1.0/CLAUDE.md +121 -0
- easypaperless-0.1.0/LICENSE +21 -0
- easypaperless-0.1.0/PKG-INFO +212 -0
- easypaperless-0.1.0/README.md +178 -0
- easypaperless-0.1.0/docs/PRD.md +46 -0
- easypaperless-0.1.0/docs/api-conventions.md +79 -0
- easypaperless-0.1.0/docs/easypaperless.html +8953 -0
- easypaperless-0.1.0/docs/index.html +7 -0
- easypaperless-0.1.0/docs/search.js +46 -0
- easypaperless-0.1.0/features/INDEX.md +23 -0
- easypaperless-0.1.0/features/PROJ-1-http-client-core.md +149 -0
- easypaperless-0.1.0/features/PROJ-10-tags-crud.md +157 -0
- easypaperless-0.1.0/features/PROJ-11-correspondents-crud.md +128 -0
- easypaperless-0.1.0/features/PROJ-12-document-types-crud.md +149 -0
- easypaperless-0.1.0/features/PROJ-13-custom-fields-crud.md +150 -0
- easypaperless-0.1.0/features/PROJ-14-storage-paths-crud.md +134 -0
- easypaperless-0.1.0/features/PROJ-15-non-document-bulk-operations.md +120 -0
- easypaperless-0.1.0/features/PROJ-16-sync-client.md +100 -0
- easypaperless-0.1.0/features/PROJ-17-document-notes.md +123 -0
- easypaperless-0.1.0/features/PROJ-2-name-to-id-resolver.md +153 -0
- easypaperless-0.1.0/features/PROJ-3-list-documents.md +305 -0
- easypaperless-0.1.0/features/PROJ-4-get-document.md +111 -0
- easypaperless-0.1.0/features/PROJ-5-update-document.md +114 -0
- easypaperless-0.1.0/features/PROJ-6-delete-document.md +82 -0
- easypaperless-0.1.0/features/PROJ-7-download-document.md +95 -0
- easypaperless-0.1.0/features/PROJ-8-upload-document.md +127 -0
- easypaperless-0.1.0/features/PROJ-9-document-bulk-operations.md +147 -0
- easypaperless-0.1.0/pyproject.toml +67 -0
- easypaperless-0.1.0/scripts/cli.py +1732 -0
- easypaperless-0.1.0/src/easypaperless/__init__.py +72 -0
- easypaperless-0.1.0/src/easypaperless/_internal/__init__.py +0 -0
- easypaperless-0.1.0/src/easypaperless/_internal/http.py +205 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/__init__.py +25 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/correspondents.py +196 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/custom_fields.py +179 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/document_bulk.py +175 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/document_types.py +198 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/documents.py +516 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/non_document_bulk.py +176 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/notes.py +76 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/storage_paths.py +210 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/tags.py +214 -0
- easypaperless-0.1.0/src/easypaperless/_internal/mixins/upload.py +149 -0
- easypaperless-0.1.0/src/easypaperless/_internal/resolvers.py +63 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/__init__.py +25 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/correspondents.py +85 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/custom_fields.py +70 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/document_bulk.py +94 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/document_types.py +85 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/documents.py +154 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/non_document_bulk.py +103 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/notes.py +29 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/storage_paths.py +89 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/tags.py +97 -0
- easypaperless-0.1.0/src/easypaperless/_internal/sync_mixins/upload.py +50 -0
- easypaperless-0.1.0/src/easypaperless/client.py +234 -0
- easypaperless-0.1.0/src/easypaperless/exceptions.py +63 -0
- easypaperless-0.1.0/src/easypaperless/models/__init__.py +39 -0
- easypaperless-0.1.0/src/easypaperless/models/_base.py +17 -0
- easypaperless-0.1.0/src/easypaperless/models/correspondents.py +34 -0
- easypaperless-0.1.0/src/easypaperless/models/custom_fields.py +44 -0
- easypaperless-0.1.0/src/easypaperless/models/document_types.py +29 -0
- easypaperless-0.1.0/src/easypaperless/models/documents.py +217 -0
- easypaperless-0.1.0/src/easypaperless/models/permissions.py +13 -0
- easypaperless-0.1.0/src/easypaperless/models/storage_paths.py +32 -0
- easypaperless-0.1.0/src/easypaperless/models/tags.py +39 -0
- easypaperless-0.1.0/src/easypaperless/sync.py +187 -0
- easypaperless-0.1.0/tests/__init__.py +0 -0
- easypaperless-0.1.0/tests/conftest.py +27 -0
- easypaperless-0.1.0/tests/integration/__init__.py +0 -0
- easypaperless-0.1.0/tests/integration/conftest.py +43 -0
- easypaperless-0.1.0/tests/integration/test_documents.py +482 -0
- easypaperless-0.1.0/tests/integration/test_notes.py +29 -0
- easypaperless-0.1.0/tests/integration/test_resources.py +87 -0
- easypaperless-0.1.0/tests/integration/test_upload.py +57 -0
- easypaperless-0.1.0/tests/test_client_bulk.py +304 -0
- easypaperless-0.1.0/tests/test_client_documents.py +1175 -0
- easypaperless-0.1.0/tests/test_client_notes.py +107 -0
- easypaperless-0.1.0/tests/test_client_tags.py +1211 -0
- easypaperless-0.1.0/tests/test_client_upload.py +311 -0
- easypaperless-0.1.0/tests/test_exceptions.py +50 -0
- easypaperless-0.1.0/tests/test_http.py +481 -0
- easypaperless-0.1.0/tests/test_models.py +129 -0
- easypaperless-0.1.0/tests/test_resolvers.py +98 -0
- easypaperless-0.1.0/tests/test_sync.py +666 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
name: Build and publish Python package
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout repository
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.11"
|
|
21
|
+
|
|
22
|
+
- name: Install build backend
|
|
23
|
+
run: pip install build
|
|
24
|
+
|
|
25
|
+
- name: Build package
|
|
26
|
+
run: python -m build
|
|
27
|
+
|
|
28
|
+
- name: Publish to PyPI
|
|
29
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
30
|
+
with:
|
|
31
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Publish to TestPyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*-rc*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.11"
|
|
18
|
+
|
|
19
|
+
- run: pip install build
|
|
20
|
+
- run: python -m build
|
|
21
|
+
|
|
22
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
23
|
+
with:
|
|
24
|
+
repository-url: https://test.pypi.org/legacy/
|
|
25
|
+
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Virtual environment
|
|
2
|
+
venv/
|
|
3
|
+
.venv/
|
|
4
|
+
|
|
5
|
+
# Environment variables
|
|
6
|
+
.env
|
|
7
|
+
.env.*
|
|
8
|
+
!.env.EXAMPLE
|
|
9
|
+
|
|
10
|
+
# Python
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*.pyo
|
|
14
|
+
*.pyd
|
|
15
|
+
*.egg-info/
|
|
16
|
+
dist/
|
|
17
|
+
build/
|
|
18
|
+
*.egg
|
|
19
|
+
|
|
20
|
+
# Test & coverage
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.coverage
|
|
23
|
+
htmlcov/
|
|
24
|
+
|
|
25
|
+
# IDE
|
|
26
|
+
.vscode/
|
|
27
|
+
.idea/
|
|
28
|
+
*.iml
|
|
29
|
+
|
|
30
|
+
# Claude Code
|
|
31
|
+
.claude/
|
|
32
|
+
|
|
33
|
+
# OS
|
|
34
|
+
.DS_Store
|
|
35
|
+
Thumbs.db
|
|
36
|
+
|
|
37
|
+
# Deleted/archived code
|
|
38
|
+
_deleted/
|
|
39
|
+
|
|
40
|
+
# Local SQLite store
|
|
41
|
+
*.db
|
|
42
|
+
*.sqlite
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Paperless API Wrapper Project
|
|
2
|
+
> A Python project template with an AI-powered development workflow using specialized skills for Requirements, Architecture, API Design, Development, QA, and Deployment.
|
|
3
|
+
|
|
4
|
+
## Tech Stack
|
|
5
|
+
- **Language:** Python 3.11+
|
|
6
|
+
- **HTTP client:** `httpx` (async-first)
|
|
7
|
+
- **Data models:** Pydantic v2
|
|
8
|
+
- **Build:** Hatch + hatchling
|
|
9
|
+
- **Linting/formatting:** Ruff
|
|
10
|
+
- **Type checking:** Mypy (strict)
|
|
11
|
+
- **Testing:** pytest + pytest-asyncio + respx
|
|
12
|
+
|
|
13
|
+
## Project Structure
|
|
14
|
+
```
|
|
15
|
+
src/
|
|
16
|
+
└── easypaperless/
|
|
17
|
+
├── __init__.py # public API: PaperlessClient, SyncPaperlessClient
|
|
18
|
+
├── client.py # async PaperlessClient (composes from async mixins)
|
|
19
|
+
├── sync.py # SyncPaperlessClient (composes from sync mixins)
|
|
20
|
+
├── exceptions.py # custom exception hierarchy
|
|
21
|
+
├── models/
|
|
22
|
+
│ ├── __init__.py
|
|
23
|
+
│ ├── documents.py
|
|
24
|
+
│ ├── tags.py
|
|
25
|
+
│ ├── correspondents.py
|
|
26
|
+
│ ├── document_types.py
|
|
27
|
+
│ ├── storage_paths.py
|
|
28
|
+
│ ├── custom_fields.py
|
|
29
|
+
│ └── permissions.py
|
|
30
|
+
└── _internal/
|
|
31
|
+
├── http.py # httpx session, auth, request helpers
|
|
32
|
+
├── resolvers.py # name-to-ID resolution and caching
|
|
33
|
+
├── mixins/ # one async mixin per resource group
|
|
34
|
+
│ ├── __init__.py
|
|
35
|
+
│ ├── documents.py
|
|
36
|
+
│ ├── notes.py
|
|
37
|
+
│ ├── upload.py
|
|
38
|
+
│ ├── document_bulk.py
|
|
39
|
+
│ ├── non_document_bulk.py
|
|
40
|
+
│ ├── tags.py
|
|
41
|
+
│ ├── correspondents.py
|
|
42
|
+
│ ├── document_types.py
|
|
43
|
+
│ ├── storage_paths.py
|
|
44
|
+
│ └── custom_fields.py
|
|
45
|
+
└── sync_mixins/ # one sync mixin per resource group (mirrors mixins/)
|
|
46
|
+
├── __init__.py
|
|
47
|
+
├── documents.py
|
|
48
|
+
├── notes.py
|
|
49
|
+
├── upload.py
|
|
50
|
+
├── document_bulk.py
|
|
51
|
+
├── non_document_bulk.py
|
|
52
|
+
├── tags.py
|
|
53
|
+
├── correspondents.py
|
|
54
|
+
├── document_types.py
|
|
55
|
+
├── storage_paths.py
|
|
56
|
+
└── custom_fields.py
|
|
57
|
+
tests/ # mirrors src/easypaperless/ structure
|
|
58
|
+
features/ # one spec file per feature (PROJ-X-name.md)
|
|
59
|
+
docs/
|
|
60
|
+
└── PRD.md
|
|
61
|
+
pyproject.toml
|
|
62
|
+
LICENSE
|
|
63
|
+
README.md
|
|
64
|
+
CHANGELOG.md
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Development Workflow
|
|
68
|
+
1. `/requirements` - Create feature spec from idea
|
|
69
|
+
2. `/architecture` - Design structure, interfaces, tech decisions (no code)
|
|
70
|
+
3. `/dev` - general coder. Implement features.
|
|
71
|
+
4. `/qa` - Tests, type-check, lint, edge cases, security audit
|
|
72
|
+
5. `/deploy` - Package, publish (PyPI / internal), release checklist
|
|
73
|
+
|
|
74
|
+
## Key Conventions
|
|
75
|
+
- **Feature IDs:** PROJ-1, PROJ-2, etc. (sequential)
|
|
76
|
+
- **Commits:** `feat(PROJ-X): description`, `fix(PROJ-X): description`
|
|
77
|
+
- **Single Responsibility:** One feature per spec file; one resource group per mixin file
|
|
78
|
+
- **No private leakage:** `__init__.py` defines exactly what is public
|
|
79
|
+
- **Human-in-the-loop:** All workflows have user approval checkpoints
|
|
80
|
+
- **naming conventions:** @docs/api-conventions.md
|
|
81
|
+
- **File size:** Keep files small and focused. Split any file that grows beyond ~200 lines into logical sub-modules (e.g. per-resource mixins). Prefer many small files over one large file.
|
|
82
|
+
|
|
83
|
+
## Build & Test Commands
|
|
84
|
+
```bash
|
|
85
|
+
# Activate venv (Windows)
|
|
86
|
+
source venv/Scripts/activate
|
|
87
|
+
|
|
88
|
+
# Install with dev dependencies
|
|
89
|
+
pip install -e ".[dev]"
|
|
90
|
+
|
|
91
|
+
# Run tests
|
|
92
|
+
pytest tests/
|
|
93
|
+
|
|
94
|
+
# Run tests with coverage
|
|
95
|
+
pytest tests/ --cov=easypaperless
|
|
96
|
+
|
|
97
|
+
# Lint + format check
|
|
98
|
+
ruff check .
|
|
99
|
+
ruff format --check .
|
|
100
|
+
|
|
101
|
+
# Type check
|
|
102
|
+
mypy src/easypaperless/
|
|
103
|
+
|
|
104
|
+
# Bump version (patch / minor / major)
|
|
105
|
+
hatch version patch
|
|
106
|
+
|
|
107
|
+
# Build package
|
|
108
|
+
hatch build
|
|
109
|
+
|
|
110
|
+
# Publish to PyPI
|
|
111
|
+
hatch publish
|
|
112
|
+
|
|
113
|
+
# Run integration tests (requires live paperless instance + tests/.env)
|
|
114
|
+
pytest tests/integration/ -m integration -v
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Product Context
|
|
118
|
+
@docs/PRD.md
|
|
119
|
+
|
|
120
|
+
## Feature Overview
|
|
121
|
+
@features/INDEX.md
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Moritz Grenke
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: easypaperless
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python API wrapper for paperless-ngx
|
|
5
|
+
Project-URL: Homepage, https://github.com/TastyMojito/easypaperless
|
|
6
|
+
Project-URL: Repository, https://github.com/TastyMojito/easypaperless
|
|
7
|
+
Project-URL: Issues, https://github.com/TastyMojito/easypaperless/issues
|
|
8
|
+
Author-email: Moritz Grenke <info@360mix.de>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: api,document management,paperless,paperless-ngx,wrapper
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: httpx>=0.27
|
|
20
|
+
Requires-Dist: pydantic>=2.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: python-dotenv>=1.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
29
|
+
Provides-Extra: docs
|
|
30
|
+
Requires-Dist: pdoc>=14; extra == 'docs'
|
|
31
|
+
Provides-Extra: scripts
|
|
32
|
+
Requires-Dist: python-dotenv>=1.0; extra == 'scripts'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# easypaperless
|
|
36
|
+
|
|
37
|
+
[](https://pypi.org/project/easypaperless/)
|
|
38
|
+
[](https://opensource.org/licenses/MIT)
|
|
39
|
+
|
|
40
|
+
**easypaperless** is a high-level easy-to-use Python API wrapper for [Paperless-ngx](https://github.com/paperless-ngx/paperless-ngx).
|
|
41
|
+
|
|
42
|
+
Unlike other wrappers that simply mirror REST endpoints, **easypaperless** is designed for humans. It provides a true abstraction layer, allowing you to interact with your document management system using intuitive Python methods and objects. You don't need to understand the underlying REST API to be productive.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## ✨ Key Features
|
|
47
|
+
|
|
48
|
+
* **True Abstraction:** Focus on your logic, not on HTTP methods or JSON payloads.
|
|
49
|
+
* **Developer Experience (DX):** Full Type Hinting support. Your IDE (VS Code, PyCharm, etc.) will provide perfect autocompletion and documentation as you type.
|
|
50
|
+
* **Extensive Coverage:** Covers all essential workflows from document management to complex bulk operations.
|
|
51
|
+
* **Async-First with Sync Support:** Built on top of httpx, easypaperless is fully asynchronous by default for high-performance applications. But it also offers a synchronous wrapper for a classic blocking workflow.
|
|
52
|
+
* **Built-in Bulk Tools:** Easily manage hundreds of tags, correspondents, or documents with single-method calls.
|
|
53
|
+
* **Intuitive Error Hierarchy:** easypaperless provides descriptive, custom exceptions that tell you exactly what went wrong,
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 📋 Requirements
|
|
58
|
+
|
|
59
|
+
* Python: 3.11 or higher
|
|
60
|
+
* Paperless-ngx: 2.18 or higher (only tested with 2.18 so far)
|
|
61
|
+
|
|
62
|
+
### Core Dependencies:
|
|
63
|
+
|
|
64
|
+
* httpx>=0.27
|
|
65
|
+
* Pydantic>=2.0
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 🚀 Installation
|
|
70
|
+
|
|
71
|
+
Install the package via pip:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
|
|
75
|
+
pip install easypaperless
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 🛠 Quickstart
|
|
82
|
+
|
|
83
|
+
Get up and running in seconds. No API deep-dive required.
|
|
84
|
+
|
|
85
|
+
### async Client
|
|
86
|
+
|
|
87
|
+
``` Python
|
|
88
|
+
|
|
89
|
+
from easypaperless import PaperlessClient
|
|
90
|
+
import asyncio
|
|
91
|
+
|
|
92
|
+
async def main():
|
|
93
|
+
|
|
94
|
+
# create a paperless client
|
|
95
|
+
# we encourage you to use .env files to store your credentials later
|
|
96
|
+
async with PaperlessClient(url="http://localhost:8000", api_key="YOUR_TOKEN") as client:
|
|
97
|
+
# List documents — full-text search across title and OCR content, return the last three added documents
|
|
98
|
+
docs = await client.list_documents(search="test", max_results=3, ordering="added", descending=True)
|
|
99
|
+
for doc in docs:
|
|
100
|
+
print(f"Id: {doc.id} \nTitle: {doc.title} \nadded: {doc.added}\n")
|
|
101
|
+
|
|
102
|
+
# Fetch a single document
|
|
103
|
+
doc = await client.get_document(id=1)
|
|
104
|
+
print(doc.title, doc.created_date)
|
|
105
|
+
|
|
106
|
+
#check if a "API_edited" tag already exists - otherwise create it.
|
|
107
|
+
tags = await client.list_tags(name_exact="API_edited")
|
|
108
|
+
if not tags:
|
|
109
|
+
await client.create_tag(name="API_edited", color = "#40bfb7")
|
|
110
|
+
|
|
111
|
+
# Update metadata — string names are resolved to IDs automatically
|
|
112
|
+
await client.update_document(id=1, tags=["API_edited"])
|
|
113
|
+
|
|
114
|
+
# Upload and wait for processing to complete
|
|
115
|
+
#doc = await client.upload_document("path/scan.pdf", title="your title here", wait=True)
|
|
116
|
+
#print("Processed:", doc.id)
|
|
117
|
+
|
|
118
|
+
asyncio.run(main())
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### sync Client
|
|
123
|
+
|
|
124
|
+
``` Python
|
|
125
|
+
|
|
126
|
+
from easypaperless import SyncPaperlessClient
|
|
127
|
+
|
|
128
|
+
# same example with the sync client:
|
|
129
|
+
# we encourage you to use .env files to store your credentials later
|
|
130
|
+
with SyncPaperlessClient(url="http://localhost:8000", api_key="YOUR_TOKEN") as client:
|
|
131
|
+
# List documents — full-text search across title and OCR content, return the last three added documents
|
|
132
|
+
docs = client.list_documents(search="test", max_results=3, ordering="added", descending=True)
|
|
133
|
+
for doc in docs:
|
|
134
|
+
print(f"Id: {doc.id} \nTitle: {doc.title} \nadded: {doc.added}\n")
|
|
135
|
+
|
|
136
|
+
# Fetch a single document
|
|
137
|
+
doc = client.get_document(id=1)
|
|
138
|
+
print(doc.title, doc.created_date)
|
|
139
|
+
|
|
140
|
+
#check if a "API_edited" tag already exists - otherwise create it.
|
|
141
|
+
tags = client.list_tags(name_exact="API_edited")
|
|
142
|
+
if not tags:
|
|
143
|
+
client.create_tag(name="API_edited", color = "#40bfb7")
|
|
144
|
+
|
|
145
|
+
# Update metadata — string names are resolved to IDs automatically
|
|
146
|
+
client.update_document(id=1, tags=["API_edited"])
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 📖 Functional Overview
|
|
151
|
+
|
|
152
|
+
### Client
|
|
153
|
+
|
|
154
|
+
* **PaperlessClient / SyncPaperlessClient** Allows token based authentication. Allows to increase timeout period for slow instances.
|
|
155
|
+
|
|
156
|
+
### Documents
|
|
157
|
+
|
|
158
|
+
* **list_documents()** This is the workhorse of the wrapper and allows you to search for documents in your paperless-ngx instance and filter them by several criteria.
|
|
159
|
+
* **get_document()** Fetch a single document's data and optionally include its extended file metadata.
|
|
160
|
+
* **get_document_metadata()** Retrieve file-level technical metadata (checksums, sizes, MIME types) for a specific document.
|
|
161
|
+
* **update_document()** Partially update document fields like title, tags, or dates using PATCH semantics.
|
|
162
|
+
* **delete_document()** Permanently remove a document from your Paperless-ngx instance.
|
|
163
|
+
* **download_document()** Download the binary content of a document, either the archived PDF or the original file.
|
|
164
|
+
* **upload_document()** Upload a new file to Paperless-ngx and optionally wait for the processing task to complete.
|
|
165
|
+
|
|
166
|
+
### Document Notes
|
|
167
|
+
|
|
168
|
+
* **get_notes()** Retrieve all text notes attached to a specific document.
|
|
169
|
+
* **create_note()** Add a new text note to an existing document.
|
|
170
|
+
* **delete_note()** Remove a specific note from a document.
|
|
171
|
+
|
|
172
|
+
### Non-Document Entities
|
|
173
|
+
|
|
174
|
+
* **list_tags() / get_tag() / create_tag() / update_tag()** Manage document tags, supporting colors and matching algorithms.
|
|
175
|
+
* **list_correspondents() / get_correspondent() / create_correspondent()** Manage document authors, senders, or recipients.
|
|
176
|
+
* **list_document_types() / get_document_type() / create_document_type()** Manage categories like "Invoice," "Letter," or "Contract."
|
|
177
|
+
* **list_storage_paths() / get_storage_path() / create_storage_path()** Manage the physical directory structure where files are stored.
|
|
178
|
+
* **list_custom_fields() / get_custom_field() / create_custom_field()** Manage user-defined metadata fields for advanced document tracking.
|
|
179
|
+
|
|
180
|
+
### Bulk Operations
|
|
181
|
+
|
|
182
|
+
* **bulk_edit()** A low-level method to execute arbitrary batch operations on a list of document IDs. It is recommended to use the high level methods below.
|
|
183
|
+
* **bulk_add_tag()** Add a single tag to a collection of documents in one request.
|
|
184
|
+
* **bulk_remove_tag()** Strip a specific tag from multiple documents simultaneously.
|
|
185
|
+
* **bulk_modify_tags()** Atomically add and remove multiple tags across a set of documents.
|
|
186
|
+
* **bulk_delete()** Permanently delete a list of documents in a single batch.
|
|
187
|
+
* **bulk_set_correspondent()** Assign or clear the correspondent for multiple documents at once.
|
|
188
|
+
* **bulk_set_document_type()** Change the document type for a group of documents in one go.
|
|
189
|
+
* **bulk_set_storage_path()** Update the storage path for multiple documents simultaneously.
|
|
190
|
+
* **bulk_modify_custom_fields()** Batch update or remove custom field values across multiple documents.
|
|
191
|
+
* **bulk_set_permissions()** Manage ownership and access permissions for a list of documents.
|
|
192
|
+
|
|
193
|
+
### Bulk Operations (Non-Documents)
|
|
194
|
+
|
|
195
|
+
* **bulk_edit_objects()** A low level method to execute batch operations on system objects like tags or correspondents. It is recommended to use the high level methods below.
|
|
196
|
+
* **bulk_delete_tags() / bulk_delete_correspondents() / bulk_delete_document_types() / bulk_delete_storage_paths()** Batch delete various entity types to clean up your metadata.
|
|
197
|
+
* **bulk_set_permissions_tags() / _correspondents() / _document_types() / _storage_paths()** Batch update access control and ownership for specific metadata entities.
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
## 📚 Documentation
|
|
201
|
+
|
|
202
|
+
See the 👉 [Full API Reference (pdoc)](https://tastymojito.github.io/easypaperless/)
|
|
203
|
+
|
|
204
|
+
## 🤝 Contributing
|
|
205
|
+
|
|
206
|
+
Contributions are welcome! If you find a bug or have a feature request, please open an issue or submit a pull request.
|
|
207
|
+
|
|
208
|
+
## 📄 License
|
|
209
|
+
|
|
210
|
+
This project is licensed under the MIT License.
|
|
211
|
+
|
|
212
|
+
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# easypaperless
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/easypaperless/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**easypaperless** is a high-level easy-to-use Python API wrapper for [Paperless-ngx](https://github.com/paperless-ngx/paperless-ngx).
|
|
7
|
+
|
|
8
|
+
Unlike other wrappers that simply mirror REST endpoints, **easypaperless** is designed for humans. It provides a true abstraction layer, allowing you to interact with your document management system using intuitive Python methods and objects. You don't need to understand the underlying REST API to be productive.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ✨ Key Features
|
|
13
|
+
|
|
14
|
+
* **True Abstraction:** Focus on your logic, not on HTTP methods or JSON payloads.
|
|
15
|
+
* **Developer Experience (DX):** Full Type Hinting support. Your IDE (VS Code, PyCharm, etc.) will provide perfect autocompletion and documentation as you type.
|
|
16
|
+
* **Extensive Coverage:** Covers all essential workflows from document management to complex bulk operations.
|
|
17
|
+
* **Async-First with Sync Support:** Built on top of httpx, easypaperless is fully asynchronous by default for high-performance applications. But it also offers a synchronous wrapper for a classic blocking workflow.
|
|
18
|
+
* **Built-in Bulk Tools:** Easily manage hundreds of tags, correspondents, or documents with single-method calls.
|
|
19
|
+
* **Intuitive Error Hierarchy:** easypaperless provides descriptive, custom exceptions that tell you exactly what went wrong,
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 📋 Requirements
|
|
24
|
+
|
|
25
|
+
* Python: 3.11 or higher
|
|
26
|
+
* Paperless-ngx: 2.18 or higher (only tested with 2.18 so far)
|
|
27
|
+
|
|
28
|
+
### Core Dependencies:
|
|
29
|
+
|
|
30
|
+
* httpx>=0.27
|
|
31
|
+
* Pydantic>=2.0
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 🚀 Installation
|
|
36
|
+
|
|
37
|
+
Install the package via pip:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
|
|
41
|
+
pip install easypaperless
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🛠 Quickstart
|
|
48
|
+
|
|
49
|
+
Get up and running in seconds. No API deep-dive required.
|
|
50
|
+
|
|
51
|
+
### async Client
|
|
52
|
+
|
|
53
|
+
``` Python
|
|
54
|
+
|
|
55
|
+
from easypaperless import PaperlessClient
|
|
56
|
+
import asyncio
|
|
57
|
+
|
|
58
|
+
async def main():
|
|
59
|
+
|
|
60
|
+
# create a paperless client
|
|
61
|
+
# we encourage you to use .env files to store your credentials later
|
|
62
|
+
async with PaperlessClient(url="http://localhost:8000", api_key="YOUR_TOKEN") as client:
|
|
63
|
+
# List documents — full-text search across title and OCR content, return the last three added documents
|
|
64
|
+
docs = await client.list_documents(search="test", max_results=3, ordering="added", descending=True)
|
|
65
|
+
for doc in docs:
|
|
66
|
+
print(f"Id: {doc.id} \nTitle: {doc.title} \nadded: {doc.added}\n")
|
|
67
|
+
|
|
68
|
+
# Fetch a single document
|
|
69
|
+
doc = await client.get_document(id=1)
|
|
70
|
+
print(doc.title, doc.created_date)
|
|
71
|
+
|
|
72
|
+
#check if a "API_edited" tag already exists - otherwise create it.
|
|
73
|
+
tags = await client.list_tags(name_exact="API_edited")
|
|
74
|
+
if not tags:
|
|
75
|
+
await client.create_tag(name="API_edited", color = "#40bfb7")
|
|
76
|
+
|
|
77
|
+
# Update metadata — string names are resolved to IDs automatically
|
|
78
|
+
await client.update_document(id=1, tags=["API_edited"])
|
|
79
|
+
|
|
80
|
+
# Upload and wait for processing to complete
|
|
81
|
+
#doc = await client.upload_document("path/scan.pdf", title="your title here", wait=True)
|
|
82
|
+
#print("Processed:", doc.id)
|
|
83
|
+
|
|
84
|
+
asyncio.run(main())
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### sync Client
|
|
89
|
+
|
|
90
|
+
``` Python
|
|
91
|
+
|
|
92
|
+
from easypaperless import SyncPaperlessClient
|
|
93
|
+
|
|
94
|
+
# same example with the sync client:
|
|
95
|
+
# we encourage you to use .env files to store your credentials later
|
|
96
|
+
with SyncPaperlessClient(url="http://localhost:8000", api_key="YOUR_TOKEN") as client:
|
|
97
|
+
# List documents — full-text search across title and OCR content, return the last three added documents
|
|
98
|
+
docs = client.list_documents(search="test", max_results=3, ordering="added", descending=True)
|
|
99
|
+
for doc in docs:
|
|
100
|
+
print(f"Id: {doc.id} \nTitle: {doc.title} \nadded: {doc.added}\n")
|
|
101
|
+
|
|
102
|
+
# Fetch a single document
|
|
103
|
+
doc = client.get_document(id=1)
|
|
104
|
+
print(doc.title, doc.created_date)
|
|
105
|
+
|
|
106
|
+
#check if a "API_edited" tag already exists - otherwise create it.
|
|
107
|
+
tags = client.list_tags(name_exact="API_edited")
|
|
108
|
+
if not tags:
|
|
109
|
+
client.create_tag(name="API_edited", color = "#40bfb7")
|
|
110
|
+
|
|
111
|
+
# Update metadata — string names are resolved to IDs automatically
|
|
112
|
+
client.update_document(id=1, tags=["API_edited"])
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 📖 Functional Overview
|
|
117
|
+
|
|
118
|
+
### Client
|
|
119
|
+
|
|
120
|
+
* **PaperlessClient / SyncPaperlessClient** Allows token based authentication. Allows to increase timeout period for slow instances.
|
|
121
|
+
|
|
122
|
+
### Documents
|
|
123
|
+
|
|
124
|
+
* **list_documents()** This is the workhorse of the wrapper and allows you to search for documents in your paperless-ngx instance and filter them by several criteria.
|
|
125
|
+
* **get_document()** Fetch a single document's data and optionally include its extended file metadata.
|
|
126
|
+
* **get_document_metadata()** Retrieve file-level technical metadata (checksums, sizes, MIME types) for a specific document.
|
|
127
|
+
* **update_document()** Partially update document fields like title, tags, or dates using PATCH semantics.
|
|
128
|
+
* **delete_document()** Permanently remove a document from your Paperless-ngx instance.
|
|
129
|
+
* **download_document()** Download the binary content of a document, either the archived PDF or the original file.
|
|
130
|
+
* **upload_document()** Upload a new file to Paperless-ngx and optionally wait for the processing task to complete.
|
|
131
|
+
|
|
132
|
+
### Document Notes
|
|
133
|
+
|
|
134
|
+
* **get_notes()** Retrieve all text notes attached to a specific document.
|
|
135
|
+
* **create_note()** Add a new text note to an existing document.
|
|
136
|
+
* **delete_note()** Remove a specific note from a document.
|
|
137
|
+
|
|
138
|
+
### Non-Document Entities
|
|
139
|
+
|
|
140
|
+
* **list_tags() / get_tag() / create_tag() / update_tag()** Manage document tags, supporting colors and matching algorithms.
|
|
141
|
+
* **list_correspondents() / get_correspondent() / create_correspondent()** Manage document authors, senders, or recipients.
|
|
142
|
+
* **list_document_types() / get_document_type() / create_document_type()** Manage categories like "Invoice," "Letter," or "Contract."
|
|
143
|
+
* **list_storage_paths() / get_storage_path() / create_storage_path()** Manage the physical directory structure where files are stored.
|
|
144
|
+
* **list_custom_fields() / get_custom_field() / create_custom_field()** Manage user-defined metadata fields for advanced document tracking.
|
|
145
|
+
|
|
146
|
+
### Bulk Operations
|
|
147
|
+
|
|
148
|
+
* **bulk_edit()** A low-level method to execute arbitrary batch operations on a list of document IDs. It is recommended to use the high level methods below.
|
|
149
|
+
* **bulk_add_tag()** Add a single tag to a collection of documents in one request.
|
|
150
|
+
* **bulk_remove_tag()** Strip a specific tag from multiple documents simultaneously.
|
|
151
|
+
* **bulk_modify_tags()** Atomically add and remove multiple tags across a set of documents.
|
|
152
|
+
* **bulk_delete()** Permanently delete a list of documents in a single batch.
|
|
153
|
+
* **bulk_set_correspondent()** Assign or clear the correspondent for multiple documents at once.
|
|
154
|
+
* **bulk_set_document_type()** Change the document type for a group of documents in one go.
|
|
155
|
+
* **bulk_set_storage_path()** Update the storage path for multiple documents simultaneously.
|
|
156
|
+
* **bulk_modify_custom_fields()** Batch update or remove custom field values across multiple documents.
|
|
157
|
+
* **bulk_set_permissions()** Manage ownership and access permissions for a list of documents.
|
|
158
|
+
|
|
159
|
+
### Bulk Operations (Non-Documents)
|
|
160
|
+
|
|
161
|
+
* **bulk_edit_objects()** A low level method to execute batch operations on system objects like tags or correspondents. It is recommended to use the high level methods below.
|
|
162
|
+
* **bulk_delete_tags() / bulk_delete_correspondents() / bulk_delete_document_types() / bulk_delete_storage_paths()** Batch delete various entity types to clean up your metadata.
|
|
163
|
+
* **bulk_set_permissions_tags() / _correspondents() / _document_types() / _storage_paths()** Batch update access control and ownership for specific metadata entities.
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
## 📚 Documentation
|
|
167
|
+
|
|
168
|
+
See the 👉 [Full API Reference (pdoc)](https://tastymojito.github.io/easypaperless/)
|
|
169
|
+
|
|
170
|
+
## 🤝 Contributing
|
|
171
|
+
|
|
172
|
+
Contributions are welcome! If you find a bug or have a feature request, please open an issue or submit a pull request.
|
|
173
|
+
|
|
174
|
+
## 📄 License
|
|
175
|
+
|
|
176
|
+
This project is licensed under the MIT License.
|
|
177
|
+
|
|
178
|
+
|