pax_global_header00006660000000000000000000000064150530741710014515gustar00rootroot0000000000000052 comment=3e425469e0989aea88ec0d824988b51e9dbc55ce paperless-api-5.1.0/000077500000000000000000000000001505307417100142655ustar00rootroot00000000000000paperless-api-5.1.0/.devcontainer/000077500000000000000000000000001505307417100170245ustar00rootroot00000000000000paperless-api-5.1.0/.devcontainer/devcontainer.json000066400000000000000000000044641505307417100224100ustar00rootroot00000000000000{ "name": "pypaperless Developer", "context": "..", "dockerFile": "../Dockerfile.dev", "postCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder} && script/setup", "postStartCommand": "script/bootstrap", "containerEnv": { "DEVCONTAINER": "true" }, "features": {}, "runArgs": [ "-e", "GIT_EDITOR=code --wait", "--security-opt", "label=disable" ], "customizations": { "vscode": { "extensions": [ "charliermarsh.ruff", "ms-python.pylint", "ms-python.vscode-pylance", "redhat.vscode-yaml", "esbenp.prettier-vscode", "visualstudioexptteam.vscodeintellicode", "GitHub.vscode-pull-request-github", "ryanluker.vscode-coverage-gutters", "yzhang.markdown-all-in-one" ], "settings": { "coverage-gutters.customizable.context-menu": true, "coverage-gutters.customizable.status-bar-toggler-watchCoverageAndVisibleEditors-enabled": true, "coverage-gutters.showGutterCoverage": true, "coverage-gutters.showLineCoverage": true, "coverage-gutters.xmlname": "coverage.xml", "editor.formatOnPaste": false, "editor.formatOnSave": true, "editor.formatOnType": true, "files.trimTrailingWhitespace": true, "markdown.extension.toc.levels": "2..6", "markdown.extension.toc.omittedFromToc": { "docs/1_basic_usage.md": ["## Documentation"], "docs/2_documents.md": ["## Documentation"], "docs/3_custom_fields.md": ["## Documentation"], "docs/4_permissions.md": ["## Documentation"] }, "python.defaultInterpreterPath": "/home/vscode/.local/dev-venv/bin/python", "python.pythonPath": "/home/vscode/.local/dev-venv/bin/python", "python.terminal.activateEnvInCurrentTerminal": true, "python.testing.pytestArgs": [ "--no-cov", "--cov-report=term", "--cov-report=xml" ], "pylint.importStrategy": "fromEnvironment", "[python]": { "editor.defaultFormatter": "charliermarsh.ruff" }, "terminal.integrated.profiles.linux": { "zsh": { "path": "/usr/bin/zsh" } }, "terminal.integrated.defaultProfile.linux": "zsh" } } } } paperless-api-5.1.0/.editorconfig000066400000000000000000000002741505307417100167450ustar00rootroot00000000000000root = true [*] charset = utf-8 end_of_line = lf indent_style = space insert_final_newline = true trim_trailing_whitespace = true indent_size = 2 [*.md] trim_trailing_whitespace = false paperless-api-5.1.0/.gitattributes000066400000000000000000000000511505307417100171540ustar00rootroot00000000000000* text eol=lf *.py whitespace=error paperless-api-5.1.0/.github/000077500000000000000000000000001505307417100156255ustar00rootroot00000000000000paperless-api-5.1.0/.github/labels.yml000066400000000000000000000054451505307417100176220ustar00rootroot00000000000000--- - name: "auto" color: bfdadc description: "Marks a PR or issue that has been created automatically." - name: "breaking-change" color: ee0701 description: "A breaking change for existing users." - name: "bugfix" color: ee0701 description: "Inconsistencies or issues which will cause a problem for users or implementers." - name: "documentation" color: 0052cc description: "Solely about the documentation of the project." - name: "enhancement" color: 1d76db description: "Enhancement of the code, not introducing new features." - name: "refactor" color: 1d76db description: "Improvement of existing code, not introducing new features." - name: "performance" color: 1d76db description: "Improving performance, not introducing new features." - name: "new-feature" color: 0e8a16 description: "New features or options." - name: "maintenance" color: 2af79e description: "Generic maintenance tasks." - name: "ci" color: 1d76db description: "Work that improves the continuous integration." - name: "dependencies" color: 1d76db description: "Upgrade or downgrade of project dependencies." - name: "in-progress" color: fbca04 description: "Issue is currently being resolved by a developer." - name: "stale" color: fef2c0 description: "There has not been activity on this issue or PR for quite some time." - name: "no-stale" color: fef2c0 description: "This issue or PR is exempted from the stale bot." - name: "security" color: ee0701 description: "Marks a security issue that needs to be resolved asap." - name: "incomplete" color: fef2c0 description: "Marks a PR or issue that is missing information." - name: "invalid" color: fef2c0 description: "Marks a PR or issue that is missing information." - name: "beginner-friendly" color: 0e8a16 description: "Good first issue for people wanting to contribute to the project." - name: "help-wanted" color: 0e8a16 description: "We need some extra helping hands or expertise in order to resolve this." - name: "priority-critical" color: ee0701 description: "This should be dealt with ASAP. Not fixing this issue would be a serious error." - name: "priority-high" color: b60205 description: "After critical issues are fixed, these should be dealt with before any further issues." - name: "priority-medium" color: 0e8a16 description: "This issue may be useful, and needs some attention." - name: "priority-low" color: e4ea8a description: "Nice addition, maybe... someday..." - name: "major" color: b60205 description: "This PR causes a major version bump in the version number." - name: "minor" color: 0e8a16 description: "This PR causes a minor version bump in the version number." - name: "skip-changelog" color: bfdadc description: "This PR causes no version bump and won't be displayed in the changelog." paperless-api-5.1.0/.github/release-drafter.yml000066400000000000000000000020071505307417100214140ustar00rootroot00000000000000--- name-template: "v$RESOLVED_VERSION ๐ŸŒˆ" tag-template: "v$RESOLVED_VERSION" change-template: "- $TITLE @$AUTHOR (#$NUMBER)" exclude-labels: - "skip-changelog" categories: - title: "๐Ÿšจ Breaking changes" labels: - "breaking-change" - title: "โœจ New features" labels: - "new-feature" - title: "๐Ÿ› Bug fixes" labels: - "bugfix" - title: "๐Ÿš€ Enhancements" labels: - "enhancement" - "refactor" - "performance" - title: "๐Ÿงฐ Maintenance" labels: - "maintenance" - "ci" - title: "๐Ÿ“š Documentation" labels: - "documentation" - title: "โฌ†๏ธ Dependency updates" labels: - "dependencies" version-resolver: major: labels: - "major" - "breaking-change" minor: labels: - "minor" - "new-feature" patch: labels: - "bugfix" - "ci" - "dependencies" - "enhancement" - "performance" - "refactor" default: patch template: | ## Changes $CHANGES paperless-api-5.1.0/.github/renovate.json000066400000000000000000000021711505307417100203440ustar00rootroot00000000000000{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "schedule": ["* 2 1 * *"], "rebaseWhen": "behind-base-branch", "dependencyDashboard": true, "labels": ["auto", "no-stale"], "lockFileMaintenance": { "enabled": true, "automerge": true }, "commitMessagePrefix": "โฌ†๏ธ", "packageRules": [ { "matchManagers": ["pip_requirements"], "enabled": false }, { "matchUpdateTypes": ["lockFileMaintenance"], "addLabels": ["skip-changelog"] }, { "matchManagers": ["pep621"], "addLabels": ["dependencies", "python"], "rangeStrategy": "bump" }, { "matchManagers": ["pep621"], "matchDepTypes": ["dev"], "rangeStrategy": "bump" }, { "matchManagers": ["pep621"], "matchUpdateTypes": ["minor", "patch"], "automerge": true }, { "matchManagers": ["github-actions"], "addLabels": ["ci", "github_actions", "skip-changelog"], "rangeStrategy": "pin" }, { "matchManagers": ["github-actions"], "matchUpdateTypes": ["minor", "patch"], "automerge": true } ] } paperless-api-5.1.0/.github/workflows/000077500000000000000000000000001505307417100176625ustar00rootroot00000000000000paperless-api-5.1.0/.github/workflows/labels.yml000066400000000000000000000007341505307417100216530ustar00rootroot00000000000000--- name: Sync labels # yamllint disable-line rule:truthy on: push: branches: - main paths: - .github/labels.yml workflow_dispatch: jobs: labels: name: ๐Ÿท Sync labels runs-on: ubuntu-latest steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v5.0.0 - name: ๐Ÿš€ Run Label Syncer uses: micnncim/action-label-syncer@v1.3.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} paperless-api-5.1.0/.github/workflows/linting.yml000066400000000000000000000041301505307417100220470ustar00rootroot00000000000000--- name: Linting # yamllint disable-line rule:truthy on: pull_request: paths: - "**.py" - "pyproject.toml" - "uv.lock" - "pypaperless/**" - "tests/**" workflow_dispatch: env: DEFAULT_PYTHON: "3.13" jobs: lint: name: ${{ matrix.tool }} runs-on: ubuntu-latest strategy: matrix: tool: [codespell, ruff, pre-commit-hooks, pylint, yamllint] steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v5.0.0 - name: ๐Ÿ— Set up Python ${{ env.DEFAULT_PYTHON }} id: python uses: actions/setup-python@v5.6.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: ๐Ÿ— Set up uv run: | pipx install uv uv venv - name: ๐Ÿ— Install Python dependencies run: uv sync --group dev - name: ๐Ÿš€ Execute checks run: | if [ "${{ matrix.tool }}" = "codespell" ]; then uv run pre-commit run codespell --all-files elif [ "${{ matrix.tool }}" = "ruff" ]; then uv run ruff check --output-format=github . uv run ruff format --check . elif [ "${{ matrix.tool }}" = "pre-commit-hooks" ]; then uv run pre-commit run check-ast --all-files uv run pre-commit run check-case-conflict --all-files uv run pre-commit run check-docstring-first --all-files uv run pre-commit run check-json --all-files uv run pre-commit run check-merge-conflict --all-files uv run pre-commit run check-symlinks --all-files uv run pre-commit run check-toml --all-files uv run pre-commit run check-yaml --all-files uv run pre-commit run detect-private-key --all-files uv run pre-commit run end-of-file-fixer --all-files uv run pre-commit run trailing-whitespace --all-files elif [ "${{ matrix.tool }}" = "pylint" ]; then uv run pre-commit run pylint --all-files elif [ "${{ matrix.tool }}" = "yamllint" ]; then uv run yamllint . fi paperless-api-5.1.0/.github/workflows/lock.yml000066400000000000000000000006651505307417100213440ustar00rootroot00000000000000--- name: Lock # yamllint disable-line rule:truthy on: schedule: - cron: "0 5 * * *" workflow_dispatch: jobs: lock: name: ๐Ÿ”’ Lock closed issues and PRs runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v5.0.1 with: github-token: ${{ github.token }} issue-inactive-days: "30" issue-lock-reason: "" pr-inactive-days: "1" pr-lock-reason: "" paperless-api-5.1.0/.github/workflows/pr-labels.yml000066400000000000000000000014121505307417100222640ustar00rootroot00000000000000--- name: PR Labels # yamllint disable-line rule:truthy on: pull_request_target: types: - opened - synchronize - labeled - unlabeled workflow_call: jobs: pr_labels: name: Verify runs-on: ubuntu-latest steps: - name: ๐Ÿท Verify PR has a valid label uses: jesusvasquez333/verify-pr-label-action@v1.4.0 with: pull-request-number: "${{ github.event.pull_request.number }}" github-token: "${{ secrets.GITHUB_TOKEN }}" valid-labels: >- breaking-change, bugfix, ci, dependencies, documentation, enhancement, maintenance, new-feature, performance, refactor disable-reviews: true paperless-api-5.1.0/.github/workflows/release-drafter.yml000066400000000000000000000005721505307417100234560ustar00rootroot00000000000000--- name: Release Drafter # yamllint disable-line rule:truthy on: push: branches: - main workflow_dispatch: jobs: update_release_draft: name: ๐Ÿ“ Draft release runs-on: ubuntu-latest steps: - name: ๐Ÿš€ Run Release Drafter uses: release-drafter/release-drafter@v6.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} paperless-api-5.1.0/.github/workflows/release.yml000066400000000000000000000027331505307417100220320ustar00rootroot00000000000000--- name: Release # yamllint disable-line rule:truthy on: release: types: - published env: DEFAULT_PYTHON: "3.13" jobs: release: name: Releasing to PyPi runs-on: ubuntu-latest environment: name: release url: https://pypi.org/p/pypaperless permissions: contents: write id-token: write # important for trusted publishing (pypi) steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v5.0.0 - name: ๐Ÿ— Set up Python ${{ env.DEFAULT_PYTHON }} id: python uses: actions/setup-python@v5.6.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: ๐Ÿ— Set up uv run: | pipx install uv uv venv - name: ๐Ÿ— Install Python dependencies run: uv sync --group dev - name: ๐Ÿ— Set package version run: | version="${{ github.event.release.tag_name }}" version="${version,,}" version="${version#v}" sed -i "s/^version = .*/version = \"${version}\"/" pyproject.toml - name: ๐Ÿ— Build package run: uv build - name: ๐Ÿš€ Publish to PyPi uses: pypa/gh-action-pypi-publish@v1.12.4 with: verbose: true print-hash: true - name: โœ๏ธ Sign published artifacts uses: sigstore/gh-action-sigstore-python@v3.0.1 with: inputs: ./dist/*.tar.gz ./dist/*.whl release-signing-artifacts: true paperless-api-5.1.0/.github/workflows/stale.yml000066400000000000000000000025521505307417100215210ustar00rootroot00000000000000--- name: Stale # yamllint disable-line rule:truthy on: schedule: - cron: "0 3 * * *" workflow_dispatch: jobs: stale: name: ๐Ÿงน Clean up stale issues and PRs runs-on: ubuntu-latest steps: - name: ๐Ÿš€ Run stale uses: actions/stale@v9.1.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 days-before-close: 7 remove-stale-when-updated: true stale-issue-label: "stale" exempt-issue-labels: "no-stale,help-wanted" stale-issue-message: > There hasn't been any activity on this issue recently, so we have to clean up some inactive issues. Please make sure to update to the latest version and check if that solves the issue. Let us know if that works for you by leaving a comment ๐Ÿ‘ This issue has now been marked as stale and will be closed if no further activity occurs. Thank you. stale-pr-label: "stale" exempt-pr-labels: "no-stale" stale-pr-message: > There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days. Thank you for your contribution! paperless-api-5.1.0/.github/workflows/tests.yml000066400000000000000000000030711505307417100215500ustar00rootroot00000000000000--- name: Testing # yamllint disable-line rule:truthy on: pull_request: paths: - "**.py" - "pyproject.toml" - "uv.lock" - "pypaperless/**" - "tests/**" workflow_dispatch: env: DEFAULT_PYTHON: "3.13" jobs: pytest: name: Python ${{ matrix.python }} runs-on: ubuntu-latest strategy: matrix: python: ["3.12", "3.13"] steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v5.0.0 - name: ๐Ÿ— Set up Python ${{ env.DEFAULT_PYTHON }} id: python uses: actions/setup-python@v5.6.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: ๐Ÿ— Set up uv run: | pipx install uv uv venv - name: ๐Ÿ— Install Python dependencies run: uv sync --group dev - name: ๐Ÿš€ Run pytest run: uv run pytest -v --cov-report xml:coverage.xml --cov pypaperless tests - name: โฌ†๏ธ Upload coverage artifact uses: actions/upload-artifact@v4.6.2 with: name: coverage-${{ matrix.python }} path: coverage.xml coverage: runs-on: ubuntu-latest needs: pytest steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v5.0.0 with: fetch-depth: 0 - name: โฌ‡๏ธ Download coverage data uses: actions/download-artifact@v5.0.0 - name: ๐Ÿš€ Upload coverage report uses: codecov/codecov-action@v5.5.0 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true paperless-api-5.1.0/.github/workflows/typing.yml000066400000000000000000000014631505307417100217230ustar00rootroot00000000000000--- name: Typing # yamllint disable-line rule:truthy on: pull_request: paths: - "**.py" - "pyproject.toml" - "uv.lock" - "pypaperless/**" - "tests/**" workflow_dispatch: env: DEFAULT_PYTHON: "3.13" jobs: mypy: name: mypy runs-on: ubuntu-latest steps: - name: โคต๏ธ Check out code from GitHub uses: actions/checkout@v5.0.0 - name: ๐Ÿ— Set up Python ${{ env.DEFAULT_PYTHON }} id: python uses: actions/setup-python@v5.6.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: ๐Ÿ— Set up uv run: | pipx install uv uv venv - name: ๐Ÿ— Install Python dependencies run: uv sync --group dev - name: ๐Ÿš€ Run mypy run: uv run mypy pypaperless tests paperless-api-5.1.0/.gitignore000066400000000000000000000027151505307417100162620ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # OSX useful to ignore *.DS_Store .AppleDouble .LSOverride # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # pyenv .python-version # virtualenv .venv venv/ ENV/ # mypy .mypy_cache/ # ruff .ruff_cache # Visual Studio Code .vscode # IntelliJ Idea family of suites .idea *.iml ## File-based project format: *.ipr *.iws ## mpeltonen/sbt-idea plugin .idea_modules/ # PyBuilder target/ # Cookiecutter output/ python_boilerplate/ # Node node_modules/ # Deepcode AI .dccache # run run/ paperless-api-5.1.0/.pre-commit-config.yaml000066400000000000000000000074621505307417100205570ustar00rootroot00000000000000--- repos: - repo: https://github.com/renovatebot/pre-commit-hooks rev: 41.1.4 hooks: - id: renovate-config-validator - repo: local hooks: - id: ruff-check name: ๐Ÿถ Ruff Linter language: system types: [python] entry: uv run ruff check --fix require_serial: true stages: [pre-commit, pre-push, manual] - id: ruff-format name: ๐Ÿถ Ruff Formatter language: system types: [python] entry: uv run ruff format require_serial: true stages: [pre-commit, pre-push, manual] - id: check-ast name: ๐Ÿ Check Python AST language: system types: [python] entry: uv run check-ast - id: check-case-conflict name: ๐Ÿ”  Check for case conflicts language: system entry: uv run check-case-conflict - id: check-docstring-first name: โ„น๏ธ Check docstring is first language: system types: [python] entry: uv run check-docstring-first # - id: check-executables-have-shebangs # name: ๐Ÿง Check that executables have shebangs # language: system # types: [text, executable] # entry: uv run check-executables-have-shebangs # stages: [commit, push, manual] - id: check-json name: ๏ฝ› Check JSON files language: system types: [json] entry: uv run check-json - id: check-merge-conflict name: ๐Ÿ’ฅ Check for merge conflicts language: system types: [text] entry: uv run check-merge-conflict - id: check-symlinks name: ๐Ÿ”— Check for broken symlinks language: system types: [symlink] entry: uv run check-symlinks - id: check-toml name: โœ… Check TOML files language: system types: [toml] entry: uv run check-toml - id: check-xml name: โœ… Check XML files entry: uv run check-xml language: system types: [xml] - id: check-yaml name: โœ… Check YAML files language: system types: [yaml] entry: uv run check-yaml - id: codespell name: โœ… Check code for common misspellings language: system types: [text] exclude: ^(uv\.lock|requirements\.txt|requirements_dev.txt)$ entry: uv run codespell - id: detect-private-key name: ๐Ÿ•ต๏ธ Detect Private Keys language: system types: [text] entry: uv run detect-private-key - id: end-of-file-fixer name: โฎ Fix End of Files language: system types: [text] entry: uv run end-of-file-fixer stages: [pre-commit, pre-push, manual] - id: mypy name: ๐Ÿ†Ž Static type checking using mypy language: system types: [python] entry: uv run mypy require_serial: true - id: no-commit-to-branch name: ๐Ÿ›‘ Don't commit to main branch language: system entry: uv run no-commit-to-branch pass_filenames: false always_run: true args: - --branch=main - id: pylint name: ๐ŸŒŸ Starring code with pylint language: system types: [python] entry: uv run pylint - id: pytest name: ๐Ÿงช Running tests and test coverage with pytest language: system types: [python] entry: uv run pytest pass_filenames: false - id: trailing-whitespace name: โœ„ Trim Trailing Whitespace language: system types: [text] entry: uv run trailing-whitespace-fixer stages: [pre-commit, pre-push, manual] - id: yamllint name: ๐ŸŽ— Check YAML files with yamllint language: system types: [yaml] entry: uv run yamllint paperless-api-5.1.0/.vscode/000077500000000000000000000000001505307417100156265ustar00rootroot00000000000000paperless-api-5.1.0/.vscode/launch.json000066400000000000000000000004621505307417100177750ustar00rootroot00000000000000{ "version": "0.2.0", "configurations": [ { "name": "Python: pypaperless debug", "type": "debugpy", "request": "launch", "program": "run/debug.py", "console": "integratedTerminal", "justMyCode": true } ] } paperless-api-5.1.0/.yamllint000066400000000000000000000024001505307417100161130ustar00rootroot00000000000000--- ignore: - .venv rules: braces: level: error min-spaces-inside: 0 max-spaces-inside: 1 min-spaces-inside-empty: -1 max-spaces-inside-empty: -1 brackets: level: error min-spaces-inside: 0 max-spaces-inside: 0 min-spaces-inside-empty: -1 max-spaces-inside-empty: -1 colons: level: error max-spaces-before: 0 max-spaces-after: 1 commas: level: error max-spaces-before: 0 min-spaces-after: 1 max-spaces-after: 1 comments: level: error require-starting-space: true min-spaces-from-content: 1 comments-indentation: level: error document-end: level: error present: false document-start: level: error present: true empty-lines: level: error max: 1 max-start: 0 max-end: 1 hyphens: level: error max-spaces-after: 1 indentation: level: error spaces: 2 indent-sequences: true check-multi-line-strings: false key-duplicates: level: error line-length: level: warning max: 120 allow-non-breakable-words: true allow-non-breakable-inline-mappings: true new-line-at-end-of-file: level: error new-lines: level: error type: unix trailing-spaces: level: error truthy: level: error paperless-api-5.1.0/Dockerfile.dev000066400000000000000000000023711505307417100170370ustar00rootroot00000000000000FROM mcr.microsoft.com/devcontainers/python:1-3.13 SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Uninstall pre-installed formatting and linting tools # They would conflict with our pinned versions RUN \ pipx uninstall pydocstyle \ && pipx uninstall pycodestyle \ && pipx uninstall mypy \ && pipx uninstall pylint RUN \ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ # Additional library needed by some tests and accordingly by VScode Tests Discovery bluez \ ffmpeg \ libudev-dev \ libavformat-dev \ libavcodec-dev \ libavdevice-dev \ libavutil-dev \ libgammu-dev \ libswscale-dev \ libswresample-dev \ libavfilter-dev \ libpcap-dev \ libturbojpeg0 \ libyaml-dev \ libxml2 \ git \ cmake \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Install uv RUN pip3 install uv USER vscode ENV VIRTUAL_ENV="/home/vscode/.local/dev-venv" RUN uv venv $VIRTUAL_ENV ENV PATH="$VIRTUAL_ENV/bin:$PATH" # force use the venv ENV UV_PROJECT_ENVIRONMENT="$VIRTUAL_ENV" WORKDIR /workspaces # Set the default shell to bash instead of sh ENV SHELL=/bin/bash paperless-api-5.1.0/LICENSE.md000066400000000000000000000020561505307417100156740ustar00rootroot00000000000000# MIT License Copyright (c) 2022-2024 tb1337 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. paperless-api-5.1.0/README.md000066400000000000000000000066521505307417100155550ustar00rootroot00000000000000# PyPaperless [![GitHub Release][release-badge]][release-url] [![Python Version][python-badge]][python-url] [![GitHub License][license-badge]][license-url] [![Tests][tests-badge]][tests-url] [![Codecov][codecov-badge]][codecov-url] [![Linting][linting-badge]][linting-url] [![Typing][typing-badge]][typing-url] Little asynchronous client for *Paperless-ngx*, written in Python. You should at least use Python `>=3.12`. ## Features - Depends on aiohttp, works in async environments. - Token authentication preferred (credentials possible using a URL like https://user:pass@example.com) - Request single resource items. - Iterate over all resource items or request them page by page. - Create, update and delete resource items. - Add, remove and update custom fields on documents. - Almost feature complete. - **pypaperless** is designed to transport data only. Your code must organize it. Find out more about *Paperless-ngx* here: - Project: https://docs.paperless-ngx.com - API Docs: https://docs.paperless-ngx.com/api/ - Source Code: https://github.com/paperless-ngx/paperless-ngx ## Installation ```bash pip install pypaperless ``` ## Documentation Please check out the **[docs][docs-url]** for detailed instructions and examples. ## Compatibility matrix Choosing the right version of **pypaperless** for your *Paperless-ngx* instance can be tricky. This little matrix is here to help. | **pypaperless** | *Paperless-ngx* | | --------------- | --------------- | | >= 5.1 | >= 2.17 | | >= 5.0 | >= 2.17 | | >= 4.1 | >= 2.16 | | >= 4.0 | >= 2.15 | | < 4.0 | < 2.15 | * **pypaperless** `<4.0` is not compatible with *Paperless-ngx* `>=2.15` due to breaking server changes. * **pypaperless** `>=5.0` implements date changes in the document API and is therefore not compatible with *Paperless-ngx* `<2.17`. * **pypaperless** `>=5.1` will drop support for all *Paperless-ngx* versions without the Open API schema, introduced in `2.15`. Consider keeping both *Paperless-ngx* and **pypaperless** always updated. ## Authors & contributors **pypaperless** is written by [Tobias Schulz][contributors-tbsch]. Its his first Python project. Feedback appreciated. Check out all [contributors here][contributors-url]. [codecov-badge]: https://codecov.io/gh/tb1337/paperless-api/graph/badge.svg?token=IMXRBK3HRE [codecov-url]: https://app.codecov.io/gh/tb1337/paperless-api/tree/main [contributors-tbsch]: https://tbsch.de [contributors-url]: https://github.com/tb1337/paperless-api/graphs/contributors [docs-url]: https://github.com/tb1337/paperless-api/blob/main/docs/ [license-badge]: https://img.shields.io/github/license/tb1337/paperless-api [license-url]: /LICENSE.md [python-badge]: https://img.shields.io/pypi/pyversions/pypaperless [python-url]: https://pypi.org/project/pypaperless/ [tests-badge]: https://github.com/tb1337/paperless-api/actions/workflows/tests.yml/badge.svg [tests-url]: https://github.com/tb1337/paperless-api/actions [release-badge]: https://img.shields.io/github/v/release/tb1337/paperless-api [release-url]: https://github.com/tb1337/paperless-api/releases [linting-badge]: https://github.com/tb1337/paperless-api/actions/workflows/linting.yml/badge.svg [linting-url]: https://github.com/tb1337/paperless-api/actions [typing-badge]: https://github.com/tb1337/paperless-api/actions/workflows/typing.yml/badge.svg [typing-url]: https://github.com/tb1337/paperless-api/actions paperless-api-5.1.0/docs/000077500000000000000000000000001505307417100152155ustar00rootroot00000000000000paperless-api-5.1.0/docs/1_basic_usage.md000066400000000000000000000260071505307417100202310ustar00rootroot00000000000000# Basic Usage ## Documentation * [Basic Usage](1_basic_usage.md) - This page ;) * [Working with documents](2_documents.md) * [Working with custom fields](3_custom_fields.md) * [Permissions](4_permissions.md) --- **On this page:** - [Starting a session](#starting-a-session) - [Quickstart](#quickstart) - [URL rules](#url-rules) - [Custom session](#custom-session) - [Creating a token](#creating-a-token) - [Resource features](#resource-features) - [Requesting data](#requesting-data) - [Getting one item by primary key](#getting-one-item-by-primary-key) - [Retrieving a list of primary keys](#retrieving-a-list-of-primary-keys) - [Iterating over resource items](#iterating-over-resource-items) - [Iterating over pages](#iterating-over-pages) - [Reducing http requests](#reducing-http-requests) - [Manipulating data](#manipulating-data) - [Creating new items](#creating-new-items) - [Updating existing items](#updating-existing-items) - [Deleting items](#deleting-items) ## Starting a session ### Quickstart Just import the module and start using it. Note that we must be async. ```python import asyncio from pypaperless import Paperless paperless = Paperless("localhost:8000", "your-secret-token") # see main() examples asyncio.run(main()) ``` **main() Example 1** ```python async def main(): await paperless.initialize() # do something await paperless.close() ``` **main() Example 2** ```python async def main(): async with paperless: # do something ``` ### URL rules There are some rules for the *Paperless-ngx* url. 1. Isn't a scheme applied to it? `https` is automatically used. 2. If you explicitly start with `http`, the connection will be unencrypted (not recommended). 3. Only use the **base url** of your *Paperless-ngx*. Don't add `/api` to it. ### Custom session You may want to use an existing `aiohttp.ClientSession` in some cases. Simply pass it to the `Paperless` object. ```python import aiohttp from pypaperless import Paperless my_session = aiohttp.ClientSession() # ... paperless = Paperless("localhost:8000", "your-secret-token", session=my_session) ``` ### Creating a token **pypaperless** needs an API token to request and send data from and to *Paperless-ngx* for authentication purposes. It is recommended to create a technical user and assign a token to it via Django Admin, when you bootstrap any project with **pypaperless**. If you need to create that token by providing credentials, **pypaperless** ships with a little helper for that task. ```python token = Paperless.generate_api_token( "localhost:8000", "test_user", "your-password-here", ) ``` As for `Paperless` itself, you can provide a custom `aiohttp.ClientSession` object. ```python url = "localhost:8000" my_session = aiohttp.ClientSession() token = Paperless.generate_api_token( "localhost:8000", "test_user", "not-so-secret-password-anymore", session=my_session, ) ``` > [!CAUTION] > Hardcoding credentials or tokens is never good practise. Use that with caution. > [!NOTE] > Executed http requests:
> `POST` `https://localhost:8000/api/token/` ## Resource features | Resource | Request | Iterate | Create | Update | Delete | Permissions | | -------------- | -------- | ------- | ------ | ------ | ------ | ----------- | | config | x | | correspondents | x | x | x | x | x | x | | custom_fields | x | x | x | x | x | | document_types | x | x | x | x | x | x | | documents | x | x | x | x | x | x | | groups | x | x | | logs | **n.a.** | | mail_accounts | x | x | | | | x | | mail_rules | x | x | | | | x | | saved_views | x | x | | | | x | | share_links | x | x | x | x | x | | statistics | x | | status | x | | storage_paths | x | x | x | x | x | x | | tags | x | x | x | x | x | x | | tasks | x | x\* | | | | users | x | x | | | | workflows | x | x | | | \*: Only `__aiter__` is supported. `logs` are not implemented, as they return plain text. Since logs return plain text, support for this resource is currently not implemented. ## Requesting data Retrieving data from *Paperless-ngx* is really easy, there are different possibilities to achieve that. ### Getting one item by primary key This is the most common use case, since **pypaperless** always returns references to other resource items by their primary keys. You must resolve these references on your own. The returned objects are always `PaperlessModel`s. ```python document = await paperless.documents(1337) doc_type = await paperless.document_types(document.document_type) # 23 print(f"Document '{document.title}' is an {doc_type.name}.") #-> Document 'Order #23: Desktop Table' is an Invoice. ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/1337/`
> `GET` `https://localhost:8000/api/document_types/23/` ### Retrieving a list of primary keys Since resource items are requested by their primary key, it could be useful to request a list of all available primary keys. ```python item_keys = await paperless.documents.all() #-> [1, 2, 3, ...] ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/?page=1` ### Iterating over resource items Iteration enables you to execute mass operations of any kind. Like requesting single items, the iterator always returns `PaperlessModel`s. ```python count = 0 async for item in paperless.documents: if item.correspondent == 1: count += 1 print(f"{count} documents are currently stored for correspondent 1.") #-> 5 documents are currently stored for correspondent 1. ``` > [!NOTE] > The code above executes many http requests, depending on the count of your stored documents:
> `GET` `https://localhost:8000/api/documents/?page=1`
> `GET` `https://localhost:8000/api/documents/?page=2`
> `...`
> `GET` `https://localhost:8000/api/documents/?page=19` ### Iterating over pages Instead of iterating over resource items, you may want to iterate over pagination results in some cases. The `Page` model itself delivers the possibility to check for the existence of previous and next pages, item counts, accessing the raw (`.results`) or processed data (`.items`), and so on. ```python page_iter = aiter(paperless.documents.pages()) page = await anext(page_iter) #-> page.current_page == 1 page = await anext(page_iter) #-> page.current_page == 2 ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/?page=1`
> `GET` `https://localhost:8000/api/documents/?page=2` ### Reducing http requests Requesting many pages can be time-consuming, so a better way to apply the filter (mentioned [here](#iterating-over-resource-items)) is to use the `reduce` context manager. Technically, it applies query parameters to the http request, which are interpreted as filters by *Paperless-ngx*. ```python filters = { "correspondent__id": 1, } async with paperless.documents.reduce(**filters) as filtered: async for item in filtered: count += 1 # ... #-> 5 documents are currently stored for correspondent 1. ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/?page=1&correspondent__id=1` > [!TIP] > The `reduce` context works with all previously mentioned methods: `__aiter__`, `all` and `pages`. > [!IMPORTANT] > There are many filters available, **pypaperless** doesn't provide a complete list. I am working on it. At the moment, you must use the Django Rest framework http endpoint of *Paperless-ngx* in your browser and experiment with the **Filter** button on each resource. > > *Paperless-ngx* simply ignores filters which don't exist and treats them as no filter instead of raising errors, be careful. ## Manipulating data **pypaperless** offers creation, update and deletion of resource items. These features are enabled where it makes (at least for me) sense, *Paperless-ngx* itself offers full CRUD functionality. Please check the [resource features](#resource-features) table at the top of this README. If you need CRUD for another resource, please let me know and open an [issue](https://github.com/tb1337/paperless-api/issues) with your specific use-case. ### Creating new items The process of creating items consists of three parts: retrieving a new draft instance from **pypaperless**, apply data to it and call `save`. You can choose whether applying data to the draft via `kwargs` or by assigning it to the draft instance, or both. If you do not need to further use the created item, the draft instance can be safely discarded after saving, as it cannot be reused (database constraint violation). ```python from pypaperless.models.common import MatchingAlgorithmType draft = paperless.correspondents.draft( name="New correspondent", is_insensitive=True, # this works ) draft.matching_algorithm = MatchingAlgorithmType.ANY draft.match = 'any word "or small strings" match' draft.is_insensitive = False # and this, too! new_pk = await draft.save() #-> 42 ``` > [!NOTE] > Executed http requests:
> `POST` `https://localhost:8000/api/correspondents/` ### Updating existing items When it comes to updating data, you can choose between http `PATCH` (only changed fields) or `PUT` (all fields) methods. Usually updating only changed fields will do the trick. You can continue working with the class instance after updating, as the `update` method applies new data from *Paperless-ngx* to it. ```python item = await paperless.documents(23) item.title = "New document title" success = await item.update() success = await item.update(only_changed=False) # put all fields #-> True ``` > [!NOTE] > Executed http requests:
> `PATCH` `http://localhost:8000/api/documents/23/`
> **OR**
> `PUT` `http://localhost:8000/api/documents/23/` > [!TIP] > The actual payload of the request is completely different here, and I recommend you to use `PATCH` whenever possible. It is cleaner and much safer, as it only updates fields which have _actually_ changed. **PATCH** ```json { "title": "New document title" } ``` **PUT** ```json { "title": "New document title", "content": "...", "correspondents": ["..."], "document_types": ["..."], "storage_paths": ["..."], "...": "..." // and every other field } ``` ### Deleting items Last but not least, it is also possible to remove data from *Paperless-ngx*. > [!CAUTION] > This will permanently delete data from your database. There is no point of return. Be careful. ```python item = await paperless.documents(23) success = await item.delete() #-> True ``` > [!NOTE] > Executed http requests:
> `DELETE` `http://localhost:8000/api/documents/23/` paperless-api-5.1.0/docs/2_documents.md000066400000000000000000000167211505307417100177700ustar00rootroot00000000000000# Working with documents Some *Paperless-ngx* resources provide more features as others, especially when it comes to `Documents`. Lets take a closer look! ## Documentation * [Basic Usage](1_basic_usage.md) * [Working with documents](2_documents.md) - This page ;) * [Working with custom fields](3_custom_fields.md) * [Permissions](4_permissions.md) --- **On this page:** - [Binary image/pdf data](#binary-imagepdf-data) - [Lookup specific documents](#lookup-specific-documents) - [1. Search query](#1-search-query) - [2. More like](#2-more-like) - [Search results](#search-results) - [Metadata](#metadata) - [Notes](#notes) - [Suggestions](#suggestions) - [Next available ASN](#next-available-asn) ## Binary image/pdf data You can access the binary image/pdf data by using the following methods. They all return a `DownloadedDocument` class instance, which holds the actual binary data and provides some more useful attributes, like content type, disposition type and filename. **Example 1: Provide a primary key** ```python download = await paperless.documents.download(23) preview = await paperless.documents.preview(23) thumbnail = await paperless.documents.thumbnail(23) ``` **Example 2: Already fetched item** ```python document = await paperless.documents(23) download = await document.get_download() preview = await document.get_preview() thumbnail = await document.get_thumbnail() ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/23/download/`
> `GET` `https://localhost:8000/api/documents/23/preview/`
> `GET` `https://localhost:8000/api/documents/23/thumb/` ## Lookup specific documents If you want to search for documents, *Paperless-ngx* offers two possibilities to achieve that. **pypaperless** implements two iterable shortcuts for that. ### 1. Search query Search query documentation: https://docs.paperless-ngx.com/usage/#basic-usage_searching ```python async for document in paperless.documents.search("type:invoice"): # do something ``` > [!NOTE] > The code above executes many http requests, depending on the count of your matched documents:
> `GET` `https://localhost:8000/api/documents/?page=1&query=type%3Ainvoice`
> `GET` `https://localhost:8000/api/documents/?page=2&query=type%3Ainvoice`
> `...`
> `GET` `https://localhost:8000/api/documents/?page=19&query=type%3Ainvoice` ### 2. More like Search for similar documents like the permitted document primary key. ```python async for document in paperless.documents.more_like(23): # do something ``` > [!NOTE] > The code above executes many http requests, depending on the count of your matched documents:
> `GET` `https://localhost:8000/api/documents/?page=1&more_like_id=23`
> `GET` `https://localhost:8000/api/documents/?page=2&more_like_id=23`
> `...`
> `GET` `https://localhost:8000/api/documents/?page=19&more_like_id=23` ### Search results While iterating over search results, `Document` models are extended with another field: `search_hit`. Lets take a closer look at it. ```python async for document in paperless.documents.more_like(23): print(f"{document.id} matched query by {document.search_hit.score}.") #-> 42 matched query by 13.37. ``` To make life easier, you have the possibility to check whether a `Document` model has been initialized from a search or not: ```python document = await paperless.documents(23) # no search if document.has_search_hit: print("result of a search query") else: print("not a result from a query") #-> not a result from a query ``` ## Metadata *Paperless-ngx* stores some metadata about your documents. If you wish to access them, there are two ways to achieve that. **Example 1: Provide a primary key** ```python metadata = await paperless.documents.metadata(23) ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/23/metadata/` **Example 2: Already fetched item** ```python document = await paperless.documents(23) metadata = await document.get_metadata() ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/23/`
> `GET` `https://localhost:8000/api/documents/23/metadata/` ## Notes Documents can be commented with so-called notes. *Paperless-ngx* supports requesting, creating and deleting those. **pypaperless** includes built-in support for it, too. **Getting notes** Document notes are always available as `list[DocumentNote]` after requesting them. There are two ways of requesting document notes: ```python # by primary key list_of_notes = await paperless.documents.notes(23) ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/23/notes/` or ```python # by already fetched item document = await paperless.documents(23) list_of_notes = await document.notes() ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/23/`
> `GET` `https://localhost:8000/api/documents/23/notes/` **Creating notes** You can add new notes. Updating existing notes isn't possible due to *Paperless-ngx* API limitations. There are two ways of creating a new note draft: ```python # by document primary key draft = paperless.documents.notes.draft(23) ``` or ```python # by already fetched document document = await paperless.documents(23) draft = document.notes.draft() ``` After creating the draft, do your work with it and tell *Paperless-ngx* to store it: ```python draft.note = "Lorem ipsum" new_note_pk, document_pk = await draft.save() #-> 42, 23 ``` > [!NOTE] > Executed http requests:
> `POST` `https://localhost:8000/api/documents/23/notes/` **Deleting notes** Sometimes it may be necessary to delete document notes. > [!CAUTION] > This will permanently delete data from your database. There is no way to recover deleted data. Be careful. ```python a_note = list_of_notes.pop() # document note with example pk 42 success = await a_note.delete() #-> True ``` > [!NOTE] > Executed http requests:
> `DELETE` `https://localhost:8000/api/documents/23/notes/?id=42` ## Suggestions One of the key functionalities of *Paperless-ngx* is _classification_: it is the workflow of assigning classifiers to your documents, like correspondents or tags. *Paperless-ngx* does that by auto-assigning or suggesting them to you. These suggestions can be accessed by **pypaperless**, as well. **Example 1: Provide a primary key** ```python suggestions = await paperless.documents.suggestions(23) ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/23/suggestions/` **Example 2: Already fetched item** ```python document = await paperless.documents(23) suggestions = await document.get_suggestions() ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/23/`
> `GET` `https://localhost:8000/api/documents/23/suggestions/` The returned `DocumentSuggestions` instance stores a list of suggested resource items for each classifier: correspondents, tags, document_types, storage_paths and dates. This is what the raw data looks like: ```json { "correspondents": [ 11 ], "tags": [1, 33, 7], "document_types": [ 15 ], "storage_paths": [ 2 ], "dates": [ "2023-01-02", "2024-03-04", "2025-05-06" ] } ``` ## Next available ASN Simply returns the next available archive serial number as `int`. ```python next_asn = await paperless.documents.get_next_asn() #-> 1337 ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/next_asn/` paperless-api-5.1.0/docs/3_custom_fields.md000066400000000000000000000270361505307417100206310ustar00rootroot00000000000000# Working with custom fields When classifying your documents, you may want to add custom fields to them. As of `v5.0`, **pypaperless** introduces a completely new way of working with them! ## Documentation * [Basic Usage](1_basic_usage.md) * [Working with documents](2_documents.md) * [Working with custom fields](3_custom_fields.md) - This page ;) * [Permissions](4_permissions.md) --- **On this page:** - [Introduction to custom fields](#introduction-to-custom-fields) - [Caching](#caching) - [Providing a cache](#providing-a-cache) - [Without cache](#without-cache) - [Checking for custom fields](#checking-for-custom-fields) - [Iterating over custom fields](#iterating-over-custom-fields) - [Fetching custom field values](#fetching-custom-field-values) - [Without fallback (get)](#without-fallback-get) - [With fallback (default)](#with-fallback-default) - [Adding custom fields to a document](#adding-custom-fields-to-a-document) - [Draft a new value](#draft-a-new-value) - [Attaching to a document](#attaching-to-a-document) - [Removing custom fields from a document](#removing-custom-fields-from-a-document) - [Choose a custom field](#choose-a-custom-field) - [Detach it from document](#detach-it-from-document) - [Updating custom field values](#updating-custom-field-values) - [Special custom field data types](#special-custom-field-data-types) - [Date](#date) - [Document reference](#document-reference) - [Monetary](#monetary) - [Select](#select) ## Introduction to custom fields In *Paperless-ngx*, custom fields allow you to enrich your documents with additional, structured data. Prior to `v5.0`, working with them was cumbersome: you had to loop through field IDs and values, manually resolve field metadata, and handle parsing based on their types. Here is an example of how the *Paperless-ngx* API returns custom field instances: ```json { "custom_fields": [ { "value": 42, "field": 11 }, ] } ``` As you can see, this only gives you the fieldโ€™s ID. To get more details (like its name, type, or metadata), you must fetch the full `CustomField`: ```json { "id": 1, "name": "Very Important Document (VID)", "data_type": "boolean", "extra_data": { "select_options": [ null ], "default_currency": null }, "document_count": 23 } ``` To simplify this, **pypaperless** now provides a smarter, more convenient interface for interacting with custom fields. ## Caching ### Providing a cache You can now cache the list of custom fields for your *Paperless-ngx* instance. This allows **pypaperless** to map fields to rich model classes automatically when documents are fetched: ```python # initialize the cache paperless.cache.custom_fields = await paperless.custom_fields.as_dict() ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/custom_fields/` Now, when you fetch a document, its fields are automatically mapped to their corresponding typed value objects: ```python document = await paperless.documents(1337) print(list(document.custom_fields)) #-> [ # CustomFieldIntegerValue(field=1, value=42, name='Any Number Field', data_type=, extra_data={'select_options': [None], 'default_currency': None}), # CustomFieldBooleanValue(field=3, value=True, ...), # CustomFieldSelectValue(field=5, ...) # ] ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/1337/` ### Without cache If you donโ€™t provide a cache, custom fields are returned as generic `CustomFieldValue` instances: ```python print(list(document.custom_fields)) #-> [ # CustomFieldValue(field=1, value=42), # CustomFieldValue(field=3, value=True), # CustomFieldValue(field=5, ...) # ] ``` > [!TIP] > This approach can be useful in scenarios where you're certain that, for example, field 1 contains the integer value you need to operate on. ## Checking for custom fields To check whether a custom field is attached to a document: **Example 1: using a `CustomField` instance** ```python specific_custom_field = await paperless.custom_fields(1) if specific_custom_field in document.custom_fields: print("Custom field 1 is present!") else: print("Custom field 1 is missing!") ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/custom_fields/1/` **Example 2: using the field's ID** ```python custom_field_id = 1 field = document.custom_fields.get(custom_field_id) # do something with: field.value ``` ## Iterating over custom fields To iterate through all custom fields of a document: ```python for field in document.custom_fields: # do something ``` ## Fetching custom field values Its time to work with specific custom field values. **pypaperless** provides different ways to retrieve the actual value of a field. ### Without fallback (get) > [!CAUTION] > Note that `document.custom_fields.get(...)` will raise `ItemNotFoundError` if the given custom field doesn't exist in the document data. If that could happen and you prefer not to perform an existence check before, you should use `.default(...)`. **Example 1: using `CustomField` instance** ```python specific_custom_field = await paperless.custom_fields(1) field = document.custom_fields.get(specific_custom_field) print(field.value) #-> 42 ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/custom_fields/1/` **Example 2: using the field's ID** ```python custom_field_id = 1 field = document.custom_fields.get(custom_field_id) print(field.value) #-> 42 ``` **Example 3: Type safety** Due to the dynamic data structure of the *Paperless-ngx* API, static typing for `CustomFieldValue` instances is not possible in your development environment. However, if you don't want to give up type safety during development, you can either use `typing.cast()` or the following approach: ```python from pypaperless.models.common import CustomFieldIntegerValue field = document.custom_fields.get(custom_field_id, expected_type=CustomFieldIntegerValue) ``` > [!TIP] > A `TypeError` is raised if the type of the custom field does not correspond to the expected type. ### With fallback (default) This avoids errors if the field is missing and returns `None` instead. **Example 1: using `CustomField` instance** ```python specific_custom_field = await paperless.custom_fields(1) if field := document.custom_fields.default(specific_custom_field): print(field.value) #-> 42 ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/custom_fields/1/` **Example 2: using the field's ID** ```python custom_field_id = 1 if field := document.custom_fields.default(custom_field_id): print(field.value) #-> 42 ``` **Example 3: Type safety** Just like with `get()`, this method ensures type safety as well. ```python from pypaperless.models.common import CustomFieldIntegerValue field = document.custom_fields.default(custom_field_id, expected_type=CustomFieldIntegerValue) ``` > [!TIP] > A `TypeError` is raised if the type of the custom field does not correspond to the expected type. ## Adding custom fields to a document If you want to add new custom fields to your documents, you have to draft custom field values and add them to the documents custom fields list. ### Draft a new value Use the `draft_value` method on a `CustomField`: **Example 1: with cache ([read about caching](#providing-a-cache))** ```python my_int_field = await paperless.custom_fields(1) new_field_value = my_int_field.draft_value(42) print(new_field_value) #-> CustomFieldIntegerValue(field=1, value=42, name='My Integer Field', data_type=, ...) ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/custom_fields/1/` **Example 2: without cache** ```python new_field_value = my_int_field.draft_value(42) print(new_field_value) #-> CustomFieldValue(field=1, value=42, ...) ``` **Example 3: Typing** There is optional type mapping for your development environment the same way as with `get()` and `default()`. Unlike the previous cases, **no exception is raised** if the type doesn't match. ```python from pypaperless.models.common import CustomFieldIntegerValue new_field_value = my_int_field.draft_value(42, expected_type=CustomFieldIntegerValue) ``` ### Attaching to a document The new custom field value is ready to go: ```python document = await paperless.documents(1337) document.custom_fields.add(new_field_value) # or simply use document.custom_fields += new_field_value ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/1337/` > [!CAUTION] > Don't forget to call `document.update()` to persist your change in the *Paperless-ngx* database. You can read more about that [here](1_basic_usage.md#updating-existing-items). ## Removing custom fields from a document In some cases, you may want to remove custom fields from documents again. It is as easy as adding fields. ### Choose a custom field You can remove fields in three ways. **Example 1: using `CustomField` instance** ```python my_int_field = await paperless.custom_fields(1) ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/custom_fields/1/` **Example 2: using the field's ID** ```python my_int_field = 1 ``` **Example 3: using `CustomFieldValue` instance** ```python my_int_field = document.custom_fields.get(1) ``` ### Detach it from document The custom field value is ready to be removed: ```python document = await paperless.documents(1337) document.custom_fields.remove(my_int_field) # or simply use document.custom_fields -= my_int_field ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/1337/` > [!CAUTION] > Don't forget to call `document.update()` to persist your change in the *Paperless-ngx* database. You can read more about that [here](1_basic_usage.md#updating-existing-items). ## Updating custom field values As with every other entity in **pypaperless**, custom field values can be manipulated by just setting a new value to them. **Example 1: integer custom field** ```python document = await paperless.documents(1337) if field := document.custom_fields.default(1): field.value = 23 await document.update() ``` **Example 2: monetary custom field** This custom field value is one of the special cases, [scroll down](#monetary) to read more about them. ```python document = await paperless.documents(1337) if field := document.custom_fields.default(2): field.amount = 42.23 await document.update() print(field.value) #-> EUR42.23 ``` ## Special custom field data types There are many data types for custom fields in *Paperless-ngx*, for example, strings and integers. While both are very common, special data types are also available. **pypaperless** provides some extra functionality for these. ### Date Values in `CustomFieldDateValue` are parsed into `datetime.date`. If parsing fails, the raw string or `None` is returned. ### Document reference Returns a list of document IDs. You can fetch full documents manually if needed. ### Monetary Monetary values such as `EUR123.45` are exposed via: * `.amount`: Gets or sets the actual amount. * `.currency`: Gets or sets the currency (EUR, USD, ...). > [!WARNING] > Input is not validated client-side. Invalid values will trigger API errors. ### Select The `CustomFieldSelectValue` raw value is set to an internal ID by *Paperless-ngx*. **pypaperless** ships with properties which resolve the real values for you. * `.label`: Returns the label for `value` or falls back to `None`. * `.labels`: Returns the list of labels of the `CustomField`. paperless-api-5.1.0/docs/4_permissions.md000066400000000000000000000057211505307417100203420ustar00rootroot00000000000000# Permissions Some resources of *Paperless-ngx* support retrieving and updating of object-level permissions. They are tricky, so here is little guidance for handling them. ## Documentation * [Basic Usage](1_basic_usage.md) * [Working with documents](2_documents.md) * [Working with custom fields](3_custom_fields.md) * [Permissions](4_permissions.md) - This page ;) --- **On this page:** - [About permissions in **pypaperless**](#about-permissions-in-pypaperless) - [Toggle requesting](#toggle-requesting) - [Create item with permissions](#create-item-with-permissions) - [Update permissions](#update-permissions) ## About permissions in **pypaperless** At the moment, **pypaperless** provides only minimal support for permissions. Due to the way *Paperless-ngx* implements and exposes object-level permissions via the Django API, itโ€™s hard to envision practical use cases for managing them automatically. > [!CAUTION] > The permission model is subject to change in the future, when I feel the need for it. ## Toggle requesting When requesting data from *Paperless-ngx*, it delivers two permission fields by default: `owner` and `user_can_change`. You have to explicitly call the API to return the permissions table by enabling that feature. To do so, you have to enable it one by one for each resource in **pypaperless**. ```python paperless.documents.request_permissions = True document = await paperless.documents(23) print(document.has_permissions) #-> True ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/23/?full_perms=true` Requesting permissions stays enabled until it gets disabled again. ```python paperless.documents.request_permissions = False document = await paperless.documents(23) print(document.has_permissions) #-> False ``` > [!NOTE] > Executed http requests:
> `GET` `https://localhost:8000/api/documents/23/` ## Create item with permissions When creating new resource items, you can apply permissions by setting a `PermissionTableType` to the optional `set_permissions` field. > [!WARNING] > Both `PermissionTableType` and `PermissionSetType` automatically initialize empty lists for their fields unless you provided a value. ```python from pypaperless.models.common import PermissionSetType, PermissionTableType draft = paperless.correspondents.draft() draft.name = "Correspondent with perms" draft.set_permissions = PermissionTableType( view=PermissionSetType( users=[23], ), ) # ... ``` ## Update permissions If you want to change permissions of a resource item, you have to enable requesting them before fetching it. The `permissions` field gets available then, ready for modifications. ```python paperless.documents.request_permissions = True document = await paperless.documents(23) if document.has_permissions: document.permissions.view.users.append(5) await document.update() ``` > [!NOTE] > Executed http requests:
> `PATCH` `https://localhost:8000/api/documents/23/?full_perms=true` paperless-api-5.1.0/docs/README.md000066400000000000000000000002631505307417100164750ustar00rootroot00000000000000# Documentation * [Basic Usage](1_basic_usage.md) * [Working with documents](2_documents.md) * [Working with custom fields](3_custom_fields.md) * [Permissions](4_permissions.md) paperless-api-5.1.0/pypaperless/000077500000000000000000000000001505307417100166345ustar00rootroot00000000000000paperless-api-5.1.0/pypaperless/__init__.py000066400000000000000000000001111505307417100207360ustar00rootroot00000000000000"""PyPaperless.""" from .api import Paperless __all__ = ("Paperless",) paperless-api-5.1.0/pypaperless/api.py000066400000000000000000000306621505307417100177660ustar00rootroot00000000000000"""PyPaperless.""" import logging from collections.abc import AsyncGenerator from contextlib import asynccontextmanager from io import BytesIO from json.decoder import JSONDecodeError from typing import Any, Protocol import aiohttp from yarl import URL from . import helpers from .const import API_PATH, API_VERSION, PaperlessResource from .exceptions import ( BadJsonResponseError, InitializationError, JsonResponseWithError, PaperlessConnectionError, PaperlessForbiddenError, PaperlessInactiveOrDeletedError, PaperlessInvalidTokenError, ) from .models.base import HelperBase from .models.common import PaperlessCache class PaperlessProtocol(Protocol): """Protocol for `Paperless` instances.""" config: helpers.ConfigHelper correspondents: helpers.CorrespondentHelper custom_fields: helpers.CustomFieldHelper documents: helpers.DocumentHelper document_types: helpers.DocumentTypeHelper groups: helpers.GroupHelper mail_accounts: helpers.MailAccountHelper mail_rules: helpers.MailRuleHelper saved_views: helpers.SavedViewHelper share_links: helpers.ShareLinkHelper statistics: helpers.StatisticHelper remote_version: helpers.RemoteVersionHelper status: helpers.StatusHelper storage_paths: helpers.StoragePathHelper tags: helpers.TagHelper tasks: helpers.TaskHelper users: helpers.UserHelper workflows: helpers.WorkflowHelper class Paperless(PaperlessProtocol): """Retrieves and manipulates data from and to Paperless via REST.""" _helper_map: dict[str, type[HelperBase]] = { PaperlessResource.CONFIG: helpers.ConfigHelper, PaperlessResource.CORRESPONDENTS: helpers.CorrespondentHelper, PaperlessResource.CUSTOM_FIELDS: helpers.CustomFieldHelper, PaperlessResource.DOCUMENTS: helpers.DocumentHelper, PaperlessResource.DOCUMENT_TYPES: helpers.DocumentTypeHelper, PaperlessResource.GROUPS: helpers.GroupHelper, PaperlessResource.MAIL_ACCOUNTS: helpers.MailAccountHelper, PaperlessResource.MAIL_RULES: helpers.MailRuleHelper, PaperlessResource.SAVED_VIEWS: helpers.SavedViewHelper, PaperlessResource.SHARE_LINKS: helpers.ShareLinkHelper, PaperlessResource.STATISTICS: helpers.StatisticHelper, PaperlessResource.REMOTE_VERSION: helpers.RemoteVersionHelper, PaperlessResource.STATUS: helpers.StatusHelper, PaperlessResource.STORAGE_PATHS: helpers.StoragePathHelper, PaperlessResource.TAGS: helpers.TagHelper, PaperlessResource.TASKS: helpers.TaskHelper, PaperlessResource.USERS: helpers.UserHelper, PaperlessResource.WORKFLOWS: helpers.WorkflowHelper, } async def __aenter__(self) -> "Paperless": """Return context manager.""" await self.initialize() return self async def __aexit__(self, *_: object) -> None: """Exit context manager.""" await self.close() def __init__( self, url: str | URL, token: str | None = None, *, session: aiohttp.ClientSession | None = None, request_args: dict[str, Any] | None = None, request_api_version: int | None = None, ) -> None: """Initialize a `Paperless` instance. You have to permit either a session, or an url / token pair. `url`: A hostname or IP-address as string, or yarl.URL object. `token`: An api token created in Paperless Django settings, or via the helper function. `session`: A custom `PaperlessSession` object, if existing. `request_args` are passed to each request method call as additional kwargs, ssl stuff for example. You should read the aiohttp docs to learn more about it. """ self._base_url = self._create_base_url(url) self._cache = PaperlessCache() self._initialized = False self._request_api_version = request_api_version or API_VERSION self._request_args = request_args or {} self._session = session self._token = token self._api_version = API_VERSION self._version: str | None = None self.logger = logging.getLogger(f"{__package__}") @property def base_url(self) -> str: """Return the base url of the Paperless api endpoint.""" return str(self._base_url) @property def cache(self) -> PaperlessCache: """Return the cache object.""" return self._cache @property def is_initialized(self) -> bool: """Return `True` if connection is initialized.""" return self._initialized @property def host_api_version(self) -> int: """Return the api version of the Paperless host.""" return self._api_version @property def host_version(self) -> str | None: """Return the version of the Paperless host.""" return self._version @staticmethod def _create_base_url(url: str | URL) -> URL: """Create URL from string or URL and prepare for further use.""" # reverse compatibility, fall back to https if isinstance(url, str): if "://" not in url: url = f"https://{url}" url = url.rstrip("/") url = URL(url) # scheme check. fall back to https if url.scheme not in ("https", "http"): url = URL(url).with_scheme("https") return url @staticmethod def _process_form(data: dict[str, Any]) -> aiohttp.FormData: """Process form data and create a `aiohttp.FormData` object. Every field item gets converted to a string-like object. """ form = aiohttp.FormData(quote_fields=False) def _add_form_value(name: str | None, value: Any) -> Any: if value is None: return params = {} if isinstance(value, dict): for dict_key, dict_value in value.items(): _add_form_value(dict_key, dict_value) return if isinstance(value, list | set): for list_value in value: _add_form_value(name, list_value) return if isinstance(value, tuple): if len(value) == 2: params["filename"] = f"{value[1]}" value = value[0] if name is not None: form.add_field( name, BytesIO(value) if isinstance(value, bytes) else f"{value}", **params ) _add_form_value(None, data) return form @staticmethod async def generate_api_token( url: str, username: str, password: str, session: aiohttp.ClientSession | None = None, ) -> str: """Request Paperless to generate an api token for the given credentials. Warning: the request is plain and insecure. Don't use this in production environments or businesses. Warning: error handling is low for this method, as it is just a helper. Example: ------- ```python token = Paperless.generate_api_token("example.com:8000", "api_user", "secret_password") paperless = Paperless("example.com:8000", token) # do something ``` """ external_session = session is not None session = session or aiohttp.ClientSession() try: url = url.rstrip("/") json = { "username": username, "password": password, } res = await session.request("post", f"{url}{API_PATH['token']}", json=json) data = await res.json() res.raise_for_status() return str(data["token"]) except (JSONDecodeError, KeyError) as exc: message = "Token is missing in response." raise BadJsonResponseError(message) from exc except aiohttp.ClientResponseError as exc: raise JsonResponseWithError(payload={"error": data}) from exc finally: if not external_session: await session.close() async def close(self) -> None: """Clean up connection.""" if self._session: await self._session.close() self.logger.info("Closed.") async def initialize(self) -> None: """Initialize and test the connection to DRF.""" async with self.request("get", API_PATH["index"]) as res: try: res.raise_for_status() self._api_version = self._request_api_version or int( res.headers.get("x-api-version", API_VERSION) ) self._version = res.headers.get("x-version", None) await res.json() except (aiohttp.ClientResponseError, JSONDecodeError) as exc: raise InitializationError from exc # initialize helpers for attr, helper_cls in self._helper_map.items(): setattr(self, attr, helper_cls(self)) self._initialized = True self.logger.info("Initialized.") @asynccontextmanager async def request( # noqa: PLR0913 # pylint: disable=too-many-positional-arguments self, method: str, path: str, json: dict[str, Any] | None = None, data: dict[str, Any] | aiohttp.FormData | None = None, form: dict[str, Any] | None = None, params: dict[str, Any] | None = None, **kwargs: Any, ) -> AsyncGenerator[aiohttp.ClientResponse]: """Send a request to the Paperless api and return the `aiohttp.ClientResponse`. This method provides a little interface for utilizing `aiohttp.FormData`. `method`: A http method: get, post, patch, put, delete, head, options `path`: A path to the endpoint or a string url. `json`: A dict containing the json data. `data`: A dict containing the data to send in the request body. `form`: A dict with form data, which gets converted to `aiohttp.FormData` and replaces `data`. `params`: A dict with query parameters. `kwargs`: Optional attributes for the `aiohttp.ClientSession.request` method. """ if self._session is None: self._session = aiohttp.ClientSession() # add headers headers = { "Accept": f"application/json; version={self._request_api_version}", } if self._token: headers["Authorization"] = f"Token {self._token}" # Merge with any user-defined headers (optional) if "headers" in kwargs: kwargs["headers"].update(headers) else: kwargs["headers"] = headers # add request args kwargs.update(self._request_args) # overwrite data with a form, when there is a form payload if isinstance(form, dict): data = self._process_form(form) # add base path url = f"{self._base_url}{path}" if not path.startswith("http") else path try: res = await self._session.request( method=method, url=url, json=json, data=data, params=params, **kwargs, ) self.logger.debug("%s (%d): %s", method.upper(), res.status, res.url) except aiohttp.ClientConnectionError as err: raise PaperlessConnectionError from err # error handling for 401 and 403 codes if res.status == 401: try: error_data = await res.json() detail = error_data.get("detail", "") except JSONDecodeError: detail = "" if "inactive" in detail.lower() or "deleted" in detail.lower(): raise PaperlessInactiveOrDeletedError(res) raise PaperlessInvalidTokenError(res) if res.status == 403: raise PaperlessForbiddenError(res) yield res async def request_json( self, method: str, endpoint: str, **kwargs: Any, ) -> Any: """Make a request to the api and parse response json to dict.""" async with self.request(method, endpoint, **kwargs) as res: if res.content_type != "application/json": raise BadJsonResponseError(res) try: payload = await res.json() except ValueError: raise BadJsonResponseError(res) from None if res.status == 400: raise JsonResponseWithError(payload) res.raise_for_status() return payload paperless-api-5.1.0/pypaperless/const.py000066400000000000000000000100301505307417100203260ustar00rootroot00000000000000"""PyPaperless constants.""" from __future__ import annotations from enum import StrEnum API_VERSION = 9 CONFIG = "config" CONSUMPTION_TEMPLATES = "consumption_templates" CORRESPONDENTS = "correspondents" CUSTOM_FIELDS = "custom_fields" DOCUMENTS = "documents" DOCUMENT_TYPES = "document_types" GROUPS = "groups" LOGS = "logs" MAIL_ACCOUNTS = "mail_accounts" MAIL_RULES = "mail_rules" SAVED_VIEWS = "saved_views" SHARE_LINKS = "share_links" STATISTICS = "statistics" REMOTE_VERSION = "remote_version" STATUS = "status" STORAGE_PATHS = "storage_paths" TAGS = "tags" TASKS = "tasks" USERS = "users" WORKFLOW_ACTIONS = "workflow_actions" WORKFLOWS = "workflows" WORKFLOW_TRIGGERS = "workflow_triggers" UNKNOWN = "unknown" API_PATH = { "index": "/api/schema/", "token": "/api/token/", f"{CONFIG}": f"/api/{CONFIG}/", f"{CONFIG}_single": f"/api/{CONFIG}/{{pk}}/", f"{CORRESPONDENTS}": f"/api/{CORRESPONDENTS}/", f"{CORRESPONDENTS}_single": f"/api/{CORRESPONDENTS}/{{pk}}/", f"{CUSTOM_FIELDS}": f"/api/{CUSTOM_FIELDS}/", f"{CUSTOM_FIELDS}_single": f"/api/{CUSTOM_FIELDS}/{{pk}}/", f"{DOCUMENTS}": f"/api/{DOCUMENTS}/", f"{DOCUMENTS}_download": f"/api/{DOCUMENTS}/{{pk}}/download/", f"{DOCUMENTS}_meta": f"/api/{DOCUMENTS}/{{pk}}/metadata/", f"{DOCUMENTS}_next_asn": f"/api/{DOCUMENTS}/next_asn/", f"{DOCUMENTS}_notes": f"/api/{DOCUMENTS}/{{pk}}/notes/", f"{DOCUMENTS}_preview": f"/api/{DOCUMENTS}/{{pk}}/preview/", f"{DOCUMENTS}_thumbnail": f"/api/{DOCUMENTS}/{{pk}}/thumb/", f"{DOCUMENTS}_post": f"/api/{DOCUMENTS}/post_document/", f"{DOCUMENTS}_single": f"/api/{DOCUMENTS}/{{pk}}/", f"{DOCUMENTS}_suggestions": f"/api/{DOCUMENTS}/{{pk}}/suggestions/", f"{DOCUMENT_TYPES}": f"/api/{DOCUMENT_TYPES}/", f"{DOCUMENT_TYPES}_single": f"/api/{DOCUMENT_TYPES}/{{pk}}/", f"{GROUPS}": f"/api/{GROUPS}/", f"{GROUPS}_single": f"/api/{GROUPS}/{{pk}}/", f"{MAIL_ACCOUNTS}": f"/api/{MAIL_ACCOUNTS}/", f"{MAIL_ACCOUNTS}_single": f"/api/{MAIL_ACCOUNTS}/{{pk}}/", f"{MAIL_RULES}": f"/api/{MAIL_RULES}/", f"{MAIL_RULES}_single": f"/api/{MAIL_RULES}/{{pk}}/", f"{SAVED_VIEWS}": f"/api/{SAVED_VIEWS}/", f"{SAVED_VIEWS}_single": f"/api/{SAVED_VIEWS}/{{pk}}/", f"{SHARE_LINKS}": f"/api/{SHARE_LINKS}/", f"{SHARE_LINKS}_single": f"/api/{SHARE_LINKS}/{{pk}}/", f"{STATISTICS}": f"/api/{STATISTICS}/", f"{REMOTE_VERSION}": f"/api/{REMOTE_VERSION}/", f"{STATUS}": f"/api/{STATUS}/", f"{STORAGE_PATHS}": f"/api/{STORAGE_PATHS}/", f"{STORAGE_PATHS}_single": f"/api/{STORAGE_PATHS}/{{pk}}/", f"{TAGS}": f"/api/{TAGS}/", f"{TAGS}_single": f"/api/{TAGS}/{{pk}}/", f"{TASKS}": f"/api/{TASKS}/", f"{TASKS}_single": f"/api/{TASKS}/{{pk}}/", f"{USERS}": f"/api/{USERS}/", f"{USERS}_single": f"/api/{USERS}/{{pk}}/", f"{WORKFLOWS}": f"/api/{WORKFLOWS}/", f"{WORKFLOWS}_single": f"/api/{WORKFLOWS}/{{pk}}/", f"{WORKFLOW_ACTIONS}": f"/api/{WORKFLOW_ACTIONS}/", f"{WORKFLOW_ACTIONS}_single": f"/api/{WORKFLOW_ACTIONS}/{{pk}}/", f"{WORKFLOW_TRIGGERS}": f"/api/{WORKFLOW_TRIGGERS}/", f"{WORKFLOW_TRIGGERS}_single": f"/api/{WORKFLOW_TRIGGERS}/{{pk}}/", } class PaperlessResource(StrEnum): """Represent paths of api endpoints.""" CONFIG = CONFIG CONSUMPTION_TEMPLATES = CONSUMPTION_TEMPLATES CORRESPONDENTS = CORRESPONDENTS CUSTOM_FIELDS = CUSTOM_FIELDS DOCUMENTS = DOCUMENTS DOCUMENT_TYPES = DOCUMENT_TYPES GROUPS = GROUPS LOGS = LOGS MAIL_ACCOUNTS = MAIL_ACCOUNTS MAIL_RULES = MAIL_RULES SAVED_VIEWS = SAVED_VIEWS SHARE_LINKS = SHARE_LINKS STATISTICS = STATISTICS REMOTE_VERSION = REMOTE_VERSION STATUS = STATUS STORAGE_PATHS = STORAGE_PATHS TAGS = TAGS TASKS = TASKS USERS = USERS WORKFLOWS = WORKFLOWS WORKFLOW_ACTIONS = WORKFLOW_ACTIONS WORKFLOW_TRIGGERS = WORKFLOW_TRIGGERS UNKNOWN = UNKNOWN @classmethod def _missing_(cls: type[PaperlessResource], *_: object) -> PaperlessResource: """Set default member on unknown value.""" return cls.UNKNOWN paperless-api-5.1.0/pypaperless/exceptions.py000066400000000000000000000056761505307417100214050ustar00rootroot00000000000000"""PyPaperless exceptions.""" from typing import Any class PaperlessError(Exception): """Base exception for PyPaperless.""" # Sessions and requests class InitializationError(PaperlessError): """Raise when initializing a `Paperless` instance without valid url or token.""" class PaperlessConnectionError(InitializationError, PaperlessError): """Raise when connection to Paperless is not possible.""" class PaperlessAuthError(InitializationError, PaperlessError): """Raise when response is 401 code.""" class PaperlessInvalidTokenError(PaperlessAuthError): """Raise when response is 401 due invalid access token.""" class PaperlessInactiveOrDeletedError(PaperlessAuthError): """Raise when response is 401 code due user is inactive or deleted.""" class PaperlessForbiddenError(InitializationError, PaperlessError): """Raise when response is 403 code.""" class BadJsonResponseError(PaperlessError): """Raise when response is no valid json.""" class JsonResponseWithError(PaperlessError): """Raise when Paperless accepted the request, but responded with an error payload.""" def __init__(self, payload: Any) -> None: """Initialize a `JsonResponseWithError` instance.""" def _parse_payload(payload: Any, key: list[str] | None = None) -> tuple[list[str], str]: """Parse first suitable error from payload.""" if key is None: key = [] if isinstance(payload, list): return _parse_payload(payload.pop(0), key) if isinstance(payload, dict): if "error" in payload: key.append("error") return _parse_payload(payload["error"], key) new_key = next(iter(payload)) key.append(new_key) return _parse_payload(payload[new_key], key) return key, payload key, message = _parse_payload(payload) if len(key) == 0: key.append("error") key_chain = " -> ".join(key) super().__init__(f"Paperless [{key_chain}]: {message}") # Models class AsnRequestError(PaperlessError): """Raise when getting an error during requesting the next asn.""" class DraftFieldRequiredError(PaperlessError): """Raise when trying to save models with missing required fields.""" class DraftNotSupportedError(PaperlessError): """Raise when trying to draft unsupported models.""" class ItemNotFoundError(PaperlessError): """Raise when trying to access non-existing items in PaperlessModelData classes.""" class PrimaryKeyRequiredError(PaperlessError): """Raise when trying to access model data without supplying a pk.""" # Tasks class TaskNotFoundError(PaperlessError): """Raise when trying to access a task by non-existing uuid.""" def __init__(self, task_id: str) -> None: """Initialize a `TaskNotFound` instance.""" super().__init__(f"Task with UUID {task_id} not found.") paperless-api-5.1.0/pypaperless/helpers.py000066400000000000000000000020051505307417100206450ustar00rootroot00000000000000"""PyPaperless helpers.""" # pylint: disable=unused-import from .models.base import HelperBase # noqa: F401 from .models.classifiers import ( # noqa: F401 CorrespondentHelper, DocumentTypeHelper, StoragePathHelper, TagHelper, ) from .models.config import ConfigHelper # noqa: F401 from .models.custom_fields import CustomFieldHelper # noqa: F401 from .models.documents import DocumentHelper, DocumentMetaHelper, DocumentNoteHelper # noqa: F401 from .models.mails import MailAccountHelper, MailRuleHelper # noqa: F401 from .models.permissions import GroupHelper, UserHelper # noqa: F401 from .models.remote_version import RemoteVersionHelper # noqa: F401 from .models.saved_views import SavedViewHelper # noqa: F401 from .models.share_links import ShareLinkHelper # noqa: F401 from .models.statistics import StatisticHelper # noqa: F401 from .models.status import StatusHelper # noqa: F401 from .models.tasks import TaskHelper # noqa: F401 from .models.workflows import WorkflowHelper # noqa: F401 paperless-api-5.1.0/pypaperless/models/000077500000000000000000000000001505307417100201175ustar00rootroot00000000000000paperless-api-5.1.0/pypaperless/models/__init__.py000066400000000000000000000025361505307417100222360ustar00rootroot00000000000000"""PyPaperless models.""" from .classifiers import ( Correspondent, CorrespondentDraft, DocumentType, DocumentTypeDraft, StoragePath, StoragePathDraft, Tag, TagDraft, ) from .config import Config from .custom_fields import CustomField, CustomFieldDraft from .documents import ( Document, DocumentDraft, DocumentMeta, DocumentNote, DocumentNoteDraft, ) from .mails import MailAccount, MailRule from .pages import Page from .permissions import Group, User from .remote_version import RemoteVersion from .saved_views import SavedView from .share_links import ShareLink, ShareLinkDraft from .statistics import Statistic from .status import Status from .tasks import Task from .workflows import Workflow, WorkflowAction, WorkflowTrigger __all__ = ( "Config", "Correspondent", "CorrespondentDraft", "CustomField", "CustomFieldDraft", "Document", "DocumentDraft", "DocumentMeta", "DocumentNote", "DocumentNoteDraft", "DocumentType", "DocumentTypeDraft", "Group", "MailAccount", "MailRule", "Page", "RemoteVersion", "SavedView", "ShareLink", "ShareLinkDraft", "Statistic", "Status", "StoragePath", "StoragePathDraft", "Tag", "TagDraft", "Task", "User", "Workflow", "WorkflowAction", "WorkflowTrigger", ) paperless-api-5.1.0/pypaperless/models/base.py000066400000000000000000000073311505307417100214070ustar00rootroot00000000000000"""Provide base classes.""" from abc import ABC, abstractmethod from dataclasses import Field, dataclass, fields from typing import TYPE_CHECKING, Any, Protocol, Self, TypeVar, final from pypaperless.const import API_PATH, PaperlessResource from pypaperless.models.utils import dict_value_to_object if TYPE_CHECKING: from pypaperless import Paperless ResourceT = TypeVar("ResourceT", bound="PaperlessModel") class PaperlessBase: """Superclass for all classes in PyPaperless.""" _api_path = API_PATH["index"] def __init__(self, api: "Paperless") -> None: """Initialize a `PaperlessBase` instance.""" self._api = api class HelperProtocol[ResourceT](Protocol): """Protocol for any `HelperBase` instances and its ancestors.""" _api: "Paperless" _api_path: str _resource: PaperlessResource _resource_cls: type[ResourceT] class HelperBase(PaperlessBase): """Base class for all helpers in PyPaperless.""" _resource: PaperlessResource @dataclass(init=False) class PaperlessModelProtocol(Protocol): """Protocol for any `PaperlessBase` instances and its ancestors.""" _api: "Paperless" _api_path: str _data: dict[str, Any] _fetched: bool _params: dict[str, Any] # fmt: off def _get_dataclass_fields(self) -> list[Field]: ... def _set_dataclass_fields(self) -> None: ... # fmt: on @dataclass(init=False) class PaperlessModel(PaperlessBase): """Base class for all models in PyPaperless.""" def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `PaperlessModel` instance.""" super().__init__(api) self._data = {} self._data.update(data) self._fetched = False self._params: dict[str, Any] = {} @final @classmethod def create_with_data( cls, api: "Paperless", data: dict[str, Any], *, fetched: bool = False, ) -> Self: """Return a new instance of `cls` from `data`. Primarily used by class factories to create new model instances. Example: `document = Document.create_with_data(...)` """ item = cls(api, data=data) item._fetched = fetched if fetched: item._set_dataclass_fields() return item @final def _get_dataclass_fields(self) -> list[Field]: """Get the dataclass fields.""" return [ field for field in fields(self) if (not field.name.startswith("_") or field.name == "__search_hit__") ] @final def _set_dataclass_fields(self) -> None: """Set the dataclass fields from `self._data`.""" for field in self._get_dataclass_fields(): value = dict_value_to_object( f"{self.__class__.__name__}.{field.name}", self._data.get(field.name), field.type, field.default, self._api, ) setattr(self, field.name, value) @property def is_fetched(self) -> bool: """Return whether the `model data` is fetched or not.""" return self._fetched async def load(self) -> None: """Get `model data` from DRF.""" data = await self._api.request_json("get", self._api_path, params=self._params) self._data.update(data) self._set_dataclass_fields() self._fetched = True class PaperlessModelData(ABC): """Base class for all custom data types in PyPaperless.""" @classmethod @abstractmethod def unserialize(cls, api: "Paperless", data: Any) -> Self: """Return a new instance of `cls` from `data`.""" @abstractmethod def serialize(self) -> Any: """Serialize the class data.""" paperless-api-5.1.0/pypaperless/models/classifiers.py000066400000000000000000000150731505307417100230060ustar00rootroot00000000000000"""Provide `Correspondent`, `DocumentType`, `StoragePath` and `Tag` related models and helpers.""" import datetime from dataclasses import dataclass from typing import TYPE_CHECKING, Any from pypaperless.const import API_PATH, PaperlessResource from .base import HelperBase, PaperlessModel from .mixins import helpers, models if TYPE_CHECKING: from pypaperless import Paperless @dataclass(init=False) class Correspondent( PaperlessModel, models.MatchingFieldsMixin, models.SecurableMixin, models.UpdatableMixin, models.DeletableMixin, ): """Represent a Paperless `Correspondent`.""" _api_path = API_PATH["correspondents_single"] id: int | None = None slug: str | None = None name: str | None = None document_count: int | None = None last_correspondence: datetime.date | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `Correspondent` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @dataclass(init=False) class CorrespondentDraft( PaperlessModel, models.MatchingFieldsMixin, models.SecurableDraftMixin, models.CreatableMixin, ): """Represent a new `Correspondent`, which is not yet stored in Paperless.""" _api_path = API_PATH["correspondents"] _create_required_fields = { "name", "match", "matching_algorithm", "is_insensitive", } name: str | None = None @dataclass(init=False) class DocumentType( PaperlessModel, models.MatchingFieldsMixin, models.SecurableMixin, models.UpdatableMixin, models.DeletableMixin, ): """Represent a Paperless `DocumentType`.""" _api_path = API_PATH["document_types_single"] id: int | None = None slug: str | None = None name: str | None = None document_count: int | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `DocumentType` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @dataclass(init=False) class DocumentTypeDraft( PaperlessModel, models.MatchingFieldsMixin, models.SecurableDraftMixin, models.CreatableMixin, ): """Represent a new `DocumentType`, which is not yet stored in Paperless.""" _api_path = API_PATH["document_types"] _create_required_fields = { "name", "match", "matching_algorithm", "is_insensitive", } name: str | None = None owner: int | None = None @dataclass(init=False) class StoragePath( PaperlessModel, models.MatchingFieldsMixin, models.SecurableMixin, models.UpdatableMixin, models.DeletableMixin, ): """Represent a Paperless `StoragePath`.""" _api_path = API_PATH["storage_paths_single"] id: int | None = None slug: str | None = None name: str | None = None path: str | None = None document_count: int | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `StoragePath` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @dataclass(init=False) class StoragePathDraft( PaperlessModel, models.MatchingFieldsMixin, models.SecurableDraftMixin, models.CreatableMixin, ): """Represent a new `StoragePath`, which is not yet stored in Paperless.""" _api_path = API_PATH["storage_paths"] _create_required_fields = { "name", "path", "match", "matching_algorithm", "is_insensitive", } name: str | None = None path: str | None = None owner: int | None = None @dataclass(init=False) class Tag( PaperlessModel, models.MatchingFieldsMixin, models.SecurableMixin, models.UpdatableMixin, models.DeletableMixin, ): """Represent a Paperless `Tag`.""" _api_path = API_PATH["tags_single"] id: int | None = None slug: str | None = None name: str | None = None color: str | None = None text_color: str | None = None is_inbox_tag: bool | None = None document_count: int | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `Tag` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @dataclass(init=False) class TagDraft( PaperlessModel, models.MatchingFieldsMixin, models.SecurableDraftMixin, models.CreatableMixin, ): """Represent a new `Tag`, which is not yet stored in Paperless.""" _api_path = API_PATH["tags"] _create_required_fields = { "name", "color", "is_inbox_tag", "match", "matching_algorithm", "is_insensitive", } name: str | None = None color: str | None = None text_color: str | None = None is_inbox_tag: bool | None = None owner: int | None = None class CorrespondentHelper( HelperBase, helpers.SecurableMixin, helpers.CallableMixin[Correspondent], helpers.DraftableMixin[CorrespondentDraft], helpers.IterableMixin[Correspondent], ): """Represent a factory for Paperless `Correspondent` models.""" _api_path = API_PATH["correspondents"] _resource = PaperlessResource.CORRESPONDENTS _draft_cls = CorrespondentDraft _resource_cls = Correspondent class DocumentTypeHelper( HelperBase, helpers.SecurableMixin, helpers.CallableMixin[DocumentType], helpers.DraftableMixin[DocumentTypeDraft], helpers.IterableMixin[DocumentType], ): """Represent a factory for Paperless `DocumentType` models.""" _api_path = API_PATH["document_types"] _resource = PaperlessResource.DOCUMENT_TYPES _draft_cls = DocumentTypeDraft _resource_cls = DocumentType class StoragePathHelper( HelperBase, helpers.SecurableMixin, helpers.CallableMixin[StoragePath], helpers.DraftableMixin[StoragePathDraft], helpers.IterableMixin[StoragePath], ): """Represent a factory for Paperless `StoragePath` models.""" _api_path = API_PATH["storage_paths"] _resource = PaperlessResource.STORAGE_PATHS _draft_cls = StoragePathDraft _resource_cls = StoragePath class TagHelper( HelperBase, helpers.SecurableMixin, helpers.CallableMixin[Tag], helpers.DraftableMixin[TagDraft], helpers.IterableMixin[Tag], ): """Represent a factory for Paperless `Tag` models.""" _api_path = API_PATH["tags"] _resource = PaperlessResource.TAGS _draft_cls = TagDraft _resource_cls = Tag paperless-api-5.1.0/pypaperless/models/common.py000066400000000000000000000271221505307417100217650ustar00rootroot00000000000000"""PyPaperless common types.""" import contextlib import datetime import re from dataclasses import dataclass, field from enum import Enum, StrEnum from typing import TYPE_CHECKING, Any, TypedDict, TypeVar if TYPE_CHECKING: from pypaperless import Paperless from .classifiers import Correspondent, DocumentType, StoragePath, Tag from .custom_fields import CustomField # custom_fields class CustomFieldExtraDataSelectOptions(TypedDict): """Represent the `extra_data.select_options` field of a `CustomField`.""" id: str | None label: str | None class CustomFieldExtraData(TypedDict): """Represent the `extra_data` field of a `CustomField`.""" default_currency: str | None select_options: list[CustomFieldExtraDataSelectOptions | None] class CustomFieldType(Enum): """Represent a subtype of `CustomField`.""" STRING = "string" URL = "url" DATE = "date" BOOLEAN = "boolean" INTEGER = "integer" FLOAT = "float" MONETARY = "monetary" DOCUMENT_LINK = "documentlink" SELECT = "select" UNKNOWN = "unknown" @classmethod def _missing_(cls: type, *_: object) -> "CustomFieldType": """Set default member on unknown value.""" return CustomFieldType.UNKNOWN # documents @dataclass(kw_only=True) class CustomFieldValue: """Represent a subtype of `CustomField`.""" field: int | None = None value: Any | None = None name: str | None = None data_type: CustomFieldType | None = None extra_data: CustomFieldExtraData | None = None CustomFieldValueT = TypeVar("CustomFieldValueT", bound=CustomFieldValue) @dataclass(kw_only=True) class CustomFieldBooleanValue(CustomFieldValue): """Represent a boolean `CustomFieldValue`.""" value: bool | None = None @dataclass(kw_only=True) class CustomFieldDateValue(CustomFieldValue): """Represent a date `CustomFieldValue`.""" value: datetime.date | str | None = None def __post_init__(self) -> None: """Convert the value to a datetime.""" if isinstance(self.value, str): with contextlib.suppress(ValueError): dt = datetime.datetime.fromisoformat(self.value) self.value = dt.date() @dataclass(kw_only=True) class CustomFieldDocumentLinkValue(CustomFieldValue): """Represent a document link `CustomFieldValue`.""" value: list[int] | None = None @dataclass(kw_only=True) class CustomFieldFloatValue(CustomFieldValue): """Represent a float `CustomFieldValue`.""" value: float | None = None @dataclass(kw_only=True) class CustomFieldIntegerValue(CustomFieldValue): """Represent an integer `CustomFieldValue`.""" value: int | None = None @dataclass(kw_only=True) class CustomFieldMonetaryValue(CustomFieldValue): """Represent a monetary `CustomFieldValue`.""" value: str | None = None @property def currency(self) -> str | None: """Return the currency of the `value` field.""" if self.value and (match := re.match(r"^([a-zA-Z]{3})", self.value)): return match.group(1) if match else self.value if self.extra_data and (default_currency := self.extra_data.get("default_currency", None)): return default_currency return "" @currency.setter def currency(self, new_currency: str) -> None: """Override the currency of the field.""" value = self.value or "" value = re.sub(r"^[a-zA-Z]{3}", "", value) if new_currency and re.match(r"^[A-Z]{3}$", new_currency): self.value = f"{new_currency}{value}" else: self.value = value @property def amount(self) -> float | None: """Return the amount without currentcy of the `value` field.""" if self.value: numeric = re.sub(r"[^\d.]", "", self.value) return float(numeric) return None @amount.setter def amount(self, new_amount: float) -> None: """Set a new amount and construct the internal needed value.""" self.value = f"{self.currency}{new_amount:.2f}" @dataclass(kw_only=True) class CustomFieldSelectValue(CustomFieldValue): """Represent a select `CustomFieldValue`.""" value: int | str | None = None @property def labels(self) -> list[CustomFieldExtraDataSelectOptions | None]: """Return the list of labels of the `CustomField`.""" if not self.extra_data: return [] return self.extra_data["select_options"] @property def label(self) -> str | None: """Return the label for `value` or fall back to `None`.""" for opt in self.labels: if opt and opt["id"] == self.value: return opt["label"] return None @dataclass(kw_only=True) class CustomFieldStringValue(CustomFieldValue): """Represent a string `CustomFieldValue`.""" value: str | None = None @dataclass(kw_only=True) class CustomFieldURLValue(CustomFieldValue): """Represent an url `CustomFieldValue`.""" value: str | None = None CUSTOM_FIELD_TYPE_VALUE_MAP: dict[CustomFieldType, type[CustomFieldValue]] = { CustomFieldType.BOOLEAN: CustomFieldBooleanValue, CustomFieldType.DATE: CustomFieldDateValue, CustomFieldType.DOCUMENT_LINK: CustomFieldDocumentLinkValue, CustomFieldType.FLOAT: CustomFieldFloatValue, CustomFieldType.INTEGER: CustomFieldIntegerValue, CustomFieldType.MONETARY: CustomFieldMonetaryValue, CustomFieldType.SELECT: CustomFieldSelectValue, CustomFieldType.STRING: CustomFieldStringValue, CustomFieldType.URL: CustomFieldURLValue, } # documents @dataclass(kw_only=True) class DocumentMetadataType: """Represent a subtype of `DocumentMeta`.""" namespace: str | None = None prefix: str | None = None key: str | None = None value: str | None = None # documents @dataclass(kw_only=True) class DocumentSearchHitType: """Represent a subtype of `Document`.""" score: float | None = None highlights: str | None = None note_highlights: str | None = None rank: int | None = None # api @dataclass(kw_only=True) class MasterDataInstance: """Represent a `MasterDataInstance`.""" api: "Paperless" is_initialized: bool = False correspondents: list["Correspondent"] = field(default_factory=list) custom_fields: list["CustomField"] = field(default_factory=list) document_types: list["DocumentType"] = field(default_factory=list) storage_paths: list["StoragePath"] = field(default_factory=list) tags: list["Tag"] = field(default_factory=list) # mixins/models/data_fields, used for classifiers class MatchingAlgorithmType(Enum): """Represent a subtype of `Correspondent`, `DocumentType`, `StoragePath` and `Tag`.""" NONE = 0 ANY = 1 ALL = 2 LITERAL = 3 REGEX = 4 FUZZY = 5 AUTO = 6 UNKNOWN = -1 @classmethod def _missing_(cls: type, *_: object) -> "MatchingAlgorithmType": """Set default member on unknown value.""" return MatchingAlgorithmType.UNKNOWN # api @dataclass(kw_only=True) class PaperlessCache: """Represent a Paperless cache object.""" custom_fields: dict[int, "CustomField"] | None = None # mixins/models/securable @dataclass(kw_only=True) class PermissionSetType: """Represent a Paperless permission set.""" users: list[int] = field(default_factory=list) groups: list[int] = field(default_factory=list) # mixins/models/securable @dataclass(kw_only=True) class PermissionTableType: """Represent a Paperless permissions type.""" view: PermissionSetType = field(default_factory=PermissionSetType) change: PermissionSetType = field(default_factory=PermissionSetType) # documents class RetrieveFileMode(StrEnum): """Represent a subtype of `DownloadedDocument`.""" DOWNLOAD = "download" PREVIEW = "preview" THUMBNAIL = "thumb" # saved_views @dataclass(kw_only=True) class SavedViewFilterRuleType: """Represent a subtype of `SavedView`.""" rule_type: int | None = None value: str | None = None # share_links class ShareLinkFileVersionType(Enum): """Represent a subtype of `ShareLink`.""" ARCHIVE = "archive" ORIGINAL = "original" UNKNOWN = "unknown" @classmethod def _missing_(cls: type, *_: object) -> "ShareLinkFileVersionType": """Set default member on unknown value.""" return ShareLinkFileVersionType.UNKNOWN # statistics @dataclass(kw_only=True) class StatisticDocumentFileTypeCount: """Represent a Paperless statistics file type count.""" mime_type: str | None = None mime_type_count: int | None = None # status class StatusType(Enum): """Represent a subtype of `Status`.""" OK = "OK" ERROR = "ERROR" WARNING = "WARNING" UNKNOWN = "UNKNOWN" @classmethod def _missing_(cls: type, *_: object) -> "StatusType": """Set default member on unknown value.""" return StatusType.UNKNOWN # status @dataclass(kw_only=True) class StatusDatabaseMigrationStatusType: """Represent a subtype of `StatusDatabaseType`.""" latest_migration: str | None = None unapplied_migrations: list[str] = field(default_factory=list) # status @dataclass(kw_only=True) class StatusDatabaseType: """Represent a subtype of `Status`.""" type: str | None = None url: str | None = None status: StatusType | None = None error: str | None = None migration_status: StatusDatabaseMigrationStatusType | None = None # status @dataclass(kw_only=True) class StatusStorageType: """Represent a subtype of `Status`.""" total: int | None = None available: int | None = None # status @dataclass(kw_only=True) class StatusTasksType: """Represent a subtype of `Status`.""" redis_url: str | None = None redis_status: StatusType | None = None redis_error: str | None = None celery_status: StatusType | None = None celery_url: str | None = None celery_error: str | None = None index_status: StatusType | None = None index_last_modified: datetime.datetime | None = None index_error: str | None = None classifier_status: StatusType | None = None classifier_last_trained: datetime.datetime | None = None classifier_error: str | None = None sanity_check_status: StatusType | None = None sanity_check_last_run: datetime.datetime | None = None sanity_check_error: str | None = None # tasks class TaskStatusType(Enum): """Represent a subtype of `Task`.""" PENDING = "PENDING" STARTED = "STARTED" SUCCESS = "SUCCESS" FAILURE = "FAILURE" UNKNOWN = "UNKNOWN" @classmethod def _missing_(cls: type, *_: object) -> "TaskStatusType": """Set default member on unknown value.""" return TaskStatusType.UNKNOWN # workflows class WorkflowActionType(Enum): """Represent a subtype of `Workflow`.""" ASSIGNMENT = 1 UNKNOWN = -1 @classmethod def _missing_(cls: type, *_: object) -> "WorkflowActionType": """Set default member on unknown value.""" return WorkflowActionType.UNKNOWN # workflows class WorkflowTriggerType(Enum): """Represent a subtype of `Workflow`.""" CONSUMPTION = 1 DOCUMENT_ADDED = 2 DOCUMENT_UPDATED = 3 UNKNOWN = -1 @classmethod def _missing_(cls: type, *_: object) -> "WorkflowTriggerType": """Set default member on unknown value.""" return WorkflowTriggerType.UNKNOWN # workflows class WorkflowTriggerSourceType(Enum): """Represent a subtype of `Workflow`.""" CONSUME_FOLDER = 1 API_UPLOAD = 2 MAIL_FETCH = 3 UNKNOWN = -1 @classmethod def _missing_(cls: type, *_: object) -> "WorkflowTriggerSourceType": """Set default member on unknown value.""" return WorkflowTriggerSourceType.UNKNOWN paperless-api-5.1.0/pypaperless/models/config.py000066400000000000000000000036431505307417100217440ustar00rootroot00000000000000"""Provide `Config` related models and helpers.""" from dataclasses import dataclass from typing import TYPE_CHECKING, Any from pypaperless.const import API_PATH, PaperlessResource from .base import HelperBase, PaperlessModel from .mixins import helpers if TYPE_CHECKING: from pypaperless import Paperless @dataclass(init=False) class Config(PaperlessModel): """Represent a Paperless `Config`.""" _api_path = API_PATH["config_single"] id: int | None = None user_args: str | None = None output_type: str | None = None pages: int | None = None language: str | None = None mode: str | None = None skip_archive_file: str | None = None image_dpi: int | None = None unpaper_clean: str | None = None deskew: bool | None = None rotate_pages: bool | None = None rotate_pages_threshold: float | None = None max_image_pixels: float | None = None color_conversion_strategy: str | None = None app_title: str | None = None app_logo: str | None = None barcode_tag_mapping: dict[str, str] | None = None barcodes_enabled: bool | None = None barcode_enable_tiff_support: bool | None = None barcode_string: str | None = None barcode_retain_split_pages: bool | None = None barcode_enable_asn: bool | None = None barcode_asn_prefix: str | None = None barcode_upscale: float | None = None barcode_dpi: int | None = None barcode_max_pages: int | None = None barcode_enable_tag: bool | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `Config` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) class ConfigHelper(HelperBase, helpers.CallableMixin[Config]): """Represent a factory for Paperless `Config` models.""" _api_path = API_PATH["config"] _resource = PaperlessResource.CONFIG _resource_cls = Config paperless-api-5.1.0/pypaperless/models/custom_fields.py000066400000000000000000000055711505307417100233410ustar00rootroot00000000000000"""Provide `CustomField` related models and helpers.""" from dataclasses import dataclass from typing import TYPE_CHECKING, Any, overload from pypaperless.const import API_PATH, PaperlessResource from .base import HelperBase, PaperlessModel from .common import ( CUSTOM_FIELD_TYPE_VALUE_MAP, CustomFieldExtraData, CustomFieldType, CustomFieldValue, CustomFieldValueT, ) from .mixins import helpers, models if TYPE_CHECKING: from pypaperless import Paperless @dataclass(init=False) class CustomField( PaperlessModel, models.UpdatableMixin, models.DeletableMixin, ): """Represent a Paperless `CustomField`.""" _api_path = API_PATH["custom_fields_single"] id: int name: str | None = None data_type: CustomFieldType | None = None extra_data: CustomFieldExtraData | None = None document_count: int | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `Document` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @overload def draft_value(self, value: Any) -> CustomFieldValue: ... @overload def draft_value( self, value: Any, expected_type: type[CustomFieldValueT] ) -> CustomFieldValueT: ... def draft_value( self, value: Any, expected_type: type[CustomFieldValueT] | None = None, # noqa: ARG002 # pylint: disable=unused-argument ) -> CustomFieldValue | CustomFieldValueT: """Draft a new `CustomFieldValue` instance.""" cache = self._api.cache.custom_fields if cache and self.id in cache: klass = CUSTOM_FIELD_TYPE_VALUE_MAP.get( self.data_type or CustomFieldType.UNKNOWN, CustomFieldValue ) klass_data = { "field": self.id, "value": value, "name": self.name, "data_type": self.data_type, "extra_data": self.extra_data, } return klass(**klass_data) return CustomFieldValue(field=self.id, value=value) @dataclass(init=False) class CustomFieldDraft(PaperlessModel, models.CreatableMixin): """Represent a new Paperless `CustomField`, which is not stored in Paperless.""" _api_path = API_PATH["custom_fields"] _create_required_fields = {"name", "data_type"} name: str | None = None data_type: CustomFieldType | None = None extra_data: CustomFieldExtraData | None = None class CustomFieldHelper( HelperBase, helpers.CallableMixin[CustomField], helpers.DraftableMixin[CustomFieldDraft], helpers.IterableMixin[CustomField], ): """Represent a factory for Paperless `CustomField` models.""" _api_path = API_PATH["custom_fields"] _resource = PaperlessResource.CUSTOM_FIELDS _draft_cls = CustomFieldDraft _resource_cls = CustomField paperless-api-5.1.0/pypaperless/models/documents.py000066400000000000000000000603621505307417100225010ustar00rootroot00000000000000"""Provide `Document` related models and helpers.""" import datetime from collections.abc import AsyncGenerator, Iterator from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Self, cast, overload from pypaperless.const import API_PATH, PaperlessResource from pypaperless.exceptions import AsnRequestError, ItemNotFoundError, PrimaryKeyRequiredError from pypaperless.models.utils import object_to_dict_value from .base import HelperBase, PaperlessModel, PaperlessModelData from .common import ( CUSTOM_FIELD_TYPE_VALUE_MAP, CustomFieldType, CustomFieldValue, CustomFieldValueT, DocumentMetadataType, DocumentSearchHitType, RetrieveFileMode, ) from .custom_fields import CustomField from .mixins import helpers, models if TYPE_CHECKING: from pypaperless import Paperless class DocumentCustomFieldList(PaperlessModelData): """Represent a list of Paperless custom field instances typically on documents.""" def __init__(self, api: "Paperless", data: list[dict[str, Any]]) -> None: """Initialize a `DocumentCustomFieldList` instance.""" self._api = api self._data = data self._fields: list[CustomFieldValue] = [] cache = api.cache.custom_fields for item in data: if cache and (field := cache.get(item["field"], None)): klass = CUSTOM_FIELD_TYPE_VALUE_MAP.get( field.data_type or CustomFieldType.UNKNOWN, CustomFieldValue ) klass_data = { **item, "name": field.name, "data_type": field.data_type, "extra_data": field.extra_data, } self._fields.append(klass(**klass_data)) else: self._fields.append(CustomFieldValue(**item)) def __contains__(self, field: int | CustomField) -> bool: """Check if the given `CustomField` or its id is present in `DocumentCustomFieldList`.""" item_id = field.id if isinstance(field, CustomField) else field return any(item.field == item_id for item in self._fields) def __iter__(self) -> Iterator[CustomFieldValue]: """Iterate over custom fields. Example: ------- ```python for item in document.custom_fields: # do something ``` """ yield from self._fields def __iadd__(self, field: CustomFieldValue) -> Self: """Add a new `CustomFieldValue` to a document.""" return self.add(field) def add(self, field: CustomFieldValue) -> Self: """Add a new `CustomFieldValue` to a document.""" self._fields.append(field) return self def __isub__(self, field: CustomFieldValue | CustomField | int) -> Self: """Remove a `CustomFieldValue` from a document.""" return self.remove(field) def remove(self, field: CustomFieldValue | CustomField | int) -> Self: """Remove a `CustomFieldValue` from a document.""" item_id = ( field.id if isinstance(field, CustomField) else field.field if isinstance(field, CustomFieldValue) else field ) self._fields = [field for field in self._fields if field.field != item_id] return self @overload def default(self, field: int | CustomField) -> CustomFieldValue | None: ... @overload def default( self, field: int | CustomField, expected_type: type[CustomFieldValueT] ) -> CustomFieldValueT | None: ... def default( self, field: int | CustomField, expected_type: type[CustomFieldValueT] | None = None ) -> CustomFieldValue | CustomFieldValueT | None: """Access and return a (typed) `CustomFieldValue`, or `None`.""" try: value = self.get(field) except ItemNotFoundError: return None if expected_type is not None and not isinstance(value, expected_type): msg = f"Expected {expected_type.__name__}, got {type(value).__name__}" raise TypeError(msg) return value @overload def get(self, field: int | CustomField) -> CustomFieldValue: ... @overload def get( self, field: int | CustomField, expected_type: type[CustomFieldValueT] ) -> CustomFieldValueT: ... def get( self, field: int | CustomField, expected_type: type[CustomFieldValueT] | None = None ) -> CustomFieldValue | CustomFieldValueT: """Access and return a (typed) `CustomFieldValue` from the list.""" item_id = field.id if isinstance(field, CustomField) else field for item in self._fields: if item.field == item_id: if expected_type is not None and not isinstance(item, expected_type): msg = f"Expected {expected_type.__name__}, got {type(item).__name__}" raise TypeError(msg) return item raise ItemNotFoundError @classmethod def unserialize(cls, api: "Paperless", data: list[dict[str, Any]]) -> Self: """Return a new instance of `cls` from `data`. Primarily used by `dict_value_to_object` when instantiating model classes. """ return cls(api, data=data) def serialize(self) -> list[dict[str, Any]]: """Serialize the class data.""" return [{"field": field.field, "value": field.value} for field in self._fields] @dataclass(init=False) class Document( PaperlessModel, models.SecurableMixin, models.UpdatableMixin, models.DeletableMixin, ): """Represent a Paperless `Document`.""" _api_path = API_PATH["documents_single"] id: int | None = None correspondent: int | None = None document_type: int | None = None storage_path: int | None = None title: str | None = None content: str | None = None tags: list[int] | None = None created: datetime.date | None = None modified: datetime.datetime | None = None added: datetime.datetime | None = None deleted_at: datetime.datetime | None = None archive_serial_number: int | None = None original_file_name: str | None = None archived_file_name: str | None = None is_shared_by_requester: bool | None = None custom_fields: DocumentCustomFieldList | None = None page_count: int | None = None mime_type: str | None = None __search_hit__: DocumentSearchHitType | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `Document` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) self.notes = DocumentNoteHelper(api, data.get("id")) @property def created_date(self) -> datetime.date | None: """Backward compatibility for the removed `created_date` field.""" return self.created @property def has_search_hit(self) -> bool: """Return if the document has a search hit attached.""" return self.__search_hit__ is not None @property def search_hit(self) -> DocumentSearchHitType | None: """Return the document search hit.""" return self.__search_hit__ async def get_download(self, *, original: bool = False) -> "DownloadedDocument": """Request and return the `DownloadedDocument` class.""" return await self._api.documents.download(cast("int", self.id), original=original) async def get_metadata(self) -> "DocumentMeta": """Request and return the documents `DocumentMeta` class.""" return await self._api.documents.metadata(cast("int", self.id)) async def get_preview(self, *, original: bool = False) -> "DownloadedDocument": """Request and return the `DownloadedDocument` class.""" return await self._api.documents.preview(cast("int", self.id), original=original) async def get_suggestions(self) -> "DocumentSuggestions": """Request and return the `DocumentSuggestions` class.""" return await self._api.documents.suggestions(cast("int", self.id)) async def get_thumbnail(self, *, original: bool = False) -> "DownloadedDocument": """Request and return the `DownloadedDocument` class.""" return await self._api.documents.thumbnail(cast("int", self.id), original=original) @dataclass(init=False) class DocumentDraft(PaperlessModel, models.CreatableMixin): """Represent a new Paperless `Document`, which is not stored in Paperless.""" _api_path = API_PATH["documents_post"] _create_required_fields = {"document"} document: bytes | None = None filename: str | None = None title: str | None = None created: datetime.datetime | None = None correspondent: int | None = None document_type: int | None = None storage_path: int | None = None tags: int | list[int] | None = None archive_serial_number: int | None = None custom_fields: list[int] | None = None def _serialize(self) -> dict[str, Any]: """Serialize.""" data = { "form": { field.name: object_to_dict_value(getattr(self, field.name)) for field in self._get_dataclass_fields() if field.name not in {"document", "filename"} } } data["form"].update( { "document": ( (self.document, self.filename) if self.filename is not None else self.document ) } ) return data @dataclass(init=False) class DocumentNote(PaperlessModel): """Represent a Paperless `DocumentNote`.""" _api_path = API_PATH["documents_notes"] id: int | None = None note: str | None = None created: datetime.datetime | None = None document: int | None = None user: int | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `DocumentNote` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("document")) async def delete(self) -> bool: """Delete a `resource item` from DRF. There is no point of return. Return `True` when deletion was successful, `False` otherwise. Example: ------- ```python # request document notes notes = await paperless.documents.notes(42) for note in notes: if await note.delete(): print("Successfully deleted the note!") ``` """ params = { "id": self.id, } async with self._api.request("delete", self._api_path, params=params) as res: return res.status in {200, 204} # backward compatibility @dataclass(kw_only=True) class DocumentNoteDraft(PaperlessModel, models.CreatableMixin): """Represent a new Paperless `DocumentNote`, which is not stored in Paperless.""" _api_path = API_PATH["documents_notes"] _create_required_fields = {"note", "document"} note: str | None = None document: int | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `DocumentNote` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("document")) @dataclass(init=False) class DocumentMeta(PaperlessModel): """Represent a Paperless `Document`s metadata.""" _api_path = API_PATH["documents_meta"] id: int | None = None original_checksum: str | None = None original_size: int | None = None original_mime_type: str | None = None media_filename: str | None = None has_archive_version: bool | None = None original_metadata: list[DocumentMetadataType] | None = None archive_checksum: str | None = None archive_media_filename: str | None = None original_filename: str | None = None lang: str | None = None archive_size: int | None = None archive_metadata: list[DocumentMetadataType] | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `DocumentMeta` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @dataclass(init=False) class DownloadedDocument(PaperlessModel): """Represent a Paperless `Document`s downloaded file.""" _api_path = API_PATH["documents"] id: int | None = None mode: RetrieveFileMode | None = None original: bool | None = None content: bytes | None = None content_type: str | None = None disposition_filename: str | None = None disposition_type: str | None = None async def load(self) -> None: """Get `raw data` from DRF.""" self._api_path = self._api_path.format(pk=self._data.get("id")) params = { "original": "true" if self._data.get("original", False) else "false", } async with self._api.request("get", self._api_path, params=params) as res: self._data.update( { "content": await res.read(), "content_type": res.content_type, } ) if res.content_disposition is not None: self._data.update( { "disposition_filename": res.content_disposition.filename, "disposition_type": res.content_disposition.type, } ) self._set_dataclass_fields() self._fetched = True @dataclass(init=False) class DocumentSuggestions(PaperlessModel): """Represent a Paperless `Document` suggestions.""" _api_path = API_PATH["documents_suggestions"] id: int | None = None correspondents: list[int] | None = None tags: list[int] | None = None document_types: list[int] | None = None storage_paths: list[int] | None = None dates: list[datetime.date] | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `DocumentSuggestions` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) class DocumentSuggestionsHelper(HelperBase): """Represent a factory for Paperless `DocumentSuggestions` models.""" _api_path = API_PATH["documents_suggestions"] _resource = PaperlessResource.DOCUMENTS _resource_cls = DocumentSuggestions async def __call__(self, pk: int) -> DocumentSuggestions: """Request exactly one resource item.""" data = { "id": pk, } item = self._resource_cls.create_with_data(self._api, data) await item.load() return item class DocumentSubHelperBase(HelperBase): """Represent a factory for Paperless `DownloadedDocument` models.""" _api_path = API_PATH["documents_suggestions"] _resource = PaperlessResource.DOCUMENTS _resource_cls = DownloadedDocument async def __call__( self, pk: int, mode: RetrieveFileMode, api_path: str, *, original: bool, ) -> DownloadedDocument: """Request exactly one resource item.""" data = { "id": pk, "mode": mode, "original": original, } item = self._resource_cls.create_with_data(self._api, data) item._api_path = api_path # noqa: SLF001 await item.load() return item class DocumentFileDownloadHelper(DocumentSubHelperBase): """Represent a factory for Paperless `DownloadedDocument` models.""" _api_path = API_PATH["documents_download"] async def __call__( # type: ignore[override] self, pk: int, *, original: bool = False, ) -> DownloadedDocument: """Request exactly one resource item.""" return await super().__call__( pk, RetrieveFileMode.DOWNLOAD, self._api_path, original=original ) class DocumentFilePreviewHelper(DocumentSubHelperBase): """Represent a factory for Paperless `DownloadedDocument` models.""" _api_path = API_PATH["documents_preview"] async def __call__( # type: ignore[override] self, pk: int, *, original: bool = False, ) -> DownloadedDocument: """Request exactly one resource item.""" return await super().__call__( pk, RetrieveFileMode.PREVIEW, self._api_path, original=original ) class DocumentFileThumbnailHelper(DocumentSubHelperBase): """Represent a factory for Paperless `DownloadedDocument` models.""" _api_path = API_PATH["documents_thumbnail"] async def __call__( # type: ignore[override] self, pk: int, *, original: bool = False, ) -> DownloadedDocument: """Request exactly one resource item.""" return await super().__call__( pk, RetrieveFileMode.THUMBNAIL, self._api_path, original=original ) class DocumentMetaHelper(HelperBase, helpers.CallableMixin[DocumentMeta]): """Represent a factory for Paperless `DocumentMeta` models.""" _api_path = API_PATH["documents_meta"] _resource = PaperlessResource.DOCUMENTS _resource_cls = DocumentMeta class DocumentNoteHelper(HelperBase): """Represent a factory for Paperless `DocumentNote` models.""" _api_path = API_PATH["documents_notes"] _resource = PaperlessResource.DOCUMENTS _resource_cls = DocumentNote def __init__(self, api: "Paperless", attached_to: int | None = None) -> None: """Initialize a `DocumentHelper` instance.""" super().__init__(api) self._attached_to = attached_to async def __call__( self, pk: int | None = None, ) -> list[DocumentNote]: """Request and return the documents `DocumentNote` list.""" doc_pk = self._get_document_pk(pk) res = await self._api.request_json("get", self._get_api_path(doc_pk)) # We have to transform data here slightly. # There are two major differences in the data depending on which endpoint is requested. # url: documents/{:pk}/ -> # .document -> int # .user -> int # url: documents/{:pk}/notes/ -> # .document -> does not exist (so we add it here) # .user -> dict(id=int, username=str, first_name=str, last_name=str) return [ self._resource_cls.create_with_data( self._api, { **item, "document": doc_pk, "user": item["user"]["id"] if self._api.host_api_version >= 8 else item["user"], }, fetched=True, ) for item in res ] def _get_document_pk(self, pk: int | None = None) -> int: """Return the attached document pk, or the parameter.""" if not any((self._attached_to, pk)): message = f"Accessing {type(self).__name__} data without a primary key." raise PrimaryKeyRequiredError(message) return cast("int", self._attached_to or pk) def _get_api_path(self, pk: int) -> str: """Return the formatted api path.""" return self._api_path.format(pk=pk) def draft(self, pk: int | None = None, **kwargs: Any) -> DocumentNoteDraft: """Return a fresh and empty `DocumentNoteDraft` instance. Example: ------- ```python draft = paperless.documents.notes.draft(...) # do something ``` """ kwargs.update({"document": self._get_document_pk(pk)}) return DocumentNoteDraft.create_with_data( self._api, data=kwargs, fetched=True, ) class DocumentHelper( HelperBase, helpers.SecurableMixin, helpers.CallableMixin[Document], helpers.DraftableMixin[DocumentDraft], helpers.IterableMixin[Document], ): """Represent a factory for Paperless `Document` models.""" _api_path = API_PATH["documents"] _resource = PaperlessResource.DOCUMENTS _draft_cls = DocumentDraft _resource_cls = Document def __init__(self, api: "Paperless") -> None: """Initialize a `DocumentHelper` instance.""" super().__init__(api) self._download = DocumentFileDownloadHelper(api) self._meta = DocumentMetaHelper(api) self._notes = DocumentNoteHelper(api) self._preview = DocumentFilePreviewHelper(api) self._suggestions = DocumentSuggestionsHelper(api) self._thumbnail = DocumentFileThumbnailHelper(api) @property def download(self) -> DocumentFileDownloadHelper: """Download the contents of an archived file. Example: ------- ```python # request document contents directly... download = await paperless.documents.download(42) # ... or by using an already fetched document doc = await paperless.documents(42) download = await doc.get_download() ``` """ return self._download @property def metadata(self) -> DocumentMetaHelper: """Return the attached `DocumentMetaHelper` instance. Example: ------- ```python # request metadata of a document directly... metadata = await paperless.documents.metadata(42) # ... or by using an already fetched document doc = await paperless.documents(42) metadata = await doc.get_metadata() ``` """ return self._meta @property def notes(self) -> DocumentNoteHelper: """Return the attached `DocumentNoteHelper` instance. Example: ------- ```python # request document notes directly... notes = await paperless.documents.notes(42) # ... or by using an already fetched document doc = await paperless.documents(42) notes = await doc.notes() ``` """ return self._notes @property def preview(self) -> DocumentFilePreviewHelper: """Preview the contents of an archived file. Example: ------- ```python # request document contents directly... download = await paperless.documents.preview(42) # ... or by using an already fetched document doc = await paperless.documents(42) download = await doc.get_preview() ``` """ return self._preview @property def suggestions(self) -> DocumentSuggestionsHelper: """Return the attached `DocumentSuggestionsHelper` instance. Example: ------- ```python # request document suggestions directly... suggestions = await paperless.documents.suggestions(42) # ... or by using an already fetched document doc = await paperless.suggestions(42) suggestions = await doc.get_suggestions() ``` """ return self._suggestions @property def thumbnail(self) -> DocumentFileThumbnailHelper: """Download the contents of a thumbnail file. Example: ------- ```python # request document contents directly... download = await paperless.documents.thumbnail(42) # ... or by using an already fetched document doc = await paperless.documents(42) download = await doc.get_thumbnail() ``` """ return self._thumbnail async def get_next_asn(self) -> int: """Request the next archive serial number from DRF.""" async with self._api.request("get", API_PATH["documents_next_asn"]) as res: try: res.raise_for_status() return int(await res.text()) except Exception as exc: raise AsnRequestError from exc async def more_like(self, pk: int) -> AsyncGenerator[Document]: """Lookup documents similar to the given document pk. Shortcut function. Same behaviour is possible using `reduce()`. Documentation: https://docs.paperless-ngx.com/api/#searching-for-documents """ async with self.reduce(more_like_id=pk): async for item in self: yield item async def search(self, query: str) -> AsyncGenerator[Document]: """Lookup documents by a search query. Shortcut function. Same behaviour is possible using `reduce()`. Documentation: https://docs.paperless-ngx.com/usage/#basic-usage_searching """ async with self.reduce(query=query): async for item in self: yield item paperless-api-5.1.0/pypaperless/models/generators/000077500000000000000000000000001505307417100222705ustar00rootroot00000000000000paperless-api-5.1.0/pypaperless/models/generators/__init__.py000066400000000000000000000001351505307417100244000ustar00rootroot00000000000000"""PyPaperless generators.""" from .page import PageGenerator __all__ = ("PageGenerator",) paperless-api-5.1.0/pypaperless/models/generators/page.py000066400000000000000000000041001505307417100235510ustar00rootroot00000000000000"""Provide the PageGenerator class.""" from collections.abc import AsyncIterator from copy import deepcopy from typing import TYPE_CHECKING, Any, Self from pypaperless.models.base import PaperlessBase from pypaperless.models.pages import Page if TYPE_CHECKING: from pypaperless import Paperless class PageGenerator(PaperlessBase, AsyncIterator): """Iterator for DRF paginated endpoints. `api`: An instance of :class:`Paperless`. `url`: A url returning DRF page contents. `resource`: A target resource model type for mapping results with. `params`: Optional dict of query string parameters. """ _page: Page | None def __aiter__(self) -> Self: """Return self as iterator.""" return self async def __anext__(self) -> Page: """Return next item from the current batch.""" if self._page is not None and self._page.is_last_page: raise StopAsyncIteration res = await self._api.request_json("get", self._url, params=self.params) data = { **res, "_api_path": self._url, "current_page": self.params["page"], "page_size": self.params["page_size"], } self._page = Page.create_with_data(self._api, data, fetched=True) # dirty attach the resource to the data class self._page._resource_cls = self._resource_cls # noqa: SLF001 # rise page by one to request next page on next iteration self.params["page"] += 1 # we do not reach this point without a self._page object, so: ignore type error return self._page def __init__( self, api: "Paperless", url: str, resource_cls: type, params: dict[str, Any] | None = None, ) -> None: """Initialize `PageGenerator` class instance.""" super().__init__(api) self._page = None self._resource_cls = resource_cls self._url = url self.params = deepcopy(params) if params else {} self.params.setdefault("page", 1) self.params.setdefault("page_size", 150) paperless-api-5.1.0/pypaperless/models/mails.py000066400000000000000000000061321505307417100216000ustar00rootroot00000000000000"""Provide `MailRule` related models and helpers.""" import datetime from dataclasses import dataclass from typing import TYPE_CHECKING, Any from pypaperless.const import API_PATH, PaperlessResource from .base import HelperBase, PaperlessModel from .mixins import helpers, models if TYPE_CHECKING: from pypaperless import Paperless @dataclass(init=False) class MailAccount(PaperlessModel, models.SecurableMixin): """Represent a Paperless `MailAccount`.""" _api_path = API_PATH["mail_accounts_single"] id: int | None = None name: str | None = None imap_server: str | None = None imap_port: int | None = None imap_security: int | None = None username: str | None = None # exclude that from the dataclass # password: str | None = None # noqa: ERA001 character_set: str | None = None is_token: bool | None = None account_type: int | None = None expiration: datetime.datetime | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `MailAccount` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @dataclass(init=False) class MailRule(PaperlessModel, models.SecurableMixin): """Represent a Paperless `MailRule`.""" _api_path = API_PATH["mail_rules_single"] id: int | None = None name: str | None = None account: int | None = None enabled: bool | None = None folder: str | None = None filter_from: str | None = None filter_to: str | None = None filter_subject: str | None = None filter_body: str | None = None filter_attachment_filename_include: str | None = None filter_attachment_filename_exclude: str | None = None maximum_age: int | None = None action: int | None = None action_parameter: str | None = None assign_title_from: int | None = None assign_tags: list[int] | None = None assign_correspondent_from: int | None = None assign_correspondent: int | None = None assign_document_type: int | None = None assign_owner_from_rule: bool | None = None order: int | None = None attachment_type: int | None = None consumption_scope: int | None = None pdf_layout: int | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `MailRule` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) class MailAccountHelper( HelperBase, helpers.CallableMixin[MailAccount], helpers.IterableMixin[MailAccount], helpers.SecurableMixin, ): """Represent a factory for Paperless `MailAccount` models.""" _api_path = API_PATH["mail_accounts"] _resource = PaperlessResource.MAIL_ACCOUNTS _resource_cls = MailAccount class MailRuleHelper( HelperBase, helpers.CallableMixin[MailRule], helpers.IterableMixin[MailRule], helpers.SecurableMixin, ): """Represent a factory for Paperless `MailRule` models.""" _api_path = API_PATH["mail_rules"] _resource = PaperlessResource.MAIL_RULES _resource_cls = MailRule paperless-api-5.1.0/pypaperless/models/mixins/000077500000000000000000000000001505307417100214265ustar00rootroot00000000000000paperless-api-5.1.0/pypaperless/models/mixins/__init__.py000066400000000000000000000000451505307417100235360ustar00rootroot00000000000000"""Mixins for PyPaperless models.""" paperless-api-5.1.0/pypaperless/models/mixins/helpers/000077500000000000000000000000001505307417100230705ustar00rootroot00000000000000paperless-api-5.1.0/pypaperless/models/mixins/helpers/__init__.py000066400000000000000000000004401505307417100251770ustar00rootroot00000000000000"""Mixins for PyPaperless helpers.""" from .callable import CallableMixin from .draftable import DraftableMixin from .iterable import IterableMixin from .securable import SecurableMixin __all__ = ( "CallableMixin", "DraftableMixin", "IterableMixin", "SecurableMixin", ) paperless-api-5.1.0/pypaperless/models/mixins/helpers/callable.py000066400000000000000000000017611505307417100252060ustar00rootroot00000000000000"""CallableMixin for PyPaperless helpers.""" from pypaperless.models.base import HelperProtocol, ResourceT class CallableMixin(HelperProtocol[ResourceT]): """Provide methods for calling a specific resource item.""" async def __call__( self, pk: int, *, lazy: bool = False, ) -> ResourceT: """Request exactly one resource item. Example: ------- ```python # request a document document = await paperless.documents(42) # initialize a model and request it later document = await paperless.documents(42, lazy=True) ``` """ data = { "id": pk, } item = self._resource_cls.create_with_data(self._api, data) # set requesting full permissions if getattr(self, "_request_full_perms", False): item._params.update({"full_perms": "true"}) # noqa: SLF001 if not lazy: await item.load() return item paperless-api-5.1.0/pypaperless/models/mixins/helpers/draftable.py000066400000000000000000000016241505307417100253710ustar00rootroot00000000000000"""DraftableMixin for PyPaperless helpers.""" from typing import Any from pypaperless.exceptions import DraftNotSupportedError from pypaperless.models.base import HelperProtocol, ResourceT class DraftableMixin(HelperProtocol[ResourceT]): """Provide the `draft` method for PyPaperless helpers.""" _draft_cls: type[ResourceT] def draft(self, **kwargs: Any) -> ResourceT: """Return a fresh and empty `PaperlessModel` instance. Example: ------- ```python draft = paperless.documents.draft(document=bytes(...), title="New Document") # do something ``` """ if not hasattr(self, "_draft_cls"): message = "Helper class has no _draft_cls attribute." raise DraftNotSupportedError(message) kwargs.update({"id": -1}) return self._draft_cls.create_with_data(self._api, data=kwargs, fetched=True) paperless-api-5.1.0/pypaperless/models/mixins/helpers/iterable.py000066400000000000000000000073361505307417100252420ustar00rootroot00000000000000"""IterableMixin for PyPaperless helpers.""" from collections.abc import AsyncGenerator, AsyncIterator from contextlib import asynccontextmanager from typing import TYPE_CHECKING, Any, Self from pypaperless.models.base import HelperProtocol, ResourceT from pypaperless.models.generators import PageGenerator if TYPE_CHECKING: from pypaperless.models import Page class IterableMixin(HelperProtocol[ResourceT]): """Provide methods for iterating over resource items.""" _aiter_filters: dict[str, str | int] | None async def __aiter__(self) -> AsyncIterator[ResourceT]: """Iterate over resource items. Example: ------- ```python async for item in paperless.documents: # do something ``` """ async for page in self.pages(): for item in page: yield item @asynccontextmanager async def reduce( self: Self, **kwargs: str | int, ) -> AsyncGenerator[Self]: """Provide context for iterating over resource items with query parameters. `kwargs`: Insert any Paperless api supported filter keywords here. You can provide `page` and `page_size` parameters, as well. Example: ------- ```python filters = { "page_size": 1337, "title__icontains": "2023", } async with paperless.documents.reduce(**filters): # iterate over resource items ... async for item in paperless.documents: ... # ... or iterate pages as-is async for page in paperless.documents.pages(): ... ``` """ self._aiter_filters = kwargs yield self self._aiter_filters = None async def all(self) -> list[int]: """Return a list of all resource item primary keys. When used within a `reduce` context, returns a list of filtered primary keys. """ page = await anext(self.pages(page=1)) return page.all async def as_dict(self) -> dict[int, ResourceT]: """Shortcut for returning a primary key/object dict of all resource items. When used within a `reduce` context, data is filtered. """ return {item.id: item async for item in self} # type: ignore[attr-defined] async def as_list(self) -> list[ResourceT]: """Shortcut for returning a list of all resource items. When used within a `reduce` context, data is filtered. """ return [item async for item in self] def pages( self, page: int = 1, page_size: int = 150, ) -> "AsyncIterator[Page[ResourceT]]": """Iterate over resource pages. `page`: A page number to start with. `page_size`: The page size for each requested batch. Example: ------- ```python async for item in paperless.documents.pages(): # do something ``` """ params: dict[str, Any] = getattr(self, "_aiter_filters", None) or {} for param, value in params.items(): if param.endswith("__in"): try: value.extend([]) # throw AttributeError if not a list params[param] = ",".join(map(str, value)) except AttributeError: # value is not a list, don't modify continue params.setdefault("page", page) params.setdefault("page_size", page_size) # set requesting full permissions if getattr(self, "_request_full_perms", False): params.update({"full_perms": "true"}) return PageGenerator(self._api, self._api_path, self._resource_cls, params=params) paperless-api-5.1.0/pypaperless/models/mixins/helpers/securable.py000066400000000000000000000013751505307417100254150ustar00rootroot00000000000000"""SecurableMixin for PyPaperless helpers.""" class SecurableMixin: """Provide the `request_full_permissions` property for PyPaperless helpers.""" _request_full_perms: bool = False @property def request_permissions(self) -> bool: """Return whether the helper requests items with the `permissions` table, or not. Documentation: https://docs.paperless-ngx.com/api/#permissions """ return self._request_full_perms @request_permissions.setter def request_permissions(self, value: bool) -> None: """Set whether the helper requests items with the `permissions` table, or not. Documentation: https://docs.paperless-ngx.com/api/#permissions """ self._request_full_perms = value paperless-api-5.1.0/pypaperless/models/mixins/models/000077500000000000000000000000001505307417100227115ustar00rootroot00000000000000paperless-api-5.1.0/pypaperless/models/mixins/models/__init__.py000066400000000000000000000006351505307417100250260ustar00rootroot00000000000000"""Mixins for PyPaperless models.""" from .creatable import CreatableMixin from .data_fields import MatchingFieldsMixin from .deletable import DeletableMixin from .securable import SecurableDraftMixin, SecurableMixin from .updatable import UpdatableMixin __all__ = ( "CreatableMixin", "DeletableMixin", "MatchingFieldsMixin", "SecurableDraftMixin", "SecurableMixin", "UpdatableMixin", ) paperless-api-5.1.0/pypaperless/models/mixins/models/creatable.py000066400000000000000000000041751505307417100252140ustar00rootroot00000000000000"""CreatableMixin for PyPaperless models.""" from typing import Any, cast from pypaperless.exceptions import DraftFieldRequiredError from pypaperless.models.base import PaperlessModelProtocol from pypaperless.models.utils import object_to_dict_value class CreatableMixin(PaperlessModelProtocol): """Provide the `save` method for PyPaperless models.""" _create_required_fields: set[str] async def save(self) -> int | str | tuple[int, int]: """Create a new `resource item` in Paperless. Return the created item `id`, or a `task_id` in case of documents. Example: ------- ```python draft = paperless.documents.draft(document=bytes(...)) draft.title = "Add a title" # request Paperless to store the new item draft.save() ``` """ self.validate() kwdict = self._serialize() res = await self._api.request_json("post", self._api_path, **kwdict) if type(self).__name__ == "DocumentNoteDraft": return ( cast("int", max(item.get("id") for item in res)), cast("int", kwdict["json"]["document"]), ) if isinstance(res, dict): return int(res["id"]) return str(res) def _serialize(self) -> dict[str, Any]: """Serialize.""" data = { "json": { field.name: object_to_dict_value(getattr(self, field.name)) for field in self._get_dataclass_fields() }, } # check for empty permissions as they will raise if None if "set_permissions" in data["json"] and data["json"]["set_permissions"] is None: del data["json"]["set_permissions"] return data def validate(self) -> None: """Check required fields before persisting the item to Paperless.""" missing = [field for field in self._create_required_fields if getattr(self, field) is None] if len(missing) == 0: return message = f"Missing fields for saving a `{type(self).__name__}`: {', '.join(missing)}." raise DraftFieldRequiredError(message) paperless-api-5.1.0/pypaperless/models/mixins/models/data_fields.py000066400000000000000000000005761505307417100255320ustar00rootroot00000000000000"""PermissionFieldsMixin for PyPaperless models.""" from dataclasses import dataclass from pypaperless.models.common import MatchingAlgorithmType @dataclass class MatchingFieldsMixin: """Provide shared matching fields for PyPaperless models.""" match: str | None = None matching_algorithm: MatchingAlgorithmType | None = None is_insensitive: bool | None = None paperless-api-5.1.0/pypaperless/models/mixins/models/deletable.py000066400000000000000000000013631505307417100252070ustar00rootroot00000000000000"""DeletableMixin for PyPaperless models.""" from pypaperless.models.base import PaperlessModelProtocol class DeletableMixin(PaperlessModelProtocol): """Provide the `delete` method for PyPaperless models.""" async def delete(self) -> bool: """Delete a `resource item` from DRF. There is no point of return. Return `True` when deletion was successful, `False` otherwise. Example: ------- ```python # request a document document = await paperless.documents(42) if await document.delete(): print("Successfully deleted the document!") ``` """ async with self._api.request("delete", self._api_path) as res: return res.status == 204 paperless-api-5.1.0/pypaperless/models/mixins/models/securable.py000066400000000000000000000013461505307417100252340ustar00rootroot00000000000000"""SecurableMixin for PyPaperless models.""" from dataclasses import dataclass from pypaperless.models.common import PermissionTableType @dataclass(kw_only=True) class SecurableMixin: """Provide permission fields for PyPaperless models.""" owner: int | None = None user_can_change: bool | None = None permissions: PermissionTableType | None = None @property def has_permissions(self) -> bool: """Return if the model data includes the permission field.""" return self.permissions is not None @dataclass(kw_only=True) class SecurableDraftMixin: """Provide permission fields for PyPaperless draft models.""" owner: int | None = None set_permissions: PermissionTableType | None = None paperless-api-5.1.0/pypaperless/models/mixins/models/updatable.py000066400000000000000000000051211505307417100252230ustar00rootroot00000000000000"""UpdatableMixin for PyPaperless models.""" from copy import deepcopy from typing import Any from pypaperless.models.base import PaperlessModelProtocol from pypaperless.models.utils import object_to_dict_value from .securable import SecurableMixin class UpdatableMixin(PaperlessModelProtocol): """Provide the `update` method for PyPaperless models.""" _data: dict[str, Any] async def update(self, *, only_changed: bool = True) -> bool: """Send actually changed `model data` to DRF. Return `True` when any attribute was updated, `False` otherwise. Example: ------- ```python # request a document document = await paperless.documents(42) document.title = "New Title" if await document.update(): print("Successfully updated a field!") ``` """ updated = False if only_changed: updated = await self._patch_fields() else: updated = await self._put_fields() self._set_dataclass_fields() return updated def _check_permissions_field(self, data: dict) -> None: """Check.""" if SecurableMixin not in type(self).__bases__: return if not self.has_permissions: # type: ignore[attr-defined] return if "permissions" in data: data["set_permissions"] = deepcopy(data["permissions"]) del data["permissions"] async def _patch_fields(self) -> bool: """Use the http `PATCH` method for updating only changed fields.""" changed = {} for field in self._get_dataclass_fields(): new_value = object_to_dict_value(getattr(self, field.name)) if field.name in self._data and new_value != self._data[field.name]: changed[field.name] = new_value if len(changed) == 0: return False self._check_permissions_field(changed) self._data = await self._api.request_json( "patch", self._api_path, json=changed, params=self._params, ) return True async def _put_fields(self) -> bool: """Use the http `PUT` method to replace all fields.""" data = { field.name: object_to_dict_value(getattr(self, field.name)) for field in self._get_dataclass_fields() } self._check_permissions_field(data) self._data = await self._api.request_json( "put", self._api_path, json=data, params=self._params, ) return True paperless-api-5.1.0/pypaperless/models/pages.py000066400000000000000000000051051505307417100215710ustar00rootroot00000000000000"""Provide the `Paginated` class.""" import math from collections.abc import Iterator from dataclasses import dataclass, field from typing import Any, Generic from pypaperless.const import API_PATH from .base import PaperlessModel, ResourceT @dataclass(init=False) class Page(PaperlessModel, Generic[ResourceT]): # noqa: UP046 """Represent a Paperless DRF `Paginated`.""" _api_path = API_PATH["index"] _resource_cls: type[ResourceT] # our fields current_page: int page_size: int # DRF fields count: int next: str | None = None previous: str | None = None all: list[int] = field(default_factory=list) results: list[dict[str, Any]] = field(default_factory=list) def __iter__(self) -> Iterator[ResourceT]: """Return iter of `.items`.""" return iter(self.items) @property def current_count(self) -> int: """Return the item count of the current page.""" return len(self.results) @property def has_next_page(self) -> bool: """Return whether there is a next page or not.""" return self.next_page is not None @property def has_previous_page(self) -> bool: """Return whether there is a previous page or not.""" return self.previous_page is not None @property def items(self) -> list[ResourceT]: """Return the results list field with mapped PyPaperless `models`. Example: ------- ```python async for page in paperless.documents.pages(): assert isinstance(page.results.pop(), Document) # fails, it is a dict assert isinstance(page.items.pop(), Document) # ok ``` """ def mapper(data: dict[str, Any]) -> ResourceT: return self._resource_cls.create_with_data(self._api, data, fetched=True) return list(map(mapper, self._data["results"])) @property def is_last_page(self) -> bool: """Return whether we are on the last page or not.""" return not self.has_next_page @property def last_page(self) -> int: """Return the last page number.""" return math.ceil(self.count / self.page_size) @property def next_page(self) -> int | None: """Return the next page number if a next page exists.""" if self.next is None: return None return self.current_page + 1 @property def previous_page(self) -> int | None: """Return the previous page number if a previous page exists.""" if self.previous is None: return None return self.current_page - 1 paperless-api-5.1.0/pypaperless/models/permissions.py000066400000000000000000000042631505307417100230510ustar00rootroot00000000000000"""Provide `User` and 'Group' related models and helpers.""" import datetime from dataclasses import dataclass from typing import TYPE_CHECKING, Any from pypaperless.const import API_PATH, PaperlessResource from .base import HelperBase, PaperlessModel from .mixins import helpers if TYPE_CHECKING: from pypaperless import Paperless @dataclass(init=False) class Group(PaperlessModel): """Represent a Paperless `Group`.""" _api_path = API_PATH["groups_single"] id: int name: str | None = None permissions: list[str] | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `Group` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @dataclass(init=False) class User(PaperlessModel): """Represent a Paperless `User`.""" _api_path = API_PATH["users_single"] id: int username: str | None = None # exclude that from the dataclass # password: str | None = None # noqa: ERA001 email: str | None = None first_name: str | None = None last_name: str | None = None date_joined: datetime.datetime | None = None is_staff: bool | None = None is_active: bool | None = None is_superuser: bool | None = None groups: list[int] | None = None user_permissions: list[str] | None = None inherited_permissions: list[str] | None = None is_mfa_enabled: bool | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `User` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) class GroupHelper( HelperBase, helpers.CallableMixin[Group], helpers.IterableMixin[Group], ): """Represent a factory for Paperless `Group` models.""" _api_path = API_PATH["groups"] _resource = PaperlessResource.GROUPS _resource_cls = Group class UserHelper( HelperBase, helpers.CallableMixin[User], helpers.IterableMixin[User], ): """Represent a factory for Paperless `User` models.""" _api_path = API_PATH["users"] _resource = PaperlessResource.USERS _resource_cls = User paperless-api-5.1.0/pypaperless/models/remote_version.py000066400000000000000000000016221505307417100235320ustar00rootroot00000000000000"""Provide `Remote Version` related models and helpers.""" from dataclasses import dataclass from pypaperless.const import API_PATH, PaperlessResource from .base import HelperBase, PaperlessModel @dataclass(init=False) class RemoteVersion(PaperlessModel): """Represent Paperless `Remote Version`.""" _api_path = API_PATH["remote_version"] version: str | None = None update_available: bool | None = None class RemoteVersionHelper(HelperBase): """Represent a factory for Paperless `Remote Version` models.""" _api_path = API_PATH["remote_version"] _resource = PaperlessResource.REMOTE_VERSION _resource_cls = RemoteVersion async def __call__(self) -> RemoteVersion: """Request the `Remote Version` model data.""" res = await self._api.request_json("get", self._api_path) return self._resource_cls.create_with_data(self._api, res, fetched=True) paperless-api-5.1.0/pypaperless/models/saved_views.py000066400000000000000000000027111505307417100230110ustar00rootroot00000000000000"""Provide `SavedView` related models and helpers.""" from dataclasses import dataclass from typing import TYPE_CHECKING, Any from pypaperless.const import API_PATH, PaperlessResource from .base import HelperBase, PaperlessModel from .common import SavedViewFilterRuleType from .mixins import helpers, models if TYPE_CHECKING: from pypaperless import Paperless @dataclass(init=False) class SavedView(PaperlessModel, models.SecurableMixin): """Represent a Paperless `SavedView`.""" _api_path = API_PATH["saved_views_single"] id: int | None = None name: str | None = None show_on_dashboard: bool | None = None show_in_sidebar: bool | None = None sort_field: str | None = None sort_reverse: bool | None = None filter_rules: list[SavedViewFilterRuleType] | None = None page_size: int | None = None display_mode: str | None = None display_fields: list[str] | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `SavedView` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) class SavedViewHelper( HelperBase, helpers.CallableMixin[SavedView], helpers.IterableMixin[SavedView], helpers.SecurableMixin, ): """Represent a factory for Paperless `SavedView` models.""" _api_path = API_PATH["saved_views"] _resource = PaperlessResource.SAVED_VIEWS _resource_cls = SavedView paperless-api-5.1.0/pypaperless/models/share_links.py000066400000000000000000000034501505307417100227750ustar00rootroot00000000000000"""Provide `ShareLink` related models and helpers.""" import datetime from dataclasses import dataclass from typing import TYPE_CHECKING, Any from pypaperless.const import API_PATH, PaperlessResource from .base import HelperBase, PaperlessModel from .common import ShareLinkFileVersionType from .mixins import helpers, models if TYPE_CHECKING: from pypaperless import Paperless @dataclass(init=False) class ShareLink( PaperlessModel, models.DeletableMixin, models.UpdatableMixin, ): """Represent a Paperless `ShareLink`.""" _api_path = API_PATH["share_links_single"] id: int created: datetime.datetime | None = None expiration: datetime.datetime | None = None slug: str | None = None document: int | None = None file_version: ShareLinkFileVersionType | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `ShareLink` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @dataclass(init=False) class ShareLinkDraft(PaperlessModel, models.CreatableMixin): """Represent a new Paperless `ShareLink`, which is not stored in Paperless.""" _api_path = API_PATH["share_links"] _create_required_fields = {"document", "file_version"} expiration: datetime.datetime | None = None document: int | None = None file_version: ShareLinkFileVersionType | None = None class ShareLinkHelper( HelperBase, helpers.CallableMixin[ShareLink], helpers.DraftableMixin[ShareLinkDraft], helpers.IterableMixin[ShareLink], ): """Represent a factory for Paperless `ShareLink` models.""" _api_path = API_PATH["share_links"] _resource = PaperlessResource.SHARE_LINKS _draft_cls = ShareLinkDraft _resource_cls = ShareLink paperless-api-5.1.0/pypaperless/models/statistics.py000066400000000000000000000024421505307417100226650ustar00rootroot00000000000000"""Provide `Statistics` related models and helpers.""" from dataclasses import dataclass from pypaperless.const import API_PATH, PaperlessResource from .base import HelperBase, PaperlessModel from .common import StatisticDocumentFileTypeCount @dataclass(init=False) class Statistic(PaperlessModel): """Represent Paperless `Statistic`.""" _api_path = API_PATH["statistics"] documents_total: int | None = None documents_inbox: int | None = None inbox_tag: int | None = None inbox_tags: list[int] | None = None document_file_type_counts: list[StatisticDocumentFileTypeCount] | None = None character_count: int | None = None tag_count: int | None = None correspondent_count: int | None = None document_type_count: int | None = None storage_path_count: int | None = None current_asn: int | None = None class StatisticHelper(HelperBase): """Represent a factory for Paperless `Statistic` models.""" _api_path = API_PATH["statistics"] _resource = PaperlessResource.STATISTICS _resource_cls = Statistic async def __call__(self) -> Statistic: """Request the `Statistic` model data.""" res = await self._api.request_json("get", self._api_path) return self._resource_cls.create_with_data(self._api, res, fetched=True) paperless-api-5.1.0/pypaperless/models/status.py000066400000000000000000000033471505307417100220230ustar00rootroot00000000000000"""Provide `Status` related models and helpers.""" from dataclasses import dataclass from typing import cast from pypaperless.const import API_PATH, PaperlessResource from pypaperless.models.common import ( StatusDatabaseType, StatusStorageType, StatusTasksType, StatusType, ) from .base import HelperBase, PaperlessModel @dataclass(init=False) class Status(PaperlessModel): """Represent a Paperless `Status`.""" _api_path = API_PATH["status"] pngx_version: str | None = None server_os: str | None = None install_type: str | None = None storage: StatusStorageType | None = None database: StatusDatabaseType | None = None tasks: StatusTasksType | None = None @property def has_errors(self) -> bool: """Return whether any status flag is `ERROR`.""" statuses: list[StatusType] = [ self.database.status if self.database and self.database.status else StatusType.OK, *[ cast("StatusType", getattr(self.tasks, status, StatusType.OK)) for status in ( "redis_status", "celery_status", "classifier_status", ) if self.tasks ], ] return any(st == StatusType.ERROR for st in statuses) class StatusHelper(HelperBase): """Represent a factory for the Paperless `Status` model.""" _api_path = API_PATH["status"] _resource = PaperlessResource.STATUS _resource_cls = Status async def __call__(self) -> Status: """Request the `Status` model data.""" res = await self._api.request_json("get", self._api_path) return self._resource_cls.create_with_data(self._api, res, fetched=True) paperless-api-5.1.0/pypaperless/models/tasks.py000066400000000000000000000052701505307417100216220ustar00rootroot00000000000000"""Provide `Task` related models and helpers.""" from collections.abc import AsyncIterator from dataclasses import dataclass from typing import TYPE_CHECKING, Any from pypaperless.const import API_PATH, PaperlessResource from pypaperless.exceptions import TaskNotFoundError from .base import HelperBase, PaperlessModel from .common import TaskStatusType if TYPE_CHECKING: from pypaperless import Paperless @dataclass(init=False) class Task(PaperlessModel): """Represent a Paperless `Task`.""" _api_path = API_PATH["tasks_single"] id: int | None = None task_id: str | None = None task_file_name: str | None = None date_created: str | None = None date_done: str | None = None type: str | None = None status: TaskStatusType | None = None result: str | None = None acknowledged: bool | None = None related_document: int | None = None owner: int | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `Task` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) class TaskHelper(HelperBase): """Represent a factory for Paperless `Task` models.""" _api_path = API_PATH["tasks"] _resource = PaperlessResource.TASKS _resource_cls = Task async def __aiter__(self) -> AsyncIterator[Task]: """Iterate over task items. Example: ------- ```python async for task in paperless.tasks: # do something ``` """ res = await self._api.request_json("get", self._api_path) for data in res: yield self._resource_cls.create_with_data(self._api, data, fetched=True) async def __call__(self, task_id: int | str) -> Task: """Request exactly one task by id. If task_id is `str`: interpret it as a task uuid. If task_id is `int`: interpret it as a primary key. Example: ------- ```python task = await paperless.tasks("uuid-string") task = await paperless.tasks(1337) ``` """ if isinstance(task_id, str): params = { "task_id": task_id, } res = await self._api.request_json("get", self._api_path, params=params) try: item = self._resource_cls.create_with_data(self._api, res.pop(), fetched=True) except IndexError as exc: raise TaskNotFoundError(task_id) from exc else: data = { "id": task_id, } item = self._resource_cls.create_with_data(self._api, data) await item.load() return item paperless-api-5.1.0/pypaperless/models/utils/000077500000000000000000000000001505307417100212575ustar00rootroot00000000000000paperless-api-5.1.0/pypaperless/models/utils/__init__.py000066400000000000000000000174531505307417100234020ustar00rootroot00000000000000"""Utils for pypaperless models. Since there are common use-cases in transforming dicts to dataclass et vice-versa, we borrowed some snippets from aiohue instead of re-inventing the wheel. pypaperless is meant to be the api library for a Home Assistant integration, so it should be okay I think. https://github.com/home-assistant-libs/aiohue/ Thanks for the excellent work, guys! """ # mypy: ignore-errors # pylint: disable=all import logging from dataclasses import MISSING, asdict, fields, is_dataclass from datetime import date, datetime from enum import Enum from types import NoneType, UnionType from typing import TYPE_CHECKING, Any, Union, get_args, get_origin, get_type_hints import pypaperless.models.base as paperless_base if TYPE_CHECKING: from pypaperless import Paperless def _str_to_datetime(datetimestr: str) -> datetime: """Parse datetime from string.""" return datetime.fromisoformat(datetimestr.replace("Z", "+00:00")) # noqa: FURB162 def _str_to_date(datestr: str) -> date: """Parse date from string.""" return datetime.fromisoformat(datestr).date() def _dateobj_to_str(value: date | datetime) -> str: """Parse string from date objects.""" return value.isoformat().replace("+00:00", "Z") def _is_typeddict(cls: type) -> bool: """Check whether a type is a `TypedDict` or not.""" return ( isinstance(cls, type) and issubclass(cls, dict) and hasattr(cls, "__annotations__") and getattr(cls, "__total__", None) is not None ) def object_to_dict_value(value: Any) -> Any: """Convert object values to their correspondending json values.""" if isinstance(value, dict): return {k: object_to_dict_value(v) for k, v in value.items()} if isinstance(value, list): return [object_to_dict_value(item) for item in value] if isinstance(value, Enum): return value.value if isinstance(value, (date, datetime)): return _dateobj_to_str(value) if isinstance(value, paperless_base.PaperlessModelData): return object_to_dict_value(value.serialize()) if is_dataclass(value): return object_to_dict_value(asdict(value)) return value def dict_value_to_object( # noqa: C901, PLR0915 name: str, value: Any, value_type: Any, default: Any = MISSING, _api: "Paperless | None" = None, ) -> Any: """Try to parse a value from raw (json) data and type annotations. Since there are common use-cases in transforming dicts to dataclass et vice-versa, we borrowed some snippets from aiohue instead of re-inventing the wheel. pypaperless is meant to be the api library for a Home Assistant integration, so it should be okay I think. https://github.com/home-assistant-libs/aiohue/ """ # pypaperless addition try: is_paperless_model = _api is not None and issubclass( value_type, paperless_base.PaperlessModel, ) except TypeError: # happens if value_type is not a class is_paperless_model = False try: is_paperless_data = _api is not None and issubclass( value_type, paperless_base.PaperlessModelData, ) except TypeError: # happens if value_type is not a class is_paperless_data = False # ruff: noqa: PLR0911, PLR0912 if isinstance(value_type, str): # this shouldn't happen, but just in case value_type = get_type_hints(value_type, globals(), locals()) if is_paperless_data: # create class instance if its custom data return value_type.unserialize(api=_api, data=value) if isinstance(value, dict): # always prefer classes that have a from_dict if hasattr(value_type, "from_dict"): return value_type.from_dict(value) # pypaperless addition for typeddicts if _is_typeddict(value_type): return value if value is None and not isinstance(default, type(MISSING)): return default if value is None and value_type is NoneType: return None if is_dataclass(value_type) and isinstance(value, dict): if is_paperless_model: return value_type.create_with_data(api=_api, data=value, fetched=True) return value_type( **{ field.name: dict_value_to_object( f"{value_type.__name__}.{field.name}", value.get(field.name), field.type, field.default, _api, ) for field in fields(value_type) } ) # get origin value type and inspect one-by-one origin: Any = get_origin(value_type) if origin in (list, tuple, set) and isinstance(value, list | tuple | set): return origin( dict_value_to_object(name, subvalue, get_args(value_type)[0], _api=_api) for subvalue in value if subvalue is not None ) # handle dictionary where we should inspect all values if origin is dict: subkey_type = get_args(value_type)[0] subvalue_type = get_args(value_type)[1] return { dict_value_to_object(subkey, subkey, subkey_type, _api=_api): dict_value_to_object( f"{subkey}.value", subvalue, subvalue_type, _api=_api ) for subkey, subvalue in value.items() } # handle Union type if origin is Union or origin is UnionType: # try all possible types sub_value_types = get_args(value_type) for sub_arg_type in sub_value_types: if value is NoneType and sub_arg_type is NoneType: return value if value == {} and sub_arg_type is NoneType: # handle case where optional value is received as empty dict from api return None # try them all until one succeeds try: return dict_value_to_object(name, value, sub_arg_type, _api=_api) except (KeyError, TypeError, ValueError): pass # if we get to this point, all possibilities failed # find out if we should raise or log this err = ( f"Value {value} of type {type(value)} is invalid for {name}, " f"expected value of type {value_type}" ) if NoneType not in sub_value_types: # raise exception, we have no idea how to handle this value raise TypeError(err) # failed to parse the (sub) value but None allowed, log only logging.getLogger(__name__).warning(err) return None if origin is type: return get_type_hints(value, globals(), locals()) # handle Any as value type (which is basically unprocessable) if value_type is Any: return value # raise if value is None and the value is required according to annotations if value is None and value_type is not NoneType: message = f"`{name}` of type `{value_type}` is required." raise KeyError(message) try: if issubclass(value_type, Enum): return value_type(value) if issubclass(value_type, datetime): return _str_to_datetime(value) if issubclass(value_type, date): return _str_to_date(value) except TypeError: # happens if value_type is not a class pass # common type conversions (e.g. int as string) if value_type is float and isinstance(value, int): return float(value) if value_type is int and isinstance(value, str) and value.isnumeric(): return int(value) # If we reach this point, we could not match the value with the type and we raise if not isinstance(value, value_type): message = f"Value {value} of type {type(value)} is invalid for {name}, \ expected value of type {value_type}" raise TypeError(message) return value paperless-api-5.1.0/pypaperless/models/workflows.py000066400000000000000000000130161505307417100225270ustar00rootroot00000000000000"""Provide `Workflow` related models and helpers.""" from dataclasses import dataclass from typing import TYPE_CHECKING, Any from pypaperless.const import API_PATH, PaperlessResource from .base import HelperBase, PaperlessModel from .common import WorkflowActionType, WorkflowTriggerSourceType, WorkflowTriggerType from .mixins import helpers, models if TYPE_CHECKING: from pypaperless import Paperless @dataclass(init=False) class WorkflowAction(PaperlessModel): """Represent a Paperless `WorkflowAction`.""" _api_path = API_PATH["workflow_actions_single"] id: int | None = None type: WorkflowActionType | None = None assign_title: str | None = None assign_tags: list[int] | None = None assign_correspondent: int | None = None assign_document_type: int | None = None assign_storage_path: int | None = None assign_view_users: list[int] | None = None assign_view_groups: list[int] | None = None assign_change_users: list[int] | None = None assign_change_groups: list[int] | None = None assign_custom_fields: list[int] | None = None assign_custom_fields_values: dict[str, Any] | None = None remove_all_tags: bool | None = None remove_tags: list[int] | None = None remove_all_correspondents: bool | None = None remove_correspondents: list[int] | None = None remove_all_document_types: bool | None = None remove_document_types: list[int] | None = None remove_all_storage_paths: bool | None = None remove_storage_paths: list[int] | None = None remove_custom_fields: list[int] | None = None remove_all_custom_fields: bool | None = None remove_all_owners: bool | None = None remove_all_permissions: bool | None = None remove_view_users: list[int] | None = None remove_view_groups: list[int] | None = None remove_change_users: list[int] | None = None remove_change_groups: list[int] | None = None email: int | None = None webhook: dict[str, Any] | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `Workflow` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @dataclass(init=False) class WorkflowTrigger(PaperlessModel, models.MatchingFieldsMixin): """Represent a Paperless `WorkflowTrigger`.""" _api_path = API_PATH["workflow_triggers_single"] id: int | None = None sources: list[WorkflowTriggerSourceType] | None = None type: WorkflowTriggerType | None = None filter_path: str | None = None filter_filename: str | None = None filter_mailrule: int | None = None filter_has_tags: list[int] | None = None filter_has_correspondent: int | None = None filter_has_document_type: int | None = None schedule_offset_days: int | None = None schedule_is_recurring: bool | None = None schedule_recurring_interval_days: int | None = None schedule_date_field: str | None = None schedule_date_custom_field: int | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `Workflow` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) @dataclass(init=False) class Workflow(PaperlessModel): """Represent a Paperless `Workflow`.""" _api_path = API_PATH["workflows_single"] id: int | None = None name: str | None = None order: int | None = None enabled: bool | None = None actions: list[WorkflowAction] | None = None triggers: list[WorkflowTrigger] | None = None def __init__(self, api: "Paperless", data: dict[str, Any]) -> None: """Initialize a `Workflow` instance.""" super().__init__(api, data) self._api_path = self._api_path.format(pk=data.get("id")) class WorkflowActionHelper( HelperBase, helpers.CallableMixin[WorkflowAction], helpers.IterableMixin[WorkflowAction], ): """Represent a factory for Paperless `WorkflowAction` models.""" _api_path = API_PATH["workflow_actions"] _resource = PaperlessResource.WORKFLOW_ACTIONS _resource_cls = WorkflowAction class WorkflowTriggerHelper( HelperBase, helpers.CallableMixin[WorkflowTrigger], helpers.IterableMixin[WorkflowTrigger], ): """Represent a factory for Paperless `WorkflowTrigger` models.""" _api_path = API_PATH["workflow_triggers"] _resource = PaperlessResource.WORKFLOW_TRIGGERS _resource_cls = WorkflowTrigger class WorkflowHelper( HelperBase, helpers.CallableMixin[Workflow], helpers.IterableMixin[Workflow], ): """Represent a factory for Paperless `Workflow` models.""" _api_path = API_PATH["workflows"] _resource = PaperlessResource.WORKFLOWS _resource_cls = Workflow def __init__(self, api: "Paperless") -> None: """Initialize a `WorkflowHelper` instance.""" super().__init__(api) self._actions = WorkflowActionHelper(api) self._triggers = WorkflowTriggerHelper(api) @property def actions(self) -> WorkflowActionHelper: """Return the attached `WorkflowActionHelper` instance. Example: ------- ```python wf_action = await paperless.workflows.actions(5) ``` """ return self._actions @property def triggers(self) -> WorkflowTriggerHelper: """Return the attached `WorkflowTriggerHelper` instance. Example: ------- ```python wf_trigger = await paperless.workflows.triggers(23) ``` """ return self._triggers paperless-api-5.1.0/pypaperless/py.typed000066400000000000000000000000001505307417100203210ustar00rootroot00000000000000paperless-api-5.1.0/pyproject.toml000066400000000000000000000067311505307417100172100ustar00rootroot00000000000000[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "pypaperless" version = "0.0.0" license = {text = "MIT"} description = "Little api client for paperless(-ngx)." readme = "README.md" authors = [ {name = "Tobias Schulz", email = "public.dev@tbsch.de"} ] keywords = ["library", "async", "api-client", "python3", "paperless-ngx"] classifiers = [ "Development Status :: 5 - Production/Stable", "Framework :: AsyncIO", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Libraries :: Python Modules" ] requires-python = ">=3.13, <3.14" dependencies = [ "aiohttp>=3.12.15", "yarl>=1.20.1", ] [dependency-groups] dev = [ "aioresponses>=0.7.8", "codespell>=2.4.1", "covdefaults>=2.3.0", "coverage>=7.10.5", "mypy>=1.17.1", "pre-commit>=4.3.0", "pre-commit-hooks>=6.0.0", "pylint>=3.3.8", "pytest>=8.4.1", "pytest-aiohttp>=1.1.0", "pytest-asyncio>=1.1.0", "pytest-cov>=6.2.1", "ruff>=0.12.10", "yamllint>=1.37.1", ] [project.urls] "Homepage" = "https://github.com/tb1337/paperless-api" "Source Code" = "https://github.com/tb1337/paperless-api" "Bug Reports" = "https://github.com/tb1337/paperless-api/issues" "Coverage" = "https://codecov.io/gh/tb1337/paperless-api" [tool.coverage.run] plugins = ["covdefaults"] source = ["pypaperless"] [tool.coverage.report] fail_under = 95 omit = [ "pypaperless/models/utils/__init__.py" ] show_missing = true [tool.mypy] platform = "linux" python_version = "3.13" follow_imports = "normal" ignore_missing_imports = true check_untyped_defs = true disallow_any_generics = false disallow_incomplete_defs = true disallow_subclassing_any = true disallow_untyped_calls = true disallow_untyped_decorators = true disallow_untyped_defs = true no_implicit_optional = true show_error_codes = true warn_incomplete_stub = true warn_no_return = true warn_redundant_casts = true warn_return_any = true warn_unreachable = true warn_unused_configs = true warn_unused_ignores = true [tool.pylint.MASTER] ignore = [ "tests/", ] [tool.pylint.BASIC] good-names = [ "_", "ex", "fp", "i", "id", "j", "k", "on", "Run", "T", "wv", ] [tool.pylint."MESSAGES CONTROL"] disable = [ "duplicate-code", "no-name-in-module", # currently throws "too-few-public-methods", "too-many-ancestors", "too-many-arguments", "too-many-instance-attributes", "too-many-public-methods", ] [tool.pylint.SIMILARITIES] ignore-imports = true [tool.pylint.FORMAT] max-line-length = 100 [tool.pylint.DESIGN] max-attributes = 20 [tool.pytest.ini_options] addopts = "--cov --cov-report=term --cov-report=xml" asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "session" [tool.ruff] line-length = 100 target-version = "py313" [tool.ruff.lint] ignore = [ "ANN401", # Opinioated warning on disallowing dynamically typed expressions "D203", # Conflicts with other rules "D213", # Conflicts with other rules "D417", # False positives in some occasions "PLR2004", # Just annoying, not really useful "RUF012", # Just annoying # Conflicts with the Ruff formatter "COM812", "ISC001", ] select = ["ALL"] [tool.ruff.lint.flake8-pytest-style] fixture-parentheses = false mark-parentheses = false [tool.ruff.lint.isort] known-first-party = ["pypaperless"] [tool.ruff.lint.mccabe] max-complexity = 25 paperless-api-5.1.0/script/000077500000000000000000000000001505307417100155715ustar00rootroot00000000000000paperless-api-5.1.0/script/bootstrap000077500000000000000000000003261505307417100175350ustar00rootroot00000000000000#!/bin/sh # Resolve all dependencies that the application requires to run. # Stop on errors set -e cd "$(dirname "$0")/.." uv venv $VIRTUAL_ENV echo "Installing development dependencies..." uv sync --group dev paperless-api-5.1.0/script/setup000077500000000000000000000012071505307417100166570ustar00rootroot00000000000000#!/usr/bin/env bash # Setups the repository. # Stop on errors set -e cd "$(dirname "$0")/.." # Add default vscode settings if not existing SETTINGS_FILE=./.vscode/settings.json SETTINGS_TEMPLATE_FILE=./.vscode/settings.default.json if [ ! -f "$SETTINGS_FILE" ]; then echo "Copy $SETTINGS_TEMPLATE_FILE to $SETTINGS_FILE." cp "$SETTINGS_TEMPLATE_FILE" "$SETTINGS_FILE" fi if [ ! -n "$VIRTUAL_ENV" ]; then if [ -x "$(command -v uv)" ]; then uv venv .venv else python3 -m venv .venv fi source .venv/bin/activate fi if ! [ -x "$(command -v uv)" ]; then python3 -m pip install uv fi script/bootstrap pre-commit install paperless-api-5.1.0/tests/000077500000000000000000000000001505307417100154275ustar00rootroot00000000000000paperless-api-5.1.0/tests/__init__.py000066400000000000000000000107631505307417100175470ustar00rootroot00000000000000"""Tests for pypaperless.""" from dataclasses import dataclass from typing import Any from pypaperless import helpers, models from pypaperless.const import PaperlessResource from pypaperless.models import common from .data import ( DATA_CONFIG, DATA_CORRESPONDENTS, DATA_CUSTOM_FIELDS, DATA_DOCUMENT_TYPES, DATA_DOCUMENTS, DATA_GROUPS, DATA_MAIL_ACCOUNTS, DATA_MAIL_RULES, DATA_SAVED_VIEWS, DATA_SHARE_LINKS, DATA_STATUS, DATA_STORAGE_PATHS, DATA_TAGS, DATA_TASKS, DATA_USERS, DATA_WORKFLOWS, ) # mypy: ignore-errors @dataclass class ResourceTestMapping: """Mapping for test cases.""" resource: str data: dict[str, Any] | list[dict[str, Any]] helper_cls: type model_cls: type draft_cls: type | None = None draft_defaults: dict[str, Any] | None = None CONFIG_MAP = ResourceTestMapping( PaperlessResource.CONFIG, DATA_CONFIG, helpers.ConfigHelper, models.Config, ) CORRESPONDENT_MAP = ResourceTestMapping( PaperlessResource.CORRESPONDENTS, DATA_CORRESPONDENTS, helpers.CorrespondentHelper, models.Correspondent, models.CorrespondentDraft, { "name": "New Correspondent", "match": "", "matching_algorithm": common.MatchingAlgorithmType.ANY, "is_insensitive": True, }, ) CUSTOM_FIELD_MAP = ResourceTestMapping( PaperlessResource.CUSTOM_FIELDS, DATA_CUSTOM_FIELDS, helpers.CustomFieldHelper, models.CustomField, models.CustomFieldDraft, { "name": "New Custom Field", "data_type": common.CustomFieldType.BOOLEAN, }, ) DOCUMENT_MAP = ResourceTestMapping( PaperlessResource.DOCUMENTS, DATA_DOCUMENTS, helpers.DocumentHelper, models.Document, models.DocumentDraft, { "document": b"...example...content...", "tags": [1, 2, 3], "correspondent": 1, "document_type": 1, "storage_path": 1, "title": "New Document", "created": None, "archive_serial_number": 1, }, ) DOCUMENT_TYPE_MAP = ResourceTestMapping( PaperlessResource.DOCUMENT_TYPES, DATA_DOCUMENT_TYPES, helpers.DocumentTypeHelper, models.DocumentType, models.DocumentTypeDraft, { "name": "New Document Type", "match": "", "matching_algorithm": common.MatchingAlgorithmType.ANY, "is_insensitive": True, }, ) GROUP_MAP = ResourceTestMapping( PaperlessResource.GROUPS, DATA_GROUPS, helpers.GroupHelper, models.Group, ) MAIL_ACCOUNT_MAP = ResourceTestMapping( PaperlessResource.MAIL_ACCOUNTS, DATA_MAIL_ACCOUNTS, helpers.MailAccountHelper, models.MailAccount, ) MAIL_RULE_MAP = ResourceTestMapping( PaperlessResource.MAIL_RULES, DATA_MAIL_RULES, helpers.MailRuleHelper, models.MailRule, ) SAVED_VIEW_MAP = ResourceTestMapping( PaperlessResource.SAVED_VIEWS, DATA_SAVED_VIEWS, helpers.SavedViewHelper, models.SavedView, ) SHARE_LINK_MAP = ResourceTestMapping( PaperlessResource.SHARE_LINKS, DATA_SHARE_LINKS, helpers.ShareLinkHelper, models.ShareLink, models.ShareLinkDraft, { "expiration": None, "document": 1, "file_version": common.ShareLinkFileVersionType.ORIGINAL, }, ) STATUS_MAP = ResourceTestMapping( PaperlessResource.STATUS, DATA_STATUS, helpers.StatusHelper, models.Status, ) STORAGE_PATH_MAP = ResourceTestMapping( PaperlessResource.STORAGE_PATHS, DATA_STORAGE_PATHS, helpers.StoragePathHelper, models.StoragePath, models.StoragePathDraft, { "name": "New Storage Path", "path": "path/to/test", "match": "", "matching_algorithm": common.MatchingAlgorithmType.ANY, "is_insensitive": True, }, ) TAG_MAP = ResourceTestMapping( PaperlessResource.TAGS, DATA_TAGS, helpers.TagHelper, models.Tag, models.TagDraft, { "name": "New Tag", "color": "#012345", "text_color": "#987654", "is_inbox_tag": False, "match": "", "matching_algorithm": common.MatchingAlgorithmType.ANY, "is_insensitive": True, }, ) TASK_MAP = ResourceTestMapping( PaperlessResource.TASKS, DATA_TASKS, helpers.TaskHelper, models.Task, ) USER_MAP = ResourceTestMapping( PaperlessResource.USERS, DATA_USERS, helpers.UserHelper, models.User, ) WORKFLOW_MAP = ResourceTestMapping( PaperlessResource.WORKFLOWS, DATA_WORKFLOWS, helpers.WorkflowHelper, models.Workflow, ) paperless-api-5.1.0/tests/conftest.py000066400000000000000000000021421505307417100176250ustar00rootroot00000000000000"""Setup pytest.""" from collections.abc import AsyncGenerator, Generator from typing import Any import pytest from aioresponses import aioresponses from pypaperless import Paperless from pypaperless.const import API_PATH from .const import PAPERLESS_TEST_REQ_ARGS, PAPERLESS_TEST_TOKEN, PAPERLESS_TEST_URL from .data import DATA_SCHEMA # mypy: ignore-errors @pytest.fixture(name="resp") def aioresponses_fixture() -> Generator[aioresponses]: """Return aioresponses fixture.""" with aioresponses() as m: yield m @pytest.fixture(name="api") def api_obj_fixture() -> Paperless: """Return Paperless.""" return Paperless( PAPERLESS_TEST_URL, PAPERLESS_TEST_TOKEN, request_args=PAPERLESS_TEST_REQ_ARGS, ) @pytest.fixture(name="paperless") async def paperless_fixture( resp: aioresponses, api: Paperless, ) -> AsyncGenerator[Paperless, Any]: """Return a Paperless object with given version.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['index']}", status=200, payload=DATA_SCHEMA, ) async with api: yield api paperless-api-5.1.0/tests/const.py000066400000000000000000000003761505307417100171350ustar00rootroot00000000000000"""Test constants.""" PAPERLESS_TEST_URL = "https://paperless.not-existing.internal" PAPERLESS_TEST_TOKEN = "abcdef0123456789" PAPERLESS_TEST_USER = "test-user" PAPERLESS_TEST_PASSWORD = "not-so-secret-password" PAPERLESS_TEST_REQ_ARGS = {"ssl": False} paperless-api-5.1.0/tests/data/000077500000000000000000000000001505307417100163405ustar00rootroot00000000000000paperless-api-5.1.0/tests/data/__init__.py000066400000000000000000000037551505307417100204630ustar00rootroot00000000000000"""Raw data constants.""" import json from pathlib import Path from .config import DATA_CONFIG from .correspondents import DATA_CORRESPONDENTS from .custom_fields import DATA_CUSTOM_FIELDS from .document_metadata import DATA_DOCUMENT_METADATA from .document_notes import DATA_DOCUMENT_NOTES from .document_suggestions import DATA_DOCUMENT_SUGGESTIONS from .document_types import DATA_DOCUMENT_TYPES from .documents import DATA_DOCUMENTS from .documents_search import DATA_DOCUMENTS_SEARCH from .groups import DATA_GROUPS from .mail import DATA_MAIL_ACCOUNTS, DATA_MAIL_RULES from .object_permissions import DATA_OBJECT_PERMISSIONS from .paths import DATA_PATHS from .remote_version import DATA_REMOTE_VERSION from .saved_views import DATA_SAVED_VIEWS from .share_links import DATA_SHARE_LINKS from .statistics import DATA_STATISTICS from .status import DATA_STATUS from .storage_paths import DATA_STORAGE_PATHS from .tags import DATA_TAGS from .tasks import DATA_TASKS from .token import DATA_TOKEN from .users import DATA_USERS from .workflow import DATA_WORKFLOW_ACTIONS, DATA_WORKFLOW_TRIGGERS, DATA_WORKFLOWS # mypy: ignore-errors def _read_schema() -> dict: filepath = Path("tests/data/schema.json") with Path.open(filepath, mode="r", encoding="utf-8") as file: return json.load(file) DATA_SCHEMA = _read_schema() __all__ = ( "DATA_CONFIG", "DATA_CORRESPONDENTS", "DATA_CUSTOM_FIELDS", "DATA_DOCUMENTS", "DATA_DOCUMENTS_SEARCH", "DATA_DOCUMENT_METADATA", "DATA_DOCUMENT_NOTES", "DATA_DOCUMENT_SUGGESTIONS", "DATA_DOCUMENT_TYPES", "DATA_GROUPS", "DATA_MAIL_ACCOUNTS", "DATA_MAIL_RULES", "DATA_OBJECT_PERMISSIONS", "DATA_PATHS", "DATA_REMOTE_VERSION", "DATA_SAVED_VIEWS", "DATA_SCHEMA", "DATA_SHARE_LINKS", "DATA_STATISTICS", "DATA_STATUS", "DATA_STORAGE_PATHS", "DATA_TAGS", "DATA_TASKS", "DATA_TOKEN", "DATA_USERS", "DATA_WORKFLOWS", "DATA_WORKFLOW_ACTIONS", "DATA_WORKFLOW_TRIGGERS", ) paperless-api-5.1.0/tests/data/config.py000066400000000000000000000010051505307417100201530ustar00rootroot00000000000000"""Config snapshot.""" DATA_CONFIG = [ { "id": 1, "user_args": None, "output_type": "pdf", "pages": None, "language": "eng", "mode": None, "skip_archive_file": None, "image_dpi": None, "unpaper_clean": None, "deskew": None, "rotate_pages": None, "rotate_pages_threshold": None, "max_image_pixels": None, "color_conversion_strategy": None, "app_title": None, "app_logo": None, } ] paperless-api-5.1.0/tests/data/correspondents.py000066400000000000000000000041001505307417100217550ustar00rootroot00000000000000"""Correspondents snapshot.""" DATA_CORRESPONDENTS = { "count": 5, "next": None, "previous": None, "all": [1, 2, 3, 4, 5], "results": [ { "id": 1, "slug": "sample-correspondent", "name": "Sample Correspondent", "match": "", "matching_algorithm": 1, "is_insensitive": True, "document_count": 12, "last_correspondence": "2022-10-18", "owner": None, "user_can_change": True, }, { "id": 2, "slug": "burger-fastfood-delivery-billing", "name": "Burger FastFood Delivery Billing", "match": "", "matching_algorithm": 1, "is_insensitive": True, "document_count": 6, "last_correspondence": "2006-03-07", "owner": None, "user_can_change": True, }, { "id": 3, "slug": "example-company", "name": "Example Company", "match": "", "matching_algorithm": 6, "is_insensitive": True, "document_count": 3, "last_correspondence": "2022-03-11", "owner": 3, "user_can_change": True, }, { "id": 4, "slug": "no-illness-more-money-health-insurance", "name": "No Illness More Money Health Insurance", "match": "", "matching_algorithm": 1, "is_insensitive": True, "document_count": 6, "last_correspondence": "2000-07-18", "owner": None, "user_can_change": True, }, { "id": 5, "slug": "your-cash-is-my-cash-bank", "name": "Your Cash is my Cash Bank", "match": "Your Cash My Bank", "matching_algorithm": 2, "is_insensitive": True, "document_count": 20, "last_correspondence": "2023-08-28", "owner": None, "user_can_change": True, }, ], } paperless-api-5.1.0/tests/data/custom_fields.py000066400000000000000000000013111505307417100215460ustar00rootroot00000000000000"""Custom fields snapshot.""" DATA_CUSTOM_FIELDS = { "count": 8, "next": None, "previous": None, "all": [8, 7, 6, 5, 4, 3, 2, 1], "results": [ {"id": 8, "name": "Custom Link", "data_type": "documentlink"}, {"id": 7, "name": "Custom URL", "data_type": "url"}, {"id": 6, "name": "Custom Text -added-", "data_type": "string"}, {"id": 5, "name": "Custom MONEYY $$$", "data_type": "monetary"}, {"id": 4, "name": "Custom Floating", "data_type": "float"}, {"id": 3, "name": "Custom Int", "data_type": "integer"}, {"id": 2, "name": "Custom Date", "data_type": "date"}, {"id": 1, "name": "Custom Bool", "data_type": "boolean"}, ], } paperless-api-5.1.0/tests/data/document_metadata.py000066400000000000000000000047651505307417100224040ustar00rootroot00000000000000"""Document metadata snapshot.""" DATA_DOCUMENT_METADATA = { "original_checksum": "18e2352cc13379d19bd9ce329428bb99", "original_size": 190348, "original_mime_type": "application/pdf", "media_filename": "xxx.pdf", "has_archive_version": True, "original_metadata": [], "archive_checksum": "8a69542583571337dd263f8bb3cf23bd", "archive_media_filename": "0000226.pdf", "original_filename": None, "lang": "de", "archive_size": 161371, "archive_metadata": [ { "namespace": "http://ns.adobe.com/pdf/1.3/", "prefix": "pdf", "key": "Producer", "value": "pikepdf 2.16.1", }, { "namespace": "http://ns.adobe.com/xap/1.0/", "prefix": "xmp", "key": "ModifyDate", "value": "2022-02-12T11:34:40+00:00", }, { "namespace": "http://ns.adobe.com/xap/1.0/", "prefix": "xmp", "key": "CreateDate", "value": "2022-01-07T13:40:49", }, { "namespace": "http://ns.adobe.com/xap/1.0/", "prefix": "xmp", "key": "CreatorTool", "value": "ocrmypdf 12.3.2 / Tesseract OCR-PDF 4.1.1", }, { "namespace": "http://ns.adobe.com/xap/1.0/mm/", "prefix": "xmpMM", "key": "DocumentID", "value": "uuid:13371337-2342-1337-4242-133723426665", }, { "namespace": "http://purl.org/dc/elements/1.1/", "prefix": "dc", "key": "format", "value": "application/pdf", }, { "namespace": "http://purl.org/dc/elements/1.1/", "prefix": "dc", "key": "title", "value": "Octopus Energy updated", }, { "namespace": "http://www.aiim.org/pdfa/ns/id/", "prefix": "pdfaid", "key": "part", "value": "2", }, { "namespace": "http://www.aiim.org/pdfa/ns/id/", "prefix": "pdfaid", "key": "conformance", "value": "B", }, { "namespace": "http://purl.org/dc/elements/1.1/", "prefix": "dc", "key": "creator", "value": "None", }, { "namespace": "http://ns.adobe.com/xap/1.0/", "prefix": "xmp", "key": "MetadataDate", "value": "2022-02-12T11:34:40.383718+00:00", }, ], } paperless-api-5.1.0/tests/data/document_notes.py000066400000000000000000000015441505307417100217440ustar00rootroot00000000000000"""Document notes snapshot.""" DATA_DOCUMENT_NOTES = [ { "id": 1, "note": "Sample note 1.", "created": "2023-12-21T18:08:11.481206+00:00", "user": { "id": 1, "username": "test", "first_name": "Peter", "last_name": "Patch", }, }, { "id": 2, "note": "Sample note 2.", "created": "2023-12-21T08:26:33.260968+00:00", "user": { "id": 2, "username": "test", "first_name": "Peter", "last_name": "Patch", }, }, { "id": 3, "note": "Sample note 3.", "created": "2023-12-21T08:26:31.782811+00:00", "user": { "id": 3, "username": "test", "first_name": "Peter", "last_name": "Patch", }, }, ] paperless-api-5.1.0/tests/data/document_suggestions.py000066400000000000000000000004511505307417100231620ustar00rootroot00000000000000"""Document suggestions snapshot.""" DATA_DOCUMENT_SUGGESTIONS = { "correspondents": [26], "tags": [ 1, 2, 3, ], "document_types": [4], "storage_paths": [ 3, 5, ], "dates": [ "2022-01-07", "2023-01-07", ], } paperless-api-5.1.0/tests/data/document_types.py000066400000000000000000000032301505307417100217520ustar00rootroot00000000000000"""Document types snapshot.""" DATA_DOCUMENT_TYPES = { "count": 5, "next": None, "previous": None, "all": [1, 2, 3, 4, 5], "results": [ { "id": 1, "slug": "bescheid", "name": "Bescheid", "match": "", "matching_algorithm": 1, "is_insensitive": True, "document_count": 42, "owner": None, "user_can_change": True, }, { "id": 2, "slug": "document", "name": "Document", "match": "", "matching_algorithm": 1, "is_insensitive": True, "document_count": 218, "owner": None, "user_can_change": None, }, { "id": 3, "slug": "kontoauszug", "name": "Kontoauszug", "match": "", "matching_algorithm": 6, "is_insensitive": True, "document_count": 227, "owner": None, "user_can_change": None, }, { "id": 4, "slug": "kundigung", "name": "Kuendigung", "match": "", "matching_algorithm": 1, "is_insensitive": True, "document_count": 24, "owner": None, "user_can_change": True, }, { "id": 5, "slug": "rechnung", "name": "Rechnung", "match": "", "matching_algorithm": 6, "is_insensitive": True, "document_count": 246, "owner": None, "user_can_change": True, }, ], } paperless-api-5.1.0/tests/data/documents.py000066400000000000000000000051401505307417100207130ustar00rootroot00000000000000"""Documents snapshot.""" DATA_DOCUMENTS = { "count": 2, "next": None, "previous": "", "all": [1, 2], "results": [ { "id": 1, "correspondent": 1, "document_type": 2, "storage_path": None, "title": "Crazy Document", "content": "some OCRd text", "tags": [], "created": "2011-06-22", "modified": "2023-08-08T06:06:35.495972+00:00", "added": "2023-06-30T05:44:14.317925+00:00", "archive_serial_number": None, "original_file_name": "Scan_2023-06-29_113857.pdf", "archived_file_name": "2011-06-22 filename.pdf", "owner": 2, "user_can_change": True, "notes": [], "custom_fields": [], }, { "id": 2, "correspondent": 2, "document_type": 1, "storage_path": 1, "title": "Salty Document", "content": "OCRd text from document", "tags": [1], "created": "2022-01-07", "modified": "2023-12-13T16:15:02.148852+00:00", "added": "2022-02-12T11:34:50.072000+00:00", "archive_serial_number": 1, "original_file_name": None, "archived_file_name": "2022-01-07.pdf", "owner": 1, "user_can_change": True, "notes": [ { "id": 1, "note": "Sample note 1.", "created": "2023-12-21T18:08:11.481206+00:00", "document": 2, "user": 1, }, { "id": 2, "note": "Sample note 2.", "created": "2023-12-21T08:26:33.260968+00:00", "document": 2, "user": 2, }, { "id": 3, "note": "Sample note 3.", "created": "2023-12-21T08:26:31.782811+00:00", "document": 2, "user": 3, }, ], "custom_fields": [ {"value": [1094, 944], "field": 8}, {"value": "https://www.example.com", "field": 7}, {"value": "This is a text", "field": 6}, {"value": 1000.0, "field": 5}, {"value": 13.37, "field": 4}, {"value": 42, "field": 3}, {"value": "2099-12-31", "field": 2}, {"value": True, "field": 1}, ], }, ], } paperless-api-5.1.0/tests/data/documents_search.py000066400000000000000000000021321505307417100222360ustar00rootroot00000000000000"""Documents search snapshot.""" # mypy: ignore-errors DATA_DOCUMENTS_SEARCH = { "count": 1, "next": None, "previous": None, "all": [1], "results": [ { "id": 1, "correspondent": 1, "document_type": 2, "storage_path": None, "title": "Crazy Document", "content": "some OCRd text", "tags": [], "created": "2011-06-22T00:00:00+00:00", "created_date": "2011-06-22", "modified": "2023-08-08T06:06:35.495972+00:00", "added": "2023-06-30T05:44:14.317925+00:00", "archive_serial_number": None, "original_file_name": "Scan_2023-06-29_113857.pdf", "archived_file_name": "2011-06-22 filename.pdf", "owner": 2, "user_can_change": True, "notes": [], "custom_fields": [], "__search_hit__": { "score": 1.0, "highlights": "some neat hint", "note_highlights": "", "rank": 0, }, }, ], } paperless-api-5.1.0/tests/data/groups.py000066400000000000000000000030071505307417100202310ustar00rootroot00000000000000"""Groups snapshot.""" DATA_GROUPS = { "count": 1, "next": None, "previous": None, "all": [1], "results": [ { "id": 1, "name": "Sample Group", "permissions": [ "add_consumptiontemplate", "change_consumptiontemplate", "delete_consumptiontemplate", "view_consumptiontemplate", "view_correspondent", "add_customfield", "change_customfield", "delete_customfield", "view_customfield", "add_customfieldinstance", "change_customfieldinstance", "delete_customfieldinstance", "view_customfieldinstance", "add_document", "change_document", "delete_document", "view_document", "view_documenttype", "add_note", "change_note", "delete_note", "view_note", "add_savedview", "change_savedview", "delete_savedview", "view_savedview", "add_sharelink", "change_sharelink", "delete_sharelink", "view_sharelink", "view_tag", "add_uisettings", "change_uisettings", "delete_uisettings", "view_uisettings", ], }, ], } paperless-api-5.1.0/tests/data/mail.py000066400000000000000000000030131505307417100176310ustar00rootroot00000000000000"""Mail snapshots.""" # mypy: ignore-errors DATA_MAIL_ACCOUNTS = { "count": 1, "next": None, "previous": None, "all": [1], "results": [ { "id": 1, "name": "Test Account", "imap_server": "imap.omega.net", "imap_port": 1337, "imap_security": 2, "username": "omega-weapon", "password": "********************", "character_set": "UTF-8", "is_token": False, "owner": 1, "user_can_change": True, } ], } DATA_MAIL_RULES = { "count": 1, "next": None, "previous": None, "all": [1], "results": [ { "id": 1, "name": "Test", "account": 1, "folder": "INBOX", "filter_from": None, "filter_to": None, "filter_subject": None, "filter_body": None, "filter_attachment_filename_include": None, "filter_attachment_filename_exclude": None, "maximum_age": 3, "action": 3, "action_parameter": None, "assign_title_from": 1, "assign_tags": [], "assign_correspondent_from": 1, "assign_correspondent": None, "assign_document_type": None, "assign_owner_from_rule": True, "order": 1, "attachment_type": 1, "consumption_scope": 1, "owner": 1, "user_can_change": True, } ], } paperless-api-5.1.0/tests/data/object_permissions.py000066400000000000000000000003111505307417100226060ustar00rootroot00000000000000"""Object permissions snapshot.""" DATA_OBJECT_PERMISSIONS = { "view": { "users": [1, 2], "groups": [], }, "change": { "users": [], "groups": [1], }, } paperless-api-5.1.0/tests/data/paths.py000066400000000000000000000021721505307417100200330ustar00rootroot00000000000000"""Paths snapshot.""" from tests.const import PAPERLESS_TEST_URL DATA_PATHS = { "correspondents": f"{PAPERLESS_TEST_URL}/api/correspondents/", "document_types": f"{PAPERLESS_TEST_URL}/api/document_types/", "documents": f"{PAPERLESS_TEST_URL}/api/documents/", "logs": f"{PAPERLESS_TEST_URL}/api/logs/", "tags": f"{PAPERLESS_TEST_URL}/api/tags/", "saved_views": f"{PAPERLESS_TEST_URL}/api/saved_views/", "tasks": f"{PAPERLESS_TEST_URL}/api/tasks/", "users": f"{PAPERLESS_TEST_URL}/api/users/", "groups": f"{PAPERLESS_TEST_URL}/api/groups/", "mail_accounts": f"{PAPERLESS_TEST_URL}/api/mail_accounts/", "mail_rules": f"{PAPERLESS_TEST_URL}/api/mail_rules/", "storage_paths": f"{PAPERLESS_TEST_URL}/api/storage_paths/", "config": f"{PAPERLESS_TEST_URL}/api/config/", "custom_fields": f"{PAPERLESS_TEST_URL}/api/custom_fields/", "share_links": f"{PAPERLESS_TEST_URL}/api/share_links/", "workflows": f"{PAPERLESS_TEST_URL}/api/workflows/", "workflow_actions": f"{PAPERLESS_TEST_URL}/api/workflow_actions/", "workflow_triggers": f"{PAPERLESS_TEST_URL}/api/workflow_triggers/", } paperless-api-5.1.0/tests/data/remote_version.py000066400000000000000000000001471505307417100217540ustar00rootroot00000000000000"""Remote version snapshot.""" DATA_REMOTE_VERSION = {"version": "v2.15.3", "update_available": True} paperless-api-5.1.0/tests/data/saved_views.py000066400000000000000000000037731505307417100212430ustar00rootroot00000000000000"""Saved views snapshot.""" DATA_SAVED_VIEWS = { "count": 5, "next": None, "previous": None, "all": [1, 2, 3, 4, 5], "results": [ { "id": 1, "name": "New Items", "show_on_dashboard": False, "show_in_sidebar": False, "sort_field": None, "sort_reverse": False, "filter_rules": [{"rule_type": 6, "value": "1"}], "owner": 1, "user_can_change": True, }, { "id": 2, "name": "New Items Alpha", "show_on_dashboard": True, "show_in_sidebar": False, "sort_field": "title", "sort_reverse": False, "filter_rules": [ {"rule_type": 6, "value": "1"}, {"rule_type": 33, "value": "7"}, ], "owner": 1, "user_can_change": True, }, { "id": 3, "name": "New Items Omega", "show_on_dashboard": True, "show_in_sidebar": False, "sort_field": None, "sort_reverse": False, "filter_rules": [ {"rule_type": 6, "value": "1"}, {"rule_type": 35, "value": "7"}, ], "owner": 1, "user_can_change": True, }, { "id": 4, "name": "Tax", "show_on_dashboard": False, "show_in_sidebar": True, "sort_field": "created", "sort_reverse": True, "filter_rules": [{"rule_type": 6, "value": "3"}], "owner": 1, "user_can_change": True, }, { "id": 5, "name": "Todo", "show_on_dashboard": False, "show_in_sidebar": True, "sort_field": "created", "sort_reverse": True, "filter_rules": [{"rule_type": 6, "value": "4"}], "owner": 1, "user_can_change": True, }, ], } paperless-api-5.1.0/tests/data/schema.json000066400000000000000000021426111505307417100205020ustar00rootroot00000000000000{ "openapi": "3.0.3", "info": { "title": "Paperless-ngx REST API", "version": "6.0.0 (9)", "description": "OpenAPI Spec for Paperless-ngx" }, "paths": { "/api/bulk_edit_objects/": { "post": { "operationId": "bulk_edit_objects", "description": "Perform a bulk edit operation on a list of objects", "externalDocs": { "description": "Further documentation", "url": "https://docs.paperless-ngx.com/api/#objects" }, "tags": [ "bulk_edit_objects" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkEditObjectsRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkEditResult" } } }, "description": "" } } } }, "/api/config/": { "get": { "operationId": "config_list", "description": "Get the application configuration", "externalDocs": { "description": "Application Configuration", "url": "https://docs.paperless-ngx.com/configuration/" }, "tags": [ "config" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/ApplicationConfiguration" } } } }, "description": "" } } } }, "/api/config/{id}/": { "get": { "operationId": "config_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this paperless application settings.", "required": true } ], "tags": [ "config" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApplicationConfiguration" } } }, "description": "" } } }, "put": { "operationId": "config_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this paperless application settings.", "required": true } ], "tags": [ "config" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApplicationConfigurationRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/ApplicationConfigurationRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/ApplicationConfigurationRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApplicationConfiguration" } } }, "description": "" } } }, "patch": { "operationId": "config_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this paperless application settings.", "required": true } ], "tags": [ "config" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedApplicationConfigurationRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedApplicationConfigurationRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedApplicationConfigurationRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApplicationConfiguration" } } }, "description": "" } } }, "delete": { "operationId": "config_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this paperless application settings.", "required": true } ], "tags": [ "config" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/correspondents/": { "get": { "operationId": "correspondents_list", "parameters": [ { "in": "query", "name": "full_perms", "schema": { "type": "boolean" } }, { "in": "query", "name": "id", "schema": { "type": "integer" } }, { "in": "query", "name": "id__in", "schema": { "type": "array", "items": { "type": "integer" } }, "description": "Multiple values may be separated by commas.", "explode": false, "style": "form" }, { "in": "query", "name": "name__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "name__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "name__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "name__istartswith", "schema": { "type": "string" } }, { "name": "ordering", "required": false, "in": "query", "description": "Which field to use when ordering the results.", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "correspondents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedCorrespondentList" } } }, "description": "" } } }, "post": { "operationId": "correspondents_create", "tags": [ "correspondents" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CorrespondentRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/CorrespondentRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/CorrespondentRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Correspondent" } } }, "description": "" } } } }, "/api/correspondents/{id}/": { "get": { "operationId": "correspondents_retrieve", "parameters": [ { "in": "query", "name": "full_perms", "schema": { "type": "boolean" } }, { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this correspondent.", "required": true } ], "tags": [ "correspondents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Correspondent" } } }, "description": "" } } }, "put": { "operationId": "correspondents_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this correspondent.", "required": true } ], "tags": [ "correspondents" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CorrespondentRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/CorrespondentRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/CorrespondentRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Correspondent" } } }, "description": "" } } }, "patch": { "operationId": "correspondents_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this correspondent.", "required": true } ], "tags": [ "correspondents" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedCorrespondentRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedCorrespondentRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedCorrespondentRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Correspondent" } } }, "description": "" } } }, "delete": { "operationId": "correspondents_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this correspondent.", "required": true } ], "tags": [ "correspondents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/custom_fields/": { "get": { "operationId": "custom_fields_list", "parameters": [ { "in": "query", "name": "id", "schema": { "type": "integer" } }, { "in": "query", "name": "id__in", "schema": { "type": "array", "items": { "type": "integer" } }, "description": "Multiple values may be separated by commas.", "explode": false, "style": "form" }, { "in": "query", "name": "name__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "name__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "name__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "name__istartswith", "schema": { "type": "string" } }, { "name": "ordering", "required": false, "in": "query", "description": "Which field to use when ordering the results.", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "custom_fields" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedCustomFieldList" } } }, "description": "" } } }, "post": { "operationId": "custom_fields_create", "tags": [ "custom_fields" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomFieldRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/CustomFieldRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/CustomFieldRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomField" } } }, "description": "" } } } }, "/api/custom_fields/{id}/": { "get": { "operationId": "custom_fields_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this custom field.", "required": true } ], "tags": [ "custom_fields" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomField" } } }, "description": "" } } }, "put": { "operationId": "custom_fields_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this custom field.", "required": true } ], "tags": [ "custom_fields" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomFieldRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/CustomFieldRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/CustomFieldRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomField" } } }, "description": "" } } }, "patch": { "operationId": "custom_fields_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this custom field.", "required": true } ], "tags": [ "custom_fields" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedCustomFieldRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedCustomFieldRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedCustomFieldRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomField" } } }, "description": "" } } }, "delete": { "operationId": "custom_fields_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this custom field.", "required": true } ], "tags": [ "custom_fields" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/document_types/": { "get": { "operationId": "document_types_list", "parameters": [ { "in": "query", "name": "full_perms", "schema": { "type": "boolean" } }, { "in": "query", "name": "id", "schema": { "type": "integer" } }, { "in": "query", "name": "id__in", "schema": { "type": "array", "items": { "type": "integer" } }, "description": "Multiple values may be separated by commas.", "explode": false, "style": "form" }, { "in": "query", "name": "name__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "name__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "name__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "name__istartswith", "schema": { "type": "string" } }, { "name": "ordering", "required": false, "in": "query", "description": "Which field to use when ordering the results.", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "document_types" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedDocumentTypeList" } } }, "description": "" } } }, "post": { "operationId": "document_types_create", "tags": [ "document_types" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentTypeRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/DocumentTypeRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/DocumentTypeRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentType" } } }, "description": "" } } } }, "/api/document_types/{id}/": { "get": { "operationId": "document_types_retrieve", "parameters": [ { "in": "query", "name": "full_perms", "schema": { "type": "boolean" } }, { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document type.", "required": true } ], "tags": [ "document_types" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentType" } } }, "description": "" } } }, "put": { "operationId": "document_types_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document type.", "required": true } ], "tags": [ "document_types" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentTypeRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/DocumentTypeRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/DocumentTypeRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentType" } } }, "description": "" } } }, "patch": { "operationId": "document_types_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document type.", "required": true } ], "tags": [ "document_types" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedDocumentTypeRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedDocumentTypeRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedDocumentTypeRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentType" } } }, "description": "" } } }, "delete": { "operationId": "document_types_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document type.", "required": true } ], "tags": [ "document_types" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/documents/": { "get": { "operationId": "documents_list", "description": "Document views including search", "parameters": [ { "in": "query", "name": "added__date__gt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "added__date__gte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "added__date__lt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "added__date__lte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "added__day", "schema": { "type": "number" } }, { "in": "query", "name": "added__gt", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "added__gte", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "added__lt", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "added__lte", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "added__month", "schema": { "type": "number" } }, { "in": "query", "name": "added__year", "schema": { "type": "number" } }, { "in": "query", "name": "archive_serial_number", "schema": { "type": "integer" } }, { "in": "query", "name": "archive_serial_number__gt", "schema": { "type": "integer" } }, { "in": "query", "name": "archive_serial_number__gte", "schema": { "type": "integer" } }, { "in": "query", "name": "archive_serial_number__isnull", "schema": { "type": "boolean" } }, { "in": "query", "name": "archive_serial_number__lt", "schema": { "type": "integer" } }, { "in": "query", "name": "archive_serial_number__lte", "schema": { "type": "integer" } }, { "in": "query", "name": "checksum__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "checksum__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "checksum__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "checksum__istartswith", "schema": { "type": "string" } }, { "in": "query", "name": "content__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "content__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "content__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "content__istartswith", "schema": { "type": "string" } }, { "in": "query", "name": "correspondent__id", "schema": { "type": "integer" } }, { "in": "query", "name": "correspondent__id__in", "schema": { "type": "array", "items": { "type": "integer" } }, "description": "Multiple values may be separated by commas.", "explode": false, "style": "form" }, { "in": "query", "name": "correspondent__id__none", "schema": { "type": "integer" } }, { "in": "query", "name": "correspondent__isnull", "schema": { "type": "boolean" } }, { "in": "query", "name": "correspondent__name__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "correspondent__name__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "correspondent__name__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "correspondent__name__istartswith", "schema": { "type": "string" } }, { "in": "query", "name": "created__date__gt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__date__gte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__date__lt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__date__lte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__day", "schema": { "type": "number" } }, { "in": "query", "name": "created__gt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__gte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__lt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__lte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__month", "schema": { "type": "number" } }, { "in": "query", "name": "created__year", "schema": { "type": "number" } }, { "in": "query", "name": "custom_field_query", "schema": { "type": "string", "minLength": 1 } }, { "in": "query", "name": "custom_fields__icontains", "schema": { "type": "string", "minLength": 1 } }, { "in": "query", "name": "custom_fields__id__all", "schema": { "type": "integer" } }, { "in": "query", "name": "custom_fields__id__in", "schema": { "type": "integer" } }, { "in": "query", "name": "custom_fields__id__none", "schema": { "type": "integer" } }, { "in": "query", "name": "document_type__id", "schema": { "type": "integer" } }, { "in": "query", "name": "document_type__id__in", "schema": { "type": "array", "items": { "type": "integer" } }, "description": "Multiple values may be separated by commas.", "explode": false, "style": "form" }, { "in": "query", "name": "document_type__id__none", "schema": { "type": "integer" } }, { "in": "query", "name": "document_type__isnull", "schema": { "type": "boolean" } }, { "in": "query", "name": "document_type__name__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "document_type__name__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "document_type__name__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "document_type__name__istartswith", "schema": { "type": "string" } }, { "in": "query", "name": "fields", "schema": { "type": "array", "items": { "type": "string" } } }, { "in": "query", "name": "full_perms", "schema": { "type": "boolean" } }, { "in": "query", "name": "has_custom_fields", "schema": { "type": "boolean" }, "description": "Has custom field" }, { "in": "query", "name": "id", "schema": { "type": "integer" } }, { "in": "query", "name": "id__in", "schema": { "type": "array", "items": { "type": "integer" } }, "description": "Multiple values may be separated by commas.", "explode": false, "style": "form" }, { "in": "query", "name": "is_in_inbox", "schema": { "type": "boolean" } }, { "in": "query", "name": "is_tagged", "schema": { "type": "boolean" }, "description": "Is tagged" }, { "in": "query", "name": "mime_type", "schema": { "type": "string" } }, { "in": "query", "name": "modified__date__gt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "modified__date__gte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "modified__date__lt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "modified__date__lte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "modified__day", "schema": { "type": "number" } }, { "in": "query", "name": "modified__gt", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "modified__gte", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "modified__lt", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "modified__lte", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "modified__month", "schema": { "type": "number" } }, { "in": "query", "name": "modified__year", "schema": { "type": "number" } }, { "name": "ordering", "required": false, "in": "query", "description": "Which field to use when ordering the results.", "schema": { "type": "string" } }, { "in": "query", "name": "original_filename__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "original_filename__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "original_filename__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "original_filename__istartswith", "schema": { "type": "string" } }, { "in": "query", "name": "owner__id", "schema": { "type": "integer" } }, { "in": "query", "name": "owner__id__in", "schema": { "type": "array", "items": { "type": "integer" } }, "description": "Multiple values may be separated by commas.", "explode": false, "style": "form" }, { "in": "query", "name": "owner__id__none", "schema": { "type": "integer" } }, { "in": "query", "name": "owner__isnull", "schema": { "type": "boolean" } }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } }, { "in": "query", "name": "query", "schema": { "type": "string" }, "description": "Advanced search query string" }, { "name": "search", "required": false, "in": "query", "description": "A search term.", "schema": { "type": "string" } }, { "in": "query", "name": "shared_by__id", "schema": { "type": "boolean" } }, { "in": "query", "name": "storage_path__id", "schema": { "type": "integer" } }, { "in": "query", "name": "storage_path__id__in", "schema": { "type": "array", "items": { "type": "integer" } }, "description": "Multiple values may be separated by commas.", "explode": false, "style": "form" }, { "in": "query", "name": "storage_path__id__none", "schema": { "type": "integer" } }, { "in": "query", "name": "storage_path__isnull", "schema": { "type": "boolean" } }, { "in": "query", "name": "storage_path__name__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "storage_path__name__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "storage_path__name__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "storage_path__name__istartswith", "schema": { "type": "string" } }, { "in": "query", "name": "tags__id", "schema": { "type": "integer" } }, { "in": "query", "name": "tags__id__all", "schema": { "type": "integer" } }, { "in": "query", "name": "tags__id__in", "schema": { "type": "integer" } }, { "in": "query", "name": "tags__id__none", "schema": { "type": "integer" } }, { "in": "query", "name": "tags__name__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "tags__name__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "tags__name__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "tags__name__istartswith", "schema": { "type": "string" } }, { "in": "query", "name": "title__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "title__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "title__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "title__istartswith", "schema": { "type": "string" } }, { "in": "query", "name": "title_content", "schema": { "type": "string", "minLength": 1 } } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedDocumentList" } } }, "description": "" } } } }, "/api/documents/{id}/": { "get": { "operationId": "documents_retrieve", "description": "Retrieve a single document", "parameters": [ { "in": "query", "name": "fields", "schema": { "type": "array", "items": { "type": "string" } } }, { "in": "query", "name": "full_perms", "schema": { "type": "boolean" } }, { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Document" } } }, "description": "" }, "400": { "description": "No response body" } } }, "put": { "operationId": "documents_update", "description": "Pass a user object to serializer", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true } ], "tags": [ "documents" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DocumentRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/DocumentRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/DocumentRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Document" } } }, "description": "" } } }, "patch": { "operationId": "documents_partial_update", "description": "Pass a user object to serializer", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true } ], "tags": [ "documents" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedDocumentRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedDocumentRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedDocumentRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Document" } } }, "description": "" } } }, "delete": { "operationId": "documents_destroy", "description": "Pass a user object to serializer", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/documents/{id}/download/": { "get": { "operationId": "documents_download_retrieve", "description": "Download the document", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true }, { "in": "query", "name": "original", "schema": { "type": "boolean" } } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "string", "format": "binary" } } }, "description": "" } } } }, "/api/documents/{id}/email/": { "post": { "operationId": "documents_email_create", "description": "Email the document to one or more recipients as an attachment.", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true } ], "tags": [ "documents" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailRequestRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/EmailRequestRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/EmailRequestRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EmailResponse" } } }, "description": "" }, "400": { "description": "No response body" }, "403": { "description": "No response body" }, "404": { "description": "No response body" }, "500": { "description": "No response body" } } } }, "/api/documents/{id}/history/": { "get": { "operationId": "documents_history_list", "description": "View the document history", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedLogEntryList" } } }, "description": "" }, "400": { "description": "No response body" }, "403": { "description": "No response body" }, "404": { "description": "No response body" } } } }, "/api/documents/{id}/metadata/": { "get": { "operationId": "documents_metadata_retrieve", "description": "View the document metadata", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Metadata" } } }, "description": "" }, "400": { "description": "No response body" }, "403": { "description": "No response body" }, "404": { "description": "No response body" } } } }, "/api/documents/{id}/notes/": { "get": { "operationId": "documents_notes_list", "description": "View, add, or delete notes for the document", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true }, { "in": "query", "name": "id", "schema": { "type": "integer" }, "description": "Note ID to delete (used only for DELETE requests)" }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedNotesList" } } }, "description": "" }, "400": { "description": "No response body" }, "403": { "description": "No response body" }, "404": { "description": "No response body" } } }, "post": { "operationId": "documents_notes_create", "description": "View, add, or delete notes for the document", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true }, { "in": "query", "name": "id", "schema": { "type": "integer" }, "description": "Note ID to delete (used only for DELETE requests)" }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "documents" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NoteCreateRequestRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/NoteCreateRequestRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/NoteCreateRequestRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedNotesList" } } }, "description": "" }, "400": { "description": "No response body" }, "403": { "description": "No response body" }, "404": { "description": "No response body" } } }, "delete": { "operationId": "documents_notes_destroy", "description": "View, add, or delete notes for the document", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true }, { "in": "query", "name": "id", "schema": { "type": "integer" }, "description": "Note ID to delete (used only for DELETE requests)" }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedNotesList" } } }, "description": "" }, "400": { "description": "No response body" }, "403": { "description": "No response body" }, "404": { "description": "No response body" } } } }, "/api/documents/{id}/preview/": { "get": { "operationId": "documents_preview_retrieve", "description": "View the document preview", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "string", "format": "binary" } } }, "description": "" } } } }, "/api/documents/{id}/share_links/": { "get": { "operationId": "document_share_links", "description": "View share links for the document", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string" }, "required": true } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer" }, "created": { "type": "string", "format": "date-time" }, "expiration": { "type": "string", "format": "date-time" }, "slug": { "type": "string" } } } } } }, "description": "" }, "400": { "description": "No response body" }, "403": { "description": "No response body" }, "404": { "description": "No response body" } } } }, "/api/documents/{id}/suggestions/": { "get": { "operationId": "documents_suggestions_retrieve", "description": "View suggestions for the document", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Suggestions" } } }, "description": "" }, "400": { "description": "No response body" }, "403": { "description": "No response body" }, "404": { "description": "No response body" } } } }, "/api/documents/{id}/thumb/": { "get": { "operationId": "documents_thumb_retrieve", "description": "View the document thumbnail", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this document.", "required": true } ], "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "string", "format": "binary" } } }, "description": "" } } } }, "/api/documents/bulk_download/": { "post": { "operationId": "documents_bulk_download_create", "tags": [ "documents" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkDownloadRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkDownload" } } }, "description": "" } } } }, "/api/documents/bulk_edit/": { "post": { "operationId": "bulk_edit", "description": "Perform a bulk edit operation on a list of documents", "externalDocs": { "description": "Further documentation", "url": "https://docs.paperless-ngx.com/api/#bulk-editing" }, "tags": [ "documents" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkEditRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BulkEditDocumentsResult" } } }, "description": "" } } } }, "/api/documents/next_asn/": { "get": { "operationId": "documents_next_asn_retrieve", "description": "Get the next available Archive Serial Number (ASN) for a new document", "tags": [ "documents" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "integer" } } }, "description": "" } } } }, "/api/documents/post_document/": { "post": { "operationId": "documents_post_document_create", "description": "Upload a document via the API", "externalDocs": { "description": "Further documentation", "url": "https://docs.paperless-ngx.com/api/#file-uploads" }, "tags": [ "documents" ], "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PostDocumentRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "" } } } }, "/api/documents/selection_data/": { "post": { "operationId": "documents_selection_data_create", "description": "Get selection data for the selected documents", "tags": [ "documents" ], "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/DocumentListRequest" } }, "application/json": { "schema": { "$ref": "#/components/schemas/DocumentListRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SelectionData" } } }, "description": "" } } } }, "/api/groups/": { "get": { "operationId": "groups_list", "parameters": [ { "in": "query", "name": "name__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "name__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "name__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "name__istartswith", "schema": { "type": "string" } }, { "name": "ordering", "required": false, "in": "query", "description": "Which field to use when ordering the results.", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "groups" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedGroupList" } } }, "description": "" } } }, "post": { "operationId": "groups_create", "tags": [ "groups" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GroupRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/GroupRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/GroupRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Group" } } }, "description": "" } } } }, "/api/groups/{id}/": { "get": { "operationId": "groups_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this group.", "required": true } ], "tags": [ "groups" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Group" } } }, "description": "" } } }, "put": { "operationId": "groups_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this group.", "required": true } ], "tags": [ "groups" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GroupRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/GroupRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/GroupRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Group" } } }, "description": "" } } }, "patch": { "operationId": "groups_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this group.", "required": true } ], "tags": [ "groups" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedGroupRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedGroupRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedGroupRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Group" } } }, "description": "" } } }, "delete": { "operationId": "groups_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this group.", "required": true } ], "tags": [ "groups" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/logs/": { "get": { "operationId": "logs_list", "description": "Logs view", "tags": [ "logs" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } }, "description": "" } } } }, "/api/logs/{id}/": { "get": { "operationId": "retrieve_log", "description": "Single log view", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "string" }, "required": true } ], "tags": [ "logs" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } }, "description": "" }, "404": { "description": "No response body" } } } }, "/api/mail_accounts/": { "get": { "operationId": "mail_accounts_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "mail_accounts" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedMailAccountList" } } }, "description": "" } } }, "post": { "operationId": "mail_accounts_create", "tags": [ "mail_accounts" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailAccount" } } }, "description": "" } } } }, "/api/mail_accounts/{id}/": { "get": { "operationId": "mail_accounts_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this mail account.", "required": true } ], "tags": [ "mail_accounts" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailAccount" } } }, "description": "" } } }, "put": { "operationId": "mail_accounts_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this mail account.", "required": true } ], "tags": [ "mail_accounts" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailAccount" } } }, "description": "" } } }, "patch": { "operationId": "mail_accounts_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this mail account.", "required": true } ], "tags": [ "mail_accounts" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedMailAccountRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedMailAccountRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedMailAccountRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailAccount" } } }, "description": "" } } }, "delete": { "operationId": "mail_accounts_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this mail account.", "required": true } ], "tags": [ "mail_accounts" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/mail_accounts/{id}/process/": { "post": { "operationId": "mail_account_process", "description": "Manually process the selected mail account for new messages.", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this mail account.", "required": true } ], "tags": [ "mail_accounts" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailAccountProcessResponse" } } }, "description": "" }, "404": { "description": "No response body" } } } }, "/api/mail_accounts/test/": { "post": { "operationId": "mail_account_test", "description": "Test a mail account", "tags": [ "mail_accounts" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/MailAccountRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailAccountTestResponse" } } }, "description": "" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "" } } } }, "/api/mail_rules/": { "get": { "operationId": "mail_rules_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "mail_rules" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedMailRuleList" } } }, "description": "" } } }, "post": { "operationId": "mail_rules_create", "tags": [ "mail_rules" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailRuleRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/MailRuleRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/MailRuleRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailRule" } } }, "description": "" } } } }, "/api/mail_rules/{id}/": { "get": { "operationId": "mail_rules_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this mail rule.", "required": true } ], "tags": [ "mail_rules" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailRule" } } }, "description": "" } } }, "put": { "operationId": "mail_rules_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this mail rule.", "required": true } ], "tags": [ "mail_rules" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailRuleRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/MailRuleRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/MailRuleRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailRule" } } }, "description": "" } } }, "patch": { "operationId": "mail_rules_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this mail rule.", "required": true } ], "tags": [ "mail_rules" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedMailRuleRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedMailRuleRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedMailRuleRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MailRule" } } }, "description": "" } } }, "delete": { "operationId": "mail_rules_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this mail rule.", "required": true } ], "tags": [ "mail_rules" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/oauth/callback/": { "get": { "operationId": "oauth_callback_retrieve", "description": "Callback view for OAuth2 authentication", "tags": [ "oauth" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "description": "No response body" } } } }, "/api/profile/": { "get": { "operationId": "profile_retrieve", "description": "User profile view, only available when logged in", "tags": [ "profile" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Profile" } } }, "description": "" } } }, "patch": { "operationId": "profile_partial_update", "description": "User profile view, only available when logged in", "tags": [ "profile" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedProfileRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedProfileRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedProfileRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Profile" } } }, "description": "" } } } }, "/api/profile/disconnect_social_account/": { "post": { "operationId": "profile_disconnect_social_account_create", "description": "Disconnects a social account provider from the user account", "tags": [ "profile" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer" } }, "required": [ "id" ] } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "integer" } } }, "description": "" }, "400": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "" } } } }, "/api/profile/generate_auth_token/": { "post": { "operationId": "profile_generate_auth_token_create", "description": "Generates (or re-generates) an auth token, requires a logged in user\nunlike the default DRF endpoint", "tags": [ "profile" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "" } } } }, "/api/profile/social_account_providers/": { "get": { "operationId": "profile_social_account_providers_retrieve", "description": "List of social account providers", "tags": [ "profile" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "object", "additionalProperties": {} } } }, "description": "" } } } }, "/api/profile/totp/": { "get": { "operationId": "profile_totp_retrieve", "description": "Generates a new TOTP secret and returns the URL and SVG", "tags": [ "profile" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "object", "additionalProperties": {} } } }, "description": "" } } }, "post": { "operationId": "profile_totp_create", "description": "Validates a TOTP code and activates the TOTP authenticator", "tags": [ "profile" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "secret": { "type": "string" }, "code": { "type": "string" } }, "required": [ "secret", "code" ] } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "object", "additionalProperties": {} } } }, "description": "" } } }, "delete": { "operationId": "profile_totp_destroy", "description": "Deactivates the TOTP authenticator", "tags": [ "profile" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "boolean" } } }, "description": "" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "" } } } }, "/api/remote_version/": { "get": { "operationId": "remote_version_retrieve", "description": "Get the current version of the Paperless-NGX server", "tags": [ "remote_version" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] }, {} ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "object", "additionalProperties": {} } } }, "description": "" } } } }, "/api/saved_views/": { "get": { "operationId": "saved_views_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "saved_views" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedSavedViewList" } } }, "description": "" } } }, "post": { "operationId": "saved_views_create", "tags": [ "saved_views" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SavedViewRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/SavedViewRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/SavedViewRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SavedView" } } }, "description": "" } } } }, "/api/saved_views/{id}/": { "get": { "operationId": "saved_views_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this saved view.", "required": true } ], "tags": [ "saved_views" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SavedView" } } }, "description": "" } } }, "put": { "operationId": "saved_views_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this saved view.", "required": true } ], "tags": [ "saved_views" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SavedViewRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/SavedViewRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/SavedViewRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SavedView" } } }, "description": "" } } }, "patch": { "operationId": "saved_views_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this saved view.", "required": true } ], "tags": [ "saved_views" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedSavedViewRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedSavedViewRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedSavedViewRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SavedView" } } }, "description": "" } } }, "delete": { "operationId": "saved_views_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this saved view.", "required": true } ], "tags": [ "saved_views" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/search/": { "get": { "operationId": "search_retrieve", "description": "Global search", "parameters": [ { "in": "query", "name": "db_only", "schema": { "type": "boolean" }, "description": "Search only the database" }, { "in": "query", "name": "query", "schema": { "type": "string" }, "description": "Query to search for", "required": true } ], "tags": [ "search" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchResult" } } }, "description": "" } } } }, "/api/search/autocomplete/": { "get": { "operationId": "search_autocomplete_list", "description": "Get a list of all available tags", "parameters": [ { "in": "query", "name": "limit", "schema": { "type": "integer" }, "description": "Number of completions to return" }, { "in": "query", "name": "term", "schema": { "type": "string" }, "description": "Term to search for" } ], "tags": [ "search" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } }, "description": "" } } } }, "/api/share_links/": { "get": { "operationId": "share_links_list", "parameters": [ { "in": "query", "name": "created__date__gt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__date__gte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__date__lt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__date__lte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "created__day", "schema": { "type": "number" } }, { "in": "query", "name": "created__gt", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "created__gte", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "created__lt", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "created__lte", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "created__month", "schema": { "type": "number" } }, { "in": "query", "name": "created__year", "schema": { "type": "number" } }, { "in": "query", "name": "expiration__date__gt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "expiration__date__gte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "expiration__date__lt", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "expiration__date__lte", "schema": { "type": "string", "format": "date" } }, { "in": "query", "name": "expiration__day", "schema": { "type": "number" } }, { "in": "query", "name": "expiration__gt", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "expiration__gte", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "expiration__lt", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "expiration__lte", "schema": { "type": "string", "format": "date-time" } }, { "in": "query", "name": "expiration__month", "schema": { "type": "number" } }, { "in": "query", "name": "expiration__year", "schema": { "type": "number" } }, { "name": "ordering", "required": false, "in": "query", "description": "Which field to use when ordering the results.", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "share_links" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedShareLinkList" } } }, "description": "" } } }, "post": { "operationId": "share_links_create", "tags": [ "share_links" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ShareLinkRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/ShareLinkRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/ShareLinkRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ShareLink" } } }, "description": "" } } } }, "/api/share_links/{id}/": { "get": { "operationId": "share_links_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this share link.", "required": true } ], "tags": [ "share_links" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ShareLink" } } }, "description": "" } } }, "put": { "operationId": "share_links_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this share link.", "required": true } ], "tags": [ "share_links" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ShareLinkRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/ShareLinkRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/ShareLinkRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ShareLink" } } }, "description": "" } } }, "patch": { "operationId": "share_links_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this share link.", "required": true } ], "tags": [ "share_links" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedShareLinkRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedShareLinkRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedShareLinkRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ShareLink" } } }, "description": "" } } }, "delete": { "operationId": "share_links_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this share link.", "required": true } ], "tags": [ "share_links" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/statistics/": { "get": { "operationId": "statistics_retrieve", "description": "Get statistics for the current user", "tags": [ "statistics" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "object", "additionalProperties": {} } } }, "description": "" } } } }, "/api/status/": { "get": { "operationId": "status_retrieve", "description": "Get the current system status of the Paperless-NGX server", "tags": [ "status" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SystemStatus" } } }, "description": "" } } } }, "/api/storage_paths/": { "get": { "operationId": "storage_paths_list", "parameters": [ { "in": "query", "name": "full_perms", "schema": { "type": "boolean" } }, { "in": "query", "name": "id", "schema": { "type": "integer" } }, { "in": "query", "name": "id__in", "schema": { "type": "array", "items": { "type": "integer" } }, "description": "Multiple values may be separated by commas.", "explode": false, "style": "form" }, { "in": "query", "name": "name__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "name__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "name__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "name__istartswith", "schema": { "type": "string" } }, { "name": "ordering", "required": false, "in": "query", "description": "Which field to use when ordering the results.", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } }, { "in": "query", "name": "path__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "path__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "path__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "path__istartswith", "schema": { "type": "string" } } ], "tags": [ "storage_paths" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedStoragePathList" } } }, "description": "" } } }, "post": { "operationId": "storage_paths_create", "tags": [ "storage_paths" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoragePathRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/StoragePathRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/StoragePathRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoragePath" } } }, "description": "" } } } }, "/api/storage_paths/{id}/": { "get": { "operationId": "storage_paths_retrieve", "parameters": [ { "in": "query", "name": "full_perms", "schema": { "type": "boolean" } }, { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this storage path.", "required": true } ], "tags": [ "storage_paths" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoragePath" } } }, "description": "" } } }, "put": { "operationId": "storage_paths_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this storage path.", "required": true } ], "tags": [ "storage_paths" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoragePathRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/StoragePathRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/StoragePathRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoragePath" } } }, "description": "" } } }, "patch": { "operationId": "storage_paths_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this storage path.", "required": true } ], "tags": [ "storage_paths" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedStoragePathRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedStoragePathRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedStoragePathRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoragePath" } } }, "description": "" } } }, "delete": { "operationId": "storage_paths_destroy", "description": "When a storage path is deleted, see if documents\nusing it require a rename/move", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this storage path.", "required": true } ], "tags": [ "storage_paths" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/storage_paths/test/": { "post": { "operationId": "storage_paths_test_create", "description": "Test storage path against a document", "tags": [ "storage_paths" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoragePathRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/StoragePathRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/StoragePathRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StoragePath" } } }, "description": "" } } } }, "/api/tags/": { "get": { "operationId": "tags_list", "parameters": [ { "in": "query", "name": "full_perms", "schema": { "type": "boolean" } }, { "in": "query", "name": "id", "schema": { "type": "integer" } }, { "in": "query", "name": "id__in", "schema": { "type": "array", "items": { "type": "integer" } }, "description": "Multiple values may be separated by commas.", "explode": false, "style": "form" }, { "in": "query", "name": "name__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "name__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "name__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "name__istartswith", "schema": { "type": "string" } }, { "name": "ordering", "required": false, "in": "query", "description": "Which field to use when ordering the results.", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "tags" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedTagList" } } }, "description": "" } } }, "post": { "operationId": "tags_create", "tags": [ "tags" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TagRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/TagRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/TagRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Tag" } } }, "description": "" } } } }, "/api/tags/{id}/": { "get": { "operationId": "tags_retrieve", "parameters": [ { "in": "query", "name": "full_perms", "schema": { "type": "boolean" } }, { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this tag.", "required": true } ], "tags": [ "tags" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Tag" } } }, "description": "" } } }, "put": { "operationId": "tags_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this tag.", "required": true } ], "tags": [ "tags" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TagRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/TagRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/TagRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Tag" } } }, "description": "" } } }, "patch": { "operationId": "tags_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this tag.", "required": true } ], "tags": [ "tags" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedTagRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedTagRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedTagRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Tag" } } }, "description": "" } } }, "delete": { "operationId": "tags_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this tag.", "required": true } ], "tags": [ "tags" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/tasks/": { "get": { "operationId": "tasks_list", "parameters": [ { "in": "query", "name": "acknowledged", "schema": { "type": "boolean" }, "description": "Acknowledged" }, { "name": "ordering", "required": false, "in": "query", "description": "Which field to use when ordering the results.", "schema": { "type": "string" } }, { "in": "query", "name": "status", "schema": { "type": "string", "title": "Task State", "enum": [ "FAILURE", "PENDING", "RECEIVED", "RETRY", "REVOKED", "STARTED", "SUCCESS" ] }, "description": "Current state of the task being run\n\n* `FAILURE` - FAILURE\n* `PENDING` - PENDING\n* `RECEIVED` - RECEIVED\n* `RETRY` - RETRY\n* `REVOKED` - REVOKED\n* `STARTED` - STARTED\n* `SUCCESS` - SUCCESS" }, { "in": "query", "name": "task_id", "schema": { "type": "string" }, "description": "Filter tasks by Celery UUID" }, { "in": "query", "name": "task_name", "schema": { "type": "string", "nullable": true, "enum": [ "check_sanity", "consume_file", "index_optimize", "train_classifier" ] }, "description": "Name of the task that was run\n\n* `consume_file` - Consume File\n* `train_classifier` - Train Classifier\n* `check_sanity` - Check Sanity\n* `index_optimize` - Index Optimize" }, { "in": "query", "name": "type", "schema": { "type": "string", "title": "Task Type", "enum": [ "auto_task", "manual_task", "scheduled_task" ] }, "description": "The type of task that was run\n\n* `auto_task` - Auto Task\n* `scheduled_task` - Scheduled Task\n* `manual_task` - Manual Task" } ], "tags": [ "tasks" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/TasksView" } } } }, "description": "" } } } }, "/api/tasks/{id}/": { "get": { "operationId": "tasks_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this paperless task.", "required": true }, { "in": "query", "name": "task_id", "schema": { "type": "string" }, "description": "Filter tasks by Celery UUID" } ], "tags": [ "tasks" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TasksView" } } }, "description": "" } } } }, "/api/tasks/acknowledge/": { "post": { "operationId": "acknowledge_tasks", "description": "Acknowledge a list of tasks", "parameters": [ { "in": "query", "name": "task_id", "schema": { "type": "string" }, "description": "Filter tasks by Celery UUID" } ], "tags": [ "tasks" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "tasks": { "type": "array", "items": { "type": "integer" } } }, "required": [ "tasks" ] } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AcknowledgeTasks" } } }, "description": "" }, "400": { "description": "No response body" } } } }, "/api/tasks/run/": { "post": { "operationId": "tasks_run_create", "parameters": [ { "in": "query", "name": "task_id", "schema": { "type": "string" }, "description": "Filter tasks by Celery UUID" } ], "tags": [ "tasks" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TasksViewRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/TasksViewRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/TasksViewRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TasksView" } } }, "description": "" } } } }, "/api/token/": { "post": { "operationId": "token_create", "tags": [ "token" ], "requestBody": { "content": { "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PaperlessAuthTokenRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PaperlessAuthTokenRequest" } }, "application/json": { "schema": { "$ref": "#/components/schemas/PaperlessAuthTokenRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaperlessAuthToken" } } }, "description": "" } } } }, "/api/trash/": { "get": { "operationId": "trash_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "trash" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "description": "No response body" } } }, "post": { "operationId": "trash_create", "tags": [ "trash" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TrashRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/TrashRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/TrashRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "description": "No response body" } } } }, "/api/ui_settings/": { "get": { "operationId": "ui_settings_retrieve", "tags": [ "ui_settings" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UiSettingsView" } } }, "description": "" } } }, "post": { "operationId": "ui_settings_create", "tags": [ "ui_settings" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UiSettingsViewRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/UiSettingsViewRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/UiSettingsViewRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UiSettingsView" } } }, "description": "" } } } }, "/api/users/": { "get": { "operationId": "users_list", "parameters": [ { "name": "ordering", "required": false, "in": "query", "description": "Which field to use when ordering the results.", "schema": { "type": "string" } }, { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } }, { "in": "query", "name": "username__icontains", "schema": { "type": "string" } }, { "in": "query", "name": "username__iendswith", "schema": { "type": "string" } }, { "in": "query", "name": "username__iexact", "schema": { "type": "string" } }, { "in": "query", "name": "username__istartswith", "schema": { "type": "string" } } ], "tags": [ "users" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedUserList" } } }, "description": "" } } }, "post": { "operationId": "users_create", "tags": [ "users" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/UserRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/UserRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } }, "description": "" } } } }, "/api/users/{id}/": { "get": { "operationId": "users_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this user.", "required": true } ], "tags": [ "users" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } }, "description": "" } } }, "put": { "operationId": "users_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this user.", "required": true } ], "tags": [ "users" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/UserRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/UserRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } }, "description": "" } } }, "patch": { "operationId": "users_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this user.", "required": true } ], "tags": [ "users" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedUserRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedUserRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedUserRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } }, "description": "" } } }, "delete": { "operationId": "users_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this user.", "required": true } ], "tags": [ "users" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/users/{id}/deactivate_totp/": { "post": { "operationId": "users_deactivate_totp_create", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this user.", "required": true } ], "tags": [ "users" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "boolean" } } }, "description": "" }, "404": { "content": { "application/json": { "schema": { "type": "string" } } }, "description": "" } } } }, "/api/workflow_actions/": { "get": { "operationId": "workflow_actions_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "workflow_actions" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedWorkflowActionList" } } }, "description": "" } } }, "post": { "operationId": "workflow_actions_create", "tags": [ "workflow_actions" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowActionRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/WorkflowActionRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/WorkflowActionRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowAction" } } }, "description": "" } } } }, "/api/workflow_actions/{id}/": { "get": { "operationId": "workflow_actions_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow action.", "required": true } ], "tags": [ "workflow_actions" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowAction" } } }, "description": "" } } }, "put": { "operationId": "workflow_actions_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow action.", "required": true } ], "tags": [ "workflow_actions" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowActionRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/WorkflowActionRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/WorkflowActionRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowAction" } } }, "description": "" } } }, "patch": { "operationId": "workflow_actions_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow action.", "required": true } ], "tags": [ "workflow_actions" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedWorkflowActionRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedWorkflowActionRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedWorkflowActionRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowAction" } } }, "description": "" } } }, "delete": { "operationId": "workflow_actions_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow action.", "required": true } ], "tags": [ "workflow_actions" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/workflow_triggers/": { "get": { "operationId": "workflow_triggers_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "workflow_triggers" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedWorkflowTriggerList" } } }, "description": "" } } }, "post": { "operationId": "workflow_triggers_create", "tags": [ "workflow_triggers" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowTriggerRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/WorkflowTriggerRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/WorkflowTriggerRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowTrigger" } } }, "description": "" } } } }, "/api/workflow_triggers/{id}/": { "get": { "operationId": "workflow_triggers_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow trigger.", "required": true } ], "tags": [ "workflow_triggers" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowTrigger" } } }, "description": "" } } }, "put": { "operationId": "workflow_triggers_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow trigger.", "required": true } ], "tags": [ "workflow_triggers" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowTriggerRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/WorkflowTriggerRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/WorkflowTriggerRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowTrigger" } } }, "description": "" } } }, "patch": { "operationId": "workflow_triggers_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow trigger.", "required": true } ], "tags": [ "workflow_triggers" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedWorkflowTriggerRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedWorkflowTriggerRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedWorkflowTriggerRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowTrigger" } } }, "description": "" } } }, "delete": { "operationId": "workflow_triggers_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow trigger.", "required": true } ], "tags": [ "workflow_triggers" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } }, "/api/workflows/": { "get": { "operationId": "workflows_list", "parameters": [ { "name": "page", "required": false, "in": "query", "description": "A page number within the paginated result set.", "schema": { "type": "integer" } }, { "name": "page_size", "required": false, "in": "query", "description": "Number of results to return per page.", "schema": { "type": "integer" } } ], "tags": [ "workflows" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedWorkflowList" } } }, "description": "" } } }, "post": { "operationId": "workflows_create", "tags": [ "workflows" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/WorkflowRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/WorkflowRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Workflow" } } }, "description": "" } } } }, "/api/workflows/{id}/": { "get": { "operationId": "workflows_retrieve", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow.", "required": true } ], "tags": [ "workflows" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Workflow" } } }, "description": "" } } }, "put": { "operationId": "workflows_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow.", "required": true } ], "tags": [ "workflows" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkflowRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/WorkflowRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/WorkflowRequest" } } }, "required": true }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Workflow" } } }, "description": "" } } }, "patch": { "operationId": "workflows_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow.", "required": true } ], "tags": [ "workflows" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PatchedWorkflowRequest" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/PatchedWorkflowRequest" } }, "multipart/form-data": { "schema": { "$ref": "#/components/schemas/PatchedWorkflowRequest" } } } }, "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Workflow" } } }, "description": "" } } }, "delete": { "operationId": "workflows_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { "type": "integer" }, "description": "A unique integer value identifying this workflow.", "required": true } ], "tags": [ "workflows" ], "security": [ { "PaperelessBasicAuthentication": [] }, { "tokenAuth": [] }, { "cookieAuth": [] } ], "responses": { "204": { "description": "No response body" } } } } }, "components": { "schemas": { "AccountTypeEnum": { "enum": [ 1, 2, 3 ], "type": "integer", "description": "* `1` - IMAP\n* `2` - Gmail OAuth\n* `3` - Outlook OAuth" }, "AcknowledgeTasks": { "type": "object", "properties": { "result": { "type": "integer" } }, "required": [ "result" ] }, "Actor": { "type": "object", "properties": { "id": { "type": "integer" }, "username": { "type": "string" } }, "required": [ "id", "username" ] }, "ApplicationConfiguration": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "user_args": { "nullable": true }, "barcode_tag_mapping": { "nullable": true }, "output_type": { "nullable": true, "title": "Sets the output PDF type", "oneOf": [ { "$ref": "#/components/schemas/OutputTypeEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "pages": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Do OCR from page 1 to this value" }, "language": { "type": "string", "nullable": true, "title": "Do OCR using these languages", "maxLength": 32 }, "mode": { "nullable": true, "title": "Sets the OCR mode", "oneOf": [ { "$ref": "#/components/schemas/ModeEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "skip_archive_file": { "nullable": true, "title": "Controls the generation of an archive file", "oneOf": [ { "$ref": "#/components/schemas/SkipArchiveFileEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "image_dpi": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Sets image DPI fallback value" }, "unpaper_clean": { "nullable": true, "title": "Controls the unpaper cleaning", "oneOf": [ { "$ref": "#/components/schemas/UnpaperCleanEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "deskew": { "type": "boolean", "nullable": true, "title": "Enables deskew" }, "rotate_pages": { "type": "boolean", "nullable": true, "title": "Enables page rotation" }, "rotate_pages_threshold": { "type": "number", "format": "double", "minimum": 0.0, "nullable": true, "title": "Sets the threshold for rotation of pages" }, "max_image_pixels": { "type": "number", "format": "double", "minimum": 0.0, "nullable": true, "title": "Sets the maximum image size for decompression" }, "color_conversion_strategy": { "nullable": true, "title": "Sets the Ghostscript color conversion strategy", "oneOf": [ { "$ref": "#/components/schemas/ColorConversionStrategyEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "app_title": { "type": "string", "nullable": true, "title": "Application title", "maxLength": 48 }, "app_logo": { "type": "string", "format": "uri", "nullable": true, "title": "Application logo", "pattern": "(?:jpg|png|gif|svg)$" }, "barcodes_enabled": { "type": "boolean", "nullable": true, "title": "Enables barcode scanning" }, "barcode_enable_tiff_support": { "type": "boolean", "nullable": true, "title": "Enables barcode TIFF support" }, "barcode_string": { "type": "string", "nullable": true, "title": "Sets the barcode string", "maxLength": 32 }, "barcode_retain_split_pages": { "type": "boolean", "nullable": true, "title": "Retains split pages" }, "barcode_enable_asn": { "type": "boolean", "nullable": true, "title": "Enables ASN barcode" }, "barcode_asn_prefix": { "type": "string", "nullable": true, "title": "Sets the ASN barcode prefix", "maxLength": 32 }, "barcode_upscale": { "type": "number", "format": "double", "minimum": 1.0, "nullable": true, "title": "Sets the barcode upscale factor" }, "barcode_dpi": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Sets the barcode DPI" }, "barcode_max_pages": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Sets the maximum pages for barcode" }, "barcode_enable_tag": { "type": "boolean", "nullable": true, "title": "Enables tag barcode" } }, "required": [ "barcode_tag_mapping", "id", "user_args" ] }, "ApplicationConfigurationRequest": { "type": "object", "properties": { "user_args": { "nullable": true }, "barcode_tag_mapping": { "nullable": true }, "output_type": { "nullable": true, "title": "Sets the output PDF type", "oneOf": [ { "$ref": "#/components/schemas/OutputTypeEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "pages": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Do OCR from page 1 to this value" }, "language": { "type": "string", "nullable": true, "title": "Do OCR using these languages", "maxLength": 32 }, "mode": { "nullable": true, "title": "Sets the OCR mode", "oneOf": [ { "$ref": "#/components/schemas/ModeEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "skip_archive_file": { "nullable": true, "title": "Controls the generation of an archive file", "oneOf": [ { "$ref": "#/components/schemas/SkipArchiveFileEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "image_dpi": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Sets image DPI fallback value" }, "unpaper_clean": { "nullable": true, "title": "Controls the unpaper cleaning", "oneOf": [ { "$ref": "#/components/schemas/UnpaperCleanEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "deskew": { "type": "boolean", "nullable": true, "title": "Enables deskew" }, "rotate_pages": { "type": "boolean", "nullable": true, "title": "Enables page rotation" }, "rotate_pages_threshold": { "type": "number", "format": "double", "minimum": 0.0, "nullable": true, "title": "Sets the threshold for rotation of pages" }, "max_image_pixels": { "type": "number", "format": "double", "minimum": 0.0, "nullable": true, "title": "Sets the maximum image size for decompression" }, "color_conversion_strategy": { "nullable": true, "title": "Sets the Ghostscript color conversion strategy", "oneOf": [ { "$ref": "#/components/schemas/ColorConversionStrategyEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "app_title": { "type": "string", "nullable": true, "title": "Application title", "maxLength": 48 }, "app_logo": { "type": "string", "format": "binary", "nullable": true, "title": "Application logo", "pattern": "(?:jpg|png|gif|svg)$" }, "barcodes_enabled": { "type": "boolean", "nullable": true, "title": "Enables barcode scanning" }, "barcode_enable_tiff_support": { "type": "boolean", "nullable": true, "title": "Enables barcode TIFF support" }, "barcode_string": { "type": "string", "nullable": true, "title": "Sets the barcode string", "maxLength": 32 }, "barcode_retain_split_pages": { "type": "boolean", "nullable": true, "title": "Retains split pages" }, "barcode_enable_asn": { "type": "boolean", "nullable": true, "title": "Enables ASN barcode" }, "barcode_asn_prefix": { "type": "string", "nullable": true, "title": "Sets the ASN barcode prefix", "maxLength": 32 }, "barcode_upscale": { "type": "number", "format": "double", "minimum": 1.0, "nullable": true, "title": "Sets the barcode upscale factor" }, "barcode_dpi": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Sets the barcode DPI" }, "barcode_max_pages": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Sets the maximum pages for barcode" }, "barcode_enable_tag": { "type": "boolean", "nullable": true, "title": "Enables tag barcode" } }, "required": [ "barcode_tag_mapping", "user_args" ] }, "AssignCorrespondentFromEnum": { "enum": [ 1, 2, 3, 4 ], "type": "integer", "description": "* `1` - Do not assign a correspondent\n* `2` - Use mail address\n* `3` - Use name (or mail address if not available)\n* `4` - Use correspondent selected below" }, "AssignTitleFromEnum": { "enum": [ 1, 2, 3 ], "type": "integer", "description": "* `1` - Use subject as title\n* `2` - Use attachment filename as title\n* `3` - Do not assign title from rule" }, "AttachmentTypeEnum": { "enum": [ 1, 2 ], "type": "integer", "description": "* `1` - Only process attachments.\n* `2` - Process all files, including 'inline' attachments." }, "BasicUser": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "username": { "type": "string", "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", "pattern": "^[\\w.@+-]+$", "maxLength": 150 }, "first_name": { "type": "string", "maxLength": 150 }, "last_name": { "type": "string", "maxLength": 150 } }, "required": [ "id", "username" ] }, "BasicUserRequest": { "type": "object", "properties": { "username": { "type": "string", "minLength": 1, "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", "pattern": "^[\\w.@+-]+$", "maxLength": 150 }, "first_name": { "type": "string", "maxLength": 150 }, "last_name": { "type": "string", "maxLength": 150 } }, "required": [ "username" ] }, "BlankEnum": { "enum": [ "" ] }, "BulkDownload": { "type": "object", "properties": { "content": { "allOf": [ { "$ref": "#/components/schemas/ContentEnum" } ], "default": "archive" }, "compression": { "allOf": [ { "$ref": "#/components/schemas/CompressionEnum" } ], "default": "none" }, "follow_formatting": { "type": "boolean", "default": false } } }, "BulkDownloadRequest": { "type": "object", "properties": { "documents": { "type": "array", "items": { "type": "integer" }, "writeOnly": true }, "content": { "allOf": [ { "$ref": "#/components/schemas/ContentEnum" } ], "default": "archive" }, "compression": { "allOf": [ { "$ref": "#/components/schemas/CompressionEnum" } ], "default": "none" }, "follow_formatting": { "type": "boolean", "default": false } }, "required": [ "documents" ] }, "BulkEditDocumentsResult": { "type": "object", "properties": { "result": { "type": "string" } }, "required": [ "result" ] }, "BulkEditObjectsRequest": { "type": "object", "properties": { "objects": { "type": "array", "items": { "type": "integer" }, "writeOnly": true }, "object_type": { "allOf": [ { "$ref": "#/components/schemas/ObjectTypeEnum" } ], "writeOnly": true }, "operation": { "allOf": [ { "$ref": "#/components/schemas/OperationEnum" } ], "writeOnly": true }, "owner": { "type": "integer", "nullable": true }, "permissions": { "type": "object", "additionalProperties": {}, "writeOnly": true, "title": "Set permissions" }, "merge": { "type": "boolean", "writeOnly": true, "default": false } }, "required": [ "object_type", "objects", "operation" ] }, "BulkEditRequest": { "type": "object", "properties": { "documents": { "type": "array", "items": { "type": "integer" }, "writeOnly": true }, "method": { "allOf": [ { "$ref": "#/components/schemas/MethodEnum" } ], "writeOnly": true }, "parameters": { "type": "object", "additionalProperties": {}, "writeOnly": true, "default": {} } }, "required": [ "documents", "method" ] }, "BulkEditResult": { "type": "object", "properties": { "result": { "type": "string" } }, "required": [ "result" ] }, "Classifier": { "type": "object", "properties": { "status": { "type": "string" }, "error": { "type": "string" }, "last_trained": { "type": "string", "format": "date-time" } }, "required": [ "error", "last_trained", "status" ] }, "ColorConversionStrategyEnum": { "enum": [ "LeaveColorUnchanged", "RGB", "UseDeviceIndependentColor", "Gray", "CMYK" ], "type": "string", "description": "* `LeaveColorUnchanged` - LeaveColorUnchanged\n* `RGB` - RGB\n* `UseDeviceIndependentColor` - UseDeviceIndependentColor\n* `Gray` - Gray\n* `CMYK` - CMYK" }, "CompressionEnum": { "enum": [ "none", "deflated", "bzip2", "lzma" ], "type": "string", "description": "* `none` - none\n* `deflated` - deflated\n* `bzip2` - bzip2\n* `lzma` - lzma" }, "ConsumptionScopeEnum": { "enum": [ 1, 2, 3 ], "type": "integer", "description": "* `1` - Only process attachments.\n* `2` - Process full Mail (with embedded attachments in file) as .eml\n* `3` - Process full Mail (with embedded attachments in file) as .eml + process attachments as separate documents" }, "ContentEnum": { "enum": [ "archive", "originals", "both" ], "type": "string", "description": "* `archive` - archive\n* `originals` - originals\n* `both` - both" }, "Correspondent": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "slug": { "type": "string", "readOnly": true }, "name": { "type": "string", "maxLength": 128 }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "document_count": { "type": "integer", "readOnly": true }, "last_correspondence": { "type": "string", "format": "date", "readOnly": true }, "owner": { "type": "integer", "nullable": true }, "permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "readOnly": true }, "user_can_change": { "type": "boolean", "readOnly": true } }, "required": [ "document_count", "id", "last_correspondence", "name", "permissions", "slug", "user_can_change" ] }, "CorrespondentCounts": { "type": "object", "properties": { "id": { "type": "integer" }, "document_count": { "type": "integer" } }, "required": [ "document_count", "id" ] }, "CorrespondentRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true } }, "required": [ "name" ] }, "CustomField": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "name": { "type": "string", "maxLength": 128 }, "data_type": { "$ref": "#/components/schemas/DataTypeEnum" }, "extra_data": { "nullable": true, "description": "Extra data for the custom field, such as select options" }, "document_count": { "type": "integer", "readOnly": true } }, "required": [ "data_type", "document_count", "id", "name" ] }, "CustomFieldCounts": { "type": "object", "properties": { "id": { "type": "integer" }, "document_count": { "type": "integer" } }, "required": [ "document_count", "id" ] }, "CustomFieldInstance": { "type": "object", "properties": { "value": { "oneOf": [ { "type": "string" }, { "type": "integer" }, { "type": "number", "format": "double" }, { "type": "object", "additionalProperties": {} } ], "nullable": true, "description": "Given the *incoming* primitive data, return the value for this field\nthat should be validated and transformed to a native value." }, "field": { "type": "integer" } }, "required": [ "field", "value" ] }, "CustomFieldInstanceRequest": { "type": "object", "properties": { "value": { "oneOf": [ { "type": "string" }, { "type": "integer" }, { "type": "number", "format": "double" }, { "type": "object", "additionalProperties": {} } ], "nullable": true, "description": "Given the *incoming* primitive data, return the value for this field\nthat should be validated and transformed to a native value." }, "field": { "type": "integer" } }, "required": [ "field", "value" ] }, "CustomFieldRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "data_type": { "$ref": "#/components/schemas/DataTypeEnum" }, "extra_data": { "nullable": true, "description": "Extra data for the custom field, such as select options" } }, "required": [ "data_type", "name" ] }, "DataTypeEnum": { "enum": [ "string", "url", "date", "boolean", "integer", "float", "monetary", "documentlink", "select" ], "type": "string", "description": "* `string` - string\n* `url` - url\n* `date` - date\n* `boolean` - boolean\n* `integer` - integer\n* `float` - float\n* `monetary` - monetary\n* `documentlink` - documentlink\n* `select` - select" }, "Database": { "type": "object", "properties": { "type": { "type": "string" }, "url": { "type": "string" }, "status": { "type": "string" }, "error": { "type": "string" }, "migration_status": { "$ref": "#/components/schemas/MigrationStatus" } }, "required": [ "error", "migration_status", "status", "type", "url" ] }, "DisplayModeEnum": { "enum": [ "table", "smallCards", "largeCards" ], "type": "string", "description": "* `table` - Table\n* `smallCards` - Small Cards\n* `largeCards` - Large Cards" }, "Document": { "type": "object", "description": "Adds update nested feature", "properties": { "id": { "type": "integer", "readOnly": true }, "correspondent": { "type": "integer", "nullable": true }, "document_type": { "type": "integer", "nullable": true }, "storage_path": { "type": "integer", "nullable": true }, "title": { "type": "string", "maxLength": 128 }, "content": { "type": "string", "description": "The raw, text-only data of the document. This field is primarily used for searching." }, "tags": { "type": "array", "items": { "type": "integer" } }, "created": { "type": "string", "format": "date" }, "created_date": { "type": "string", "format": "date", "deprecated": true }, "modified": { "type": "string", "format": "date-time", "readOnly": true }, "added": { "type": "string", "format": "date-time", "readOnly": true }, "deleted_at": { "type": "string", "format": "date-time", "nullable": true }, "archive_serial_number": { "type": "integer", "maximum": 4294967295, "minimum": 0, "format": "int64", "nullable": true, "description": "The position of this document in your physical document archive." }, "original_file_name": { "type": "string", "nullable": true, "readOnly": true }, "archived_file_name": { "type": "string", "nullable": true, "readOnly": true }, "owner": { "type": "integer", "nullable": true }, "permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "readOnly": true }, "user_can_change": { "type": "boolean", "readOnly": true }, "is_shared_by_requester": { "type": "boolean", "readOnly": true }, "notes": { "type": "array", "items": { "$ref": "#/components/schemas/Notes" }, "readOnly": true }, "custom_fields": { "type": "array", "items": { "$ref": "#/components/schemas/CustomFieldInstance" } }, "page_count": { "type": "integer", "nullable": true, "readOnly": true }, "mime_type": { "type": "string", "readOnly": true } }, "required": [ "added", "archived_file_name", "correspondent", "document_type", "id", "is_shared_by_requester", "mime_type", "modified", "notes", "original_file_name", "page_count", "permissions", "storage_path", "tags", "user_can_change" ] }, "DocumentListRequest": { "type": "object", "properties": { "documents": { "type": "array", "items": { "type": "integer" }, "writeOnly": true } }, "required": [ "documents" ] }, "DocumentRequest": { "type": "object", "description": "Adds update nested feature", "properties": { "correspondent": { "type": "integer", "nullable": true }, "document_type": { "type": "integer", "nullable": true }, "storage_path": { "type": "integer", "nullable": true }, "title": { "type": "string", "maxLength": 128 }, "content": { "type": "string", "description": "The raw, text-only data of the document. This field is primarily used for searching." }, "tags": { "type": "array", "items": { "type": "integer" } }, "created": { "type": "string", "format": "date" }, "created_date": { "type": "string", "format": "date", "deprecated": true }, "deleted_at": { "type": "string", "format": "date-time", "nullable": true }, "archive_serial_number": { "type": "integer", "maximum": 4294967295, "minimum": 0, "format": "int64", "nullable": true, "description": "The position of this document in your physical document archive." }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true }, "custom_fields": { "type": "array", "items": { "$ref": "#/components/schemas/CustomFieldInstanceRequest" } }, "remove_inbox_tags": { "type": "boolean", "writeOnly": true, "nullable": true, "default": false } }, "required": [ "correspondent", "document_type", "storage_path", "tags" ] }, "DocumentType": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "slug": { "type": "string", "readOnly": true }, "name": { "type": "string", "maxLength": 128 }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "document_count": { "type": "integer", "readOnly": true }, "owner": { "type": "integer", "nullable": true }, "permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "readOnly": true }, "user_can_change": { "type": "boolean", "readOnly": true } }, "required": [ "document_count", "id", "name", "permissions", "slug", "user_can_change" ] }, "DocumentTypeCounts": { "type": "object", "properties": { "id": { "type": "integer" }, "document_count": { "type": "integer" } }, "required": [ "document_count", "id" ] }, "DocumentTypeRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true } }, "required": [ "name" ] }, "EmailRequestRequest": { "type": "object", "properties": { "addresses": { "type": "string", "minLength": 1 }, "subject": { "type": "string", "minLength": 1 }, "message": { "type": "string", "minLength": 1 }, "use_archive_version": { "type": "boolean", "default": true } }, "required": [ "addresses", "message", "subject" ] }, "EmailResponse": { "type": "object", "properties": { "message": { "type": "string" } }, "required": [ "message" ] }, "FileVersionEnum": { "enum": [ "archive", "original" ], "type": "string", "description": "* `archive` - Archive\n* `original` - Original" }, "Group": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "name": { "type": "string", "maxLength": 150 }, "permissions": { "type": "array", "items": { "type": "string" } } }, "required": [ "id", "name", "permissions" ] }, "GroupRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 150 }, "permissions": { "type": "array", "items": { "type": "string", "minLength": 1 } } }, "required": [ "name", "permissions" ] }, "ImapSecurityEnum": { "enum": [ 1, 2, 3 ], "type": "integer", "description": "* `1` - No encryption\n* `2` - Use SSL\n* `3` - Use STARTTLS" }, "Index": { "type": "object", "properties": { "status": { "type": "string" }, "error": { "type": "string" }, "last_modified": { "type": "string", "format": "date-time" } }, "required": [ "error", "last_modified", "status" ] }, "LogEntry": { "type": "object", "properties": { "id": { "type": "integer" }, "timestamp": { "type": "string", "format": "date-time" }, "action": { "type": "string" }, "changes": { "type": "object", "additionalProperties": {} }, "actor": { "$ref": "#/components/schemas/Actor" } }, "required": [ "action", "actor", "changes", "id", "timestamp" ] }, "MailAccount": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "name": { "type": "string", "maxLength": 256 }, "imap_server": { "type": "string", "maxLength": 256 }, "imap_port": { "type": "integer", "maximum": 9223372036854775807, "minimum": -9223372036854775808, "format": "int64", "nullable": true, "description": "This is usually 143 for unencrypted and STARTTLS connections, and 993 for SSL connections." }, "imap_security": { "allOf": [ { "$ref": "#/components/schemas/ImapSecurityEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "username": { "type": "string", "maxLength": 256 }, "password": { "type": "string" }, "character_set": { "type": "string", "description": "The character set to use when communicating with the mail server, such as 'UTF-8' or 'US-ASCII'.", "maxLength": 256 }, "is_token": { "type": "boolean", "title": "Is token authentication" }, "owner": { "type": "integer", "nullable": true }, "user_can_change": { "type": "boolean", "readOnly": true }, "account_type": { "allOf": [ { "$ref": "#/components/schemas/AccountTypeEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "expiration": { "type": "string", "format": "date-time", "nullable": true, "description": "The expiration date of the refresh token. " } }, "required": [ "id", "imap_server", "name", "password", "user_can_change", "username" ] }, "MailAccountProcessResponse": { "type": "object", "properties": { "result": { "type": "string", "default": "OK" } } }, "MailAccountRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 256 }, "imap_server": { "type": "string", "minLength": 1, "maxLength": 256 }, "imap_port": { "type": "integer", "maximum": 9223372036854775807, "minimum": -9223372036854775808, "format": "int64", "nullable": true, "description": "This is usually 143 for unencrypted and STARTTLS connections, and 993 for SSL connections." }, "imap_security": { "allOf": [ { "$ref": "#/components/schemas/ImapSecurityEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "username": { "type": "string", "minLength": 1, "maxLength": 256 }, "password": { "type": "string", "minLength": 1 }, "character_set": { "type": "string", "minLength": 1, "description": "The character set to use when communicating with the mail server, such as 'UTF-8' or 'US-ASCII'.", "maxLength": 256 }, "is_token": { "type": "boolean", "title": "Is token authentication" }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true }, "account_type": { "allOf": [ { "$ref": "#/components/schemas/AccountTypeEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "expiration": { "type": "string", "format": "date-time", "nullable": true, "description": "The expiration date of the refresh token. " } }, "required": [ "imap_server", "name", "password", "username" ] }, "MailAccountTestResponse": { "type": "object", "properties": { "success": { "type": "boolean" } }, "required": [ "success" ] }, "MailRule": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "name": { "type": "string", "maxLength": 256 }, "account": { "type": "integer" }, "enabled": { "type": "boolean" }, "folder": { "type": "string", "description": "Subfolders must be separated by a delimiter, often a dot ('.') or slash ('/'), but it varies by mail server.", "maxLength": 256 }, "filter_from": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_to": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_subject": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_body": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_attachment_filename_include": { "type": "string", "nullable": true, "title": "Filter attachment filename inclusive", "description": "Only consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.", "maxLength": 256 }, "filter_attachment_filename_exclude": { "type": "string", "nullable": true, "title": "Filter attachment filename exclusive", "description": "Do not consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.", "maxLength": 256 }, "maximum_age": { "type": "integer", "maximum": 9223372036854775807, "minimum": 0, "format": "int64", "description": "Specified in days." }, "action": { "allOf": [ { "$ref": "#/components/schemas/MailRuleActionEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "action_parameter": { "type": "string", "nullable": true, "default": "" }, "assign_title_from": { "allOf": [ { "$ref": "#/components/schemas/AssignTitleFromEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "assign_tags": { "type": "array", "items": { "type": "integer", "nullable": true } }, "assign_correspondent_from": { "allOf": [ { "$ref": "#/components/schemas/AssignCorrespondentFromEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "assign_correspondent": { "type": "integer", "nullable": true }, "assign_document_type": { "type": "integer", "nullable": true }, "assign_owner_from_rule": { "type": "boolean", "title": "Assign the rule owner to documents" }, "order": { "type": "integer" }, "attachment_type": { "allOf": [ { "$ref": "#/components/schemas/AttachmentTypeEnum" } ], "description": "Inline attachments include embedded images, so it's best to combine this option with a filename filter.\n\n* `1` - Only process attachments.\n* `2` - Process all files, including 'inline' attachments.", "minimum": 0, "maximum": 9223372036854775807 }, "consumption_scope": { "allOf": [ { "$ref": "#/components/schemas/ConsumptionScopeEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "pdf_layout": { "allOf": [ { "$ref": "#/components/schemas/PdfLayoutEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "owner": { "type": "integer", "nullable": true }, "user_can_change": { "type": "boolean", "readOnly": true } }, "required": [ "account", "id", "name", "user_can_change" ] }, "MailRuleActionEnum": { "enum": [ 1, 2, 3, 4, 5 ], "type": "integer", "description": "* `1` - Delete\n* `2` - Move to specified folder\n* `3` - Mark as read, don't process read mails\n* `4` - Flag the mail, don't process flagged mails\n* `5` - Tag the mail with specified tag, don't process tagged mails" }, "MailRuleRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 256 }, "account": { "type": "integer" }, "enabled": { "type": "boolean" }, "folder": { "type": "string", "minLength": 1, "description": "Subfolders must be separated by a delimiter, often a dot ('.') or slash ('/'), but it varies by mail server.", "maxLength": 256 }, "filter_from": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_to": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_subject": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_body": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_attachment_filename_include": { "type": "string", "nullable": true, "title": "Filter attachment filename inclusive", "description": "Only consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.", "maxLength": 256 }, "filter_attachment_filename_exclude": { "type": "string", "nullable": true, "title": "Filter attachment filename exclusive", "description": "Do not consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.", "maxLength": 256 }, "maximum_age": { "type": "integer", "maximum": 9223372036854775807, "minimum": 0, "format": "int64", "description": "Specified in days." }, "action": { "allOf": [ { "$ref": "#/components/schemas/MailRuleActionEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "action_parameter": { "type": "string", "nullable": true, "minLength": 1, "default": "" }, "assign_title_from": { "allOf": [ { "$ref": "#/components/schemas/AssignTitleFromEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "assign_tags": { "type": "array", "items": { "type": "integer", "nullable": true } }, "assign_correspondent_from": { "allOf": [ { "$ref": "#/components/schemas/AssignCorrespondentFromEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "assign_correspondent": { "type": "integer", "nullable": true }, "assign_document_type": { "type": "integer", "nullable": true }, "assign_owner_from_rule": { "type": "boolean", "title": "Assign the rule owner to documents" }, "order": { "type": "integer" }, "attachment_type": { "allOf": [ { "$ref": "#/components/schemas/AttachmentTypeEnum" } ], "description": "Inline attachments include embedded images, so it's best to combine this option with a filename filter.\n\n* `1` - Only process attachments.\n* `2` - Process all files, including 'inline' attachments.", "minimum": 0, "maximum": 9223372036854775807 }, "consumption_scope": { "allOf": [ { "$ref": "#/components/schemas/ConsumptionScopeEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "pdf_layout": { "allOf": [ { "$ref": "#/components/schemas/PdfLayoutEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true } }, "required": [ "account", "name" ] }, "MatchingAlgorithm": { "enum": [ 0, 1, 2, 3, 4, 5, 6 ], "type": "integer", "description": "* `0` - None\n* `1` - Any word\n* `2` - All words\n* `3` - Exact match\n* `4` - Regular expression\n* `5` - Fuzzy word\n* `6` - Automatic" }, "Metadata": { "type": "object", "properties": { "original_checksum": { "type": "string" }, "original_size": { "type": "integer" }, "original_mime_type": { "type": "string" }, "media_filename": { "type": "string" }, "has_archive_version": { "type": "boolean" }, "original_metadata": { "type": "object", "additionalProperties": {} }, "archive_checksum": { "type": "string" }, "archive_media_filename": { "type": "string" }, "original_filename": { "type": "string" }, "archive_size": { "type": "integer" }, "archive_metadata": { "type": "object", "additionalProperties": {} }, "lang": { "type": "string" } }, "required": [ "archive_checksum", "archive_media_filename", "archive_metadata", "archive_size", "has_archive_version", "lang", "media_filename", "original_checksum", "original_filename", "original_metadata", "original_mime_type", "original_size" ] }, "MethodEnum": { "enum": [ "set_correspondent", "set_document_type", "set_storage_path", "add_tag", "remove_tag", "modify_tags", "modify_custom_fields", "delete", "reprocess", "set_permissions", "rotate", "merge", "split", "delete_pages", "edit_pdf" ], "type": "string", "description": "* `set_correspondent` - set_correspondent\n* `set_document_type` - set_document_type\n* `set_storage_path` - set_storage_path\n* `add_tag` - add_tag\n* `remove_tag` - remove_tag\n* `modify_tags` - modify_tags\n* `modify_custom_fields` - modify_custom_fields\n* `delete` - delete\n* `reprocess` - reprocess\n* `set_permissions` - set_permissions\n* `rotate` - rotate\n* `merge` - merge\n* `split` - split\n* `delete_pages` - delete_pages\n* `edit_pdf` - edit_pdf" }, "MigrationStatus": { "type": "object", "properties": { "latest_migration": { "type": "string" }, "unapplied_migrations": { "type": "array", "items": { "type": "string" } } }, "required": [ "latest_migration", "unapplied_migrations" ] }, "ModeEnum": { "enum": [ "skip", "redo", "force", "skip_noarchive" ], "type": "string", "description": "* `skip` - skip\n* `redo` - redo\n* `force` - force\n* `skip_noarchive` - skip_noarchive" }, "NoteCreateRequestRequest": { "type": "object", "properties": { "note": { "type": "string", "minLength": 1 } }, "required": [ "note" ] }, "Notes": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "note": { "type": "string", "title": "Content", "description": "Note for the document" }, "created": { "type": "string", "format": "date-time" }, "user": { "allOf": [ { "$ref": "#/components/schemas/BasicUser" } ], "readOnly": true } }, "required": [ "id", "user" ] }, "NotesRequest": { "type": "object", "properties": { "note": { "type": "string", "title": "Content", "description": "Note for the document" }, "created": { "type": "string", "format": "date-time" } } }, "NullEnum": { "enum": [ null ] }, "ObjectTypeEnum": { "enum": [ "tags", "correspondents", "document_types", "storage_paths" ], "type": "string", "description": "* `tags` - tags\n* `correspondents` - correspondents\n* `document_types` - document_types\n* `storage_paths` - storage_paths" }, "OperationEnum": { "enum": [ "set_permissions", "delete" ], "type": "string", "description": "* `set_permissions` - set_permissions\n* `delete` - delete" }, "OutputTypeEnum": { "enum": [ "pdf", "pdfa", "pdfa-1", "pdfa-2", "pdfa-3" ], "type": "string", "description": "* `pdf` - pdf\n* `pdfa` - pdfa\n* `pdfa-1` - pdfa-1\n* `pdfa-2` - pdfa-2\n* `pdfa-3` - pdfa-3" }, "PaginatedCorrespondentList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/Correspondent" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedCustomFieldList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/CustomField" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedDocumentList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/Document" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedDocumentTypeList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/DocumentType" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedGroupList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/Group" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedLogEntryList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/LogEntry" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedMailAccountList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/MailAccount" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedMailRuleList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/MailRule" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedNotesList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/Notes" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedSavedViewList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/SavedView" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedShareLinkList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/ShareLink" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedStoragePathList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/StoragePath" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedTagList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/Tag" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedUserList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/User" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedWorkflowActionList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/WorkflowAction" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedWorkflowList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/Workflow" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaginatedWorkflowTriggerList": { "type": "object", "required": [ "count", "results" ], "properties": { "count": { "type": "integer", "example": 123 }, "next": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", "nullable": true, "format": "uri", "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { "$ref": "#/components/schemas/WorkflowTrigger" } }, "all": { "type": "array", "example": "[1, 2, 3]" } } }, "PaperlessAuthToken": { "type": "object", "properties": { "token": { "type": "string", "readOnly": true } }, "required": [ "token" ] }, "PaperlessAuthTokenRequest": { "type": "object", "properties": { "username": { "type": "string", "writeOnly": true, "minLength": 1 }, "password": { "type": "string", "writeOnly": true, "minLength": 1 }, "code": { "type": "string", "writeOnly": true, "minLength": 1, "title": "MFA Code" } }, "required": [ "password", "username" ] }, "PatchedApplicationConfigurationRequest": { "type": "object", "properties": { "user_args": { "nullable": true }, "barcode_tag_mapping": { "nullable": true }, "output_type": { "nullable": true, "title": "Sets the output PDF type", "oneOf": [ { "$ref": "#/components/schemas/OutputTypeEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "pages": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Do OCR from page 1 to this value" }, "language": { "type": "string", "nullable": true, "title": "Do OCR using these languages", "maxLength": 32 }, "mode": { "nullable": true, "title": "Sets the OCR mode", "oneOf": [ { "$ref": "#/components/schemas/ModeEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "skip_archive_file": { "nullable": true, "title": "Controls the generation of an archive file", "oneOf": [ { "$ref": "#/components/schemas/SkipArchiveFileEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "image_dpi": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Sets image DPI fallback value" }, "unpaper_clean": { "nullable": true, "title": "Controls the unpaper cleaning", "oneOf": [ { "$ref": "#/components/schemas/UnpaperCleanEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "deskew": { "type": "boolean", "nullable": true, "title": "Enables deskew" }, "rotate_pages": { "type": "boolean", "nullable": true, "title": "Enables page rotation" }, "rotate_pages_threshold": { "type": "number", "format": "double", "minimum": 0.0, "nullable": true, "title": "Sets the threshold for rotation of pages" }, "max_image_pixels": { "type": "number", "format": "double", "minimum": 0.0, "nullable": true, "title": "Sets the maximum image size for decompression" }, "color_conversion_strategy": { "nullable": true, "title": "Sets the Ghostscript color conversion strategy", "oneOf": [ { "$ref": "#/components/schemas/ColorConversionStrategyEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "app_title": { "type": "string", "nullable": true, "title": "Application title", "maxLength": 48 }, "app_logo": { "type": "string", "format": "binary", "nullable": true, "title": "Application logo", "pattern": "(?:jpg|png|gif|svg)$" }, "barcodes_enabled": { "type": "boolean", "nullable": true, "title": "Enables barcode scanning" }, "barcode_enable_tiff_support": { "type": "boolean", "nullable": true, "title": "Enables barcode TIFF support" }, "barcode_string": { "type": "string", "nullable": true, "title": "Sets the barcode string", "maxLength": 32 }, "barcode_retain_split_pages": { "type": "boolean", "nullable": true, "title": "Retains split pages" }, "barcode_enable_asn": { "type": "boolean", "nullable": true, "title": "Enables ASN barcode" }, "barcode_asn_prefix": { "type": "string", "nullable": true, "title": "Sets the ASN barcode prefix", "maxLength": 32 }, "barcode_upscale": { "type": "number", "format": "double", "minimum": 1.0, "nullable": true, "title": "Sets the barcode upscale factor" }, "barcode_dpi": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Sets the barcode DPI" }, "barcode_max_pages": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "Sets the maximum pages for barcode" }, "barcode_enable_tag": { "type": "boolean", "nullable": true, "title": "Enables tag barcode" } } }, "PatchedCorrespondentRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true } } }, "PatchedCustomFieldRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "data_type": { "$ref": "#/components/schemas/DataTypeEnum" }, "extra_data": { "nullable": true, "description": "Extra data for the custom field, such as select options" } } }, "PatchedDocumentRequest": { "type": "object", "description": "Adds update nested feature", "properties": { "correspondent": { "type": "integer", "nullable": true }, "document_type": { "type": "integer", "nullable": true }, "storage_path": { "type": "integer", "nullable": true }, "title": { "type": "string", "maxLength": 128 }, "content": { "type": "string", "description": "The raw, text-only data of the document. This field is primarily used for searching." }, "tags": { "type": "array", "items": { "type": "integer" } }, "created": { "type": "string", "format": "date" }, "created_date": { "type": "string", "format": "date", "deprecated": true }, "deleted_at": { "type": "string", "format": "date-time", "nullable": true }, "archive_serial_number": { "type": "integer", "maximum": 4294967295, "minimum": 0, "format": "int64", "nullable": true, "description": "The position of this document in your physical document archive." }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true }, "custom_fields": { "type": "array", "items": { "$ref": "#/components/schemas/CustomFieldInstanceRequest" } }, "remove_inbox_tags": { "type": "boolean", "writeOnly": true, "nullable": true, "default": false } } }, "PatchedDocumentTypeRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true } } }, "PatchedGroupRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 150 }, "permissions": { "type": "array", "items": { "type": "string", "minLength": 1 } } } }, "PatchedMailAccountRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 256 }, "imap_server": { "type": "string", "minLength": 1, "maxLength": 256 }, "imap_port": { "type": "integer", "maximum": 9223372036854775807, "minimum": -9223372036854775808, "format": "int64", "nullable": true, "description": "This is usually 143 for unencrypted and STARTTLS connections, and 993 for SSL connections." }, "imap_security": { "allOf": [ { "$ref": "#/components/schemas/ImapSecurityEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "username": { "type": "string", "minLength": 1, "maxLength": 256 }, "password": { "type": "string", "minLength": 1 }, "character_set": { "type": "string", "minLength": 1, "description": "The character set to use when communicating with the mail server, such as 'UTF-8' or 'US-ASCII'.", "maxLength": 256 }, "is_token": { "type": "boolean", "title": "Is token authentication" }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true }, "account_type": { "allOf": [ { "$ref": "#/components/schemas/AccountTypeEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "expiration": { "type": "string", "format": "date-time", "nullable": true, "description": "The expiration date of the refresh token. " } } }, "PatchedMailRuleRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 256 }, "account": { "type": "integer" }, "enabled": { "type": "boolean" }, "folder": { "type": "string", "minLength": 1, "description": "Subfolders must be separated by a delimiter, often a dot ('.') or slash ('/'), but it varies by mail server.", "maxLength": 256 }, "filter_from": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_to": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_subject": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_body": { "type": "string", "nullable": true, "maxLength": 256 }, "filter_attachment_filename_include": { "type": "string", "nullable": true, "title": "Filter attachment filename inclusive", "description": "Only consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.", "maxLength": 256 }, "filter_attachment_filename_exclude": { "type": "string", "nullable": true, "title": "Filter attachment filename exclusive", "description": "Do not consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.", "maxLength": 256 }, "maximum_age": { "type": "integer", "maximum": 9223372036854775807, "minimum": 0, "format": "int64", "description": "Specified in days." }, "action": { "allOf": [ { "$ref": "#/components/schemas/MailRuleActionEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "action_parameter": { "type": "string", "nullable": true, "minLength": 1, "default": "" }, "assign_title_from": { "allOf": [ { "$ref": "#/components/schemas/AssignTitleFromEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "assign_tags": { "type": "array", "items": { "type": "integer", "nullable": true } }, "assign_correspondent_from": { "allOf": [ { "$ref": "#/components/schemas/AssignCorrespondentFromEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "assign_correspondent": { "type": "integer", "nullable": true }, "assign_document_type": { "type": "integer", "nullable": true }, "assign_owner_from_rule": { "type": "boolean", "title": "Assign the rule owner to documents" }, "order": { "type": "integer" }, "attachment_type": { "allOf": [ { "$ref": "#/components/schemas/AttachmentTypeEnum" } ], "description": "Inline attachments include embedded images, so it's best to combine this option with a filename filter.\n\n* `1` - Only process attachments.\n* `2` - Process all files, including 'inline' attachments.", "minimum": 0, "maximum": 9223372036854775807 }, "consumption_scope": { "allOf": [ { "$ref": "#/components/schemas/ConsumptionScopeEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "pdf_layout": { "allOf": [ { "$ref": "#/components/schemas/PdfLayoutEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true } } }, "PatchedProfileRequest": { "type": "object", "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string", "minLength": 1 }, "first_name": { "type": "string", "maxLength": 150 }, "last_name": { "type": "string", "maxLength": 150 } } }, "PatchedSavedViewRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "show_on_dashboard": { "type": "boolean" }, "show_in_sidebar": { "type": "boolean" }, "sort_field": { "type": "string", "nullable": true, "maxLength": 128 }, "sort_reverse": { "type": "boolean" }, "filter_rules": { "type": "array", "items": { "$ref": "#/components/schemas/SavedViewFilterRuleRequest" } }, "page_size": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "View page size" }, "display_mode": { "nullable": true, "title": "View display mode", "oneOf": [ { "$ref": "#/components/schemas/DisplayModeEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "display_fields": { "nullable": true, "title": "Document display fields" }, "owner": { "type": "integer", "nullable": true } } }, "PatchedShareLinkRequest": { "type": "object", "properties": { "expiration": { "type": "string", "format": "date-time", "nullable": true }, "document": { "type": "integer" }, "file_version": { "$ref": "#/components/schemas/FileVersionEnum" } } }, "PatchedStoragePathRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "path": { "type": "string", "minLength": 1 }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true } } }, "PatchedTagRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "color": { "type": "string", "minLength": 1, "maxLength": 7 }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "is_inbox_tag": { "type": "boolean", "description": "Marks this tag as an inbox tag: All newly consumed documents will be tagged with inbox tags." }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true } } }, "PatchedUserRequest": { "type": "object", "properties": { "username": { "type": "string", "minLength": 1, "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", "pattern": "^[\\w.@+-]+$", "maxLength": 150 }, "email": { "type": "string", "format": "email", "title": "Email address", "maxLength": 254 }, "password": { "type": "string", "minLength": 1 }, "first_name": { "type": "string", "maxLength": 150 }, "last_name": { "type": "string", "maxLength": 150 }, "date_joined": { "type": "string", "format": "date-time" }, "is_staff": { "type": "boolean", "title": "Staff status", "description": "Designates whether the user can log into this admin site." }, "is_active": { "type": "boolean", "title": "Active", "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts." }, "is_superuser": { "type": "boolean", "title": "Superuser status", "description": "Designates that this user has all permissions without explicitly assigning them." }, "groups": { "type": "array", "items": { "type": "integer" }, "description": "The groups this user belongs to. A user will get all permissions granted to each of their groups." }, "user_permissions": { "type": "array", "items": { "type": "string", "minLength": 1 } } } }, "PatchedWorkflowActionRequest": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "type": { "allOf": [ { "$ref": "#/components/schemas/WorkflowActionTypeEnum" } ], "title": "Workflow Action Type", "minimum": 0, "maximum": 9223372036854775807 }, "assign_title": { "type": "string", "nullable": true, "description": "Assign a document title, can include some placeholders, see documentation.", "maxLength": 256 }, "assign_tags": { "type": "array", "items": { "type": "integer", "nullable": true } }, "assign_correspondent": { "type": "integer", "nullable": true }, "assign_document_type": { "type": "integer", "nullable": true }, "assign_storage_path": { "type": "integer", "nullable": true }, "assign_owner": { "type": "integer", "nullable": true, "title": "Assign this owner" }, "assign_view_users": { "type": "array", "items": { "type": "integer", "title": "Grant view permissions to these users" }, "title": "Grant view permissions to these users" }, "assign_view_groups": { "type": "array", "items": { "type": "integer", "title": "Grant view permissions to these groups" }, "title": "Grant view permissions to these groups" }, "assign_change_users": { "type": "array", "items": { "type": "integer", "title": "Grant change permissions to these users" }, "title": "Grant change permissions to these users" }, "assign_change_groups": { "type": "array", "items": { "type": "integer", "title": "Grant change permissions to these groups" }, "title": "Grant change permissions to these groups" }, "assign_custom_fields": { "type": "array", "items": { "type": "integer", "title": "Assign these custom fields" }, "title": "Assign these custom fields" }, "assign_custom_fields_values": { "nullable": true, "title": "Custom field values", "description": "Optional values to assign to the custom fields." }, "remove_all_tags": { "type": "boolean" }, "remove_tags": { "type": "array", "items": { "type": "integer", "title": "Remove these tag(s)" }, "title": "Remove these tag(s)" }, "remove_all_correspondents": { "type": "boolean" }, "remove_correspondents": { "type": "array", "items": { "type": "integer", "title": "Remove these correspondent(s)" }, "title": "Remove these correspondent(s)" }, "remove_all_document_types": { "type": "boolean" }, "remove_document_types": { "type": "array", "items": { "type": "integer", "title": "Remove these document type(s)" }, "title": "Remove these document type(s)" }, "remove_all_storage_paths": { "type": "boolean" }, "remove_storage_paths": { "type": "array", "items": { "type": "integer", "title": "Remove these storage path(s)" }, "title": "Remove these storage path(s)" }, "remove_custom_fields": { "type": "array", "items": { "type": "integer", "title": "Remove these custom fields" }, "title": "Remove these custom fields" }, "remove_all_custom_fields": { "type": "boolean" }, "remove_all_owners": { "type": "boolean" }, "remove_owners": { "type": "array", "items": { "type": "integer", "title": "Remove these owner(s)" }, "title": "Remove these owner(s)" }, "remove_all_permissions": { "type": "boolean" }, "remove_view_users": { "type": "array", "items": { "type": "integer", "title": "Remove view permissions for these users" }, "title": "Remove view permissions for these users" }, "remove_view_groups": { "type": "array", "items": { "type": "integer", "title": "Remove view permissions for these groups" }, "title": "Remove view permissions for these groups" }, "remove_change_users": { "type": "array", "items": { "type": "integer", "title": "Remove change permissions for these users" }, "title": "Remove change permissions for these users" }, "remove_change_groups": { "type": "array", "items": { "type": "integer", "title": "Remove change permissions for these groups" }, "title": "Remove change permissions for these groups" }, "email": { "allOf": [ { "$ref": "#/components/schemas/WorkflowActionEmailRequest" } ], "nullable": true }, "webhook": { "allOf": [ { "$ref": "#/components/schemas/WorkflowActionWebhookRequest" } ], "nullable": true } } }, "PatchedWorkflowRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 256 }, "order": { "type": "integer" }, "enabled": { "type": "boolean" }, "triggers": { "type": "array", "items": { "$ref": "#/components/schemas/WorkflowTriggerRequest" } }, "actions": { "type": "array", "items": { "$ref": "#/components/schemas/WorkflowActionRequest" } } } }, "PatchedWorkflowTriggerRequest": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "sources": { "type": "array", "items": { "$ref": "#/components/schemas/SourcesEnum" }, "default": [ 1, 2, 3 ] }, "type": { "allOf": [ { "$ref": "#/components/schemas/WorkflowTriggerTypeEnum" } ], "title": "Trigger Type" }, "filter_path": { "type": "string", "nullable": true, "description": "Only consume documents with a path that matches this if specified. Wildcards specified as * are allowed. Case insensitive.", "maxLength": 256 }, "filter_filename": { "type": "string", "nullable": true, "description": "Only consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.", "maxLength": 256 }, "filter_mailrule": { "type": "integer", "nullable": true, "title": "Filter documents from this mail rule" }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/WorkflowTriggerMatchingAlgorithmEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "match": { "type": "string", "maxLength": 256 }, "is_insensitive": { "type": "boolean" }, "filter_has_tags": { "type": "array", "items": { "type": "integer", "title": "Has these tag(s)" }, "title": "Has these tag(s)" }, "filter_has_correspondent": { "type": "integer", "nullable": true, "title": "Has this correspondent" }, "filter_has_document_type": { "type": "integer", "nullable": true, "title": "Has this document type" }, "schedule_offset_days": { "type": "integer", "maximum": 9223372036854775807, "minimum": -9223372036854775808, "format": "int64", "description": "The number of days to offset the schedule trigger by." }, "schedule_is_recurring": { "type": "boolean", "description": "If the schedule should be recurring." }, "schedule_recurring_interval_days": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "title": "Schedule recurring delay in days", "description": "The number of days between recurring schedule triggers." }, "schedule_date_field": { "allOf": [ { "$ref": "#/components/schemas/ScheduleDateFieldEnum" } ], "description": "The field to check for a schedule trigger.\n\n* `added` - Added\n* `created` - Created\n* `modified` - Modified\n* `custom_field` - Custom Field" }, "schedule_date_custom_field": { "type": "integer", "nullable": true } } }, "PdfLayoutEnum": { "enum": [ 0, 1, 2, 3, 4 ], "type": "integer", "description": "* `0` - System default\n* `1` - Text, then HTML\n* `2` - HTML, then text\n* `3` - HTML only\n* `4` - Text only" }, "PostDocumentRequest": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time", "writeOnly": true, "nullable": true }, "document": { "type": "string", "format": "binary", "writeOnly": true }, "title": { "type": "string", "writeOnly": true, "minLength": 1 }, "correspondent": { "type": "integer", "writeOnly": true, "nullable": true }, "document_type": { "type": "integer", "writeOnly": true, "nullable": true }, "storage_path": { "type": "integer", "writeOnly": true, "nullable": true }, "tags": { "type": "array", "items": { "type": "integer", "writeOnly": true, "title": "Tags" }, "writeOnly": true }, "archive_serial_number": { "type": "integer", "maximum": 4294967295, "minimum": 0, "format": "int64", "writeOnly": true, "title": "ASN" }, "custom_fields": { "type": "array", "items": { "type": "integer", "writeOnly": true, "title": "Custom fields" }, "writeOnly": true }, "from_webui": { "type": "boolean", "writeOnly": true, "title": "Documents are from Paperless-ngx WebUI" } }, "required": [ "document" ] }, "Profile": { "type": "object", "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string" }, "first_name": { "type": "string", "maxLength": 150 }, "last_name": { "type": "string", "maxLength": 150 }, "auth_token": { "type": "string", "readOnly": true }, "social_accounts": { "type": "array", "items": { "$ref": "#/components/schemas/SocialAccount" }, "readOnly": true }, "has_usable_password": { "type": "boolean", "readOnly": true }, "is_mfa_enabled": { "type": "boolean", "readOnly": true } }, "required": [ "auth_token", "has_usable_password", "is_mfa_enabled", "social_accounts" ] }, "RuleTypeEnum": { "enum": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 ], "type": "integer", "description": "* `0` - title contains\n* `1` - content contains\n* `2` - ASN is\n* `3` - correspondent is\n* `4` - document type is\n* `5` - is in inbox\n* `6` - has tag\n* `7` - has any tag\n* `8` - created before\n* `9` - created after\n* `10` - created year is\n* `11` - created month is\n* `12` - created day is\n* `13` - added before\n* `14` - added after\n* `15` - modified before\n* `16` - modified after\n* `17` - does not have tag\n* `18` - does not have ASN\n* `19` - title or content contains\n* `20` - fulltext query\n* `21` - more like this\n* `22` - has tags in\n* `23` - ASN greater than\n* `24` - ASN less than\n* `25` - storage path is\n* `26` - has correspondent in\n* `27` - does not have correspondent in\n* `28` - has document type in\n* `29` - does not have document type in\n* `30` - has storage path in\n* `31` - does not have storage path in\n* `32` - owner is\n* `33` - has owner in\n* `34` - does not have owner\n* `35` - does not have owner in\n* `36` - has custom field value\n* `37` - is shared by me\n* `38` - has custom fields\n* `39` - has custom field in\n* `40` - does not have custom field in\n* `41` - does not have custom field\n* `42` - custom fields query\n* `43` - created to\n* `44` - created from\n* `45` - added to\n* `46` - added from\n* `47` - mime type is" }, "SanityCheck": { "type": "object", "properties": { "status": { "type": "string" }, "error": { "type": "string" }, "last_run": { "type": "string", "format": "date-time" } }, "required": [ "error", "last_run", "status" ] }, "SavedView": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "name": { "type": "string", "maxLength": 128 }, "show_on_dashboard": { "type": "boolean" }, "show_in_sidebar": { "type": "boolean" }, "sort_field": { "type": "string", "nullable": true, "maxLength": 128 }, "sort_reverse": { "type": "boolean" }, "filter_rules": { "type": "array", "items": { "$ref": "#/components/schemas/SavedViewFilterRule" } }, "page_size": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "View page size" }, "display_mode": { "nullable": true, "title": "View display mode", "oneOf": [ { "$ref": "#/components/schemas/DisplayModeEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "display_fields": { "nullable": true, "title": "Document display fields" }, "owner": { "type": "integer", "nullable": true }, "user_can_change": { "type": "boolean", "readOnly": true } }, "required": [ "filter_rules", "id", "name", "show_in_sidebar", "show_on_dashboard", "user_can_change" ] }, "SavedViewFilterRule": { "type": "object", "properties": { "rule_type": { "allOf": [ { "$ref": "#/components/schemas/RuleTypeEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "value": { "type": "string", "nullable": true, "maxLength": 255 } }, "required": [ "rule_type" ] }, "SavedViewFilterRuleRequest": { "type": "object", "properties": { "rule_type": { "allOf": [ { "$ref": "#/components/schemas/RuleTypeEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "value": { "type": "string", "nullable": true, "maxLength": 255 } }, "required": [ "rule_type" ] }, "SavedViewRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "show_on_dashboard": { "type": "boolean" }, "show_in_sidebar": { "type": "boolean" }, "sort_field": { "type": "string", "nullable": true, "maxLength": 128 }, "sort_reverse": { "type": "boolean" }, "filter_rules": { "type": "array", "items": { "$ref": "#/components/schemas/SavedViewFilterRuleRequest" } }, "page_size": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "nullable": true, "title": "View page size" }, "display_mode": { "nullable": true, "title": "View display mode", "oneOf": [ { "$ref": "#/components/schemas/DisplayModeEnum" }, { "$ref": "#/components/schemas/BlankEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "display_fields": { "nullable": true, "title": "Document display fields" }, "owner": { "type": "integer", "nullable": true } }, "required": [ "filter_rules", "name", "show_in_sidebar", "show_on_dashboard" ] }, "ScheduleDateFieldEnum": { "enum": [ "added", "created", "modified", "custom_field" ], "type": "string", "description": "* `added` - Added\n* `created` - Created\n* `modified` - Modified\n* `custom_field` - Custom Field" }, "SearchResult": { "type": "object", "properties": { "total": { "type": "integer" }, "documents": { "type": "array", "items": { "$ref": "#/components/schemas/Document" } }, "saved_views": { "type": "array", "items": { "$ref": "#/components/schemas/SavedView" } }, "tags": { "type": "array", "items": { "$ref": "#/components/schemas/Tag" } }, "correspondents": { "type": "array", "items": { "$ref": "#/components/schemas/Correspondent" } }, "document_types": { "type": "array", "items": { "$ref": "#/components/schemas/DocumentType" } }, "storage_paths": { "type": "array", "items": { "$ref": "#/components/schemas/StoragePath" } }, "users": { "type": "array", "items": { "$ref": "#/components/schemas/User" } }, "groups": { "type": "array", "items": { "$ref": "#/components/schemas/Group" } }, "mail_rules": { "type": "array", "items": { "$ref": "#/components/schemas/MailRule" } }, "mail_accounts": { "type": "array", "items": { "$ref": "#/components/schemas/MailAccount" } }, "workflows": { "type": "array", "items": { "$ref": "#/components/schemas/Workflow" } }, "custom_fields": { "type": "array", "items": { "$ref": "#/components/schemas/CustomField" } } }, "required": [ "correspondents", "custom_fields", "document_types", "documents", "groups", "mail_accounts", "mail_rules", "saved_views", "storage_paths", "tags", "total", "users", "workflows" ] }, "SelectionData": { "type": "object", "properties": { "selected_correspondents": { "type": "array", "items": { "$ref": "#/components/schemas/CorrespondentCounts" } }, "selected_tags": { "type": "array", "items": { "$ref": "#/components/schemas/TagCounts" } }, "selected_document_types": { "type": "array", "items": { "$ref": "#/components/schemas/DocumentTypeCounts" } }, "selected_storage_paths": { "type": "array", "items": { "$ref": "#/components/schemas/StoragePathCounts" } }, "selected_custom_fields": { "type": "array", "items": { "$ref": "#/components/schemas/CustomFieldCounts" } } }, "required": [ "selected_correspondents", "selected_custom_fields", "selected_document_types", "selected_storage_paths", "selected_tags" ] }, "ShareLink": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "created": { "type": "string", "format": "date-time", "readOnly": true }, "expiration": { "type": "string", "format": "date-time", "nullable": true }, "slug": { "type": "string", "readOnly": true, "pattern": "^[-a-zA-Z0-9_]+$" }, "document": { "type": "integer" }, "file_version": { "$ref": "#/components/schemas/FileVersionEnum" } }, "required": [ "created", "id", "slug" ] }, "ShareLinkRequest": { "type": "object", "properties": { "expiration": { "type": "string", "format": "date-time", "nullable": true }, "document": { "type": "integer" }, "file_version": { "$ref": "#/components/schemas/FileVersionEnum" } } }, "SkipArchiveFileEnum": { "enum": [ "never", "with_text", "always" ], "type": "string", "description": "* `never` - never\n* `with_text` - with_text\n* `always` - always" }, "SocialAccount": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "provider": { "type": "string", "maxLength": 200 }, "name": { "type": "string", "readOnly": true } }, "required": [ "id", "name", "provider" ] }, "SocialAccountRequest": { "type": "object", "properties": { "provider": { "type": "string", "minLength": 1, "maxLength": 200 } }, "required": [ "provider" ] }, "SourcesEnum": { "enum": [ 1, 2, 3, 4 ], "type": "integer", "description": "* `1` - Consume Folder\n* `2` - Api Upload\n* `3` - Mail Fetch\n* `4` - Web UI" }, "StatusEnum": { "enum": [ "FAILURE", "PENDING", "RECEIVED", "RETRY", "REVOKED", "STARTED", "SUCCESS" ], "type": "string", "description": "* `FAILURE` - FAILURE\n* `PENDING` - PENDING\n* `RECEIVED` - RECEIVED\n* `RETRY` - RETRY\n* `REVOKED` - REVOKED\n* `STARTED` - STARTED\n* `SUCCESS` - SUCCESS" }, "Storage": { "type": "object", "properties": { "total": { "type": "integer" }, "available": { "type": "integer" } }, "required": [ "available", "total" ] }, "StoragePath": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "slug": { "type": "string", "readOnly": true }, "name": { "type": "string", "maxLength": 128 }, "path": { "type": "string" }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "document_count": { "type": "integer", "readOnly": true }, "owner": { "type": "integer", "nullable": true }, "user_can_change": { "type": "boolean", "readOnly": true } }, "required": [ "document_count", "id", "name", "path", "slug", "user_can_change" ] }, "StoragePathCounts": { "type": "object", "properties": { "id": { "type": "integer" }, "document_count": { "type": "integer" } }, "required": [ "document_count", "id" ] }, "StoragePathRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "path": { "type": "string", "minLength": 1 }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true } }, "required": [ "name", "path" ] }, "Suggestions": { "type": "object", "properties": { "correspondents": { "type": "array", "items": { "type": "integer" } }, "tags": { "type": "array", "items": { "type": "integer" } }, "document_types": { "type": "array", "items": { "type": "integer" } }, "storage_paths": { "type": "array", "items": { "type": "integer" } }, "dates": { "type": "array", "items": { "type": "string" } } }, "required": [ "correspondents", "dates", "document_types", "storage_paths", "tags" ] }, "SystemStatus": { "type": "object", "properties": { "pngx_version": { "type": "string" }, "server_os": { "type": "string" }, "install_type": { "type": "string" }, "storage": { "$ref": "#/components/schemas/Storage" }, "database": { "$ref": "#/components/schemas/Database" }, "tasks": { "$ref": "#/components/schemas/Tasks" }, "index": { "$ref": "#/components/schemas/Index" }, "classifier": { "$ref": "#/components/schemas/Classifier" }, "sanity_check": { "$ref": "#/components/schemas/SanityCheck" } }, "required": [ "classifier", "database", "index", "install_type", "pngx_version", "sanity_check", "server_os", "storage", "tasks" ] }, "Tag": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "slug": { "type": "string", "readOnly": true }, "name": { "type": "string", "maxLength": 128 }, "color": { "type": "string", "maxLength": 7 }, "text_color": { "type": "string", "readOnly": true }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "is_inbox_tag": { "type": "boolean", "description": "Marks this tag as an inbox tag: All newly consumed documents will be tagged with inbox tags." }, "document_count": { "type": "integer", "readOnly": true }, "owner": { "type": "integer", "nullable": true }, "user_can_change": { "type": "boolean", "readOnly": true } }, "required": [ "document_count", "id", "name", "slug", "text_color", "user_can_change" ] }, "TagCounts": { "type": "object", "properties": { "id": { "type": "integer" }, "document_count": { "type": "integer" } }, "required": [ "document_count", "id" ] }, "TagRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128 }, "color": { "type": "string", "minLength": 1, "maxLength": 7 }, "match": { "type": "string", "maxLength": 256 }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/MatchingAlgorithm" } ], "minimum": 0, "maximum": 9223372036854775807 }, "is_insensitive": { "type": "boolean" }, "is_inbox_tag": { "type": "boolean", "description": "Marks this tag as an inbox tag: All newly consumed documents will be tagged with inbox tags." }, "owner": { "type": "integer", "nullable": true }, "set_permissions": { "type": "object", "properties": { "view": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } }, "change": { "type": "object", "properties": { "users": { "type": "array", "items": { "type": "integer" } }, "groups": { "type": "array", "items": { "type": "integer" } } } } }, "writeOnly": true } }, "required": [ "name" ] }, "TaskNameEnum": { "enum": [ "consume_file", "train_classifier", "check_sanity", "index_optimize" ], "type": "string", "description": "* `consume_file` - Consume File\n* `train_classifier` - Train Classifier\n* `check_sanity` - Check Sanity\n* `index_optimize` - Index Optimize" }, "Tasks": { "type": "object", "properties": { "redis_url": { "type": "string" }, "redis_status": { "type": "string" }, "redis_error": { "type": "string" }, "celery_status": { "type": "string" } }, "required": [ "celery_status", "redis_error", "redis_status", "redis_url" ] }, "TasksView": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "task_id": { "type": "string", "description": "Celery ID for the Task that was run", "maxLength": 255 }, "task_name": { "nullable": true, "description": "Name of the task that was run\n\n* `consume_file` - Consume File\n* `train_classifier` - Train Classifier\n* `check_sanity` - Check Sanity\n* `index_optimize` - Index Optimize", "oneOf": [ { "$ref": "#/components/schemas/TaskNameEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "task_file_name": { "type": "string", "nullable": true, "title": "Task Filename", "description": "Name of the file which the Task was run for", "maxLength": 255 }, "date_created": { "type": "string", "format": "date-time", "nullable": true, "title": "Created DateTime", "description": "Datetime field when the task result was created in UTC" }, "date_done": { "type": "string", "format": "date-time", "nullable": true, "title": "Completed DateTime", "description": "Datetime field when the task was completed in UTC" }, "type": { "allOf": [ { "$ref": "#/components/schemas/TasksViewTypeEnum" } ], "title": "Task Type", "description": "The type of task that was run\n\n* `auto_task` - Auto Task\n* `scheduled_task` - Scheduled Task\n* `manual_task` - Manual Task" }, "status": { "allOf": [ { "$ref": "#/components/schemas/StatusEnum" } ], "title": "Task State", "description": "Current state of the task being run\n\n* `FAILURE` - FAILURE\n* `PENDING` - PENDING\n* `RECEIVED` - RECEIVED\n* `RETRY` - RETRY\n* `REVOKED` - REVOKED\n* `STARTED` - STARTED\n* `SUCCESS` - SUCCESS" }, "result": { "type": "string", "nullable": true, "title": "Result Data", "description": "The data returned by the task" }, "acknowledged": { "type": "boolean", "description": "If the task is acknowledged via the frontend or API" }, "related_document": { "type": "string", "nullable": true, "readOnly": true }, "owner": { "type": "integer", "nullable": true } }, "required": [ "id", "related_document", "task_id" ] }, "TasksViewRequest": { "type": "object", "properties": { "task_id": { "type": "string", "minLength": 1, "description": "Celery ID for the Task that was run", "maxLength": 255 }, "task_name": { "nullable": true, "description": "Name of the task that was run\n\n* `consume_file` - Consume File\n* `train_classifier` - Train Classifier\n* `check_sanity` - Check Sanity\n* `index_optimize` - Index Optimize", "oneOf": [ { "$ref": "#/components/schemas/TaskNameEnum" }, { "$ref": "#/components/schemas/NullEnum" } ] }, "task_file_name": { "type": "string", "nullable": true, "minLength": 1, "title": "Task Filename", "description": "Name of the file which the Task was run for", "maxLength": 255 }, "date_created": { "type": "string", "format": "date-time", "nullable": true, "title": "Created DateTime", "description": "Datetime field when the task result was created in UTC" }, "date_done": { "type": "string", "format": "date-time", "nullable": true, "title": "Completed DateTime", "description": "Datetime field when the task was completed in UTC" }, "type": { "allOf": [ { "$ref": "#/components/schemas/TasksViewTypeEnum" } ], "title": "Task Type", "description": "The type of task that was run\n\n* `auto_task` - Auto Task\n* `scheduled_task` - Scheduled Task\n* `manual_task` - Manual Task" }, "status": { "allOf": [ { "$ref": "#/components/schemas/StatusEnum" } ], "title": "Task State", "description": "Current state of the task being run\n\n* `FAILURE` - FAILURE\n* `PENDING` - PENDING\n* `RECEIVED` - RECEIVED\n* `RETRY` - RETRY\n* `REVOKED` - REVOKED\n* `STARTED` - STARTED\n* `SUCCESS` - SUCCESS" }, "result": { "type": "string", "nullable": true, "minLength": 1, "title": "Result Data", "description": "The data returned by the task" }, "acknowledged": { "type": "boolean", "description": "If the task is acknowledged via the frontend or API" }, "owner": { "type": "integer", "nullable": true } }, "required": [ "task_id" ] }, "TasksViewTypeEnum": { "enum": [ "auto_task", "scheduled_task", "manual_task" ], "type": "string", "description": "* `auto_task` - Auto Task\n* `scheduled_task` - Scheduled Task\n* `manual_task` - Manual Task" }, "TrashActionEnum": { "enum": [ "restore", "empty" ], "type": "string", "description": "* `restore` - restore\n* `empty` - empty" }, "TrashRequest": { "type": "object", "properties": { "documents": { "type": "array", "items": { "type": "integer" }, "writeOnly": true }, "action": { "allOf": [ { "$ref": "#/components/schemas/TrashActionEnum" } ], "writeOnly": true } }, "required": [ "action" ] }, "UiSettingsView": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "settings": { "type": "object", "additionalProperties": {}, "nullable": true } }, "required": [ "id" ] }, "UiSettingsViewRequest": { "type": "object", "properties": { "settings": { "type": "object", "additionalProperties": {}, "nullable": true } } }, "UnpaperCleanEnum": { "enum": [ "clean", "clean-final", "none" ], "type": "string", "description": "* `clean` - clean\n* `clean-final` - clean-final\n* `none` - none" }, "User": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "username": { "type": "string", "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", "pattern": "^[\\w.@+-]+$", "maxLength": 150 }, "email": { "type": "string", "format": "email", "title": "Email address", "maxLength": 254 }, "password": { "type": "string" }, "first_name": { "type": "string", "maxLength": 150 }, "last_name": { "type": "string", "maxLength": 150 }, "date_joined": { "type": "string", "format": "date-time" }, "is_staff": { "type": "boolean", "title": "Staff status", "description": "Designates whether the user can log into this admin site." }, "is_active": { "type": "boolean", "title": "Active", "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts." }, "is_superuser": { "type": "boolean", "title": "Superuser status", "description": "Designates that this user has all permissions without explicitly assigning them." }, "groups": { "type": "array", "items": { "type": "integer", "title": "Groups" }, "description": "The groups this user belongs to. A user will get all permissions granted to each of their groups." }, "user_permissions": { "type": "array", "items": { "type": "string" } }, "inherited_permissions": { "type": "array", "items": { "type": "string" }, "readOnly": true }, "is_mfa_enabled": { "type": "boolean", "readOnly": true } }, "required": [ "id", "inherited_permissions", "is_mfa_enabled", "username" ] }, "UserRequest": { "type": "object", "properties": { "username": { "type": "string", "minLength": 1, "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", "pattern": "^[\\w.@+-]+$", "maxLength": 150 }, "email": { "type": "string", "format": "email", "title": "Email address", "maxLength": 254 }, "password": { "type": "string", "minLength": 1 }, "first_name": { "type": "string", "maxLength": 150 }, "last_name": { "type": "string", "maxLength": 150 }, "date_joined": { "type": "string", "format": "date-time" }, "is_staff": { "type": "boolean", "title": "Staff status", "description": "Designates whether the user can log into this admin site." }, "is_active": { "type": "boolean", "title": "Active", "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts." }, "is_superuser": { "type": "boolean", "title": "Superuser status", "description": "Designates that this user has all permissions without explicitly assigning them." }, "groups": { "type": "array", "items": { "type": "integer" }, "description": "The groups this user belongs to. A user will get all permissions granted to each of their groups." }, "user_permissions": { "type": "array", "items": { "type": "string", "minLength": 1 } } }, "required": [ "username" ] }, "Workflow": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, "name": { "type": "string", "maxLength": 256 }, "order": { "type": "integer" }, "enabled": { "type": "boolean" }, "triggers": { "type": "array", "items": { "$ref": "#/components/schemas/WorkflowTrigger" } }, "actions": { "type": "array", "items": { "$ref": "#/components/schemas/WorkflowAction" } } }, "required": [ "actions", "id", "name", "triggers" ] }, "WorkflowAction": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "type": { "allOf": [ { "$ref": "#/components/schemas/WorkflowActionTypeEnum" } ], "title": "Workflow Action Type", "minimum": 0, "maximum": 9223372036854775807 }, "assign_title": { "type": "string", "nullable": true, "description": "Assign a document title, can include some placeholders, see documentation.", "maxLength": 256 }, "assign_tags": { "type": "array", "items": { "type": "integer", "nullable": true } }, "assign_correspondent": { "type": "integer", "nullable": true }, "assign_document_type": { "type": "integer", "nullable": true }, "assign_storage_path": { "type": "integer", "nullable": true }, "assign_owner": { "type": "integer", "nullable": true, "title": "Assign this owner" }, "assign_view_users": { "type": "array", "items": { "type": "integer", "title": "Grant view permissions to these users" }, "title": "Grant view permissions to these users" }, "assign_view_groups": { "type": "array", "items": { "type": "integer", "title": "Grant view permissions to these groups" }, "title": "Grant view permissions to these groups" }, "assign_change_users": { "type": "array", "items": { "type": "integer", "title": "Grant change permissions to these users" }, "title": "Grant change permissions to these users" }, "assign_change_groups": { "type": "array", "items": { "type": "integer", "title": "Grant change permissions to these groups" }, "title": "Grant change permissions to these groups" }, "assign_custom_fields": { "type": "array", "items": { "type": "integer", "title": "Assign these custom fields" }, "title": "Assign these custom fields" }, "assign_custom_fields_values": { "nullable": true, "title": "Custom field values", "description": "Optional values to assign to the custom fields." }, "remove_all_tags": { "type": "boolean" }, "remove_tags": { "type": "array", "items": { "type": "integer", "title": "Remove these tag(s)" }, "title": "Remove these tag(s)" }, "remove_all_correspondents": { "type": "boolean" }, "remove_correspondents": { "type": "array", "items": { "type": "integer", "title": "Remove these correspondent(s)" }, "title": "Remove these correspondent(s)" }, "remove_all_document_types": { "type": "boolean" }, "remove_document_types": { "type": "array", "items": { "type": "integer", "title": "Remove these document type(s)" }, "title": "Remove these document type(s)" }, "remove_all_storage_paths": { "type": "boolean" }, "remove_storage_paths": { "type": "array", "items": { "type": "integer", "title": "Remove these storage path(s)" }, "title": "Remove these storage path(s)" }, "remove_custom_fields": { "type": "array", "items": { "type": "integer", "title": "Remove these custom fields" }, "title": "Remove these custom fields" }, "remove_all_custom_fields": { "type": "boolean" }, "remove_all_owners": { "type": "boolean" }, "remove_owners": { "type": "array", "items": { "type": "integer", "title": "Remove these owner(s)" }, "title": "Remove these owner(s)" }, "remove_all_permissions": { "type": "boolean" }, "remove_view_users": { "type": "array", "items": { "type": "integer", "title": "Remove view permissions for these users" }, "title": "Remove view permissions for these users" }, "remove_view_groups": { "type": "array", "items": { "type": "integer", "title": "Remove view permissions for these groups" }, "title": "Remove view permissions for these groups" }, "remove_change_users": { "type": "array", "items": { "type": "integer", "title": "Remove change permissions for these users" }, "title": "Remove change permissions for these users" }, "remove_change_groups": { "type": "array", "items": { "type": "integer", "title": "Remove change permissions for these groups" }, "title": "Remove change permissions for these groups" }, "email": { "allOf": [ { "$ref": "#/components/schemas/WorkflowActionEmail" } ], "nullable": true }, "webhook": { "allOf": [ { "$ref": "#/components/schemas/WorkflowActionWebhook" } ], "nullable": true } } }, "WorkflowActionEmail": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "subject": { "type": "string", "title": "Email subject", "description": "The subject of the email, can include some placeholders, see documentation.", "maxLength": 256 }, "body": { "type": "string", "title": "Email body", "description": "The body (message) of the email, can include some placeholders, see documentation." }, "to": { "type": "string", "title": "Emails to", "description": "The destination email addresses, comma separated." }, "include_document": { "type": "boolean", "title": "Include document in email" } }, "required": [ "body", "subject", "to" ] }, "WorkflowActionEmailRequest": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "subject": { "type": "string", "minLength": 1, "title": "Email subject", "description": "The subject of the email, can include some placeholders, see documentation.", "maxLength": 256 }, "body": { "type": "string", "minLength": 1, "title": "Email body", "description": "The body (message) of the email, can include some placeholders, see documentation." }, "to": { "type": "string", "minLength": 1, "title": "Emails to", "description": "The destination email addresses, comma separated." }, "include_document": { "type": "boolean", "title": "Include document in email" } }, "required": [ "body", "subject", "to" ] }, "WorkflowActionRequest": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "type": { "allOf": [ { "$ref": "#/components/schemas/WorkflowActionTypeEnum" } ], "title": "Workflow Action Type", "minimum": 0, "maximum": 9223372036854775807 }, "assign_title": { "type": "string", "nullable": true, "description": "Assign a document title, can include some placeholders, see documentation.", "maxLength": 256 }, "assign_tags": { "type": "array", "items": { "type": "integer", "nullable": true } }, "assign_correspondent": { "type": "integer", "nullable": true }, "assign_document_type": { "type": "integer", "nullable": true }, "assign_storage_path": { "type": "integer", "nullable": true }, "assign_owner": { "type": "integer", "nullable": true, "title": "Assign this owner" }, "assign_view_users": { "type": "array", "items": { "type": "integer", "title": "Grant view permissions to these users" }, "title": "Grant view permissions to these users" }, "assign_view_groups": { "type": "array", "items": { "type": "integer", "title": "Grant view permissions to these groups" }, "title": "Grant view permissions to these groups" }, "assign_change_users": { "type": "array", "items": { "type": "integer", "title": "Grant change permissions to these users" }, "title": "Grant change permissions to these users" }, "assign_change_groups": { "type": "array", "items": { "type": "integer", "title": "Grant change permissions to these groups" }, "title": "Grant change permissions to these groups" }, "assign_custom_fields": { "type": "array", "items": { "type": "integer", "title": "Assign these custom fields" }, "title": "Assign these custom fields" }, "assign_custom_fields_values": { "nullable": true, "title": "Custom field values", "description": "Optional values to assign to the custom fields." }, "remove_all_tags": { "type": "boolean" }, "remove_tags": { "type": "array", "items": { "type": "integer", "title": "Remove these tag(s)" }, "title": "Remove these tag(s)" }, "remove_all_correspondents": { "type": "boolean" }, "remove_correspondents": { "type": "array", "items": { "type": "integer", "title": "Remove these correspondent(s)" }, "title": "Remove these correspondent(s)" }, "remove_all_document_types": { "type": "boolean" }, "remove_document_types": { "type": "array", "items": { "type": "integer", "title": "Remove these document type(s)" }, "title": "Remove these document type(s)" }, "remove_all_storage_paths": { "type": "boolean" }, "remove_storage_paths": { "type": "array", "items": { "type": "integer", "title": "Remove these storage path(s)" }, "title": "Remove these storage path(s)" }, "remove_custom_fields": { "type": "array", "items": { "type": "integer", "title": "Remove these custom fields" }, "title": "Remove these custom fields" }, "remove_all_custom_fields": { "type": "boolean" }, "remove_all_owners": { "type": "boolean" }, "remove_owners": { "type": "array", "items": { "type": "integer", "title": "Remove these owner(s)" }, "title": "Remove these owner(s)" }, "remove_all_permissions": { "type": "boolean" }, "remove_view_users": { "type": "array", "items": { "type": "integer", "title": "Remove view permissions for these users" }, "title": "Remove view permissions for these users" }, "remove_view_groups": { "type": "array", "items": { "type": "integer", "title": "Remove view permissions for these groups" }, "title": "Remove view permissions for these groups" }, "remove_change_users": { "type": "array", "items": { "type": "integer", "title": "Remove change permissions for these users" }, "title": "Remove change permissions for these users" }, "remove_change_groups": { "type": "array", "items": { "type": "integer", "title": "Remove change permissions for these groups" }, "title": "Remove change permissions for these groups" }, "email": { "allOf": [ { "$ref": "#/components/schemas/WorkflowActionEmailRequest" } ], "nullable": true }, "webhook": { "allOf": [ { "$ref": "#/components/schemas/WorkflowActionWebhookRequest" } ], "nullable": true } } }, "WorkflowActionTypeEnum": { "enum": [ 1, 2, 3, 4 ], "type": "integer", "description": "* `1` - Assignment\n* `2` - Removal\n* `3` - Email\n* `4` - Webhook" }, "WorkflowActionWebhook": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "url": { "type": "string", "title": "Webhook url", "description": "The destination URL for the notification.", "maxLength": 256 }, "use_params": { "type": "boolean", "title": "Use parameters" }, "as_json": { "type": "boolean", "title": "Send as JSON" }, "params": { "nullable": true, "title": "Webhook parameters", "description": "The parameters to send with the webhook URL if body not used." }, "body": { "type": "string", "nullable": true, "title": "Webhook body", "description": "The body to send with the webhook URL if parameters not used." }, "headers": { "nullable": true, "title": "Webhook headers", "description": "The headers to send with the webhook URL." }, "include_document": { "type": "boolean", "title": "Include document in webhook" } }, "required": [ "url" ] }, "WorkflowActionWebhookRequest": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "url": { "type": "string", "minLength": 1, "title": "Webhook url", "description": "The destination URL for the notification.", "maxLength": 256 }, "use_params": { "type": "boolean", "title": "Use parameters" }, "as_json": { "type": "boolean", "title": "Send as JSON" }, "params": { "nullable": true, "title": "Webhook parameters", "description": "The parameters to send with the webhook URL if body not used." }, "body": { "type": "string", "nullable": true, "title": "Webhook body", "description": "The body to send with the webhook URL if parameters not used." }, "headers": { "nullable": true, "title": "Webhook headers", "description": "The headers to send with the webhook URL." }, "include_document": { "type": "boolean", "title": "Include document in webhook" } }, "required": [ "url" ] }, "WorkflowRequest": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 256 }, "order": { "type": "integer" }, "enabled": { "type": "boolean" }, "triggers": { "type": "array", "items": { "$ref": "#/components/schemas/WorkflowTriggerRequest" } }, "actions": { "type": "array", "items": { "$ref": "#/components/schemas/WorkflowActionRequest" } } }, "required": [ "actions", "name", "triggers" ] }, "WorkflowTrigger": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "sources": { "type": "array", "items": { "$ref": "#/components/schemas/SourcesEnum" }, "default": [ 1, 2, 3 ] }, "type": { "allOf": [ { "$ref": "#/components/schemas/WorkflowTriggerTypeEnum" } ], "title": "Trigger Type" }, "filter_path": { "type": "string", "nullable": true, "description": "Only consume documents with a path that matches this if specified. Wildcards specified as * are allowed. Case insensitive.", "maxLength": 256 }, "filter_filename": { "type": "string", "nullable": true, "description": "Only consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.", "maxLength": 256 }, "filter_mailrule": { "type": "integer", "nullable": true, "title": "Filter documents from this mail rule" }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/WorkflowTriggerMatchingAlgorithmEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "match": { "type": "string", "maxLength": 256 }, "is_insensitive": { "type": "boolean" }, "filter_has_tags": { "type": "array", "items": { "type": "integer", "title": "Has these tag(s)" }, "title": "Has these tag(s)" }, "filter_has_correspondent": { "type": "integer", "nullable": true, "title": "Has this correspondent" }, "filter_has_document_type": { "type": "integer", "nullable": true, "title": "Has this document type" }, "schedule_offset_days": { "type": "integer", "maximum": 9223372036854775807, "minimum": -9223372036854775808, "format": "int64", "description": "The number of days to offset the schedule trigger by." }, "schedule_is_recurring": { "type": "boolean", "description": "If the schedule should be recurring." }, "schedule_recurring_interval_days": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "title": "Schedule recurring delay in days", "description": "The number of days between recurring schedule triggers." }, "schedule_date_field": { "allOf": [ { "$ref": "#/components/schemas/ScheduleDateFieldEnum" } ], "description": "The field to check for a schedule trigger.\n\n* `added` - Added\n* `created` - Created\n* `modified` - Modified\n* `custom_field` - Custom Field" }, "schedule_date_custom_field": { "type": "integer", "nullable": true } }, "required": [ "type" ] }, "WorkflowTriggerMatchingAlgorithmEnum": { "enum": [ 0, 1, 2, 3, 4, 5 ], "type": "integer", "description": "* `0` - None\n* `1` - Any word\n* `2` - All words\n* `3` - Exact match\n* `4` - Regular expression\n* `5` - Fuzzy word" }, "WorkflowTriggerRequest": { "type": "object", "properties": { "id": { "type": "integer", "nullable": true }, "sources": { "type": "array", "items": { "$ref": "#/components/schemas/SourcesEnum" }, "default": [ 1, 2, 3 ] }, "type": { "allOf": [ { "$ref": "#/components/schemas/WorkflowTriggerTypeEnum" } ], "title": "Trigger Type" }, "filter_path": { "type": "string", "nullable": true, "description": "Only consume documents with a path that matches this if specified. Wildcards specified as * are allowed. Case insensitive.", "maxLength": 256 }, "filter_filename": { "type": "string", "nullable": true, "description": "Only consume documents which entirely match this filename if specified. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive.", "maxLength": 256 }, "filter_mailrule": { "type": "integer", "nullable": true, "title": "Filter documents from this mail rule" }, "matching_algorithm": { "allOf": [ { "$ref": "#/components/schemas/WorkflowTriggerMatchingAlgorithmEnum" } ], "minimum": 0, "maximum": 9223372036854775807 }, "match": { "type": "string", "maxLength": 256 }, "is_insensitive": { "type": "boolean" }, "filter_has_tags": { "type": "array", "items": { "type": "integer", "title": "Has these tag(s)" }, "title": "Has these tag(s)" }, "filter_has_correspondent": { "type": "integer", "nullable": true, "title": "Has this correspondent" }, "filter_has_document_type": { "type": "integer", "nullable": true, "title": "Has this document type" }, "schedule_offset_days": { "type": "integer", "maximum": 9223372036854775807, "minimum": -9223372036854775808, "format": "int64", "description": "The number of days to offset the schedule trigger by." }, "schedule_is_recurring": { "type": "boolean", "description": "If the schedule should be recurring." }, "schedule_recurring_interval_days": { "type": "integer", "maximum": 9223372036854775807, "minimum": 1, "format": "int64", "title": "Schedule recurring delay in days", "description": "The number of days between recurring schedule triggers." }, "schedule_date_field": { "allOf": [ { "$ref": "#/components/schemas/ScheduleDateFieldEnum" } ], "description": "The field to check for a schedule trigger.\n\n* `added` - Added\n* `created` - Created\n* `modified` - Modified\n* `custom_field` - Custom Field" }, "schedule_date_custom_field": { "type": "integer", "nullable": true } }, "required": [ "type" ] }, "WorkflowTriggerTypeEnum": { "enum": [ 1, 2, 3, 4 ], "type": "integer", "description": "* `1` - Consumption Started\n* `2` - Document Added\n* `3` - Document Updated\n* `4` - Scheduled" } }, "securitySchemes": { "PaperelessBasicAuthentication": { "type": "http", "scheme": "basic" }, "cookieAuth": { "type": "apiKey", "in": "cookie", "name": "sessionid" }, "tokenAuth": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Token-based authentication with required prefix \"Token\"" } } }, "externalDocs": { "description": "Paperless-ngx API Documentation", "url": "https://docs.paperless-ngx.com/api/" } } paperless-api-5.1.0/tests/data/share_links.py000066400000000000000000000045341505307417100212220ustar00rootroot00000000000000"""Share links snapshot.""" DATA_SHARE_LINKS = { "count": 5, "next": None, "previous": None, "all": [1, 2, 3, 4, 5, 6, 7, 8], "results": [ { "id": 1, "created": "2023-12-11T14:06:49.096456Z", "expiration": "2023-12-18T14:06:49.064000Z", "slug": "GMIFR9WVPe7a0FAltmrAdmVsrrTzH6Z9yFi2jufhi5yCTAMWfF", "document": 1, "file_version": "original", }, { "id": 2, "created": "2023-12-11T14:06:53.583496Z", "expiration": "2024-01-10T14:06:53.558000Z", "slug": "Px2h3mrkIvExyTE8M8usrTLv3jtTb4MnLJ4eTAxcjy2FUmuDLq", "document": 2, "file_version": "original", }, { "id": 3, "created": "2023-12-11T14:06:55.984583Z", "expiration": None, "slug": "bDnxeQ4UmlFVUYCDrb1KBLbE4HVSW8jw3CLElcwPyAncV5eiI+00:00", "document": 1, "file_version": "original", }, { "id": 4, "created": "2023-12-11T14:07:01.448813Z", "expiration": "2023-12-12T14:07:01.423000Z", "slug": "HfzHhDzA03ZQg4t4TAlOuup59qgQA18Zjbb9eOE06PZ8KTjgOb", "document": 2, "file_version": "archive", }, { "id": 5, "created": "2023-12-11T14:11:50.710369Z", "expiration": None, "slug": "7PIGEZbeFv5yIrnpSVwj1QeXiJu0IZCiEWGIV4aUHQrfUQtXne", "document": 1, "file_version": "archive", }, { "id": 6, "created": "2023-12-11T14:11:50.710369Z", "expiration": None, "slug": "7PIGEZbeFv5yIrnpSVwj1QeXiJu0IZCiEWGIV4aUHQrfUQtXne", "document": 1, "file_version": "archive", }, { "id": 7, "created": "2023-12-11T14:11:50.710369Z", "expiration": None, "slug": "7PIGEZbeFv5yIrnpSVwj1QeXiJu0IZCiEWGIV4aUHQrfUQtXne", "document": 1, "file_version": "archive", }, { "id": 8, "created": "2023-12-11T14:11:50.710369Z", "expiration": None, "slug": "7PIGEZbeFv5yIrnpSVwj1QeXiJu0IZCiEWGIV4aUHQrfUQtXne", "document": 1, "file_version": "archive", }, ], } paperless-api-5.1.0/tests/data/statistics.py000066400000000000000000000010421505307417100211010ustar00rootroot00000000000000"""Statistics snapshot.""" DATA_STATISTICS = { "documents_total": 1337, "documents_inbox": 2, "inbox_tag": 1, "inbox_tags": [1], "document_file_type_counts": [ {"mime_type": "application/pdf", "mime_type_count": 1334}, {"mime_type": "image/jpeg", "mime_type_count": 2}, {"mime_type": "message/rfc822", "mime_type_count": 1}, ], "character_count": 13371337, "tag_count": 5, "correspondent_count": 42, "document_type_count": 23, "storage_path_count": 5, "current_asn": 84, } paperless-api-5.1.0/tests/data/status.py000066400000000000000000000017601505307417100202410ustar00rootroot00000000000000"""Status snapshot.""" DATA_STATUS = { "pngx_version": "2.6.1", "server_os": "Linux-6.6.12-linuxkit-aarch64-with-glibc2.36", "install_type": "docker", "storage": { "total": 494384795648, "available": 103324229632, }, "database": { "type": "sqlite", "url": "/usr/src/paperless/data/db.sqlite3", "status": "OK", "error": None, "migration_status": { "latest_migration": "paperless.0003_alter_applicationconfiguration_max_image_pixels", "unapplied_migrations": [], }, }, "tasks": { "redis_url": "redis://broker:6379", "redis_status": "OK", "redis_error": None, "celery_status": "OK", "index_status": "OK", "index_last_modified": "2024-03-06T07:10:55.370884+01:00", "index_error": None, "classifier_status": "OK", "classifier_last_trained": "2024-03-06T07:05:01.281804+00:00", "classifier_error": None, }, } paperless-api-5.1.0/tests/data/storage_paths.py000066400000000000000000000037451505307417100215660ustar00rootroot00000000000000"""Storage paths snapshot.""" DATA_STORAGE_PATHS = { "count": 3, "next": None, "previous": None, "all": [1, 2, 3, 4, 5], "results": [ { "id": 1, "slug": "work", "name": "Work Work Work", "path": "{owner_username}/work/{correspondent}_{created}_{document_type}_{title}", "match": "", "matching_algorithm": 6, "is_insensitive": True, "document_count": 384, "owner": 3, "user_can_change": True, }, { "id": 2, "slug": "banking", "name": "Banking", "path": "{owner_username}/banking/{correspondent}_{created}_{document_type}_{title}", "match": "", "matching_algorithm": 6, "is_insensitive": True, "document_count": 303, "owner": None, "user_can_change": True, }, { "id": 3, "slug": "another-test", "name": "Another Test", "path": "Test/Path/{doc_pk}", "match": "", "matching_algorithm": 0, "is_insensitive": True, "document_count": 0, "owner": None, "user_can_change": True, }, { "id": 4, "slug": "another-test-2", "name": "Another Test 2", "path": "Test/Path/{doc_pk}", "match": "", "matching_algorithm": 0, "is_insensitive": True, "document_count": 0, "owner": None, "user_can_change": True, }, { "id": 5, "slug": "another-test-3", "name": "Another Test 3", "path": "Test/Path/{doc_pk}", "match": "", "matching_algorithm": 0, "is_insensitive": True, "document_count": 0, "owner": None, "user_can_change": True, }, ], } paperless-api-5.1.0/tests/data/tags.py000066400000000000000000000042361505307417100176550ustar00rootroot00000000000000"""Tags snapshot.""" DATA_TAGS = { "count": 2, "next": None, "previous": None, "all": [ 1, 2, 3, 4, 5, ], "results": [ { "id": 1, "slug": "important", "name": "Important", "color": "#ff0000", "text_color": "#00ff00", "match": "", "matching_algorithm": 0, "is_insensitive": True, "is_inbox_tag": False, "document_count": 20, "owner": None, "user_can_change": True, }, { "id": 2, "slug": "inbox", "name": "Inbox", "color": "#ff0000", "text_color": "#00ff00", "match": "", "matching_algorithm": 1, "is_insensitive": True, "is_inbox_tag": True, "document_count": 20, "owner": None, "user_can_change": True, }, { "id": 3, "slug": "test-3", "name": "Test 3", "color": "#ff0000", "text_color": "#00ff00", "match": "", "matching_algorithm": 2, "is_insensitive": True, "is_inbox_tag": False, "document_count": 20, "owner": None, "user_can_change": True, }, { "id": 4, "slug": "test-4", "name": "Test 4", "color": "#ff0000", "text_color": "#00ff00", "match": "", "matching_algorithm": 3, "is_insensitive": True, "is_inbox_tag": False, "document_count": 20, "owner": None, "user_can_change": True, }, { "id": 5, "slug": "test-5", "name": "Test 5", "color": "#ff0000", "text_color": "#00ff00", "match": "", "matching_algorithm": 4, "is_insensitive": True, "is_inbox_tag": False, "document_count": 20, "owner": None, "user_can_change": True, }, ], } paperless-api-5.1.0/tests/data/tasks.py000066400000000000000000000023211505307417100200350ustar00rootroot00000000000000"""Tasks snapshot.""" DATA_TASKS = [ { "id": 1, "task_id": "11112222-aaaa-bbbb-cccc-333344445555", "task_file_name": "a.png", "date_created": "2023-12-16T13:06:29.107815+00:00", "date_done": None, "type": "file", "status": "STARTED", "result": None, "acknowledged": False, "related_document": None, }, { "id": 2, "task_id": "ffffeeee-9999-8888-7777-ddddccccbbbb", "task_file_name": "b.png", "date_created": "2023-12-16T13:06:26.117158+00:00", "date_done": "2023-12-16T13:06:29.859669+00:00", "type": "file", "status": "SUCCESS", "result": "Success. New document id 1780 created", "acknowledged": False, "related_document": "1780", }, { "id": 3, "task_id": "abcdef12-3456-7890-abcd-ef1234567890", "task_file_name": "c.png", "date_created": "2023-12-16T13:04:28.175624+00:00", "date_done": "2023-12-16T13:04:32.318797+00:00", "type": "file", "status": "SUCCESS", "result": "Success. New document id 1779 created", "acknowledged": False, "related_document": "1779", }, ] paperless-api-5.1.0/tests/data/token.py000066400000000000000000000001621505307417100200310ustar00rootroot00000000000000"""Token snapshot.""" from tests.const import PAPERLESS_TEST_TOKEN DATA_TOKEN = {"token": PAPERLESS_TEST_TOKEN} paperless-api-5.1.0/tests/data/users.py000066400000000000000000000257361505307417100200700ustar00rootroot00000000000000"""Users snapshot.""" DATA_USERS = { "count": 4, "next": None, "previous": None, "all": [1, 2, 3, 4], "results": [ { "id": 1, "username": "admin", "email": "you@inside.me", "password": "**********", "first_name": "", "last_name": "", "date_joined": "2022-02-11T22:28:25+00:00", "is_staff": True, "is_active": False, "is_superuser": False, "groups": [], "user_permissions": [], "inherited_permissions": [], }, { "id": 2, "username": "alpha", "email": "alpha@is-not.beta", "password": "**********", "first_name": "Alpha", "last_name": "Centauri", "date_joined": "2023-06-27T15:59:01.975496+00:00", "is_staff": False, "is_active": True, "is_superuser": False, "groups": [1], "user_permissions": [], "inherited_permissions": [ "documents.delete_customfieldinstance", "documents.delete_consumptiontemplate", "documents.delete_customfield", "documents.add_uisettings", "documents.view_uisettings", "documents.view_tag", "documents.change_customfield", "documents.view_sharelink", "documents.change_note", "documents.view_correspondent", "documents.change_customfieldinstance", "documents.delete_note", "documents.add_consumptiontemplate", "documents.add_customfield", "documents.change_sharelink", "documents.view_consumptiontemplate", "documents.view_documenttype", "documents.change_document", "documents.view_customfieldinstance", "documents.add_sharelink", "documents.view_document", "documents.delete_uisettings", "documents.add_savedview", "documents.view_note", "documents.view_customfield", "documents.add_customfieldinstance", "documents.change_uisettings", "documents.change_savedview", "documents.add_document", "documents.add_note", "documents.change_consumptiontemplate", "documents.delete_sharelink", "documents.delete_savedview", "documents.view_savedview", "documents.delete_document", ], }, { "id": 3, "username": "omega", "email": "omega.weapon@ff8.net", "password": "**********", "first_name": "Omega", "last_name": "Weapon", "date_joined": "2022-02-14T13:42:54+00:00", "is_staff": True, "is_active": True, "is_superuser": True, "groups": [1], "user_permissions": [], "inherited_permissions": [ "documents.change_paperlesstask", "documents.delete_customfieldinstance", "django_celery_results.delete_chordcounter", "paperless_mail.change_processedmail", "django_celery_results.view_chordcounter", "documents.view_storagepath", "documents.delete_tag", "sessions.delete_session", "documents.view_log", "django_celery_results.change_groupresult", "documents.add_uisettings", "authtoken.delete_token", "admin.add_logentry", "django_celery_results.add_groupresult", "guardian.view_userobjectpermission", "paperless_mail.add_processedmail", "documents.change_customfieldinstance", "documents.view_correspondent", "auth.add_permission", "admin.view_logentry", "documents.delete_note", "documents.add_customfield", "auth.view_permission", "documents.add_consumptiontemplate", "guardian.delete_groupobjectpermission", "documents.change_sharelink", "documents.view_consumptiontemplate", "documents.delete_log", "guardian.delete_userobjectpermission", "paperless_mail.delete_mailrule", "sessions.change_session", "documents.add_tag", "documents.view_documenttype", "auth.view_user", "auth.add_group", "guardian.view_groupobjectpermission", "documents.change_document", "documents.change_savedviewfilterrule", "paperless_mail.add_mailrule", "documents.view_customfieldinstance", "guardian.add_userobjectpermission", "documents.add_sharelink", "auth.change_user", "documents.view_document", "django_celery_results.delete_taskresult", "documents.delete_uisettings", "documents.add_savedview", "documents.view_note", "authtoken.view_token", "authtoken.change_tokenproxy", "documents.change_storagepath", "documents.change_uisettings", "django_celery_results.add_chordcounter", "paperless_mail.change_mailrule", "documents.change_documenttype", "documents.change_savedview", "auth.delete_permission", "documents.add_storagepath", "django_celery_results.change_chordcounter", "documents.add_note", "documents.change_consumptiontemplate", "documents.delete_savedview", "guardian.change_userobjectpermission", "admin.delete_logentry", "authtoken.add_token", "documents.view_savedview", "documents.delete_paperlesstask", "sessions.view_session", "auth.delete_group", "paperless_mail.delete_mailaccount", "documents.add_savedviewfilterrule", "authtoken.delete_tokenproxy", "documents.delete_consumptiontemplate", "authtoken.view_tokenproxy", "authtoken.change_token", "documents.delete_customfield", "contenttypes.change_contenttype", "documents.change_log", "paperless_mail.view_mailrule", "documents.view_uisettings", "documents.view_tag", "documents.change_customfield", "admin.change_logentry", "documents.view_sharelink", "documents.change_note", "documents.delete_savedviewfilterrule", "auth.add_user", "django_celery_results.delete_groupresult", "documents.change_correspondent", "documents.add_documenttype", "django_celery_results.view_groupresult", "documents.add_paperlesstask", "contenttypes.add_contenttype", "auth.view_group", "guardian.change_groupobjectpermission", "django_celery_results.change_taskresult", "documents.change_tag", "paperless_mail.delete_processedmail", "paperless_mail.change_mailaccount", "documents.delete_correspondent", "paperless_mail.view_processedmail", "documents.delete_storagepath", "sessions.add_session", "paperless_mail.add_mailaccount", "django_celery_results.view_taskresult", "documents.view_customfield", "documents.view_paperlesstask", "documents.add_customfieldinstance", "django_celery_results.add_taskresult", "documents.view_savedviewfilterrule", "documents.add_log", "guardian.add_groupobjectpermission", "auth.delete_user", "auth.change_permission", "documents.add_document", "documents.delete_documenttype", "documents.delete_sharelink", "documents.add_correspondent", "authtoken.add_tokenproxy", "auth.change_group", "contenttypes.delete_contenttype", "contenttypes.view_contenttype", "paperless_mail.view_mailaccount", "documents.delete_document", ], }, { "id": 4, "username": "zg_test", "email": "", "password": "**********", "first_name": "", "last_name": "", "date_joined": "2023-06-27T15:56:39.754859+00:00", "is_staff": False, "is_active": True, "is_superuser": False, "groups": [1], "user_permissions": [], "inherited_permissions": [ "documents.delete_customfieldinstance", "documents.delete_consumptiontemplate", "documents.delete_customfield", "documents.add_uisettings", "documents.view_uisettings", "documents.view_tag", "documents.change_customfield", "documents.view_sharelink", "documents.change_note", "documents.view_correspondent", "documents.change_customfieldinstance", "documents.delete_note", "documents.add_consumptiontemplate", "documents.add_customfield", "documents.change_sharelink", "documents.view_consumptiontemplate", "documents.view_documenttype", "documents.change_document", "documents.view_customfieldinstance", "documents.add_sharelink", "documents.view_document", "documents.delete_uisettings", "documents.add_savedview", "documents.view_note", "documents.view_customfield", "documents.add_customfieldinstance", "documents.change_uisettings", "documents.change_savedview", "documents.add_document", "documents.add_note", "documents.change_consumptiontemplate", "documents.delete_sharelink", "documents.delete_savedview", "documents.view_savedview", "documents.delete_document", ], }, ], } paperless-api-5.1.0/tests/data/workflow.py000066400000000000000000000115061505307417100205670ustar00rootroot00000000000000"""Workflow snapshot.""" # mypy: ignore-errors DATA_WORKFLOWS = { "count": 3, "next": None, "previous": None, "all": [1, 2, 3], "results": [ { "id": 1, "name": "Importordner Template", "order": 1, "enabled": True, "triggers": [ { "id": 1, "sources": [1], "type": 1, "filter_path": None, "filter_filename": "*.pdf", "filter_mailrule": None, "matching_algorithm": 0, "match": "", "is_insensitive": None, "filter_has_tags": [], "filter_has_correspondent": None, "filter_has_document_type": None, } ], "actions": [ { "id": 1, "type": 1, "assign_title": "Some workflow title", "assign_tags": [4], "assign_correspondent": 9, "assign_document_type": 8, "assign_storage_path": 2, "assign_owner": 3, "assign_view_users": [], "assign_view_groups": [], "assign_change_users": [], "assign_change_groups": [], "assign_custom_fields": [2], } ], }, { "id": 2, "name": "API Upload Template", "order": 2, "enabled": True, "triggers": [ { "id": 2, "sources": [1, 2], "type": 1, "filter_path": "/api/*", "filter_filename": "*.pdf", "filter_mailrule": None, "matching_algorithm": 0, "match": "", "is_insensitive": None, "filter_has_tags": [], "filter_has_correspondent": None, "filter_has_document_type": None, } ], "actions": [ { "id": 2, "type": 1, "assign_title": "API", "assign_tags": [4], "assign_correspondent": 9, "assign_document_type": 12, "assign_storage_path": 2, "assign_owner": 3, "assign_view_users": [], "assign_view_groups": [], "assign_change_users": [], "assign_change_groups": [], "assign_custom_fields": [5], } ], }, { "id": 3, "name": "Email Template", "order": 3, "enabled": False, "triggers": [ { "id": 3, "sources": [3], "type": 1, "filter_path": "/mail/*", "filter_filename": "*.eml", "filter_mailrule": 1, "matching_algorithm": 0, "match": "", "is_insensitive": True, "filter_has_tags": [], "filter_has_correspondent": None, "filter_has_document_type": None, } ], "actions": [ { "id": 3, "type": 1, "assign_title": None, "assign_tags": [], "assign_correspondent": None, "assign_document_type": None, "assign_storage_path": None, "assign_owner": 2, "assign_view_users": [3, 7], "assign_view_groups": [1], "assign_change_users": [6], "assign_change_groups": [], "assign_custom_fields": [], } ], }, ], } DATA_WORKFLOW_ACTIONS = { "count": 0, "next": None, "previous": None, "all": [], "results": [], } for wf in DATA_WORKFLOWS["results"]: DATA_WORKFLOW_ACTIONS["count"] += 1 for act in wf["actions"]: DATA_WORKFLOW_ACTIONS["all"].append(act["id"]) DATA_WORKFLOW_ACTIONS["results"].append(act) DATA_WORKFLOW_TRIGGERS = { "count": 0, "next": None, "previous": None, "all": [], "results": [], } for wf in DATA_WORKFLOWS["results"]: DATA_WORKFLOW_TRIGGERS["count"] += 1 for act in wf["triggers"]: DATA_WORKFLOW_TRIGGERS["all"].append(act["id"]) DATA_WORKFLOW_TRIGGERS["results"].append(act) paperless-api-5.1.0/tests/ruff.toml000066400000000000000000000006671505307417100172770ustar00rootroot00000000000000# This extend our general Ruff rules specifically for tests extend = "../pyproject.toml" lint.extend-select = [ "PT", # Use @pytest.fixture without parentheses ] lint.extend-ignore = [ "S101", # As these are tests, the usage of assert could be good practise, no? "S105", # Yes, we hardcoded passwords. It will be ok this time. "SLF001", # Tests will access private/protected members. "TC002", # pytest doesn't like this one. ] paperless-api-5.1.0/tests/test_common.py000066400000000000000000000516011505307417100203330ustar00rootroot00000000000000"""Paperless common tests.""" import re from dataclasses import dataclass, fields from datetime import date, datetime from enum import Enum from typing import TypedDict import aiohttp import pytest from aioresponses import aioresponses from pypaperless import Paperless from pypaperless.const import API_PATH, PaperlessResource from pypaperless.exceptions import ( BadJsonResponseError, DraftNotSupportedError, InitializationError, JsonResponseWithError, PaperlessConnectionError, PaperlessForbiddenError, PaperlessInactiveOrDeletedError, PaperlessInvalidTokenError, ) from pypaperless.models import CustomField, Page from pypaperless.models.base import HelperBase, PaperlessModel from pypaperless.models.common import ( CUSTOM_FIELD_TYPE_VALUE_MAP, CustomFieldDateValue, CustomFieldIntegerValue, CustomFieldMonetaryValue, CustomFieldSelectValue, CustomFieldType, MatchingAlgorithmType, ShareLinkFileVersionType, StatusType, TaskStatusType, WorkflowActionType, WorkflowTriggerSourceType, WorkflowTriggerType, ) from pypaperless.models.mixins import helpers from pypaperless.models.utils import dict_value_to_object, object_to_dict_value from tests.const import ( PAPERLESS_TEST_PASSWORD, PAPERLESS_TEST_TOKEN, PAPERLESS_TEST_URL, PAPERLESS_TEST_USER, ) from .data import DATA_CUSTOM_FIELDS, DATA_PATHS, DATA_TOKEN # mypy: ignore-errors class TestPaperless: """Paperless common test cases.""" async def test_init(self, resp: aioresponses, api: Paperless) -> None: """Test init.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['index']}", status=200, payload=DATA_PATHS, ) await api.initialize() assert api.is_initialized await api.close() async def test_context(self, resp: aioresponses, api: Paperless) -> None: """Test context.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['index']}", status=200, payload=DATA_PATHS, ) async with api: assert api.is_initialized async def test_properties(self, api: Paperless) -> None: """Test properties.""" # version must be None in this case, as we test against # an uninitialized Paperless object assert api.host_version is None assert api.base_url == PAPERLESS_TEST_URL async def test_init_error(self, resp: aioresponses, api: Paperless) -> None: """Test initialization error.""" # simulate connection due no configuration error with pytest.raises(PaperlessConnectionError): await api.initialize() # http status error resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['index']}", status=401, body="any html", ) with pytest.raises(PaperlessInvalidTokenError): await api.initialize() # http 401 - inactive or deleted user resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['index']}", status=401, payload={"detail": "User is inactive"}, ) with pytest.raises(PaperlessInactiveOrDeletedError): await api.initialize() # http status forbidden resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['index']}", status=403, body="any html", ) with pytest.raises(PaperlessForbiddenError): await api.initialize() # http ok, wrong payload resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['index']}", status=200, body="any html", ) with pytest.raises(InitializationError): await api.initialize() @pytest.mark.parametrize( "exception_cls", [ PaperlessConnectionError, PaperlessInvalidTokenError, PaperlessInactiveOrDeletedError, PaperlessForbiddenError, ], ) async def test_errors_are_backwards_compatible(self, exception_cls: type) -> None: """Test, if new errors are backwards compatible.""" assert issubclass(exception_cls, InitializationError) async def test_jsonresponsewitherror(self) -> None: """Test JsonResponseWithError.""" try: payload = "sample string" raise JsonResponseWithError(payload) # noqa: TRY301 except JsonResponseWithError as exc: assert exc.args[0] == "Paperless [error]: sample string" # noqa: PT017 try: payload = {"failure": "something failed"} raise JsonResponseWithError(payload) # noqa: TRY301 except JsonResponseWithError as exc: assert exc.args[0] == "Paperless [failure]: something failed" # noqa: PT017 try: payload = {"error": ["that", "should", "have", "been", "never", "happened"]} raise JsonResponseWithError(payload) # noqa: TRY301 except JsonResponseWithError as exc: assert exc.args[0] == "Paperless [error]: that" # noqa: PT017 try: payload = [{"some": [[{"weird": {"error": ["occurred"]}}]]}] raise JsonResponseWithError(payload) # noqa: TRY301 except JsonResponseWithError as exc: assert exc.args[0] == "Paperless [some -> weird -> error]: occurred" # noqa: PT017 async def test_request(self, resp: aioresponses) -> None: """Test generate request.""" # we need to use an unmocked PaperlessSession.request() method # simply don't initialize Paperless and everything will be fine api = Paperless( PAPERLESS_TEST_URL, PAPERLESS_TEST_TOKEN, ) # test ordinary 200 resp.get( PAPERLESS_TEST_URL, status=200, ) async with api.request("get", PAPERLESS_TEST_URL) as res: assert res.status # last but not least, we test sending a form to test the converter form_data = { "none_field": None, "str_field": "Hello Bytes!", "bytes_field": b"Hello String!", "tuple_field": (b"Document Content", "filename.pdf"), "int_field": 23, "float_field": 13.37, "int_list": [1, 1, 2, 3, 5, 8, 13], "dict_field": {"dict_str_field": "str", "dict_int_field": 2}, } resp.post( PAPERLESS_TEST_URL, status=200, ) async with api.request("post", PAPERLESS_TEST_URL, form=form_data) as res: assert res.status # session is still open await api.close() async def test_request_json(self, resp: aioresponses, api: Paperless) -> None: """Test requests.""" # test 400 bad request with error payload resp.get( f"{PAPERLESS_TEST_URL}/400-json-error-payload", status=400, headers={"Content-Type": "application/json"}, payload={"error": "sample message"}, ) with pytest.raises(JsonResponseWithError): await api.request_json("get", f"{PAPERLESS_TEST_URL}/400-json-error-payload") # test 200 ok with wrong content type resp.get( f"{PAPERLESS_TEST_URL}/200-text-error-payload", status=200, headers={"Content-Type": "text/plain"}, body='{"error": "sample message"}', ) with pytest.raises(BadJsonResponseError): await api.request_json("get", f"{PAPERLESS_TEST_URL}/200-text-error-payload") # test 200 ok with correct content type, but no json payload resp.get( f"{PAPERLESS_TEST_URL}/200-json-text-body", status=200, headers={"Content-Type": "application/json"}, body="test 5 23 42 1337", ) with pytest.raises(BadJsonResponseError): await api.request_json("get", f"{PAPERLESS_TEST_URL}/200-json-text-body") async def test_create_url(self) -> None: """Test create url util.""" create_url = Paperless._create_base_url # pylint: disable=protected-access # test default ssl url = create_url("hostname") assert f"{url.host}" == "hostname" assert int(url.port) == 443 # test enforce http url = create_url("http://hostname") assert int(url.port) == 80 # test non-http scheme url = create_url("ftp://hostname") assert f"{url.scheme}" == "https" # should be https even on just setting a port number url = create_url("hostname:80") assert f"{url.scheme}" == "https" # test api/api url url = create_url("hostname/api/api/") assert f"{url}" == "https://hostname/api/api" # test slashes url = create_url("hostname/api/endpoint///") assert f"{url}" == "https://hostname/api/endpoint" async def test_generate_api_token(self, resp: aioresponses, api: Paperless) -> None: """Test generate api token.""" # test successful token creation resp.post( f"{PAPERLESS_TEST_URL}{API_PATH['token']}", status=200, payload=DATA_TOKEN, ) token = await api.generate_api_token( PAPERLESS_TEST_URL, PAPERLESS_TEST_USER, PAPERLESS_TEST_PASSWORD, ) assert token == PAPERLESS_TEST_TOKEN # test token creation with wrong json answer resp.post( f"{PAPERLESS_TEST_URL}{API_PATH['token']}", status=200, payload={"blah": "any string"}, ) with pytest.raises(BadJsonResponseError): token = await api.generate_api_token( PAPERLESS_TEST_URL, PAPERLESS_TEST_USER, PAPERLESS_TEST_PASSWORD, ) # test error 400 resp.post( f"{PAPERLESS_TEST_URL}{API_PATH['token']}", status=400, payload={"non_field_errors": ["Unable to log in."]}, ) with pytest.raises(JsonResponseWithError): token = await api.generate_api_token( PAPERLESS_TEST_URL, PAPERLESS_TEST_USER, PAPERLESS_TEST_PASSWORD, ) # general exception resp.post( f"{PAPERLESS_TEST_URL}{API_PATH['token']}", exception=ValueError, ) with pytest.raises(ValueError): # noqa: PT011 token = await api.generate_api_token( PAPERLESS_TEST_URL, PAPERLESS_TEST_USER, PAPERLESS_TEST_PASSWORD, ) async def test_generate_api_token_with_session( self, resp: aioresponses, api: Paperless ) -> None: """Test generate api token with custom session.""" session = aiohttp.ClientSession() resp.post( f"{PAPERLESS_TEST_URL}{API_PATH['token']}", status=200, payload=DATA_TOKEN, ) token = await api.generate_api_token( PAPERLESS_TEST_URL, PAPERLESS_TEST_USER, PAPERLESS_TEST_PASSWORD, session=session, ) assert token == PAPERLESS_TEST_TOKEN async def test_types(self) -> None: """Test types.""" never_str = "!never_existing_type!" never_int = 99952342 assert PaperlessResource(never_str) == PaperlessResource.UNKNOWN assert CustomFieldType(never_str) == CustomFieldType.UNKNOWN assert MatchingAlgorithmType(never_int) == MatchingAlgorithmType.UNKNOWN assert ShareLinkFileVersionType(never_str) == ShareLinkFileVersionType.UNKNOWN assert StatusType(never_str) == StatusType.UNKNOWN assert TaskStatusType(never_str) == TaskStatusType.UNKNOWN assert WorkflowActionType(never_int) == WorkflowActionType.UNKNOWN assert WorkflowTriggerType(never_int) == WorkflowTriggerType.UNKNOWN assert WorkflowTriggerSourceType(never_int) == WorkflowTriggerSourceType.UNKNOWN async def test_custom_field_draft_value_wo_cache(self, paperless: Paperless) -> None: """Test draft custom field value without cache.""" custom_field = CustomField.create_with_data( paperless, data={"id": 1337, "name": "Test", "data_type": CustomFieldType.INTEGER}, fetched=True, ) field_value = custom_field.draft_value(1337) for value_type in CUSTOM_FIELD_TYPE_VALUE_MAP.values(): assert not isinstance(field_value, value_type) async def test_custom_field_draft_value_wslash_cache( self, resp: aioresponses, paperless: Paperless ) -> None: """Test draft custom field value with cache.""" # set custom fields cache resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['custom_fields']}" + r"\?.*$"), status=200, payload=DATA_CUSTOM_FIELDS, ) paperless.cache.custom_fields = await paperless.custom_fields.as_dict() custom_field = CustomField.create_with_data( api=paperless, data=DATA_CUSTOM_FIELDS["results"][5], fetched=True, ) field_value = custom_field.draft_value(1337, expected_type=CustomFieldIntegerValue) assert isinstance(field_value, CustomFieldIntegerValue) async def test_custom_field_date_value(self) -> None: """Test `CustomFieldDateValue`.""" test = CustomFieldDateValue(value="1900-01-02") assert isinstance(test.value, date) test = CustomFieldDateValue(value="1900-01-02T03:04:05.133337Z") assert isinstance(test.value, date) async def test_custom_field_monetary_value(self) -> None: """Test `CustomFieldMonetaryValue`.""" field = CustomFieldMonetaryValue(value="EUR1337.00") assert field.currency == "EUR" assert field.amount == 1337 field.amount = 123.45678 assert field.amount == 123.46 # round field.extra_data = {"default_currency": "USD"} assert field.value == "EUR123.46" field.value = "123.45" # no currency assert field.currency == "USD" field.extra_data = {} assert field.currency == "" field.currency = "EUR" assert field.value == "EUR123.45" field.currency = "" assert field.value == "123.45" field.value = None assert field.amount is None async def test_custom_field_select_value(self) -> None: """Test custom field value types.""" test = CustomFieldSelectValue( value="id2", extra_data={ "select_options": [ {"id": "id1", "label": "label1"}, {"id": "id2", "label": "label2"}, ] }, ) assert isinstance(test.labels, list) assert test.label == "label2" # test fail test.extra_data = None assert test.label is None async def test_dataclass_conversion(self) -> None: # pylint: disable=too-many-statements """Test dataclass utils.""" class SomeStatus(Enum): """Test enum.""" ACTIVE = 1 INACTIVE = 2 UNKNOWN = -1 @classmethod def _missing_(cls: "SomeStatus", *_: object) -> "SomeStatus": """Set default.""" return cls.UNKNOWN class SomeNestedExtraData(TypedDict): """Test nested TypedDict.""" ustr: str | None uany: int | str | bool | None class SomeExtraData(TypedDict): """Test TypedDict.""" a_str: str a_dict: dict[str, str] a_list: list[str] a_typeddict: SomeNestedExtraData @dataclass class SomeFriend: """Test class.""" name: str age: int @classmethod def from_dict(cls, data: dict) -> "SomeFriend": """Test from_dict stuff.""" return cls(name=str(data.get("name")), age=int(data.get("age"))) @dataclass class SomePerson: """Test class.""" name: str age: int height: float height2: float birth: date last_login: datetime friends: list[SomeFriend] | None deleted: datetime | None is_deleted: bool status: SomeStatus file: bytes meta: dict[str, str] extra_data: SomeExtraData raw_data = { "name": "Lee Tobi, Sajangnim", "age": 38, "height": 1.76, "height2": 2, "birth": "1986-05-23", "last_login": "2023-08-08T06:06:35.495972Z", "is_deleted": False, "friends": [ { "name": "Erika", "age": "50", # this should be int, check "back conversion" at bottom }, { "name": "Reinhard", "age": 40, }, ], "status": 1, "file": b"5-23-42-666-0815-1337", "meta": {"hairs": "blonde", "eyes": "blue", "loves": "Python"}, "extra_data": { "a_str": "test", "a_dict": { "key1": "val1", "key2": "val2", }, "a_list": ["a", "b", "c"], "a_typeddict": {"ustr": "hello", "uany": 1}, }, } data = { field.name: dict_value_to_object( f"_Person.{__name__}.{field.name}", raw_data.get(field.name), field.type, field.default, ) for field in fields(SomePerson) } res = SomePerson(**data) assert isinstance(res.name, str) assert isinstance(res.age, int) assert isinstance(res.height, float) assert isinstance(res.height2, float) assert isinstance(res.birth, date) assert isinstance(res.last_login, datetime) assert isinstance(res.friends, list) assert isinstance(res.friends[0], SomeFriend) assert isinstance(res.friends[0].age, int) assert isinstance(res.friends[1].age, int) assert res.deleted is None assert res.is_deleted is False assert isinstance(res.status, SomeStatus) assert isinstance(res.file, bytes) assert isinstance(res.extra_data, dict) assert isinstance(res.extra_data["a_typeddict"], dict) # back conversion back = {field.name: object_to_dict_value(getattr(res, field.name)) for field in fields(res)} assert isinstance(back["friends"][0]["age"], int) # was str in the source dict assert isinstance(back["meta"], dict) assert isinstance(back["extra_data"], dict) assert isinstance(back["extra_data"]["a_list"], list) async def test_pages_object(self, api: Paperless) -> None: """Test pages.""" @dataclass(init=False) class TestResource(PaperlessModel): """Test Resource.""" id: int | None = None data = { "count": 0, "current_page": 1, "page_size": 25, "next": "any.url", "previous": None, "all": [], "results": [], } for i in range(1, 101): data["count"] += 1 data["all"].append(i) data["results"].append({"id": i}) page = Page.create_with_data(api, data=data, fetched=True) page._resource_cls = TestResource # pylint: disable=protected-access assert isinstance(page, Page) assert page.current_count == 100 for item in page: assert isinstance(item, TestResource) # check first page assert not page.has_previous_page assert page.has_next_page assert not page.is_last_page assert page.last_page == 4 assert page.next_page == 2 assert page.previous_page is None # check inner page page.previous = "any.url" page.current_page = 3 assert page.previous_page is not None assert page.next_page is not None assert not page.is_last_page # check last page page.next = None page.current_page = 4 assert page.next_page is None assert page.is_last_page async def test_draft_exc(self, api: Paperless) -> None: """Test draft not supported.""" @dataclass(init=False) class TestResource(PaperlessModel): """Test Resource.""" class TestHelper(HelperBase, helpers.DraftableMixin): """Test Helper.""" _api_path = "any.url" _resource = "test" # draft_cls - we "forgot" to set a draft class, which will raise _resource_cls = TestResource helper = TestHelper(api) with pytest.raises(DraftNotSupportedError): # ... there it is helper.draft() paperless-api-5.1.0/tests/test_models_matrix.py000066400000000000000000000351401505307417100217120ustar00rootroot00000000000000"""Paperless basic tests.""" import re from typing import Any import aiohttp import pytest from aioresponses import CallbackResult, aioresponses from pypaperless import Paperless from pypaperless.const import API_PATH from pypaperless.exceptions import DraftFieldRequiredError from pypaperless.models import Page from pypaperless.models.common import PermissionTableType from . import ( CORRESPONDENT_MAP, CUSTOM_FIELD_MAP, DOCUMENT_MAP, DOCUMENT_TYPE_MAP, GROUP_MAP, MAIL_ACCOUNT_MAP, MAIL_RULE_MAP, SAVED_VIEW_MAP, SHARE_LINK_MAP, STORAGE_PATH_MAP, TAG_MAP, USER_MAP, WORKFLOW_MAP, ResourceTestMapping, ) from .const import PAPERLESS_TEST_URL from .data import DATA_OBJECT_PERMISSIONS # mypy: ignore-errors @pytest.mark.parametrize( "mapping", [ DOCUMENT_MAP, DOCUMENT_TYPE_MAP, CORRESPONDENT_MAP, CUSTOM_FIELD_MAP, GROUP_MAP, MAIL_ACCOUNT_MAP, MAIL_RULE_MAP, SAVED_VIEW_MAP, SHARE_LINK_MAP, STORAGE_PATH_MAP, TAG_MAP, USER_MAP, WORKFLOW_MAP, ], scope="class", ) # test models/classifiers.py # test models/custom_fields.py # test models/mails.py # test models/permissions.py # test models/saved_views.py # test models/share_links.py class TestReadOnly: """Read only resources test cases.""" async def test_pages( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test pages.""" resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}" + r"\?.*$"), status=200, payload=mapping.data, ) page = await anext(aiter(getattr(paperless, mapping.resource).pages(1))) assert isinstance(page, Page) assert isinstance(page.items, list) for item in page.items: assert isinstance(item, mapping.model_cls) async def test_as_dict( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test as_dict.""" resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}" + r"\?.*$"), status=200, payload=mapping.data, ) items = await getattr(paperless, mapping.resource).as_dict() for pk, obj in items.items(): assert isinstance(pk, int) assert isinstance(obj, mapping.model_cls) async def test_as_list( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test as_dict.""" resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}" + r"\?.*$"), status=200, payload=mapping.data, ) items = await getattr(paperless, mapping.resource).as_list() for obj in items: assert isinstance(obj, mapping.model_cls) async def test_iter( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test iter.""" resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}" + r"\?.*$"), status=200, payload=mapping.data, ) async for item in getattr(paperless, mapping.resource): assert isinstance(item, mapping.model_cls) async def test_all( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test all.""" resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}" + r"\?.*$"), status=200, payload=mapping.data, ) items = await getattr(paperless, mapping.resource).all() assert isinstance(items, list) for item in items: assert isinstance(item, int) async def test_call( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test call.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1), status=200, payload=mapping.data["results"][0], ) item = await getattr(paperless, mapping.resource)(1) assert item assert isinstance(item, mapping.model_cls) # must raise as 1337 doesn't exist resp.get( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1337), status=404, ) with pytest.raises(aiohttp.ClientResponseError): await getattr(paperless, mapping.resource)(1337) @pytest.mark.parametrize( "mapping", [ CORRESPONDENT_MAP, CUSTOM_FIELD_MAP, DOCUMENT_TYPE_MAP, SHARE_LINK_MAP, STORAGE_PATH_MAP, TAG_MAP, ], scope="class", ) # test models/classifiers.py # test models/custom_fields.py # test models/share_links.py class TestReadWrite: """R/W models test cases.""" async def test_pages( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test pages.""" resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}" + r"\?.*$"), status=200, payload=mapping.data, ) page = await anext(aiter(getattr(paperless, mapping.resource).pages(1))) assert isinstance(page, Page) assert isinstance(page.items, list) for item in page.items: assert isinstance(item, mapping.model_cls) async def test_iter( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test iter.""" resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}" + r"\?.*$"), status=200, payload=mapping.data, ) async for item in getattr(paperless, mapping.resource): assert isinstance(item, mapping.model_cls) async def test_all( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test all.""" resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}" + r"\?.*$"), status=200, payload=mapping.data, ) items = await getattr(paperless, mapping.resource).all() assert isinstance(items, list) for item in items: assert isinstance(item, int) async def test_reduce( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test iter with reduce.""" resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}" + r"\?.*$"), status=200, payload=mapping.data, ) async with getattr(paperless, mapping.resource).reduce( any_filter_param="1", any_filter_list__in=["1", "2"], any_filter_no_list__in="1", ) as q: async for item in q: assert isinstance(item, mapping.model_cls) async def test_call( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test call.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1), status=200, payload=mapping.data["results"][0], ) item = await getattr(paperless, mapping.resource)(1) assert item assert isinstance(item, mapping.model_cls) # must raise as 1337 doesn't exist resp.get( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1337), status=404, ) with pytest.raises(aiohttp.ClientResponseError): await getattr(paperless, mapping.resource)(1337) async def test_create( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test create.""" draft = getattr(paperless, mapping.resource).draft(**mapping.draft_defaults) assert isinstance(draft, mapping.draft_cls) # test empty draft fields if mapping.model_cls not in ( SHARE_LINK_MAP.model_cls, CUSTOM_FIELD_MAP.model_cls, ): backup = draft.name draft.name = None with pytest.raises(DraftFieldRequiredError): await draft.save() draft.name = backup # actually call the create endpoint resp.post( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}", status=200, payload={ "id": len(mapping.data["results"]), **draft._serialize(), # pylint: disable=protected-access }, ) new_pk = await draft.save() assert new_pk >= 1 async def test_udpate( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test update.""" update_field = "name" update_value = "Name Updated" if mapping.model_cls is SHARE_LINK_MAP.model_cls: update_field = "document" update_value = 2 # go on resp.get( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1), status=200, payload=mapping.data["results"][0], ) to_update = await getattr(paperless, mapping.resource)(1) setattr(to_update, update_field, update_value) # actually call the update endpoint resp.patch( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1), status=200, payload={ **to_update._data, # pylint: disable=protected-access update_field: update_value, }, ) await to_update.update() assert getattr(to_update, update_field) == update_value # no updates assert not await to_update.update() # force update setattr(to_update, update_field, update_value) resp.put( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1), status=200, payload={ **to_update._data, # pylint: disable=protected-access update_field: update_value, }, ) await to_update.update(only_changed=False) assert getattr(to_update, update_field) == update_value async def test_delete( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test delete.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1), status=200, payload=mapping.data["results"][0], ) to_delete = await getattr(paperless, mapping.resource)(1) resp.delete( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1), status=204, # Paperless-ngx responds with 204 on deletion ) assert await to_delete.delete() # test deletion failed resp.delete( f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1), status=404, # we send another status code ) assert not await to_delete.delete() @pytest.mark.parametrize( "mapping", [ CORRESPONDENT_MAP, DOCUMENT_MAP, DOCUMENT_TYPE_MAP, STORAGE_PATH_MAP, TAG_MAP, ], scope="class", ) # test models/classifiers.py class TestSecurableMixin: """SecurableMixin test cases.""" async def test_permissions( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test permissions.""" getattr(paperless, mapping.resource).request_permissions = True assert getattr(paperless, mapping.resource).request_permissions # request single object resp.get( re.compile( r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1) + r"\?.*$" ), status=200, payload={ **mapping.data["results"][0], "permissions": DATA_OBJECT_PERMISSIONS, }, ) item = await getattr(paperless, mapping.resource)(1) assert item.has_permissions assert isinstance(item.permissions, PermissionTableType) # request by iterator resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource]}" + r"\?.*$"), status=200, payload={ **mapping.data, "results": [ {**item, "permissions": DATA_OBJECT_PERMISSIONS} for item in mapping.data["results"] ], }, ) async for item in getattr(paperless, mapping.resource): assert isinstance(item, mapping.model_cls) assert item.has_permissions assert isinstance(item.permissions, PermissionTableType) async def test_permission_change( self, resp: aioresponses, paperless: Paperless, mapping: ResourceTestMapping ) -> None: """Test permission changes.""" getattr(paperless, mapping.resource).request_permissions = True assert getattr(paperless, mapping.resource).request_permissions resp.get( re.compile( r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1) + r"\?.*$" ), status=200, payload={ **mapping.data["results"][0], "permissions": DATA_OBJECT_PERMISSIONS, }, ) item = await getattr(paperless, mapping.resource)(1) item.permissions.view.users.append(23) def _lookup_set_permissions( # pylint: disable=unused-argument url: str, json: dict[str, Any], **kwargs: Any, # noqa: ARG001 ) -> CallbackResult: assert url assert "set_permissions" in json return CallbackResult( status=200, payload=item._data, # pylint: disable=protected-access ) resp.patch( re.compile( r"^" + f"{PAPERLESS_TEST_URL}{API_PATH[mapping.resource + '_single']}".format(pk=1) + r"\?.*$" ), callback=_lookup_set_permissions, ) await item.update() paperless-api-5.1.0/tests/test_models_specific.py000066400000000000000000000507051505307417100221770ustar00rootroot00000000000000"""Paperless basic tests.""" import datetime import re import aiohttp import pytest from aioresponses import aioresponses from pypaperless import Paperless from pypaperless.const import API_PATH from pypaperless.exceptions import ( AsnRequestError, DraftFieldRequiredError, PrimaryKeyRequiredError, TaskNotFoundError, ) from pypaperless.models import ( Config, CustomField, Document, DocumentDraft, DocumentMeta, DocumentNote, DocumentNoteDraft, Status, Task, ) from pypaperless.models.common import ( CUSTOM_FIELD_TYPE_VALUE_MAP, CustomFieldBooleanValue, CustomFieldDocumentLinkValue, CustomFieldValue, DocumentMetadataType, DocumentSearchHitType, RetrieveFileMode, StatisticDocumentFileTypeCount, StatusDatabaseType, StatusStorageType, StatusTasksType, ) from pypaperless.models.documents import ( DocumentCustomFieldList, DocumentSuggestions, DownloadedDocument, ) from pypaperless.models.workflows import WorkflowActionHelper, WorkflowTriggerHelper from . import DOCUMENT_MAP from .const import PAPERLESS_TEST_URL from .data import ( DATA_CONFIG, DATA_CUSTOM_FIELDS, DATA_DOCUMENT_METADATA, DATA_DOCUMENT_NOTES, DATA_DOCUMENT_SUGGESTIONS, DATA_DOCUMENTS, DATA_DOCUMENTS_SEARCH, DATA_REMOTE_VERSION, DATA_STATISTICS, DATA_STATUS, DATA_TASKS, ) # mypy: ignore-errors # test models/config.py class TestModelConfig: """Config test cases.""" async def test_call(self, resp: aioresponses, paperless: Paperless) -> None: """Test call.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['config_single']}".format(pk=1), status=200, payload=DATA_CONFIG[0], ) item = await paperless.config(1) assert item assert isinstance(item, Config) # must raise as 1337 doesn't exist resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['config_single']}".format(pk=1337), status=404, ) with pytest.raises(aiohttp.ClientResponseError): await paperless.config(1337) # test models/documents.py class TestModelDocuments: """Documents test cases.""" async def test_lazy(self, resp: aioresponses, paperless: Paperless) -> None: """Test laziness.""" document = Document(paperless, data={"id": 1}) assert not document.is_fetched resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=200, payload=DATA_DOCUMENTS["results"][0], ) await document.load() assert document.is_fetched async def test_create(self, resp: aioresponses, paperless: Paperless) -> None: """Test create.""" defaults = DOCUMENT_MAP.draft_defaults or {} draft = paperless.documents.draft(**defaults) assert isinstance(draft, DocumentDraft) backup = draft.document draft.document = None with pytest.raises(DraftFieldRequiredError): await draft.save() draft.document = backup # actually call the create endpoint resp.post( f"{PAPERLESS_TEST_URL}{API_PATH['documents_post']}", status=200, payload="11112222-3333-4444-5555-666677778888", ) await draft.save() async def test_create_date_property(self, paperless: Paperless) -> None: """Test create_date property - well, lol.""" document = Document.create_with_data( paperless, data={**DATA_DOCUMENTS["results"][0]}, fetched=True ) assert document.created_date == document.created async def test_udpate(self, resp: aioresponses, paperless: Paperless) -> None: """Test update.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=200, payload=DATA_DOCUMENTS["results"][0], ) to_update = await paperless.documents(1) new_title = f"{to_update.title} Updated" to_update.title = new_title # actually call the update endpoint resp.patch( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=200, payload={ **to_update._data, # pylint: disable=protected-access "title": new_title, }, ) await to_update.update() assert to_update.title == new_title async def test_delete(self, resp: aioresponses, paperless: Paperless) -> None: """Test delete.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=200, payload=DATA_DOCUMENTS["results"][0], ) to_delete = await paperless.documents(1) resp.delete( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=204, # Paperless-ngx responds with 204 on deletion ) assert await to_delete.delete() # test deletion failed resp.delete( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=404, # we send another status code ) assert not await to_delete.delete() async def test_meta(self, resp: aioresponses, paperless: Paperless) -> None: """Test meta.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=200, payload=DATA_DOCUMENTS["results"][0], ) document = await paperless.documents(1) resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_meta']}".format(pk=1), status=200, payload=DATA_DOCUMENT_METADATA, ) meta = await document.get_metadata() assert isinstance(meta, DocumentMeta) assert isinstance(meta.original_metadata, list) for item in meta.original_metadata: assert isinstance(item, DocumentMetadataType) assert isinstance(meta.archive_metadata, list) for item in meta.archive_metadata: assert isinstance(item, DocumentMetadataType) async def test_files(self, resp: aioresponses, paperless: Paperless) -> None: """Test files.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=200, payload=DATA_DOCUMENTS["results"][0], ) document = await paperless.documents(1) resp.get( re.compile( r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['documents_download']}".format(pk=1) + r"\?.*$" ), status=200, headers={ "Content-Type": "application/pdf", "Content-Disposition": "attachment;filename=any_filename.pdf", }, body=b"Binary data: download", ) download = await document.get_download() assert isinstance(download, DownloadedDocument) assert download.mode == RetrieveFileMode.DOWNLOAD resp.get( re.compile( r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['documents_preview']}".format(pk=1) + r"\?.*$" ), status=200, headers={ "Content-Type": "application/pdf", }, body=b"Binary data: preview", ) preview = await document.get_preview() assert isinstance(preview, DownloadedDocument) assert preview.mode == RetrieveFileMode.PREVIEW resp.get( re.compile( r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['documents_thumbnail']}".format(pk=1) + r"\?.*$" ), status=200, body=b"Binary data: thumbnail", ) thumbnail = await document.get_thumbnail() assert isinstance(thumbnail, DownloadedDocument) assert thumbnail.mode == RetrieveFileMode.THUMBNAIL async def test_suggestions(self, resp: aioresponses, paperless: Paperless) -> None: """Test suggestions.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=200, payload=DATA_DOCUMENTS["results"][0], ) document = await paperless.documents(1) resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_suggestions']}".format(pk=1), status=200, payload=DATA_DOCUMENT_SUGGESTIONS, ) suggestions = await document.get_suggestions() assert isinstance(suggestions, DocumentSuggestions) async def test_get_next_an(self, resp: aioresponses, paperless: Paperless) -> None: """Test get next asn.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_next_asn']}", status=200, payload=1337, ) asn = await paperless.documents.get_next_asn() assert isinstance(asn, int) resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_next_asn']}", status=500, ) with pytest.raises(AsnRequestError): await paperless.documents.get_next_asn() async def test_searching(self, resp: aioresponses, paperless: Paperless) -> None: """Test searching.""" # search resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['documents']}" + r"\?.*query.*$"), status=200, payload=DATA_DOCUMENTS_SEARCH, ) async for item in paperless.documents.search("1337"): assert isinstance(item, Document) assert item.has_search_hit assert isinstance(item.search_hit, DocumentSearchHitType) # more_like resp.get( re.compile( r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['documents']}" + r"\?.*more_like_id.*$" ), status=200, payload=DATA_DOCUMENTS_SEARCH, ) async for item in paperless.documents.more_like(1337): assert isinstance(item, Document) assert item.has_search_hit assert isinstance(item.search_hit, DocumentSearchHitType) async def test_note_call(self, resp: aioresponses, paperless: Paperless) -> None: """Test call.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=200, payload=DATA_DOCUMENTS["results"][0], ) item = await paperless.documents(1) assert isinstance(item, Document) resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_notes']}".format(pk=1), status=200, payload=DATA_DOCUMENT_NOTES, ) results = await item.notes() assert isinstance(results, list) assert len(results) > 0 for note in results: assert isinstance(note, DocumentNote) assert isinstance(note.created, datetime.datetime) with pytest.raises(PrimaryKeyRequiredError): item = await paperless.documents.notes() async def test_note_create(self, resp: aioresponses, paperless: Paperless) -> None: """Test create.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=200, payload=DATA_DOCUMENTS["results"][0], ) item = await paperless.documents(1) draft = item.notes.draft(note="Test note.") assert isinstance(draft, DocumentNoteDraft) backup = draft.note draft.note = None with pytest.raises(DraftFieldRequiredError): await draft.save() draft.note = backup # actually call the create endpoint resp.post( f"{PAPERLESS_TEST_URL}{API_PATH['documents_notes']}".format(pk=1), status=200, payload=DATA_DOCUMENT_NOTES, ) result = await draft.save() assert isinstance(result, tuple) async def test_note_delete(self, resp: aioresponses, paperless: Paperless) -> None: """Test delete.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=1), status=200, payload=DATA_DOCUMENTS["results"][0], ) item = await paperless.documents(1) resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_notes']}".format(pk=1), status=200, payload=DATA_DOCUMENT_NOTES, ) results = await item.notes() resp.delete( re.compile( r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['documents_notes']}".format(pk=1) + r"\?.*$" ), status=204, # Paperless-ngx responds with 204 on deletion ) deletion = await results.pop().delete() assert deletion async def test_custom_field_list_wo_cache( self, resp: aioresponses, paperless: Paperless ) -> None: """Test custom field list without cache.""" # request document resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=2), status=200, payload=DATA_DOCUMENTS["results"][1], ) item = await paperless.documents(2) assert isinstance(item.custom_fields, DocumentCustomFieldList) # every item MUST NOT be a derived CustomFieldValue instance for field in item.custom_fields: for value_type in CUSTOM_FIELD_TYPE_VALUE_MAP.values(): assert not isinstance(field, value_type) assert isinstance(field, CustomFieldValue) async def test_custom_field_list_wslash_cache( self, resp: aioresponses, paperless: Paperless ) -> None: """Test custom fields list with cache.""" # set custom fields cache resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['custom_fields']}" + r"\?.*$"), status=200, payload=DATA_CUSTOM_FIELDS, ) paperless.cache.custom_fields = await paperless.custom_fields.as_dict() # request document resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['documents_single']}".format(pk=2), status=200, payload=DATA_DOCUMENTS["results"][1], ) item = await paperless.documents(2) assert isinstance(item.custom_fields, DocumentCustomFieldList) # every item may be a derived class or not for field in item.custom_fields: assert isinstance(field, CustomFieldValue) # test if custom field is in document custom field values test_cf = CustomField.create_with_data( api=paperless, data=DATA_CUSTOM_FIELDS["results"][0], fetched=True, ) assert test_cf in item.custom_fields assert isinstance(item.custom_fields.get(test_cf), CustomFieldValue) assert item.custom_fields.default(test_cf) is not None assert item.custom_fields.default(-1337) is None # test typed getters assert isinstance( item.custom_fields.get(test_cf, CustomFieldDocumentLinkValue), CustomFieldDocumentLinkValue, ) assert isinstance( item.custom_fields.default(test_cf, CustomFieldDocumentLinkValue), CustomFieldDocumentLinkValue, ) with pytest.raises(TypeError): item.custom_fields.get(test_cf, CustomFieldBooleanValue) with pytest.raises(TypeError): item.custom_fields.default(test_cf, CustomFieldBooleanValue) # test remove field value item.custom_fields -= test_cf assert test_cf not in item.custom_fields # test add field value item.custom_fields += test_cf.draft_value(1337) assert test_cf in item.custom_fields # test models/remote_version.py class TestModelVersion: """Version test cases.""" async def test_call(self, resp: aioresponses, paperless: Paperless) -> None: """Test call.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['remote_version']}", status=200, payload=DATA_REMOTE_VERSION, ) remote_version = await paperless.remote_version() assert remote_version assert isinstance(remote_version.version, str) assert isinstance(remote_version.update_available, bool) # test models/statistics.py class TestModelStatistics: """Statistics test cases.""" async def test_call(self, resp: aioresponses, paperless: Paperless) -> None: """Test call.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['statistics']}", status=200, payload=DATA_STATISTICS, ) stats = await paperless.statistics() assert stats assert isinstance(stats.character_count, int) assert isinstance(stats.document_file_type_counts, list) for item in stats.document_file_type_counts: assert isinstance(item, StatisticDocumentFileTypeCount) # test models/status.py class TestModelStatus: """Status test cases.""" async def test_call(self, resp: aioresponses, paperless: Paperless) -> None: """Test call.""" resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['status']}", status=200, payload=DATA_STATUS, ) status = await paperless.status() assert status assert isinstance(status, Status) assert isinstance(status.storage, StatusStorageType) assert isinstance(status.database, StatusDatabaseType) assert isinstance(status.tasks, StatusTasksType) async def test_has_errors(self, paperless: Paperless) -> None: """Test has errors.""" data = { "database": { "status": "OK", }, "tasks": { "redis_status": "OK", "celery_status": "OK", "classifier_status": "OK", }, } # everything fine as we initialized Status with OK values only status = Status.create_with_data(paperless, data=data, fetched=True) assert status.has_errors is False # lets set something to ERROR data["database"]["status"] = "ERROR" status = Status.create_with_data(paperless, data=data, fetched=True) assert status.has_errors is True # assume any status value is None; None values are treated as no errors del data["database"]["status"] status = Status.create_with_data(paperless, data=data, fetched=True) assert status.has_errors is False # test models/tasks.py class TestModelTasks: """Tasks test cases.""" async def test_iter(self, resp: aioresponses, paperless: Paperless) -> None: """Test iter.""" resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['tasks']}" + r".*$"), status=200, payload=DATA_TASKS, ) async for item in paperless.tasks: assert isinstance(item, Task) async def test_call(self, resp: aioresponses, paperless: Paperless) -> None: """Test call.""" # by pk resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['tasks_single']}".format(pk=1), status=200, payload=DATA_TASKS[0], ) item = await paperless.tasks(1) assert item assert isinstance(item, Task) # by uuid resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['tasks']}" + r"\?task_id.*$"), status=200, payload=DATA_TASKS, ) item = await paperless.tasks("dummy-found") assert item assert isinstance(item, Task) # must raise as pk doesn't exist resp.get( f"{PAPERLESS_TEST_URL}{API_PATH['tasks_single']}".format(pk=1337), status=404, ) with pytest.raises(aiohttp.ClientResponseError): await paperless.tasks(1337) # must raise as task_id doesn't exist resp.get( re.compile(r"^" + f"{PAPERLESS_TEST_URL}{API_PATH['tasks']}" + r"\?task_id.*$"), status=200, payload=[], ) with pytest.raises(TaskNotFoundError): await paperless.tasks("dummy-not-found") # test models/workflows.py class TestModelWorkflows: """Tasks test cases.""" async def test_helpers(self, paperless: Paperless) -> None: """Test helpers.""" assert isinstance(paperless.workflows.actions, WorkflowActionHelper) assert isinstance(paperless.workflows.triggers, WorkflowTriggerHelper) paperless-api-5.1.0/uv.lock000066400000000000000000003143261505307417100156020ustar00rootroot00000000000000version = 1 revision = 2 requires-python = "==3.13.*" [[package]] name = "aiohappyeyeballs" version = "2.6.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, ] [[package]] name = "aiohttp" version = "3.12.15" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, { name = "aiosignal" }, { name = "attrs" }, { name = "frozenlist" }, { name = "multidict" }, { name = "propcache" }, { name = "yarl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2", size = 7823716, upload-time = "2025-07-29T05:52:32.215Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f2/33/918091abcf102e39d15aba2476ad9e7bd35ddb190dcdd43a854000d3da0d/aiohttp-3.12.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f922ffd05034d439dde1c77a20461cf4a1b0831e6caa26151fe7aa8aaebc315", size = 696741, upload-time = "2025-07-29T05:51:19.021Z" }, { url = "https://files.pythonhosted.org/packages/b5/2a/7495a81e39a998e400f3ecdd44a62107254803d1681d9189be5c2e4530cd/aiohttp-3.12.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ee8a8ac39ce45f3e55663891d4b1d15598c157b4d494a4613e704c8b43112cd", size = 474407, upload-time = "2025-07-29T05:51:21.165Z" }, { url = "https://files.pythonhosted.org/packages/49/fc/a9576ab4be2dcbd0f73ee8675d16c707cfc12d5ee80ccf4015ba543480c9/aiohttp-3.12.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3eae49032c29d356b94eee45a3f39fdf4b0814b397638c2f718e96cfadf4c4e4", size = 466703, upload-time = "2025-07-29T05:51:22.948Z" }, { url = "https://files.pythonhosted.org/packages/09/2f/d4bcc8448cf536b2b54eed48f19682031ad182faa3a3fee54ebe5b156387/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97752ff12cc12f46a9b20327104448042fce5c33a624f88c18f66f9368091c7", size = 1705532, upload-time = "2025-07-29T05:51:25.211Z" }, { url = "https://files.pythonhosted.org/packages/f1/f3/59406396083f8b489261e3c011aa8aee9df360a96ac8fa5c2e7e1b8f0466/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:894261472691d6fe76ebb7fcf2e5870a2ac284c7406ddc95823c8598a1390f0d", size = 1686794, upload-time = "2025-07-29T05:51:27.145Z" }, { url = "https://files.pythonhosted.org/packages/dc/71/164d194993a8d114ee5656c3b7ae9c12ceee7040d076bf7b32fb98a8c5c6/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fa5d9eb82ce98959fc1031c28198b431b4d9396894f385cb63f1e2f3f20ca6b", size = 1738865, upload-time = "2025-07-29T05:51:29.366Z" }, { url = "https://files.pythonhosted.org/packages/1c/00/d198461b699188a93ead39cb458554d9f0f69879b95078dce416d3209b54/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fa751efb11a541f57db59c1dd821bec09031e01452b2b6217319b3a1f34f3d", size = 1788238, upload-time = "2025-07-29T05:51:31.285Z" }, { url = "https://files.pythonhosted.org/packages/85/b8/9e7175e1fa0ac8e56baa83bf3c214823ce250d0028955dfb23f43d5e61fd/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5346b93e62ab51ee2a9d68e8f73c7cf96ffb73568a23e683f931e52450e4148d", size = 1710566, upload-time = "2025-07-29T05:51:33.219Z" }, { url = "https://files.pythonhosted.org/packages/59/e4/16a8eac9df39b48ae102ec030fa9f726d3570732e46ba0c592aeeb507b93/aiohttp-3.12.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:049ec0360f939cd164ecbfd2873eaa432613d5e77d6b04535e3d1fbae5a9e645", size = 1624270, upload-time = "2025-07-29T05:51:35.195Z" }, { url = "https://files.pythonhosted.org/packages/1f/f8/cd84dee7b6ace0740908fd0af170f9fab50c2a41ccbc3806aabcb1050141/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b52dcf013b57464b6d1e51b627adfd69a8053e84b7103a7cd49c030f9ca44461", size = 1677294, upload-time = "2025-07-29T05:51:37.215Z" }, { url = "https://files.pythonhosted.org/packages/ce/42/d0f1f85e50d401eccd12bf85c46ba84f947a84839c8a1c2c5f6e8ab1eb50/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b2af240143dd2765e0fb661fd0361a1b469cab235039ea57663cda087250ea9", size = 1708958, upload-time = "2025-07-29T05:51:39.328Z" }, { url = "https://files.pythonhosted.org/packages/d5/6b/f6fa6c5790fb602538483aa5a1b86fcbad66244997e5230d88f9412ef24c/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac77f709a2cde2cc71257ab2d8c74dd157c67a0558a0d2799d5d571b4c63d44d", size = 1651553, upload-time = "2025-07-29T05:51:41.356Z" }, { url = "https://files.pythonhosted.org/packages/04/36/a6d36ad545fa12e61d11d1932eef273928b0495e6a576eb2af04297fdd3c/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:47f6b962246f0a774fbd3b6b7be25d59b06fdb2f164cf2513097998fc6a29693", size = 1727688, upload-time = "2025-07-29T05:51:43.452Z" }, { url = "https://files.pythonhosted.org/packages/aa/c8/f195e5e06608a97a4e52c5d41c7927301bf757a8e8bb5bbf8cef6c314961/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:760fb7db442f284996e39cf9915a94492e1896baac44f06ae551974907922b64", size = 1761157, upload-time = "2025-07-29T05:51:45.643Z" }, { url = "https://files.pythonhosted.org/packages/05/6a/ea199e61b67f25ba688d3ce93f63b49b0a4e3b3d380f03971b4646412fc6/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51", size = 1710050, upload-time = "2025-07-29T05:51:48.203Z" }, { url = "https://files.pythonhosted.org/packages/b4/2e/ffeb7f6256b33635c29dbed29a22a723ff2dd7401fff42ea60cf2060abfb/aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0", size = 422647, upload-time = "2025-07-29T05:51:50.718Z" }, { url = "https://files.pythonhosted.org/packages/1b/8e/78ee35774201f38d5e1ba079c9958f7629b1fd079459aea9467441dbfbf5/aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84", size = 449067, upload-time = "2025-07-29T05:51:52.549Z" }, ] [[package]] name = "aioresponses" version = "0.7.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "packaging" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/03/532bbc645bdebcf3b6af3b25d46655259d66ce69abba7720b71ebfabbade/aioresponses-0.7.8.tar.gz", hash = "sha256:b861cdfe5dc58f3b8afac7b0a6973d5d7b2cb608dd0f6253d16b8ee8eaf6df11", size = 40253, upload-time = "2025-01-19T18:14:03.222Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/12/b7/584157e43c98aa89810bc2f7099e7e01c728ecf905a66cf705106009228f/aioresponses-0.7.8-py2.py3-none-any.whl", hash = "sha256:b73bd4400d978855e55004b23a3a84cb0f018183bcf066a85ad392800b5b9a94", size = 12518, upload-time = "2025-01-19T18:13:59.633Z" }, ] [[package]] name = "aiosignal" version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, ] sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] [[package]] name = "astroid" version = "3.3.11" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/18/74/dfb75f9ccd592bbedb175d4a32fc643cf569d7c218508bfbd6ea7ef9c091/astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce", size = 400439, upload-time = "2025-07-13T18:04:23.177Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/af/0f/3b8fdc946b4d9cc8cc1e8af42c4e409468c84441b933d037e101b3d72d86/astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec", size = 275612, upload-time = "2025-07-13T18:04:21.07Z" }, ] [[package]] name = "attrs" version = "25.3.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, ] [[package]] name = "cfgv" version = "3.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, ] [[package]] name = "codespell" version = "2.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/15/e0/709453393c0ea77d007d907dd436b3ee262e28b30995ea1aa36c6ffbccaf/codespell-2.4.1.tar.gz", hash = "sha256:299fcdcb09d23e81e35a671bbe746d5ad7e8385972e65dbb833a2eaac33c01e5", size = 344740, upload-time = "2025-01-28T18:52:39.411Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/20/01/b394922252051e97aab231d416c86da3d8a6d781eeadcdca1082867de64e/codespell-2.4.1-py3-none-any.whl", hash = "sha256:3dadafa67df7e4a3dbf51e0d7315061b80d265f9552ebd699b3dd6834b47e425", size = 344501, upload-time = "2025-01-28T18:52:37.057Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] name = "covdefaults" version = "2.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage" }, ] sdist = { url = "https://files.pythonhosted.org/packages/44/ee/9a6f2611f72e4c5657ae5542a510cf4164d2c673687c0ea73bb1cbd85b4d/covdefaults-2.3.0.tar.gz", hash = "sha256:4e99f679f12d792bc62e5510fa3eb59546ed47bd569e36e4fddc4081c9c3ebf7", size = 4835, upload-time = "2023-03-05T16:43:34.779Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/76/4c/823bc951445aa97e5a1b7e337690db3abf85212c8d138e170922e7916ac8/covdefaults-2.3.0-py2.py3-none-any.whl", hash = "sha256:2832961f6ffcfe4b57c338bc3418a3526f495c26fb9c54565409c5532f7c41be", size = 5144, upload-time = "2023-03-05T16:43:33.245Z" }, ] [[package]] name = "coverage" version = "7.10.5" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/61/83/153f54356c7c200013a752ce1ed5448573dca546ce125801afca9e1ac1a4/coverage-7.10.5.tar.gz", hash = "sha256:f2e57716a78bc3ae80b2207be0709a3b2b63b9f2dcf9740ee6ac03588a2015b6", size = 821662, upload-time = "2025-08-23T14:42:44.78Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9f/08/4166ecfb60ba011444f38a5a6107814b80c34c717bc7a23be0d22e92ca09/coverage-7.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ef3b83594d933020f54cf65ea1f4405d1f4e41a009c46df629dd964fcb6e907c", size = 217106, upload-time = "2025-08-23T14:41:15.268Z" }, { url = "https://files.pythonhosted.org/packages/25/d7/b71022408adbf040a680b8c64bf6ead3be37b553e5844f7465643979f7ca/coverage-7.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b96bfdf7c0ea9faebce088a3ecb2382819da4fbc05c7b80040dbc428df6af44", size = 217353, upload-time = "2025-08-23T14:41:16.656Z" }, { url = "https://files.pythonhosted.org/packages/74/68/21e0d254dbf8972bb8dd95e3fe7038f4be037ff04ba47d6d1b12b37510ba/coverage-7.10.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:63df1fdaffa42d914d5c4d293e838937638bf75c794cf20bee12978fc8c4e3bc", size = 248350, upload-time = "2025-08-23T14:41:18.128Z" }, { url = "https://files.pythonhosted.org/packages/90/65/28752c3a896566ec93e0219fc4f47ff71bd2b745f51554c93e8dcb659796/coverage-7.10.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8002dc6a049aac0e81ecec97abfb08c01ef0c1fbf962d0c98da3950ace89b869", size = 250955, upload-time = "2025-08-23T14:41:19.577Z" }, { url = "https://files.pythonhosted.org/packages/a5/eb/ca6b7967f57f6fef31da8749ea20417790bb6723593c8cd98a987be20423/coverage-7.10.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63d4bb2966d6f5f705a6b0c6784c8969c468dbc4bcf9d9ded8bff1c7e092451f", size = 252230, upload-time = "2025-08-23T14:41:20.959Z" }, { url = "https://files.pythonhosted.org/packages/bc/29/17a411b2a2a18f8b8c952aa01c00f9284a1fbc677c68a0003b772ea89104/coverage-7.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1f672efc0731a6846b157389b6e6d5d5e9e59d1d1a23a5c66a99fd58339914d5", size = 250387, upload-time = "2025-08-23T14:41:22.644Z" }, { url = "https://files.pythonhosted.org/packages/c7/89/97a9e271188c2fbb3db82235c33980bcbc733da7da6065afbaa1d685a169/coverage-7.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3f39cef43d08049e8afc1fde4a5da8510fc6be843f8dea350ee46e2a26b2f54c", size = 248280, upload-time = "2025-08-23T14:41:24.061Z" }, { url = "https://files.pythonhosted.org/packages/d1/c6/0ad7d0137257553eb4706b4ad6180bec0a1b6a648b092c5bbda48d0e5b2c/coverage-7.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2968647e3ed5a6c019a419264386b013979ff1fb67dd11f5c9886c43d6a31fc2", size = 249894, upload-time = "2025-08-23T14:41:26.165Z" }, { url = "https://files.pythonhosted.org/packages/84/56/fb3aba936addb4c9e5ea14f5979393f1c2466b4c89d10591fd05f2d6b2aa/coverage-7.10.5-cp313-cp313-win32.whl", hash = "sha256:0d511dda38595b2b6934c2b730a1fd57a3635c6aa2a04cb74714cdfdd53846f4", size = 219536, upload-time = "2025-08-23T14:41:27.694Z" }, { url = "https://files.pythonhosted.org/packages/fc/54/baacb8f2f74431e3b175a9a2881feaa8feb6e2f187a0e7e3046f3c7742b2/coverage-7.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:9a86281794a393513cf117177fd39c796b3f8e3759bb2764259a2abba5cce54b", size = 220330, upload-time = "2025-08-23T14:41:29.081Z" }, { url = "https://files.pythonhosted.org/packages/64/8a/82a3788f8e31dee51d350835b23d480548ea8621f3effd7c3ba3f7e5c006/coverage-7.10.5-cp313-cp313-win_arm64.whl", hash = "sha256:cebd8e906eb98bb09c10d1feed16096700b1198d482267f8bf0474e63a7b8d84", size = 218961, upload-time = "2025-08-23T14:41:30.511Z" }, { url = "https://files.pythonhosted.org/packages/d8/a1/590154e6eae07beee3b111cc1f907c30da6fc8ce0a83ef756c72f3c7c748/coverage-7.10.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0520dff502da5e09d0d20781df74d8189ab334a1e40d5bafe2efaa4158e2d9e7", size = 217819, upload-time = "2025-08-23T14:41:31.962Z" }, { url = "https://files.pythonhosted.org/packages/0d/ff/436ffa3cfc7741f0973c5c89405307fe39b78dcf201565b934e6616fc4ad/coverage-7.10.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d9cd64aca68f503ed3f1f18c7c9174cbb797baba02ca8ab5112f9d1c0328cd4b", size = 218040, upload-time = "2025-08-23T14:41:33.472Z" }, { url = "https://files.pythonhosted.org/packages/a0/ca/5787fb3d7820e66273913affe8209c534ca11241eb34ee8c4fd2aaa9dd87/coverage-7.10.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0913dd1613a33b13c4f84aa6e3f4198c1a21ee28ccb4f674985c1f22109f0aae", size = 259374, upload-time = "2025-08-23T14:41:34.914Z" }, { url = "https://files.pythonhosted.org/packages/b5/89/21af956843896adc2e64fc075eae3c1cadb97ee0a6960733e65e696f32dd/coverage-7.10.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1b7181c0feeb06ed8a02da02792f42f829a7b29990fef52eff257fef0885d760", size = 261551, upload-time = "2025-08-23T14:41:36.333Z" }, { url = "https://files.pythonhosted.org/packages/e1/96/390a69244ab837e0ac137989277879a084c786cf036c3c4a3b9637d43a89/coverage-7.10.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36d42b7396b605f774d4372dd9c49bed71cbabce4ae1ccd074d155709dd8f235", size = 263776, upload-time = "2025-08-23T14:41:38.25Z" }, { url = "https://files.pythonhosted.org/packages/00/32/cfd6ae1da0a521723349f3129b2455832fc27d3f8882c07e5b6fefdd0da2/coverage-7.10.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b4fdc777e05c4940b297bf47bf7eedd56a39a61dc23ba798e4b830d585486ca5", size = 261326, upload-time = "2025-08-23T14:41:40.343Z" }, { url = "https://files.pythonhosted.org/packages/4c/c4/bf8d459fb4ce2201e9243ce6c015936ad283a668774430a3755f467b39d1/coverage-7.10.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:42144e8e346de44a6f1dbd0a56575dd8ab8dfa7e9007da02ea5b1c30ab33a7db", size = 259090, upload-time = "2025-08-23T14:41:42.106Z" }, { url = "https://files.pythonhosted.org/packages/f4/5d/a234f7409896468e5539d42234016045e4015e857488b0b5b5f3f3fa5f2b/coverage-7.10.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:66c644cbd7aed8fe266d5917e2c9f65458a51cfe5eeff9c05f15b335f697066e", size = 260217, upload-time = "2025-08-23T14:41:43.591Z" }, { url = "https://files.pythonhosted.org/packages/f3/ad/87560f036099f46c2ddd235be6476dd5c1d6be6bb57569a9348d43eeecea/coverage-7.10.5-cp313-cp313t-win32.whl", hash = "sha256:2d1b73023854068c44b0c554578a4e1ef1b050ed07cf8b431549e624a29a66ee", size = 220194, upload-time = "2025-08-23T14:41:45.051Z" }, { url = "https://files.pythonhosted.org/packages/36/a8/04a482594fdd83dc677d4a6c7e2d62135fff5a1573059806b8383fad9071/coverage-7.10.5-cp313-cp313t-win_amd64.whl", hash = "sha256:54a1532c8a642d8cc0bd5a9a51f5a9dcc440294fd06e9dda55e743c5ec1a8f14", size = 221258, upload-time = "2025-08-23T14:41:46.44Z" }, { url = "https://files.pythonhosted.org/packages/eb/ad/7da28594ab66fe2bc720f1bc9b131e62e9b4c6e39f044d9a48d18429cc21/coverage-7.10.5-cp313-cp313t-win_arm64.whl", hash = "sha256:74d5b63fe3f5f5d372253a4ef92492c11a4305f3550631beaa432fc9df16fcff", size = 219521, upload-time = "2025-08-23T14:41:47.882Z" }, { url = "https://files.pythonhosted.org/packages/08/b6/fff6609354deba9aeec466e4bcaeb9d1ed3e5d60b14b57df2a36fb2273f2/coverage-7.10.5-py3-none-any.whl", hash = "sha256:0be24d35e4db1d23d0db5c0f6a74a962e2ec83c426b5cac09f4234aadef38e4a", size = 208736, upload-time = "2025-08-23T14:42:43.145Z" }, ] [[package]] name = "dill" version = "0.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, ] [[package]] name = "distlib" version = "0.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] [[package]] name = "filelock" version = "3.19.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, ] [[package]] name = "frozenlist" version = "1.7.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078, upload-time = "2025-06-09T23:02:35.538Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791, upload-time = "2025-06-09T23:01:09.368Z" }, { url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165, upload-time = "2025-06-09T23:01:10.653Z" }, { url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881, upload-time = "2025-06-09T23:01:12.296Z" }, { url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409, upload-time = "2025-06-09T23:01:13.641Z" }, { url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132, upload-time = "2025-06-09T23:01:15.264Z" }, { url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638, upload-time = "2025-06-09T23:01:16.752Z" }, { url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539, upload-time = "2025-06-09T23:01:18.202Z" }, { url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646, upload-time = "2025-06-09T23:01:19.649Z" }, { url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233, upload-time = "2025-06-09T23:01:21.175Z" }, { url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996, upload-time = "2025-06-09T23:01:23.098Z" }, { url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280, upload-time = "2025-06-09T23:01:24.808Z" }, { url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717, upload-time = "2025-06-09T23:01:26.28Z" }, { url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644, upload-time = "2025-06-09T23:01:27.887Z" }, { url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879, upload-time = "2025-06-09T23:01:29.524Z" }, { url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502, upload-time = "2025-06-09T23:01:31.287Z" }, { url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169, upload-time = "2025-06-09T23:01:35.503Z" }, { url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219, upload-time = "2025-06-09T23:01:36.784Z" }, { url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345, upload-time = "2025-06-09T23:01:38.295Z" }, { url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880, upload-time = "2025-06-09T23:01:39.887Z" }, { url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498, upload-time = "2025-06-09T23:01:41.318Z" }, { url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296, upload-time = "2025-06-09T23:01:42.685Z" }, { url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103, upload-time = "2025-06-09T23:01:44.166Z" }, { url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869, upload-time = "2025-06-09T23:01:45.681Z" }, { url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467, upload-time = "2025-06-09T23:01:47.234Z" }, { url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028, upload-time = "2025-06-09T23:01:48.819Z" }, { url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294, upload-time = "2025-06-09T23:01:50.394Z" }, { url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898, upload-time = "2025-06-09T23:01:52.234Z" }, { url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465, upload-time = "2025-06-09T23:01:53.788Z" }, { url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385, upload-time = "2025-06-09T23:01:55.769Z" }, { url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771, upload-time = "2025-06-09T23:01:57.4Z" }, { url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206, upload-time = "2025-06-09T23:01:58.936Z" }, { url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620, upload-time = "2025-06-09T23:02:00.493Z" }, { url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059, upload-time = "2025-06-09T23:02:02.072Z" }, { url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516, upload-time = "2025-06-09T23:02:03.779Z" }, { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" }, ] [[package]] name = "identify" version = "2.6.13" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/82/ca/ffbabe3635bb839aa36b3a893c91a9b0d368cb4d8073e03a12896970af82/identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32", size = 99243, upload-time = "2025-08-09T19:35:00.6Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e7/ce/461b60a3ee109518c055953729bf9ed089a04db895d47e95444071dcdef2/identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b", size = 99153, upload-time = "2025-08-09T19:34:59.1Z" }, ] [[package]] name = "idna" version = "3.10" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] [[package]] name = "isort" version = "6.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b8/21/1e2a441f74a653a144224d7d21afe8f4169e6c7c20bb13aec3a2dc3815e0/isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450", size = 821955, upload-time = "2025-02-26T21:13:16.955Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c1/11/114d0a5f4dabbdcedc1125dee0888514c3c3b16d3e9facad87ed96fad97c/isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615", size = 94186, upload-time = "2025-02-26T21:13:14.911Z" }, ] [[package]] name = "mccabe" version = "0.7.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, ] [[package]] name = "multidict" version = "6.6.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/69/7f/0652e6ed47ab288e3756ea9c0df8b14950781184d4bd7883f4d87dd41245/multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd", size = 101843, upload-time = "2025-08-11T12:08:48.217Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3a/5d/e1db626f64f60008320aab00fbe4f23fc3300d75892a3381275b3d284580/multidict-6.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f46a6e8597f9bd71b31cc708195d42b634c8527fecbcf93febf1052cacc1f16e", size = 75848, upload-time = "2025-08-11T12:07:19.912Z" }, { url = "https://files.pythonhosted.org/packages/4c/aa/8b6f548d839b6c13887253af4e29c939af22a18591bfb5d0ee6f1931dae8/multidict-6.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:22e38b2bc176c5eb9c0a0e379f9d188ae4cd8b28c0f53b52bce7ab0a9e534657", size = 45060, upload-time = "2025-08-11T12:07:21.163Z" }, { url = "https://files.pythonhosted.org/packages/eb/c6/f5e97e5d99a729bc2aa58eb3ebfa9f1e56a9b517cc38c60537c81834a73f/multidict-6.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5df8afd26f162da59e218ac0eefaa01b01b2e6cd606cffa46608f699539246da", size = 43269, upload-time = "2025-08-11T12:07:22.392Z" }, { url = "https://files.pythonhosted.org/packages/dc/31/d54eb0c62516776f36fe67f84a732f97e0b0e12f98d5685bebcc6d396910/multidict-6.6.4-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:49517449b58d043023720aa58e62b2f74ce9b28f740a0b5d33971149553d72aa", size = 237158, upload-time = "2025-08-11T12:07:23.636Z" }, { url = "https://files.pythonhosted.org/packages/c4/1c/8a10c1c25b23156e63b12165a929d8eb49a6ed769fdbefb06e6f07c1e50d/multidict-6.6.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9408439537c5afdca05edd128a63f56a62680f4b3c234301055d7a2000220f", size = 257076, upload-time = "2025-08-11T12:07:25.049Z" }, { url = "https://files.pythonhosted.org/packages/ad/86/90e20b5771d6805a119e483fd3d1e8393e745a11511aebca41f0da38c3e2/multidict-6.6.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87a32d20759dc52a9e850fe1061b6e41ab28e2998d44168a8a341b99ded1dba0", size = 240694, upload-time = "2025-08-11T12:07:26.458Z" }, { url = "https://files.pythonhosted.org/packages/e7/49/484d3e6b535bc0555b52a0a26ba86e4d8d03fd5587d4936dc59ba7583221/multidict-6.6.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52e3c8d43cdfff587ceedce9deb25e6ae77daba560b626e97a56ddcad3756879", size = 266350, upload-time = "2025-08-11T12:07:27.94Z" }, { url = "https://files.pythonhosted.org/packages/bf/b4/aa4c5c379b11895083d50021e229e90c408d7d875471cb3abf721e4670d6/multidict-6.6.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ad8850921d3a8d8ff6fbef790e773cecfc260bbfa0566998980d3fa8f520bc4a", size = 267250, upload-time = "2025-08-11T12:07:29.303Z" }, { url = "https://files.pythonhosted.org/packages/80/e5/5e22c5bf96a64bdd43518b1834c6d95a4922cc2066b7d8e467dae9b6cee6/multidict-6.6.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:497a2954adc25c08daff36f795077f63ad33e13f19bfff7736e72c785391534f", size = 254900, upload-time = "2025-08-11T12:07:30.764Z" }, { url = "https://files.pythonhosted.org/packages/17/38/58b27fed927c07035abc02befacab42491e7388ca105e087e6e0215ead64/multidict-6.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:024ce601f92d780ca1617ad4be5ac15b501cc2414970ffa2bb2bbc2bd5a68fa5", size = 252355, upload-time = "2025-08-11T12:07:32.205Z" }, { url = "https://files.pythonhosted.org/packages/d0/a1/dad75d23a90c29c02b5d6f3d7c10ab36c3197613be5d07ec49c7791e186c/multidict-6.6.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a693fc5ed9bdd1c9e898013e0da4dcc640de7963a371c0bd458e50e046bf6438", size = 250061, upload-time = "2025-08-11T12:07:33.623Z" }, { url = "https://files.pythonhosted.org/packages/b8/1a/ac2216b61c7f116edab6dc3378cca6c70dc019c9a457ff0d754067c58b20/multidict-6.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:190766dac95aab54cae5b152a56520fd99298f32a1266d66d27fdd1b5ac00f4e", size = 249675, upload-time = "2025-08-11T12:07:34.958Z" }, { url = "https://files.pythonhosted.org/packages/d4/79/1916af833b800d13883e452e8e0977c065c4ee3ab7a26941fbfdebc11895/multidict-6.6.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8f2a5ffdceab9dcd97c7a016deb2308531d5f0fced2bb0c9e1df45b3363d7", size = 261247, upload-time = "2025-08-11T12:07:36.588Z" }, { url = "https://files.pythonhosted.org/packages/c5/65/d1f84fe08ac44a5fc7391cbc20a7cedc433ea616b266284413fd86062f8c/multidict-6.6.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:59e8d40ab1f5a8597abcef00d04845155a5693b5da00d2c93dbe88f2050f2812", size = 257960, upload-time = "2025-08-11T12:07:39.735Z" }, { url = "https://files.pythonhosted.org/packages/13/b5/29ec78057d377b195ac2c5248c773703a6b602e132a763e20ec0457e7440/multidict-6.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:467fe64138cfac771f0e949b938c2e1ada2b5af22f39692aa9258715e9ea613a", size = 250078, upload-time = "2025-08-11T12:07:41.525Z" }, { url = "https://files.pythonhosted.org/packages/c4/0e/7e79d38f70a872cae32e29b0d77024bef7834b0afb406ddae6558d9e2414/multidict-6.6.4-cp313-cp313-win32.whl", hash = "sha256:14616a30fe6d0a48d0a48d1a633ab3b8bec4cf293aac65f32ed116f620adfd69", size = 41708, upload-time = "2025-08-11T12:07:43.405Z" }, { url = "https://files.pythonhosted.org/packages/9d/34/746696dffff742e97cd6a23da953e55d0ea51fa601fa2ff387b3edcfaa2c/multidict-6.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:40cd05eaeb39e2bc8939451f033e57feaa2ac99e07dbca8afe2be450a4a3b6cf", size = 45912, upload-time = "2025-08-11T12:07:45.082Z" }, { url = "https://files.pythonhosted.org/packages/c7/87/3bac136181e271e29170d8d71929cdeddeb77f3e8b6a0c08da3a8e9da114/multidict-6.6.4-cp313-cp313-win_arm64.whl", hash = "sha256:f6eb37d511bfae9e13e82cb4d1af36b91150466f24d9b2b8a9785816deb16605", size = 43076, upload-time = "2025-08-11T12:07:46.746Z" }, { url = "https://files.pythonhosted.org/packages/64/94/0a8e63e36c049b571c9ae41ee301ada29c3fee9643d9c2548d7d558a1d99/multidict-6.6.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6c84378acd4f37d1b507dfa0d459b449e2321b3ba5f2338f9b085cf7a7ba95eb", size = 82812, upload-time = "2025-08-11T12:07:48.402Z" }, { url = "https://files.pythonhosted.org/packages/25/1a/be8e369dfcd260d2070a67e65dd3990dd635cbd735b98da31e00ea84cd4e/multidict-6.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0e0558693063c75f3d952abf645c78f3c5dfdd825a41d8c4d8156fc0b0da6e7e", size = 48313, upload-time = "2025-08-11T12:07:49.679Z" }, { url = "https://files.pythonhosted.org/packages/26/5a/dd4ade298674b2f9a7b06a32c94ffbc0497354df8285f27317c66433ce3b/multidict-6.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3f8e2384cb83ebd23fd07e9eada8ba64afc4c759cd94817433ab8c81ee4b403f", size = 46777, upload-time = "2025-08-11T12:07:51.318Z" }, { url = "https://files.pythonhosted.org/packages/89/db/98aa28bc7e071bfba611ac2ae803c24e96dd3a452b4118c587d3d872c64c/multidict-6.6.4-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f996b87b420995a9174b2a7c1a8daf7db4750be6848b03eb5e639674f7963773", size = 229321, upload-time = "2025-08-11T12:07:52.965Z" }, { url = "https://files.pythonhosted.org/packages/c7/bc/01ddda2a73dd9d167bd85d0e8ef4293836a8f82b786c63fb1a429bc3e678/multidict-6.6.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc356250cffd6e78416cf5b40dc6a74f1edf3be8e834cf8862d9ed5265cf9b0e", size = 249954, upload-time = "2025-08-11T12:07:54.423Z" }, { url = "https://files.pythonhosted.org/packages/06/78/6b7c0f020f9aa0acf66d0ab4eb9f08375bac9a50ff5e3edb1c4ccd59eafc/multidict-6.6.4-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dadf95aa862714ea468a49ad1e09fe00fcc9ec67d122f6596a8d40caf6cec7d0", size = 228612, upload-time = "2025-08-11T12:07:55.914Z" }, { url = "https://files.pythonhosted.org/packages/00/44/3faa416f89b2d5d76e9d447296a81521e1c832ad6e40b92f990697b43192/multidict-6.6.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dd57515bebffd8ebd714d101d4c434063322e4fe24042e90ced41f18b6d3395", size = 257528, upload-time = "2025-08-11T12:07:57.371Z" }, { url = "https://files.pythonhosted.org/packages/05/5f/77c03b89af0fcb16f018f668207768191fb9dcfb5e3361a5e706a11db2c9/multidict-6.6.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:967af5f238ebc2eb1da4e77af5492219fbd9b4b812347da39a7b5f5c72c0fa45", size = 256329, upload-time = "2025-08-11T12:07:58.844Z" }, { url = "https://files.pythonhosted.org/packages/cf/e9/ed750a2a9afb4f8dc6f13dc5b67b514832101b95714f1211cd42e0aafc26/multidict-6.6.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a4c6875c37aae9794308ec43e3530e4aa0d36579ce38d89979bbf89582002bb", size = 247928, upload-time = "2025-08-11T12:08:01.037Z" }, { url = "https://files.pythonhosted.org/packages/1f/b5/e0571bc13cda277db7e6e8a532791d4403dacc9850006cb66d2556e649c0/multidict-6.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f683a551e92bdb7fac545b9c6f9fa2aebdeefa61d607510b3533286fcab67f5", size = 245228, upload-time = "2025-08-11T12:08:02.96Z" }, { url = "https://files.pythonhosted.org/packages/f3/a3/69a84b0eccb9824491f06368f5b86e72e4af54c3067c37c39099b6687109/multidict-6.6.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:3ba5aaf600edaf2a868a391779f7a85d93bed147854925f34edd24cc70a3e141", size = 235869, upload-time = "2025-08-11T12:08:04.746Z" }, { url = "https://files.pythonhosted.org/packages/a9/9d/28802e8f9121a6a0804fa009debf4e753d0a59969ea9f70be5f5fdfcb18f/multidict-6.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:580b643b7fd2c295d83cad90d78419081f53fd532d1f1eb67ceb7060f61cff0d", size = 243446, upload-time = "2025-08-11T12:08:06.332Z" }, { url = "https://files.pythonhosted.org/packages/38/ea/6c98add069b4878c1d66428a5f5149ddb6d32b1f9836a826ac764b9940be/multidict-6.6.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:37b7187197da6af3ee0b044dbc9625afd0c885f2800815b228a0e70f9a7f473d", size = 252299, upload-time = "2025-08-11T12:08:07.931Z" }, { url = "https://files.pythonhosted.org/packages/3a/09/8fe02d204473e14c0af3affd50af9078839dfca1742f025cca765435d6b4/multidict-6.6.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e1b93790ed0bc26feb72e2f08299691ceb6da5e9e14a0d13cc74f1869af327a0", size = 246926, upload-time = "2025-08-11T12:08:09.467Z" }, { url = "https://files.pythonhosted.org/packages/37/3d/7b1e10d774a6df5175ecd3c92bff069e77bed9ec2a927fdd4ff5fe182f67/multidict-6.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a506a77ddee1efcca81ecbeae27ade3e09cdf21a8ae854d766c2bb4f14053f92", size = 243383, upload-time = "2025-08-11T12:08:10.981Z" }, { url = "https://files.pythonhosted.org/packages/50/b0/a6fae46071b645ae98786ab738447de1ef53742eaad949f27e960864bb49/multidict-6.6.4-cp313-cp313t-win32.whl", hash = "sha256:f93b2b2279883d1d0a9e1bd01f312d6fc315c5e4c1f09e112e4736e2f650bc4e", size = 47775, upload-time = "2025-08-11T12:08:12.439Z" }, { url = "https://files.pythonhosted.org/packages/b2/0a/2436550b1520091af0600dff547913cb2d66fbac27a8c33bc1b1bccd8d98/multidict-6.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:6d46a180acdf6e87cc41dc15d8f5c2986e1e8739dc25dbb7dac826731ef381a4", size = 53100, upload-time = "2025-08-11T12:08:13.823Z" }, { url = "https://files.pythonhosted.org/packages/97/ea/43ac51faff934086db9c072a94d327d71b7d8b40cd5dcb47311330929ef0/multidict-6.6.4-cp313-cp313t-win_arm64.whl", hash = "sha256:756989334015e3335d087a27331659820d53ba432befdef6a718398b0a8493ad", size = 45501, upload-time = "2025-08-11T12:08:15.173Z" }, { url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313, upload-time = "2025-08-11T12:08:46.891Z" }, ] [[package]] name = "mypy" version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" }, { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" }, { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" }, { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, ] [[package]] name = "mypy-extensions" version = "1.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] [[package]] name = "nodeenv" version = "1.9.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] name = "pathspec" version = "0.12.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] [[package]] name = "platformdirs" version = "4.3.8" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, ] [[package]] name = "pluggy" version = "1.6.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] name = "pre-commit" version = "4.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, { name = "identify" }, { name = "nodeenv" }, { name = "pyyaml" }, { name = "virtualenv" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, ] [[package]] name = "pre-commit-hooks" version = "6.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/36/4d/93e63e48f8fd16d6c1e4cef5dabadcade4d1325c7fd6f29f075a4d2284f3/pre_commit_hooks-6.0.0.tar.gz", hash = "sha256:76d8370c006f5026cdd638a397a678d26dda735a3c88137e05885a020f824034", size = 28293, upload-time = "2025-08-09T19:25:04.6Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/12/46/eba9be9daa403fa94854ce16a458c29df9a01c6c047931c3d8be6016cd9a/pre_commit_hooks-6.0.0-py2.py3-none-any.whl", hash = "sha256:76161b76d321d2f8ee2a8e0b84c30ee8443e01376121fd1c90851e33e3bd7ee2", size = 41338, upload-time = "2025-08-09T19:25:03.513Z" }, ] [[package]] name = "propcache" version = "0.3.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286, upload-time = "2025-06-09T22:54:54.369Z" }, { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425, upload-time = "2025-06-09T22:54:55.642Z" }, { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846, upload-time = "2025-06-09T22:54:57.246Z" }, { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871, upload-time = "2025-06-09T22:54:58.975Z" }, { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720, upload-time = "2025-06-09T22:55:00.471Z" }, { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203, upload-time = "2025-06-09T22:55:01.834Z" }, { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365, upload-time = "2025-06-09T22:55:03.199Z" }, { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016, upload-time = "2025-06-09T22:55:04.518Z" }, { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596, upload-time = "2025-06-09T22:55:05.942Z" }, { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977, upload-time = "2025-06-09T22:55:07.792Z" }, { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220, upload-time = "2025-06-09T22:55:09.173Z" }, { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642, upload-time = "2025-06-09T22:55:10.62Z" }, { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789, upload-time = "2025-06-09T22:55:12.029Z" }, { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880, upload-time = "2025-06-09T22:55:13.45Z" }, { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220, upload-time = "2025-06-09T22:55:15.284Z" }, { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678, upload-time = "2025-06-09T22:55:16.445Z" }, { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560, upload-time = "2025-06-09T22:55:17.598Z" }, { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676, upload-time = "2025-06-09T22:55:18.922Z" }, { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701, upload-time = "2025-06-09T22:55:20.106Z" }, { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934, upload-time = "2025-06-09T22:55:21.5Z" }, { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316, upload-time = "2025-06-09T22:55:22.918Z" }, { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619, upload-time = "2025-06-09T22:55:24.651Z" }, { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896, upload-time = "2025-06-09T22:55:26.049Z" }, { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111, upload-time = "2025-06-09T22:55:27.381Z" }, { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334, upload-time = "2025-06-09T22:55:28.747Z" }, { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026, upload-time = "2025-06-09T22:55:30.184Z" }, { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724, upload-time = "2025-06-09T22:55:31.646Z" }, { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868, upload-time = "2025-06-09T22:55:33.209Z" }, { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322, upload-time = "2025-06-09T22:55:35.065Z" }, { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778, upload-time = "2025-06-09T22:55:36.45Z" }, { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175, upload-time = "2025-06-09T22:55:38.436Z" }, { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857, upload-time = "2025-06-09T22:55:39.687Z" }, { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] name = "pylint" version = "3.3.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "astroid" }, { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "dill" }, { name = "isort" }, { name = "mccabe" }, { name = "platformdirs" }, { name = "tomlkit" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/58/1f614a84d3295c542e9f6e2c764533eea3f318f4592dc1ea06c797114767/pylint-3.3.8.tar.gz", hash = "sha256:26698de19941363037e2937d3db9ed94fb3303fdadf7d98847875345a8bb6b05", size = 1523947, upload-time = "2025-08-09T09:12:57.234Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2d/1a/711e93a7ab6c392e349428ea56e794a3902bb4e0284c1997cff2d7efdbc1/pylint-3.3.8-py3-none-any.whl", hash = "sha256:7ef94aa692a600e82fabdd17102b73fc226758218c97473c7ad67bd4cb905d83", size = 523153, upload-time = "2025-08-09T09:12:54.836Z" }, ] [[package]] name = "pypaperless" version = "0.0.0" source = { editable = "." } dependencies = [ { name = "aiohttp" }, { name = "yarl" }, ] [package.dev-dependencies] dev = [ { name = "aioresponses" }, { name = "codespell" }, { name = "covdefaults" }, { name = "coverage" }, { name = "mypy" }, { name = "pre-commit" }, { name = "pre-commit-hooks" }, { name = "pylint" }, { name = "pytest" }, { name = "pytest-aiohttp" }, { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "ruff" }, { name = "yamllint" }, ] [package.metadata] requires-dist = [ { name = "aiohttp", specifier = ">=3.12.15" }, { name = "yarl", specifier = ">=1.20.1" }, ] [package.metadata.requires-dev] dev = [ { name = "aioresponses", specifier = ">=0.7.8" }, { name = "codespell", specifier = ">=2.4.1" }, { name = "covdefaults", specifier = ">=2.3.0" }, { name = "coverage", specifier = ">=7.10.5" }, { name = "mypy", specifier = ">=1.17.1" }, { name = "pre-commit", specifier = ">=4.3.0" }, { name = "pre-commit-hooks", specifier = ">=6.0.0" }, { name = "pylint", specifier = ">=3.3.8" }, { name = "pytest", specifier = ">=8.4.1" }, { name = "pytest-aiohttp", specifier = ">=1.1.0" }, { name = "pytest-asyncio", specifier = ">=1.1.0" }, { name = "pytest-cov", specifier = ">=6.2.1" }, { name = "ruff", specifier = ">=0.12.10" }, { name = "yamllint", specifier = ">=1.37.1" }, ] [[package]] name = "pytest" version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, ] [[package]] name = "pytest-aiohttp" version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "pytest" }, { name = "pytest-asyncio" }, ] sdist = { url = "https://files.pythonhosted.org/packages/72/4b/d326890c153f2c4ce1bf45d07683c08c10a1766058a22934620bc6ac6592/pytest_aiohttp-1.1.0.tar.gz", hash = "sha256:147de8cb164f3fc9d7196967f109ab3c0b93ea3463ab50631e56438eab7b5adc", size = 12842, upload-time = "2025-01-23T12:44:04.465Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ba/0f/e6af71c02e0f1098eaf7d2dbf3ffdf0a69fc1e0ef174f96af05cef161f1b/pytest_aiohttp-1.1.0-py3-none-any.whl", hash = "sha256:f39a11693a0dce08dd6c542d241e199dd8047a6e6596b2bcfa60d373f143456d", size = 8932, upload-time = "2025-01-23T12:44:03.27Z" }, ] [[package]] name = "pytest-asyncio" version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, ] [[package]] name = "pytest-cov" version = "6.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage" }, { name = "pluggy" }, { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, ] [[package]] name = "pyyaml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, ] [[package]] name = "ruamel-yaml" version = "0.18.15" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml-clib", marker = "platform_python_implementation == 'CPython'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3e/db/f3950f5e5031b618aae9f423a39bf81a55c148aecd15a34527898e752cf4/ruamel.yaml-0.18.15.tar.gz", hash = "sha256:dbfca74b018c4c3fba0b9cc9ee33e53c371194a9000e694995e620490fd40700", size = 146865, upload-time = "2025-08-19T11:15:10.694Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/e5/f2a0621f1781b76a38194acae72f01e37b1941470407345b6e8653ad7640/ruamel.yaml-0.18.15-py3-none-any.whl", hash = "sha256:148f6488d698b7a5eded5ea793a025308b25eca97208181b6a026037f391f701", size = 119702, upload-time = "2025-08-19T11:15:07.696Z" }, ] [[package]] name = "ruamel-yaml-clib" version = "0.2.12" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011, upload-time = "2024-10-20T10:13:04.377Z" }, { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488, upload-time = "2024-10-20T10:13:05.906Z" }, { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066, upload-time = "2024-10-20T10:13:07.26Z" }, { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785, upload-time = "2024-10-20T10:13:08.504Z" }, { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017, upload-time = "2024-10-21T11:26:48.866Z" }, { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270, upload-time = "2024-10-21T11:26:50.213Z" }, { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059, upload-time = "2024-12-11T19:58:18.846Z" }, { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583, upload-time = "2024-10-20T10:13:09.658Z" }, { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190, upload-time = "2024-10-20T10:13:10.66Z" }, ] [[package]] name = "ruff" version = "0.12.10" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/3b/eb/8c073deb376e46ae767f4961390d17545e8535921d2f65101720ed8bd434/ruff-0.12.10.tar.gz", hash = "sha256:189ab65149d11ea69a2d775343adf5f49bb2426fc4780f65ee33b423ad2e47f9", size = 5310076, upload-time = "2025-08-21T18:23:22.595Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/24/e7/560d049d15585d6c201f9eeacd2fd130def3741323e5ccf123786e0e3c95/ruff-0.12.10-py3-none-linux_armv6l.whl", hash = "sha256:8b593cb0fb55cc8692dac7b06deb29afda78c721c7ccfed22db941201b7b8f7b", size = 11935161, upload-time = "2025-08-21T18:22:26.965Z" }, { url = "https://files.pythonhosted.org/packages/d1/b0/ad2464922a1113c365d12b8f80ed70fcfb39764288ac77c995156080488d/ruff-0.12.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ebb7333a45d56efc7c110a46a69a1b32365d5c5161e7244aaf3aa20ce62399c1", size = 12660884, upload-time = "2025-08-21T18:22:30.925Z" }, { url = "https://files.pythonhosted.org/packages/d7/f1/97f509b4108d7bae16c48389f54f005b62ce86712120fd8b2d8e88a7cb49/ruff-0.12.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d59e58586829f8e4a9920788f6efba97a13d1fa320b047814e8afede381c6839", size = 11872754, upload-time = "2025-08-21T18:22:34.035Z" }, { url = "https://files.pythonhosted.org/packages/12/ad/44f606d243f744a75adc432275217296095101f83f966842063d78eee2d3/ruff-0.12.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:822d9677b560f1fdeab69b89d1f444bf5459da4aa04e06e766cf0121771ab844", size = 12092276, upload-time = "2025-08-21T18:22:36.764Z" }, { url = "https://files.pythonhosted.org/packages/06/1f/ed6c265e199568010197909b25c896d66e4ef2c5e1c3808caf461f6f3579/ruff-0.12.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b4a64f4062a50c75019c61c7017ff598cb444984b638511f48539d3a1c98db", size = 11734700, upload-time = "2025-08-21T18:22:39.822Z" }, { url = "https://files.pythonhosted.org/packages/63/c5/b21cde720f54a1d1db71538c0bc9b73dee4b563a7dd7d2e404914904d7f5/ruff-0.12.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6f4064c69d2542029b2a61d39920c85240c39837599d7f2e32e80d36401d6e", size = 13468783, upload-time = "2025-08-21T18:22:42.559Z" }, { url = "https://files.pythonhosted.org/packages/02/9e/39369e6ac7f2a1848f22fb0b00b690492f20811a1ac5c1fd1d2798329263/ruff-0.12.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:059e863ea3a9ade41407ad71c1de2badfbe01539117f38f763ba42a1206f7559", size = 14436642, upload-time = "2025-08-21T18:22:45.612Z" }, { url = "https://files.pythonhosted.org/packages/e3/03/5da8cad4b0d5242a936eb203b58318016db44f5c5d351b07e3f5e211bb89/ruff-0.12.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bef6161e297c68908b7218fa6e0e93e99a286e5ed9653d4be71e687dff101cf", size = 13859107, upload-time = "2025-08-21T18:22:48.886Z" }, { url = "https://files.pythonhosted.org/packages/19/19/dd7273b69bf7f93a070c9cec9494a94048325ad18fdcf50114f07e6bf417/ruff-0.12.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f1345fbf8fb0531cd722285b5f15af49b2932742fc96b633e883da8d841896b", size = 12886521, upload-time = "2025-08-21T18:22:51.567Z" }, { url = "https://files.pythonhosted.org/packages/c0/1d/b4207ec35e7babaee62c462769e77457e26eb853fbdc877af29417033333/ruff-0.12.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f68433c4fbc63efbfa3ba5db31727db229fa4e61000f452c540474b03de52a9", size = 13097528, upload-time = "2025-08-21T18:22:54.609Z" }, { url = "https://files.pythonhosted.org/packages/ff/00/58f7b873b21114456e880b75176af3490d7a2836033779ca42f50de3b47a/ruff-0.12.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:141ce3d88803c625257b8a6debf4a0473eb6eed9643a6189b68838b43e78165a", size = 13080443, upload-time = "2025-08-21T18:22:57.413Z" }, { url = "https://files.pythonhosted.org/packages/12/8c/9e6660007fb10189ccb78a02b41691288038e51e4788bf49b0a60f740604/ruff-0.12.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f3fc21178cd44c98142ae7590f42ddcb587b8e09a3b849cbc84edb62ee95de60", size = 11896759, upload-time = "2025-08-21T18:23:00.473Z" }, { url = "https://files.pythonhosted.org/packages/67/4c/6d092bb99ea9ea6ebda817a0e7ad886f42a58b4501a7e27cd97371d0ba54/ruff-0.12.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7d1a4e0bdfafcd2e3e235ecf50bf0176f74dd37902f241588ae1f6c827a36c56", size = 11701463, upload-time = "2025-08-21T18:23:03.211Z" }, { url = "https://files.pythonhosted.org/packages/59/80/d982c55e91df981f3ab62559371380616c57ffd0172d96850280c2b04fa8/ruff-0.12.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e67d96827854f50b9e3e8327b031647e7bcc090dbe7bb11101a81a3a2cbf1cc9", size = 12691603, upload-time = "2025-08-21T18:23:06.935Z" }, { url = "https://files.pythonhosted.org/packages/ad/37/63a9c788bbe0b0850611669ec6b8589838faf2f4f959647f2d3e320383ae/ruff-0.12.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ae479e1a18b439c59138f066ae79cc0f3ee250712a873d00dbafadaad9481e5b", size = 13164356, upload-time = "2025-08-21T18:23:10.225Z" }, { url = "https://files.pythonhosted.org/packages/47/d4/1aaa7fb201a74181989970ebccd12f88c0fc074777027e2a21de5a90657e/ruff-0.12.10-py3-none-win32.whl", hash = "sha256:9de785e95dc2f09846c5e6e1d3a3d32ecd0b283a979898ad427a9be7be22b266", size = 11896089, upload-time = "2025-08-21T18:23:14.232Z" }, { url = "https://files.pythonhosted.org/packages/ad/14/2ad38fd4037daab9e023456a4a40ed0154e9971f8d6aed41bdea390aabd9/ruff-0.12.10-py3-none-win_amd64.whl", hash = "sha256:7837eca8787f076f67aba2ca559cefd9c5cbc3a9852fd66186f4201b87c1563e", size = 13004616, upload-time = "2025-08-21T18:23:17.422Z" }, { url = "https://files.pythonhosted.org/packages/24/3c/21cf283d67af33a8e6ed242396863af195a8a6134ec581524fd22b9811b6/ruff-0.12.10-py3-none-win_arm64.whl", hash = "sha256:cc138cc06ed9d4bfa9d667a65af7172b47840e1a98b02ce7011c391e54635ffc", size = 12074225, upload-time = "2025-08-21T18:23:20.137Z" }, ] [[package]] name = "tomlkit" version = "0.13.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, ] [[package]] name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] name = "virtualenv" version = "20.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279, upload-time = "2025-08-13T14:24:05.111Z" }, ] [[package]] name = "yamllint" version = "1.37.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pathspec" }, { name = "pyyaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/46/f2/cd8b7584a48ee83f0bc94f8a32fea38734cefcdc6f7324c4d3bfc699457b/yamllint-1.37.1.tar.gz", hash = "sha256:81f7c0c5559becc8049470d86046b36e96113637bcbe4753ecef06977c00245d", size = 141613, upload-time = "2025-05-04T08:25:54.355Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/dd/b9/be7a4cfdf47e03785f657f94daea8123e838d817be76c684298305bd789f/yamllint-1.37.1-py3-none-any.whl", hash = "sha256:364f0d79e81409f591e323725e6a9f4504c8699ddf2d7263d8d2b539cd66a583", size = 68813, upload-time = "2025-05-04T08:25:52.552Z" }, ] [[package]] name = "yarl" version = "1.20.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "multidict" }, { name = "propcache" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428, upload-time = "2025-06-10T00:46:09.923Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811, upload-time = "2025-06-10T00:44:18.933Z" }, { url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078, upload-time = "2025-06-10T00:44:20.635Z" }, { url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748, upload-time = "2025-06-10T00:44:22.34Z" }, { url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595, upload-time = "2025-06-10T00:44:24.314Z" }, { url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616, upload-time = "2025-06-10T00:44:26.167Z" }, { url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324, upload-time = "2025-06-10T00:44:27.915Z" }, { url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676, upload-time = "2025-06-10T00:44:30.041Z" }, { url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614, upload-time = "2025-06-10T00:44:32.171Z" }, { url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766, upload-time = "2025-06-10T00:44:34.494Z" }, { url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615, upload-time = "2025-06-10T00:44:36.856Z" }, { url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982, upload-time = "2025-06-10T00:44:39.141Z" }, { url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792, upload-time = "2025-06-10T00:44:40.934Z" }, { url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049, upload-time = "2025-06-10T00:44:42.854Z" }, { url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774, upload-time = "2025-06-10T00:44:45.275Z" }, { url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252, upload-time = "2025-06-10T00:44:47.31Z" }, { url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198, upload-time = "2025-06-10T00:44:49.164Z" }, { url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346, upload-time = "2025-06-10T00:44:51.182Z" }, { url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826, upload-time = "2025-06-10T00:44:52.883Z" }, { url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217, upload-time = "2025-06-10T00:44:54.658Z" }, { url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700, upload-time = "2025-06-10T00:44:56.784Z" }, { url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644, upload-time = "2025-06-10T00:44:59.071Z" }, { url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452, upload-time = "2025-06-10T00:45:01.605Z" }, { url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378, upload-time = "2025-06-10T00:45:03.946Z" }, { url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261, upload-time = "2025-06-10T00:45:05.992Z" }, { url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987, upload-time = "2025-06-10T00:45:08.227Z" }, { url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361, upload-time = "2025-06-10T00:45:10.11Z" }, { url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460, upload-time = "2025-06-10T00:45:12.055Z" }, { url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486, upload-time = "2025-06-10T00:45:13.995Z" }, { url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219, upload-time = "2025-06-10T00:45:16.479Z" }, { url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693, upload-time = "2025-06-10T00:45:18.399Z" }, { url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803, upload-time = "2025-06-10T00:45:20.677Z" }, { url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709, upload-time = "2025-06-10T00:45:23.221Z" }, { url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591, upload-time = "2025-06-10T00:45:25.793Z" }, { url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003, upload-time = "2025-06-10T00:45:27.752Z" }, { url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542, upload-time = "2025-06-10T00:46:07.521Z" }, ]