mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-26 18:14:37 -05:00
Compare commits
26 Commits
f8fb224968
...
6a1060f4d2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6a1060f4d2 | ||
![]() |
5881c70051 | ||
![]() |
1f549dcd52 | ||
![]() |
79323fcea6 | ||
![]() |
f42a71dc62 | ||
![]() |
e2888f12d7 | ||
![]() |
3ad2eaf1e3 | ||
![]() |
04504c3d42 | ||
![]() |
9de605d2e6 | ||
![]() |
6964b184d1 | ||
![]() |
0e76b86066 | ||
![]() |
89e5c08a1f | ||
![]() |
0faa9e8865 | ||
![]() |
f205c4d0e2 | ||
![]() |
344b2bc0eb | ||
![]() |
817aad7c8b | ||
![]() |
d82555e644 | ||
![]() |
f3e6ed56b9 | ||
![]() |
780d1c67e9 | ||
![]() |
2b72397a4d | ||
![]() |
6c13ffaa01 | ||
![]() |
eb8e124971 | ||
![]() |
1bc77546eb | ||
![]() |
5a453653e2 | ||
![]() |
16f17829b6 | ||
![]() |
3cf1c04a83 |
@ -76,18 +76,15 @@ RUN set -eux \
|
|||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES}
|
&& apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES}
|
||||||
|
|
||||||
ARG PYTHON_PACKAGES="\
|
ARG PYTHON_PACKAGES="ca-certificates"
|
||||||
python3 \
|
|
||||||
python3-pip \
|
|
||||||
python3-wheel \
|
|
||||||
pipenv \
|
|
||||||
ca-certificates"
|
|
||||||
|
|
||||||
RUN set -eux \
|
RUN set -eux \
|
||||||
echo "Installing python packages" \
|
echo "Installing python packages" \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install --yes --quiet ${PYTHON_PACKAGES}
|
&& apt-get install --yes --quiet ${PYTHON_PACKAGES}
|
||||||
|
|
||||||
|
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
|
||||||
|
|
||||||
RUN set -eux \
|
RUN set -eux \
|
||||||
&& echo "Installing pre-built updates" \
|
&& echo "Installing pre-built updates" \
|
||||||
&& echo "Installing qpdf ${QPDF_VERSION}" \
|
&& echo "Installing qpdf ${QPDF_VERSION}" \
|
||||||
@ -131,6 +128,8 @@ RUN set -eux \
|
|||||||
&& echo "Configuring ImageMagick" \
|
&& echo "Configuring ImageMagick" \
|
||||||
&& mv paperless-policy.xml /etc/ImageMagick-6/policy.xml
|
&& mv paperless-policy.xml /etc/ImageMagick-6/policy.xml
|
||||||
|
|
||||||
|
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
|
||||||
|
|
||||||
# Packages needed only for building a few quick Python
|
# Packages needed only for building a few quick Python
|
||||||
# dependencies
|
# dependencies
|
||||||
ARG BUILD_PACKAGES="\
|
ARG BUILD_PACKAGES="\
|
||||||
@ -140,11 +139,10 @@ ARG BUILD_PACKAGES="\
|
|||||||
libpq-dev \
|
libpq-dev \
|
||||||
# https://github.com/PyMySQL/mysqlclient#linux
|
# https://github.com/PyMySQL/mysqlclient#linux
|
||||||
default-libmysqlclient-dev \
|
default-libmysqlclient-dev \
|
||||||
pkg-config \
|
pkg-config"
|
||||||
pre-commit"
|
|
||||||
|
|
||||||
# hadolint ignore=DL3042
|
# hadolint ignore=DL3042
|
||||||
RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
|
RUN --mount=type=cache,target=/root/.cache/uv,id=pip-cache \
|
||||||
set -eux \
|
set -eux \
|
||||||
&& echo "Installing build system packages" \
|
&& echo "Installing build system packages" \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
@ -169,9 +167,6 @@ RUN set -eux \
|
|||||||
&& mkdir --parents --verbose /usr/src/paperless/paperless-ngx/.venv \
|
&& mkdir --parents --verbose /usr/src/paperless/paperless-ngx/.venv \
|
||||||
&& echo "Adjusting all permissions" \
|
&& echo "Adjusting all permissions" \
|
||||||
&& chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless
|
&& chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless
|
||||||
# && echo "Collecting static files" \
|
|
||||||
# && gosu paperless python3 manage.py collectstatic --clear --no-input --link \
|
|
||||||
# && gosu paperless python3 manage.py compilemessages
|
|
||||||
|
|
||||||
VOLUME ["/usr/src/paperless/paperless-ngx/data", \
|
VOLUME ["/usr/src/paperless/paperless-ngx/data", \
|
||||||
"/usr/src/paperless/paperless-ngx/media", \
|
"/usr/src/paperless/paperless-ngx/media", \
|
||||||
|
117
.devcontainer/README.md
Normal file
117
.devcontainer/README.md
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# Paperless-ngx Development Environment
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Welcome to the Paperless-ngx development environment! This setup uses VSCode DevContainers to provide a consistent and seamless development experience.
|
||||||
|
|
||||||
|
### What are DevContainers?
|
||||||
|
|
||||||
|
DevContainers are a feature in VSCode that allows you to develop within a Docker container. This ensures that your development environment is consistent across different machines and setups. By defining a containerized environment, you can eliminate the "works on my machine" problem.
|
||||||
|
|
||||||
|
### Advantages of DevContainers
|
||||||
|
|
||||||
|
- **Consistency**: Same environment for all developers.
|
||||||
|
- **Isolation**: Separate development environment from your local machine.
|
||||||
|
- **Reproducibility**: Easily recreate the environment on any machine.
|
||||||
|
- **Pre-configured Tools**: Include all necessary tools and dependencies in the container.
|
||||||
|
|
||||||
|
## DevContainer Setup
|
||||||
|
|
||||||
|
The DevContainer configuration provides up all the necessary services for Paperless-ngx, including:
|
||||||
|
|
||||||
|
- Redis
|
||||||
|
- Gotenberg
|
||||||
|
- Tika
|
||||||
|
|
||||||
|
Data is stored using Docker volumes to ensure persistence across container restarts.
|
||||||
|
|
||||||
|
## Configuration Files
|
||||||
|
|
||||||
|
The setup includes debugging configurations (`launch.json`) and tasks (`tasks.json`) to help you manage and debug various parts of the project:
|
||||||
|
|
||||||
|
- **Backend Debugging:**
|
||||||
|
- `manage.py runserver`
|
||||||
|
- `manage.py document-consumer`
|
||||||
|
- `celery`
|
||||||
|
- **Maintenance Tasks:**
|
||||||
|
- Create superuser
|
||||||
|
- Run migrations
|
||||||
|
- Recreate virtual environment (`.venv` with `uv`)
|
||||||
|
- Compile frontend assets
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Step 1: Running the DevContainer
|
||||||
|
|
||||||
|
To start the DevContainer:
|
||||||
|
|
||||||
|
1. Open VSCode.
|
||||||
|
2. Open the project folder.
|
||||||
|
3. Open the command palette:
|
||||||
|
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||||
|
- **Mac**: `Cmd+Shift+P`
|
||||||
|
4. Type and select `Dev Containers: Rebuild and Reopen in Container`.
|
||||||
|
|
||||||
|
VSCode will build and start the DevContainer environment.
|
||||||
|
|
||||||
|
### Step 2: Initial Setup
|
||||||
|
|
||||||
|
Once the DevContainer is up and running, perform the following steps:
|
||||||
|
|
||||||
|
1. **Compile Frontend Assets**:
|
||||||
|
|
||||||
|
- Open the command palette:
|
||||||
|
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||||
|
- **Mac**: `Cmd+Shift+P`
|
||||||
|
- Select `Tasks: Run Task`.
|
||||||
|
- Choose `Frontend Compile`.
|
||||||
|
|
||||||
|
2. **Run Database Migrations**:
|
||||||
|
|
||||||
|
- Open the command palette:
|
||||||
|
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||||
|
- **Mac**: `Cmd+Shift+P`
|
||||||
|
- Select `Tasks: Run Task`.
|
||||||
|
- Choose `Migrate Database`.
|
||||||
|
|
||||||
|
3. **Create Superuser**:
|
||||||
|
- Open the command palette:
|
||||||
|
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||||
|
- **Mac**: `Cmd+Shift+P`
|
||||||
|
- Select `Tasks: Run Task`.
|
||||||
|
- Choose `Create Superuser`.
|
||||||
|
|
||||||
|
### Debugging and Running Services
|
||||||
|
|
||||||
|
You can start and debug backend services either as debugging sessions via `launch.json` or as tasks.
|
||||||
|
|
||||||
|
#### Using `launch.json`
|
||||||
|
|
||||||
|
1. Press `F5` or go to the **Run and Debug** view in VSCode.
|
||||||
|
2. Select the desired configuration:
|
||||||
|
- `Runserver`
|
||||||
|
- `Document Consumer`
|
||||||
|
- `Celery`
|
||||||
|
|
||||||
|
#### Using Tasks
|
||||||
|
|
||||||
|
1. Open the command palette:
|
||||||
|
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||||
|
- **Mac**: `Cmd+Shift+P`
|
||||||
|
2. Select `Tasks: Run Task`.
|
||||||
|
3. Choose the desired task:
|
||||||
|
- `Runserver`
|
||||||
|
- `Document Consumer`
|
||||||
|
- `Celery`
|
||||||
|
|
||||||
|
### Additional Maintenance Tasks
|
||||||
|
|
||||||
|
Additional tasks are available for common maintenance operations:
|
||||||
|
|
||||||
|
- **Recreate .venv**: For setting up the virtual environment using `uv`.
|
||||||
|
- **Migrate Database**: To apply database migrations.
|
||||||
|
- **Create Superuser**: To create an admin user for the application.
|
||||||
|
|
||||||
|
## Let's Get Started!
|
||||||
|
|
||||||
|
Follow the steps above to get your development environment up and running. Happy coding!
|
@ -3,7 +3,7 @@
|
|||||||
"dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml",
|
"dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml",
|
||||||
"service": "paperless-development",
|
"service": "paperless-development",
|
||||||
"workspaceFolder": "/usr/src/paperless/paperless-ngx",
|
"workspaceFolder": "/usr/src/paperless/paperless-ngx",
|
||||||
"postCreateCommand": "pipenv install --dev && pipenv run pre-commit install",
|
"postCreateCommand": "/bin/bash -c uv sync --dev && uv run pre-commit install",
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
|
@ -43,7 +43,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ..:/usr/src/paperless/paperless-ngx:delegated
|
- ..:/usr/src/paperless/paperless-ngx:delegated
|
||||||
- ../.devcontainer/vscode:/usr/src/paperless/paperless-ngx/.vscode:delegated # VSCode config files
|
- ../.devcontainer/vscode:/usr/src/paperless/paperless-ngx/.vscode:delegated # VSCode config files
|
||||||
- pipenv:/usr/src/paperless/paperless-ngx/.venv
|
- virtualenv:/usr/src/paperless/paperless-ngx/.venv # Virtual environment persisted in volume
|
||||||
- /usr/src/paperless/paperless-ngx/src/documents/static/frontend # Static frontend files exist only in container
|
- /usr/src/paperless/paperless-ngx/src/documents/static/frontend # Static frontend files exist only in container
|
||||||
- /usr/src/paperless/paperless-ngx/src/.pytest_cache
|
- /usr/src/paperless/paperless-ngx/src/.pytest_cache
|
||||||
- /usr/src/paperless/paperless-ngx/.ruff_cache
|
- /usr/src/paperless/paperless-ngx/.ruff_cache
|
||||||
@ -80,4 +80,7 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pipenv:
|
data:
|
||||||
|
media:
|
||||||
|
redisdata:
|
||||||
|
virtualenv:
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"label": "Start: Celery Worker",
|
"label": "Start: Celery Worker",
|
||||||
"description": "Start the Celery Worker which processes background and consume tasks",
|
"description": "Start the Celery Worker which processes background and consume tasks",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run celery --app paperless worker -l DEBUG",
|
"command": "uv run celery --app paperless worker -l DEBUG",
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/src"
|
"cwd": "${workspaceFolder}/src"
|
||||||
@ -61,7 +61,7 @@
|
|||||||
"label": "Start: Consumer Service (manage.py document_consumer)",
|
"label": "Start: Consumer Service (manage.py document_consumer)",
|
||||||
"description": "Start the Consumer Service which processes files from a directory",
|
"description": "Start the Consumer Service which processes files from a directory",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run python manage.py document_consumer",
|
"command": "uv run python manage.py document_consumer",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
@ -80,7 +80,7 @@
|
|||||||
"label": "Start: Backend Server (manage.py runserver)",
|
"label": "Start: Backend Server (manage.py runserver)",
|
||||||
"description": "Start the Backend Server which serves the Django API and the compiled Angular frontend",
|
"description": "Start the Backend Server which serves the Django API and the compiled Angular frontend",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run python manage.py runserver",
|
"command": "uv run python manage.py runserver",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
@ -99,7 +99,7 @@
|
|||||||
"label": "Maintenance: manage.py migrate",
|
"label": "Maintenance: manage.py migrate",
|
||||||
"description": "Apply database migrations",
|
"description": "Apply database migrations",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run python manage.py migrate",
|
"command": "uv run python manage.py migrate",
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
@ -118,7 +118,7 @@
|
|||||||
"label": "Maintenance: Build Documentation",
|
"label": "Maintenance: Build Documentation",
|
||||||
"description": "Build the documentation with MkDocs",
|
"description": "Build the documentation with MkDocs",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run mkdocs build --config-file mkdocs.yml && pipenv run mkdocs serve",
|
"command": "uv run mkdocs build --config-file mkdocs.yml && uv run mkdocs serve",
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
@ -137,7 +137,7 @@
|
|||||||
"label": "Maintenance: manage.py createsuperuser",
|
"label": "Maintenance: manage.py createsuperuser",
|
||||||
"description": "Create a superuser",
|
"description": "Create a superuser",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run python manage.py createsuperuser",
|
"command": "uv run python manage.py createsuperuser",
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
@ -156,7 +156,7 @@
|
|||||||
"label": "Maintenance: recreate .venv",
|
"label": "Maintenance: recreate .venv",
|
||||||
"description": "Recreate the python virtual environment and install python dependencies",
|
"description": "Recreate the python virtual environment and install python dependencies",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "rm -R -v .venv/* || pipenv install --dev",
|
"command": "rm -R -v .venv/* || uv install --dev",
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
|
@ -27,9 +27,6 @@ indent_style = space
|
|||||||
[*.md]
|
[*.md]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|
||||||
[Pipfile.lock]
|
|
||||||
indent_style = space
|
|
||||||
|
|
||||||
# Tests don't get a line width restriction. It's still a good idea to follow
|
# Tests don't get a line width restriction. It's still a good idea to follow
|
||||||
# the 79 character rule, but in the interests of clarity, tests often need to
|
# the 79 character rule, but in the interests of clarity, tests often need to
|
||||||
# violate it.
|
# violate it.
|
||||||
|
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@ -1,6 +1,8 @@
|
|||||||
# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates#package-ecosystem
|
# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates#package-ecosystem
|
||||||
|
|
||||||
version: 2
|
version: 2
|
||||||
|
# Required for uv support for now
|
||||||
|
enable-beta-ecosystems: true
|
||||||
updates:
|
updates:
|
||||||
|
|
||||||
# Enable version updates for npm
|
# Enable version updates for npm
|
||||||
@ -34,9 +36,8 @@ updates:
|
|||||||
- "eslint"
|
- "eslint"
|
||||||
|
|
||||||
# Enable version updates for Python
|
# Enable version updates for Python
|
||||||
- package-ecosystem: "pip"
|
- package-ecosystem: "uv"
|
||||||
target-branch: "dev"
|
target-branch: "dev"
|
||||||
# Look for a `Pipfile` in the `root` directory
|
|
||||||
directory: "/"
|
directory: "/"
|
||||||
# Check for updates once a week
|
# Check for updates once a week
|
||||||
schedule:
|
schedule:
|
||||||
@ -53,6 +54,7 @@ updates:
|
|||||||
- "*pytest*"
|
- "*pytest*"
|
||||||
- "ruff"
|
- "ruff"
|
||||||
- "mkdocs-material"
|
- "mkdocs-material"
|
||||||
|
- "pre-commit*"
|
||||||
django:
|
django:
|
||||||
patterns:
|
patterns:
|
||||||
- "*django*"
|
- "*django*"
|
||||||
@ -63,6 +65,10 @@ updates:
|
|||||||
update-types:
|
update-types:
|
||||||
- "minor"
|
- "minor"
|
||||||
- "patch"
|
- "patch"
|
||||||
|
pre-built:
|
||||||
|
patterns:
|
||||||
|
- psycopg*
|
||||||
|
- zxing-cpp
|
||||||
|
|
||||||
# Enable updates for GitHub Actions
|
# Enable updates for GitHub Actions
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
|
148
.github/workflows/ci.yml
vendored
148
.github/workflows/ci.yml
vendored
@ -14,9 +14,7 @@ on:
|
|||||||
- 'translations**'
|
- 'translations**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# This is the version of pipenv all the steps will use
|
DEFAULT_UV_VERSION: "0.6.x"
|
||||||
# If changing this, change Dockerfile
|
|
||||||
DEFAULT_PIP_ENV_VERSION: "2024.4.1"
|
|
||||||
# This is the default version of Python to use in most steps which aren't specific
|
# This is the default version of Python to use in most steps which aren't specific
|
||||||
DEFAULT_PYTHON_VERSION: "3.11"
|
DEFAULT_PYTHON_VERSION: "3.11"
|
||||||
|
|
||||||
@ -59,24 +57,25 @@ jobs:
|
|||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
cache: "pipenv"
|
|
||||||
cache-dependency-path: 'Pipfile.lock'
|
|
||||||
-
|
-
|
||||||
name: Install pipenv
|
name: Install uv
|
||||||
run: |
|
uses: astral-sh/setup-uv@v5
|
||||||
pip install --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }}
|
with:
|
||||||
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
|
enable-cache: true
|
||||||
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
-
|
-
|
||||||
name: Install dependencies
|
name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
|
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
||||||
-
|
|
||||||
name: List installed Python dependencies
|
|
||||||
run: |
|
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pip list
|
|
||||||
-
|
-
|
||||||
name: Make documentation
|
name: Make documentation
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run mkdocs build --config-file ./mkdocs.yml
|
uv run \
|
||||||
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
--dev \
|
||||||
|
--frozen \
|
||||||
|
mkdocs build --config-file ./mkdocs.yml
|
||||||
-
|
-
|
||||||
name: Deploy documentation
|
name: Deploy documentation
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
@ -84,7 +83,11 @@ jobs:
|
|||||||
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
|
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
|
||||||
git config --global user.name "${{ github.actor }}"
|
git config --global user.name "${{ github.actor }}"
|
||||||
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
|
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run mkdocs gh-deploy --force --no-history
|
uv run \
|
||||||
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
--dev \
|
||||||
|
--frozen \
|
||||||
|
mkdocs gh-deploy --force --no-history
|
||||||
-
|
-
|
||||||
name: Upload artifact
|
name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@ -117,12 +120,13 @@ jobs:
|
|||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "${{ matrix.python-version }}"
|
python-version: "${{ matrix.python-version }}"
|
||||||
cache: "pipenv"
|
|
||||||
cache-dependency-path: 'Pipfile.lock'
|
|
||||||
-
|
-
|
||||||
name: Install pipenv
|
name: Install uv
|
||||||
run: |
|
uses: astral-sh/setup-uv@v5
|
||||||
pip install --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }}
|
with:
|
||||||
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
|
enable-cache: true
|
||||||
|
python-version: ${{ steps.setup-python.outputs.python-version }}
|
||||||
-
|
-
|
||||||
name: Install system dependencies
|
name: Install system dependencies
|
||||||
run: |
|
run: |
|
||||||
@ -135,12 +139,14 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Install Python dependencies
|
name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run python --version
|
uv sync \
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
--group testing \
|
||||||
|
--frozen
|
||||||
-
|
-
|
||||||
name: List installed Python dependencies
|
name: List installed Python dependencies
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pip list
|
uv pip list
|
||||||
-
|
-
|
||||||
name: Tests
|
name: Tests
|
||||||
env:
|
env:
|
||||||
@ -150,17 +156,22 @@ jobs:
|
|||||||
PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
|
PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
|
||||||
PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
|
PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
|
||||||
run: |
|
run: |
|
||||||
cd src/
|
uv run \
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pytest -ra
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
--dev \
|
||||||
|
--frozen \
|
||||||
|
pytest
|
||||||
-
|
-
|
||||||
name: Upload coverage
|
name: Upload coverage
|
||||||
if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
|
if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: backend-coverage-report
|
name: backend-coverage-report
|
||||||
path: src/coverage.xml
|
path: |
|
||||||
|
coverage.xml
|
||||||
|
junit.xml
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
if-no-files-found: warn
|
if-no-files-found: error
|
||||||
-
|
-
|
||||||
name: Stop containers
|
name: Stop containers
|
||||||
if: always()
|
if: always()
|
||||||
@ -234,6 +245,8 @@ jobs:
|
|||||||
run: cd src-ui && npm run lint
|
run: cd src-ui && npm run lint
|
||||||
-
|
-
|
||||||
name: Run Jest unit tests
|
name: Run Jest unit tests
|
||||||
|
env:
|
||||||
|
JEST_JUNIT_OUTPUT_FILE: junit-report-${{ matrix.shard-index }}.xml
|
||||||
run: cd src-ui && npm run test -- --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
run: cd src-ui && npm run test -- --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||||
-
|
-
|
||||||
name: Upload Jest coverage
|
name: Upload Jest coverage
|
||||||
@ -246,7 +259,7 @@ jobs:
|
|||||||
src-ui/coverage/lcov.info
|
src-ui/coverage/lcov.info
|
||||||
src-ui/coverage/clover.xml
|
src-ui/coverage/clover.xml
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
if-no-files-found: warn
|
if-no-files-found: error
|
||||||
-
|
-
|
||||||
name: Run Playwright e2e tests
|
name: Run Playwright e2e tests
|
||||||
run: cd src-ui && npx playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
run: cd src-ui && npx playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||||
@ -258,6 +271,16 @@ jobs:
|
|||||||
name: playwright-report-${{ matrix.shard-index }}
|
name: playwright-report-${{ matrix.shard-index }}
|
||||||
path: src-ui/playwright-report
|
path: src-ui/playwright-report
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
if-no-files-found: error
|
||||||
|
-
|
||||||
|
name: Upload frontend test results
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: junit-report-${{ matrix.shard-index }}
|
||||||
|
path: src-ui/junit-report-${{ matrix.shard-index }}.xml
|
||||||
|
retention-days: 7
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
tests-coverage-upload:
|
tests-coverage-upload:
|
||||||
name: "Upload to Codecov"
|
name: "Upload to Codecov"
|
||||||
@ -281,6 +304,13 @@ jobs:
|
|||||||
path: src-ui/coverage/
|
path: src-ui/coverage/
|
||||||
pattern: playwright-report-*
|
pattern: playwright-report-*
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
-
|
||||||
|
name: Download frontend test results
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: src-ui/junit/
|
||||||
|
pattern: junit-report-*
|
||||||
|
merge-multiple: true
|
||||||
-
|
-
|
||||||
name: Upload frontend coverage to Codecov
|
name: Upload frontend coverage to Codecov
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
@ -291,6 +321,14 @@ jobs:
|
|||||||
directory: src-ui/coverage/
|
directory: src-ui/coverage/
|
||||||
# dont include backend coverage files here
|
# dont include backend coverage files here
|
||||||
files: '!coverage.xml'
|
files: '!coverage.xml'
|
||||||
|
-
|
||||||
|
name: Upload frontend test results to Codecov
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
uses: codecov/test-results-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
flags: frontend
|
||||||
|
directory: src-ui/junit/
|
||||||
-
|
-
|
||||||
name: Download backend coverage
|
name: Download backend coverage
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@ -306,6 +344,14 @@ jobs:
|
|||||||
# future expansion
|
# future expansion
|
||||||
flags: backend
|
flags: backend
|
||||||
directory: src/
|
directory: src/
|
||||||
|
-
|
||||||
|
name: Upload backend test results to Codecov
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
uses: codecov/test-results-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
flags: backend
|
||||||
|
directory: src/
|
||||||
-
|
-
|
||||||
name: Use Node.js 20
|
name: Use Node.js 20
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
@ -472,16 +518,17 @@ jobs:
|
|||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
cache: "pipenv"
|
|
||||||
cache-dependency-path: 'Pipfile.lock'
|
|
||||||
-
|
-
|
||||||
name: Install pipenv + tools
|
name: Install uv
|
||||||
run: |
|
uses: astral-sh/setup-uv@v5
|
||||||
pip install --upgrade --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }} setuptools wheel
|
with:
|
||||||
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
|
enable-cache: true
|
||||||
|
python-version: ${{ steps.setup-python.outputs.python-version }}
|
||||||
-
|
-
|
||||||
name: Install Python dependencies
|
name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
|
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
||||||
-
|
-
|
||||||
name: Install system dependencies
|
name: Install system dependencies
|
||||||
run: |
|
run: |
|
||||||
@ -502,17 +549,21 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Generate requirements file
|
name: Generate requirements file
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} requirements > requirements.txt
|
uv export --quiet --no-dev --all-extras --format requirements-txt --output-file requirements.txt
|
||||||
-
|
-
|
||||||
name: Compile messages
|
name: Compile messages
|
||||||
run: |
|
run: |
|
||||||
cd src/
|
cd src/
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run python3 manage.py compilemessages
|
uv run \
|
||||||
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
manage.py compilemessages
|
||||||
-
|
-
|
||||||
name: Collect static files
|
name: Collect static files
|
||||||
run: |
|
run: |
|
||||||
cd src/
|
cd src/
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run python3 manage.py collectstatic --no-input
|
uv run \
|
||||||
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
manage.py collectstatic --no-input
|
||||||
-
|
-
|
||||||
name: Move files
|
name: Move files
|
||||||
run: |
|
run: |
|
||||||
@ -528,8 +579,8 @@ jobs:
|
|||||||
for file_name in .dockerignore \
|
for file_name in .dockerignore \
|
||||||
.env \
|
.env \
|
||||||
Dockerfile \
|
Dockerfile \
|
||||||
Pipfile \
|
pyproject.toml \
|
||||||
Pipfile.lock \
|
uv.lock \
|
||||||
requirements.txt \
|
requirements.txt \
|
||||||
LICENSE \
|
LICENSE \
|
||||||
README.md \
|
README.md \
|
||||||
@ -631,15 +682,17 @@ jobs:
|
|||||||
ref: main
|
ref: main
|
||||||
-
|
-
|
||||||
name: Set up Python
|
name: Set up Python
|
||||||
|
id: setup-python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
cache: "pipenv"
|
|
||||||
cache-dependency-path: 'Pipfile.lock'
|
|
||||||
-
|
-
|
||||||
name: Install pipenv + tools
|
name: Install uv
|
||||||
run: |
|
uses: astral-sh/setup-uv@v5
|
||||||
pip install --upgrade --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }} setuptools wheel
|
with:
|
||||||
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
|
enable-cache: true
|
||||||
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
-
|
-
|
||||||
name: Append Changelog to docs
|
name: Append Changelog to docs
|
||||||
id: append-Changelog
|
id: append-Changelog
|
||||||
@ -655,7 +708,10 @@ jobs:
|
|||||||
CURRENT_CHANGELOG=`tail --lines +2 changelog.md`
|
CURRENT_CHANGELOG=`tail --lines +2 changelog.md`
|
||||||
echo -e "$CURRENT_CHANGELOG" >> changelog-new.md
|
echo -e "$CURRENT_CHANGELOG" >> changelog-new.md
|
||||||
mv changelog-new.md changelog.md
|
mv changelog-new.md changelog.md
|
||||||
pipenv run pre-commit run --files changelog.md || true
|
uv run \
|
||||||
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
--dev \
|
||||||
|
pre-commit run --files changelog.md || true
|
||||||
git config --global user.name "github-actions"
|
git config --global user.name "github-actions"
|
||||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
git commit -am "Changelog ${{ needs.publish-release.outputs.version }} - GHA"
|
git commit -am "Changelog ${{ needs.publish-release.outputs.version }} - GHA"
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -44,6 +44,7 @@ nosetests.xml
|
|||||||
coverage.xml
|
coverage.xml
|
||||||
*,cover
|
*,cover
|
||||||
.pytest_cache
|
.pytest_cache
|
||||||
|
junit.xml
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
*.mo
|
*.mo
|
||||||
|
@ -45,7 +45,6 @@ repos:
|
|||||||
- javascript
|
- javascript
|
||||||
- ts
|
- ts
|
||||||
- markdown
|
- markdown
|
||||||
exclude: "(^Pipfile\\.lock$)"
|
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- prettier@3.3.3
|
- prettier@3.3.3
|
||||||
- 'prettier-plugin-organize-imports@4.1.0'
|
- 'prettier-plugin-organize-imports@4.1.0'
|
||||||
@ -55,6 +54,10 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||||
|
rev: "v2.5.1"
|
||||||
|
hooks:
|
||||||
|
- id: pyproject-fmt
|
||||||
# Dockerfile hooks
|
# Dockerfile hooks
|
||||||
- repo: https://github.com/AleksaC/hadolint-py
|
- repo: https://github.com/AleksaC/hadolint-py
|
||||||
rev: v2.12.0.3
|
rev: v2.12.0.3
|
||||||
|
@ -1 +0,0 @@
|
|||||||
3.10.15
|
|
87
.ruff.toml
87
.ruff.toml
@ -1,87 +0,0 @@
|
|||||||
fix = true
|
|
||||||
line-length = 88
|
|
||||||
respect-gitignore = true
|
|
||||||
src = ["src"]
|
|
||||||
target-version = "py310"
|
|
||||||
output-format = "grouped"
|
|
||||||
show-fixes = true
|
|
||||||
|
|
||||||
# https://docs.astral.sh/ruff/settings/
|
|
||||||
# https://docs.astral.sh/ruff/rules/
|
|
||||||
[lint]
|
|
||||||
extend-select = [
|
|
||||||
"W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
|
|
||||||
"I", # https://docs.astral.sh/ruff/rules/#isort-i
|
|
||||||
"UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up
|
|
||||||
"COM", # https://docs.astral.sh/ruff/rules/#flake8-commas-com
|
|
||||||
"DJ", # https://docs.astral.sh/ruff/rules/#flake8-django-dj
|
|
||||||
"EXE", # https://docs.astral.sh/ruff/rules/#flake8-executable-exe
|
|
||||||
"ISC", # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc
|
|
||||||
"ICN", # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn
|
|
||||||
"G201", # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g
|
|
||||||
"INP", # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp
|
|
||||||
"PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie
|
|
||||||
"Q", # https://docs.astral.sh/ruff/rules/#flake8-quotes-q
|
|
||||||
"RSE", # https://docs.astral.sh/ruff/rules/#flake8-raise-rse
|
|
||||||
"T20", # https://docs.astral.sh/ruff/rules/#flake8-print-t20
|
|
||||||
"SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim
|
|
||||||
"TID", # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid
|
|
||||||
"TC", # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tc
|
|
||||||
"PLC", # https://docs.astral.sh/ruff/rules/#pylint-pl
|
|
||||||
"PLE", # https://docs.astral.sh/ruff/rules/#pylint-pl
|
|
||||||
"RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf
|
|
||||||
"FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly
|
|
||||||
"PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth
|
|
||||||
"FBT", # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt
|
|
||||||
]
|
|
||||||
ignore = ["DJ001", "SIM105", "RUF012"]
|
|
||||||
|
|
||||||
[lint.per-file-ignores]
|
|
||||||
".github/scripts/*.py" = ["E501", "INP001", "SIM117"]
|
|
||||||
"docker/wait-for-redis.py" = ["INP001", "T201"]
|
|
||||||
"src/documents/file_handling.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/management/commands/document_consumer.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/management/commands/document_exporter.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/migrations/0012_auto_20160305_0040.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/migrations/0014_document_checksum.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/migrations/1003_mime_types.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/migrations/1012_fix_archive_files.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/models.py" = ["SIM115", "PTH"] # TODO PTH Enable & remove
|
|
||||||
"src/documents/parsers.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/signals/handlers.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tasks.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_api_app_config.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_classifier.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_consumer.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_file_handling.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_management.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_management_consumer.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_management_exporter.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_management_thumbnails.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_migration_archive_files.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_migration_document_pages_count.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_migration_mime_type.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_sanity_check.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_tasks.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_views.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/views.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless/checks.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless/settings.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless/tests/test_checks.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless/urls.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless/views.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless_mail/mail.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless_mail/preprocessor.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless_tesseract/parsers.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless_tesseract/tests/test_parser.py" = ["RUF001", "PTH"] # TODO PTH Enable & remove
|
|
||||||
"src/paperless_tika/tests/test_live_tika.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless_tika/tests/test_tika_parser.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
# Testing
|
|
||||||
"*/tests/*.py" = ["E501", "SIM117"]
|
|
||||||
# Migrations
|
|
||||||
"*/migrations/*.py" = ["E501", "SIM", "T201"]
|
|
||||||
# Docker specific
|
|
||||||
"docker/rootfs/usr/local/bin/wait-for-redis.py" = ["INP001", "T201"]
|
|
||||||
|
|
||||||
[lint.isort]
|
|
||||||
force-single-line = true
|
|
@ -5,5 +5,6 @@
|
|||||||
/src-ui/ @paperless-ngx/frontend
|
/src-ui/ @paperless-ngx/frontend
|
||||||
|
|
||||||
/src/ @paperless-ngx/backend
|
/src/ @paperless-ngx/backend
|
||||||
Pipfile* @paperless-ngx/backend
|
pyproject.toml @paperless-ngx/backend
|
||||||
|
uv.lock @paperless-ngx/backend
|
||||||
*.py @paperless-ngx/backend
|
*.py @paperless-ngx/backend
|
||||||
|
45
Dockerfile
45
Dockerfile
@ -26,28 +26,11 @@ esac
|
|||||||
RUN set -eux \
|
RUN set -eux \
|
||||||
&& ./node_modules/.bin/ng build --configuration production
|
&& ./node_modules/.bin/ng build --configuration production
|
||||||
|
|
||||||
# Stage: pipenv-base
|
|
||||||
# Purpose: Generates a requirements.txt file for building
|
|
||||||
# Comments:
|
|
||||||
# - pipenv dependencies are not left in the final image
|
|
||||||
# - pipenv can't touch the final image somehow
|
|
||||||
FROM --platform=$BUILDPLATFORM docker.io/python:3.12-alpine AS pipenv-base
|
|
||||||
|
|
||||||
WORKDIR /usr/src/pipenv
|
|
||||||
|
|
||||||
COPY Pipfile* ./
|
|
||||||
|
|
||||||
RUN set -eux \
|
|
||||||
&& echo "Installing pipenv" \
|
|
||||||
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2024.4.1 \
|
|
||||||
&& echo "Generating requirement.txt" \
|
|
||||||
&& pipenv requirements > requirements.txt
|
|
||||||
|
|
||||||
# Stage: s6-overlay-base
|
# Stage: s6-overlay-base
|
||||||
# Purpose: Installs s6-overlay and rootfs
|
# Purpose: Installs s6-overlay and rootfs
|
||||||
# Comments:
|
# Comments:
|
||||||
# - Don't leave anything extra in here either
|
# - Don't leave anything extra in here either
|
||||||
FROM docker.io/python:3.12-slim-bookworm AS s6-overlay-base
|
FROM ghcr.io/astral-sh/uv:0.6.3-python3.12-bookworm-slim AS s6-overlay-base
|
||||||
|
|
||||||
WORKDIR /usr/src/s6
|
WORKDIR /usr/src/s6
|
||||||
|
|
||||||
@ -123,9 +106,12 @@ ARG GS_VERSION=10.03.1
|
|||||||
# Set Python environment variables
|
# Set Python environment variables
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
PYTHONUNBUFFERED=1 \
|
PYTHONUNBUFFERED=1 \
|
||||||
# Ignore warning from Whitenoise
|
# Ignore warning from Whitenoise about async iterators
|
||||||
PYTHONWARNINGS="ignore:::django.http.response:517" \
|
PYTHONWARNINGS="ignore:::django.http.response:517" \
|
||||||
PNGX_CONTAINERIZED=1
|
PNGX_CONTAINERIZED=1 \
|
||||||
|
# https://docs.astral.sh/uv/reference/settings/#link-mode
|
||||||
|
UV_LINK_MODE=copy \
|
||||||
|
UV_CACHE_DIR=/cache/uv/
|
||||||
|
|
||||||
#
|
#
|
||||||
# Begin installation and configuration
|
# Begin installation and configuration
|
||||||
@ -213,36 +199,25 @@ WORKDIR /usr/src/paperless/src/
|
|||||||
|
|
||||||
# Python dependencies
|
# Python dependencies
|
||||||
# Change pretty frequently
|
# Change pretty frequently
|
||||||
COPY --chown=1000:1000 --from=pipenv-base /usr/src/pipenv/requirements.txt ./
|
COPY --chown=1000:1000 ["pyproject.toml", "uv.lock", "/usr/src/paperless/src/"]
|
||||||
|
|
||||||
# Packages needed only for building a few quick Python
|
# Packages needed only for building a few quick Python
|
||||||
# dependencies
|
# dependencies
|
||||||
ARG BUILD_PACKAGES="\
|
ARG BUILD_PACKAGES="\
|
||||||
build-essential \
|
build-essential \
|
||||||
git \
|
|
||||||
# https://www.psycopg.org/docs/install.html#prerequisites
|
|
||||||
libpq-dev \
|
|
||||||
# https://github.com/PyMySQL/mysqlclient#linux
|
# https://github.com/PyMySQL/mysqlclient#linux
|
||||||
default-libmysqlclient-dev \
|
default-libmysqlclient-dev \
|
||||||
pkg-config"
|
pkg-config"
|
||||||
|
|
||||||
ARG ZXING_VERSION=2.3.0
|
|
||||||
ARG PSYCOPG_VERSION=3.2.4
|
|
||||||
|
|
||||||
# hadolint ignore=DL3042
|
# hadolint ignore=DL3042
|
||||||
RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
|
RUN --mount=type=cache,target=${UV_CACHE_DIR},id=python-cache \
|
||||||
set -eux \
|
set -eux \
|
||||||
&& echo "Installing build system packages" \
|
&& echo "Installing build system packages" \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
||||||
&& python3 -m pip install --upgrade wheel \
|
|
||||||
&& echo "Installing Python requirements" \
|
&& echo "Installing Python requirements" \
|
||||||
&& curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \
|
&& uv export --quiet --no-dev --all-extras --format requirements-txt --output-file requirements.txt \
|
||||||
https://github.com/paperless-ngx/builder/releases/download/psycopg-${PSYCOPG_VERSION}/psycopg_c-${PSYCOPG_VERSION}-cp312-cp312-linux_x86_64.whl \
|
&& uv pip install --system --no-python-downloads --python-preference system --requirements requirements.txt \
|
||||||
https://github.com/paperless-ngx/builder/releases/download/psycopg-${PSYCOPG_VERSION}/psycopg_c-${PSYCOPG_VERSION}-cp312-cp312-linux_aarch64.whl \
|
|
||||||
https://github.com/paperless-ngx/builder/releases/download/zxing-${ZXING_VERSION}/zxing_cpp-${ZXING_VERSION}-cp312-cp312-linux_aarch64.whl \
|
|
||||||
https://github.com/paperless-ngx/builder/releases/download/zxing-${ZXING_VERSION}/zxing_cpp-${ZXING_VERSION}-cp312-cp312-linux_x86_64.whl \
|
|
||||||
&& python3 -m pip install --default-timeout=1000 --find-links . --requirement requirements.txt \
|
|
||||||
&& echo "Installing NLTK data" \
|
&& echo "Installing NLTK data" \
|
||||||
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \
|
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \
|
||||||
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \
|
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \
|
||||||
|
99
Pipfile
99
Pipfile
@ -1,99 +0,0 @@
|
|||||||
[[source]]
|
|
||||||
url = "https://pypi.python.org/simple"
|
|
||||||
verify_ssl = true
|
|
||||||
name = "pypi"
|
|
||||||
|
|
||||||
[packages]
|
|
||||||
dateparser = "~=1.2"
|
|
||||||
# WARNING: django does not use semver.
|
|
||||||
# Only patch versions are guaranteed to not introduce breaking changes.
|
|
||||||
django = "~=5.1.5"
|
|
||||||
django-allauth = {extras = ["mfa", "socialaccount"], version = "*"}
|
|
||||||
django-auditlog = "*"
|
|
||||||
django-celery-results = "*"
|
|
||||||
django-compression-middleware = "*"
|
|
||||||
django-cors-headers = "*"
|
|
||||||
django-extensions = "*"
|
|
||||||
django-filter = "~=25.1"
|
|
||||||
django-guardian = "*"
|
|
||||||
django-multiselectfield = "*"
|
|
||||||
django-soft-delete = "*"
|
|
||||||
djangorestframework = "~=3.15.2"
|
|
||||||
djangorestframework-guardian = "*"
|
|
||||||
drf-spectacular = "*"
|
|
||||||
drf-spectacular-sidecar = "*"
|
|
||||||
drf-writable-nested = "*"
|
|
||||||
bleach = "*"
|
|
||||||
celery = {extras = ["redis"], version = "*"}
|
|
||||||
channels = "~=4.2"
|
|
||||||
channels-redis = "*"
|
|
||||||
concurrent-log-handler = "*"
|
|
||||||
filelock = "*"
|
|
||||||
flower = "*"
|
|
||||||
gotenberg-client = "*"
|
|
||||||
granian = "*"
|
|
||||||
httpx-oauth = "*"
|
|
||||||
imap-tools = "*"
|
|
||||||
inotifyrecursive = "~=0.3"
|
|
||||||
jinja2 = "~=3.1"
|
|
||||||
langdetect = "*"
|
|
||||||
mysqlclient = "*"
|
|
||||||
nltk = "*"
|
|
||||||
ocrmypdf = "~=16.9"
|
|
||||||
pathvalidate = "*"
|
|
||||||
pdf2image = "*"
|
|
||||||
psycopg = {version = "*", extras = ["c"]}
|
|
||||||
python-dateutil = "*"
|
|
||||||
python-dotenv = "*"
|
|
||||||
python-gnupg = "*"
|
|
||||||
python-ipware = "*"
|
|
||||||
python-magic = "*"
|
|
||||||
pyzbar = "*"
|
|
||||||
rapidfuzz = "*"
|
|
||||||
redis = {extras = ["hiredis"], version = "*"}
|
|
||||||
scikit-learn = "~=1.6"
|
|
||||||
setproctitle = "*"
|
|
||||||
tika-client = "*"
|
|
||||||
tqdm = "*"
|
|
||||||
watchdog = "~=6.0"
|
|
||||||
whitenoise = "~=6.9"
|
|
||||||
whoosh = "~=2.7"
|
|
||||||
zxing-cpp = "*"
|
|
||||||
|
|
||||||
[dev-packages]
|
|
||||||
# Linting
|
|
||||||
pre-commit = "*"
|
|
||||||
ruff = "*"
|
|
||||||
# Testing
|
|
||||||
factory-boy = "*"
|
|
||||||
pytest = "*"
|
|
||||||
pytest-cov = "*"
|
|
||||||
pytest-django = "*"
|
|
||||||
pytest-httpx = "*"
|
|
||||||
pytest-env = "*"
|
|
||||||
pytest-sugar = "*"
|
|
||||||
pytest-xdist = "*"
|
|
||||||
pytest-mock = "*"
|
|
||||||
pytest-rerunfailures = "*"
|
|
||||||
imagehash = "*"
|
|
||||||
daphne = "*"
|
|
||||||
# Documentation
|
|
||||||
mkdocs-material = "*"
|
|
||||||
mkdocs-glightbox = "*"
|
|
||||||
|
|
||||||
[typing-dev]
|
|
||||||
mypy = "*"
|
|
||||||
types-Pillow = "*"
|
|
||||||
django-filter-stubs = "*"
|
|
||||||
types-python-dateutil = "*"
|
|
||||||
djangorestframework-stubs = {extras= ["compatible-mypy"], version="*"}
|
|
||||||
celery-types = "*"
|
|
||||||
django-stubs = {extras= ["compatible-mypy"], version="*"}
|
|
||||||
types-dateparser = "*"
|
|
||||||
types-bleach = "*"
|
|
||||||
types-redis = "*"
|
|
||||||
types-tqdm = "*"
|
|
||||||
types-Markdown = "*"
|
|
||||||
types-Pygments = "*"
|
|
||||||
types-colorama = "*"
|
|
||||||
types-setuptools = "*"
|
|
4812
Pipfile.lock
generated
4812
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
@ -509,6 +509,12 @@ Invoice_{{ custom_fields|get_cf_value("Select Field") }}_{{ custom_fields|get_cf
|
|||||||
|
|
||||||
This will create a path like `invoices/2022/01/01/Invoice_OptionTwo_20220101.pdf` if the custom field "Date Field" is set to January 1, 2022 and "Select Field" is set to `OptionTwo`.
|
This will create a path like `invoices/2022/01/01/Invoice_OptionTwo_20220101.pdf` if the custom field "Date Field" is set to January 1, 2022 and "Select Field" is set to `OptionTwo`.
|
||||||
|
|
||||||
|
You can also use a custom `slugify` filter to slufigy text:
|
||||||
|
|
||||||
|
```jinja
|
||||||
|
{{ title | slugify }}
|
||||||
|
```
|
||||||
|
|
||||||
## Automatic recovery of invalid PDFs {#pdf-recovery}
|
## Automatic recovery of invalid PDFs {#pdf-recovery}
|
||||||
|
|
||||||
Paperless will attempt to "clean" certain invalid PDFs with `qpdf` before processing if, for example, the mime_type
|
Paperless will attempt to "clean" certain invalid PDFs with `qpdf` before processing if, for example, the mime_type
|
||||||
|
@ -60,7 +60,7 @@ first-time setup.
|
|||||||
|
|
||||||
Every command is executed directly from the root folder of the project unless specified otherwise.
|
Every command is executed directly from the root folder of the project unless specified otherwise.
|
||||||
|
|
||||||
1. Install prerequisites + pipenv as mentioned in
|
1. Install prerequisites + [uv](https://github.com/astral-sh/uv) as mentioned in
|
||||||
[Bare metal route](setup.md#bare_metal).
|
[Bare metal route](setup.md#bare_metal).
|
||||||
|
|
||||||
2. Copy `paperless.conf.example` to `paperless.conf` and enable debug
|
2. Copy `paperless.conf.example` to `paperless.conf` and enable debug
|
||||||
@ -75,17 +75,13 @@ first-time setup.
|
|||||||
4. Install the Python dependencies:
|
4. Install the Python dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pipenv install --dev
|
$ uv sync --dev
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
|
||||||
|
|
||||||
Using a virtual environment is highly recommended. You can spawn one via `pipenv shell`.
|
|
||||||
|
|
||||||
5. Install pre-commit hooks:
|
5. Install pre-commit hooks:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pre-commit install
|
$ uv run pre-commit install
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Apply migrations and create a superuser for your development instance:
|
6. Apply migrations and create a superuser for your development instance:
|
||||||
@ -93,8 +89,8 @@ first-time setup.
|
|||||||
```bash
|
```bash
|
||||||
# src/
|
# src/
|
||||||
|
|
||||||
python3 manage.py migrate
|
$ uv run manage.py migrate
|
||||||
python3 manage.py createsuperuser
|
$ uv run manage.py createsuperuser
|
||||||
```
|
```
|
||||||
|
|
||||||
7. You can now either ...
|
7. You can now either ...
|
||||||
@ -164,6 +160,19 @@ $ ng build --configuration production
|
|||||||
complicated IF cases. Append `# noqa: E501` to disable this check
|
complicated IF cases. Append `# noqa: E501` to disable this check
|
||||||
for certain lines.
|
for certain lines.
|
||||||
|
|
||||||
|
### Package Management
|
||||||
|
|
||||||
|
Paperless uses `uv` to manage packages and virtual environments for both development and production.
|
||||||
|
To accomplish some common tasks using `uv`, follow the shortcuts below:
|
||||||
|
|
||||||
|
To upgrade all locked packages to the latest allowed versions: `uv lock --upgrade`
|
||||||
|
|
||||||
|
To upgrade a single locked package: `uv lock --upgrade-package <package>`
|
||||||
|
|
||||||
|
To add a new package: `uv add <package>`
|
||||||
|
|
||||||
|
To add a new development package `uv add --dev <package>`
|
||||||
|
|
||||||
## Front end development
|
## Front end development
|
||||||
|
|
||||||
The front end is built using AngularJS. In order to get started, you need Node.js (version 14.15+) and
|
The front end is built using AngularJS. In order to get started, you need Node.js (version 14.15+) and
|
||||||
@ -332,27 +341,21 @@ LANGUAGES = [
|
|||||||
The documentation is built using material-mkdocs, see their [documentation](https://squidfunk.github.io/mkdocs-material/reference/).
|
The documentation is built using material-mkdocs, see their [documentation](https://squidfunk.github.io/mkdocs-material/reference/).
|
||||||
If you want to build the documentation locally, this is how you do it:
|
If you want to build the documentation locally, this is how you do it:
|
||||||
|
|
||||||
1. Have an active pipenv shell (`pipenv shell`) and install Python dependencies:
|
1. Build the documentation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pipenv install --dev
|
$ uv run mkdocs build --config-file mkdocs.yml
|
||||||
```
|
|
||||||
|
|
||||||
2. Build the documentation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdocs build --config-file mkdocs.yml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
_alternatively..._
|
_alternatively..._
|
||||||
|
|
||||||
3. Serve the documentation. This will spin up a
|
2. Serve the documentation. This will spin up a
|
||||||
copy of the documentation at http://127.0.0.1:8000
|
copy of the documentation at http://127.0.0.1:8000
|
||||||
that will automatically refresh every time you change
|
that will automatically refresh every time you change
|
||||||
something.
|
something.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdocs serve
|
$ uv run mkdocs serve
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building the Docker image
|
## Building the Docker image
|
||||||
|
@ -380,6 +380,12 @@ are released, dependency support is confirmed, etc.
|
|||||||
dependencies. This is an alternative to the above and may require adjusting
|
dependencies. This is an alternative to the above and may require adjusting
|
||||||
the example scripts to utilize the virtual environment paths
|
the example scripts to utilize the virtual environment paths
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
|
||||||
|
If you use modern Python tooling, such as `uv`, installation will not include
|
||||||
|
dependencies for Postgres or Mariadb. You can select those extras with `--extra <EXTRA>`
|
||||||
|
or all with `--all-extras`
|
||||||
|
|
||||||
9. Go to `/opt/paperless/src`, and execute the following commands:
|
9. Go to `/opt/paperless/src`, and execute the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
401
pyproject.toml
Normal file
401
pyproject.toml
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
[project]
|
||||||
|
name = "paperless-ngx"
|
||||||
|
version = "2.14.7"
|
||||||
|
description = "A community-supported supercharged version of paperless: scan, index and archive all your physical documents"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
classifiers = [
|
||||||
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
|
]
|
||||||
|
# TODO: Move certain things to groups and then utilize that further
|
||||||
|
# This will allow testing to not install a webserver, mysql, etc
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
"bleach~=6.2.0",
|
||||||
|
"celery[redis]~=5.4.0",
|
||||||
|
"channels~=4.2",
|
||||||
|
"channels-redis~=4.2",
|
||||||
|
"concurrent-log-handler~=0.9.25",
|
||||||
|
"dateparser~=1.2",
|
||||||
|
# WARNING: django does not use semver.
|
||||||
|
# Only patch versions are guaranteed to not introduce breaking changes.
|
||||||
|
"django~=5.1.6",
|
||||||
|
"django-allauth[socialaccount,mfa]~=65.4.0",
|
||||||
|
"django-auditlog~=3.0.0",
|
||||||
|
"django-celery-results~=2.5.1",
|
||||||
|
"django-compression-middleware~=0.5.0",
|
||||||
|
"django-cors-headers~=4.7.0",
|
||||||
|
"django-extensions~=3.2.3",
|
||||||
|
"django-filter~=25.1",
|
||||||
|
"django-guardian~=2.4.0",
|
||||||
|
"django-multiselectfield~=0.1.13",
|
||||||
|
"django-soft-delete~=1.0.18",
|
||||||
|
"djangorestframework~=3.15",
|
||||||
|
"djangorestframework-guardian~=0.3.0",
|
||||||
|
"drf-spectacular~=0.28",
|
||||||
|
"drf-spectacular-sidecar~=2025.2.1",
|
||||||
|
"drf-writable-nested~=0.7.1",
|
||||||
|
"filelock~=3.17.0",
|
||||||
|
"flower~=2.0.1",
|
||||||
|
"gotenberg-client~=0.9.0",
|
||||||
|
"httpx-oauth~=0.16",
|
||||||
|
"imap-tools~=1.10.0",
|
||||||
|
"inotifyrecursive~=0.3",
|
||||||
|
"jinja2~=3.1.5",
|
||||||
|
"langdetect~=1.0.9",
|
||||||
|
"nltk~=3.9.1",
|
||||||
|
"ocrmypdf~=16.9.0",
|
||||||
|
"pathvalidate~=3.2.3",
|
||||||
|
"pdf2image~=1.17.0",
|
||||||
|
"python-dateutil~=2.9.0",
|
||||||
|
"python-dotenv~=1.0.1",
|
||||||
|
"python-gnupg~=0.5.4",
|
||||||
|
"python-ipware~=3.0.0",
|
||||||
|
"python-magic~=0.4.27",
|
||||||
|
"pyzbar~=0.1.9",
|
||||||
|
"rapidfuzz~=3.12.1",
|
||||||
|
"redis[hiredis]~=5.2.1",
|
||||||
|
"scikit-learn~=1.6.1",
|
||||||
|
"setproctitle~=1.3.4",
|
||||||
|
"tika-client~=0.9.0",
|
||||||
|
"tqdm~=4.67.1",
|
||||||
|
"watchdog~=6.0",
|
||||||
|
"whitenoise~=6.9",
|
||||||
|
"whoosh~=2.7",
|
||||||
|
"zxing-cpp~=2.3.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
optional-dependencies.mariadb = [
|
||||||
|
"mysqlclient~=2.2.7",
|
||||||
|
]
|
||||||
|
optional-dependencies.postgres = [
|
||||||
|
"psycopg[c]==3.2.4",
|
||||||
|
# Direct dependency for proper resolution of the pre-built wheels
|
||||||
|
"psycopg-c==3.2.4",
|
||||||
|
]
|
||||||
|
optional-dependencies.webserver = [
|
||||||
|
"granian~=1.7.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
|
||||||
|
dev = [
|
||||||
|
{ "include-group" = "docs" },
|
||||||
|
{ "include-group" = "testing" },
|
||||||
|
{ "include-group" = "lint" },
|
||||||
|
]
|
||||||
|
|
||||||
|
docs = [
|
||||||
|
"mkdocs-glightbox~=0.4.0",
|
||||||
|
"mkdocs-material~=9.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
testing = [
|
||||||
|
"daphne",
|
||||||
|
"factory-boy~=3.3.1",
|
||||||
|
"imagehash",
|
||||||
|
"pytest~=8.3.3",
|
||||||
|
"pytest-cov~=6.0.0",
|
||||||
|
"pytest-django~=4.10.0",
|
||||||
|
"pytest-env",
|
||||||
|
"pytest-httpx",
|
||||||
|
"pytest-mock",
|
||||||
|
"pytest-rerunfailures",
|
||||||
|
"pytest-sugar",
|
||||||
|
"pytest-xdist",
|
||||||
|
]
|
||||||
|
|
||||||
|
lint = [
|
||||||
|
"pre-commit~=4.1.0",
|
||||||
|
"pre-commit-uv~=4.1.3",
|
||||||
|
"ruff~=0.9.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
typing = [
|
||||||
|
"celery-types",
|
||||||
|
"django-filter-stubs",
|
||||||
|
"django-stubs[compatible-mypy]",
|
||||||
|
"djangorestframework-stubs[compatible-mypy]",
|
||||||
|
"mypy",
|
||||||
|
"types-bleach",
|
||||||
|
"types-colorama",
|
||||||
|
"types-dateparser",
|
||||||
|
"types-markdown",
|
||||||
|
"types-pygments",
|
||||||
|
"types-python-dateutil",
|
||||||
|
"types-redis",
|
||||||
|
"types-setuptools",
|
||||||
|
"types-tqdm",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py310"
|
||||||
|
line-length = 88
|
||||||
|
src = [
|
||||||
|
"src",
|
||||||
|
]
|
||||||
|
respect-gitignore = true
|
||||||
|
# https://docs.astral.sh/ruff/settings/
|
||||||
|
fix = true
|
||||||
|
show-fixes = true
|
||||||
|
|
||||||
|
output-format = "grouped"
|
||||||
|
# https://docs.astral.sh/ruff/rules/
|
||||||
|
lint.extend-select = [
|
||||||
|
"COM", # https://docs.astral.sh/ruff/rules/#flake8-commas-com
|
||||||
|
"DJ", # https://docs.astral.sh/ruff/rules/#flake8-django-dj
|
||||||
|
"EXE", # https://docs.astral.sh/ruff/rules/#flake8-executable-exe
|
||||||
|
"FBT", # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt
|
||||||
|
"FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly
|
||||||
|
"G201", # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g
|
||||||
|
"I", # https://docs.astral.sh/ruff/rules/#isort-i
|
||||||
|
"ICN", # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn
|
||||||
|
"INP", # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp
|
||||||
|
"ISC", # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc
|
||||||
|
"PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie
|
||||||
|
"PLC", # https://docs.astral.sh/ruff/rules/#pylint-pl
|
||||||
|
"PLE", # https://docs.astral.sh/ruff/rules/#pylint-pl
|
||||||
|
"PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth
|
||||||
|
"Q", # https://docs.astral.sh/ruff/rules/#flake8-quotes-q
|
||||||
|
"RSE", # https://docs.astral.sh/ruff/rules/#flake8-raise-rse
|
||||||
|
"RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf
|
||||||
|
"SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim
|
||||||
|
"T20", # https://docs.astral.sh/ruff/rules/#flake8-print-t20
|
||||||
|
"TC", # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tc
|
||||||
|
"TID", # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid
|
||||||
|
"UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up
|
||||||
|
"W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
|
||||||
|
]
|
||||||
|
lint.ignore = [
|
||||||
|
"DJ001",
|
||||||
|
"RUF012",
|
||||||
|
"SIM105",
|
||||||
|
]
|
||||||
|
# Migrations
|
||||||
|
lint.per-file-ignores."*/migrations/*.py" = [
|
||||||
|
"E501",
|
||||||
|
"SIM",
|
||||||
|
"T201",
|
||||||
|
]
|
||||||
|
# Testing
|
||||||
|
lint.per-file-ignores."*/tests/*.py" = [
|
||||||
|
"E501",
|
||||||
|
"SIM117",
|
||||||
|
]
|
||||||
|
lint.per-file-ignores.".github/scripts/*.py" = [
|
||||||
|
"E501",
|
||||||
|
"INP001",
|
||||||
|
"SIM117",
|
||||||
|
]
|
||||||
|
# Docker specific
|
||||||
|
lint.per-file-ignores."docker/rootfs/usr/local/bin/wait-for-redis.py" = [
|
||||||
|
"INP001",
|
||||||
|
"T201",
|
||||||
|
]
|
||||||
|
lint.per-file-ignores."docker/wait-for-redis.py" = [
|
||||||
|
"INP001",
|
||||||
|
"T201",
|
||||||
|
]
|
||||||
|
lint.per-file-ignores."src/documents/file_handling.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/management/commands/document_consumer.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/management/commands/document_exporter.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/migrations/0012_auto_20160305_0040.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/migrations/0014_document_checksum.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/migrations/1003_mime_types.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/migrations/1012_fix_archive_files.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/models.py" = [
|
||||||
|
"PTH",
|
||||||
|
"SIM115",
|
||||||
|
] # TODO PTH Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/parsers.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/signals/handlers.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tasks.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_api_app_config.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_classifier.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_consumer.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_file_handling.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_management.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_management_consumer.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_management_exporter.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_management_thumbnails.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_migration_archive_files.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_migration_document_pages_count.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_migration_mime_type.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_sanity_check.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_tasks.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_views.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/views.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless/checks.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless/settings.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless/tests/test_checks.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless/urls.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless/views.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_mail/mail.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_mail/preprocessor.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_tesseract/parsers.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [
|
||||||
|
"PTH",
|
||||||
|
"RUF001",
|
||||||
|
] # TODO PTH Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_tika/tests/test_live_tika.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_tika/tests/test_tika_parser.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.isort.force-single-line = true
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
minversion = "8.0"
|
||||||
|
pythonpath = [
|
||||||
|
"src",
|
||||||
|
]
|
||||||
|
testpaths = [
|
||||||
|
"src/documents/tests/",
|
||||||
|
"src/paperless/tests/",
|
||||||
|
"src/paperless_mail/tests/",
|
||||||
|
"src/paperless_tesseract/tests/",
|
||||||
|
"src/paperless_tika/tests",
|
||||||
|
]
|
||||||
|
addopts = [
|
||||||
|
"--pythonwarnings=all",
|
||||||
|
"--cov",
|
||||||
|
"--cov-report=html",
|
||||||
|
"--cov-report=xml",
|
||||||
|
"--numprocesses=auto",
|
||||||
|
"--maxprocesses=16",
|
||||||
|
"--quiet",
|
||||||
|
"--durations=50",
|
||||||
|
"--junitxml=junit.xml",
|
||||||
|
"-o junit_family=legacy",
|
||||||
|
]
|
||||||
|
norecursedirs = [ "src/locale/", ".venv/", "src-ui/" ]
|
||||||
|
|
||||||
|
DJANGO_SETTINGS_MODULE = "paperless.settings"
|
||||||
|
|
||||||
|
[tool.pytest_env]
|
||||||
|
PAPERLESS_DISABLE_DBHANDLER = "true"
|
||||||
|
PAPERLESS_CACHE_BACKEND = "django.core.cache.backends.locmem.LocMemCache"
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
source = [
|
||||||
|
"src/",
|
||||||
|
]
|
||||||
|
omit = [
|
||||||
|
"*/tests/*",
|
||||||
|
"manage.py",
|
||||||
|
"paperless/wsgi.py",
|
||||||
|
"paperless/auth.py",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.coverage.report]
|
||||||
|
exclude_also = [
|
||||||
|
"if settings.AUDIT_LOG_ENABLED:",
|
||||||
|
"if AUDIT_LOG_ENABLED:",
|
||||||
|
"if TYPE_CHECKING:",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
plugins = [
|
||||||
|
"mypy_django_plugin.main",
|
||||||
|
"mypy_drf_plugin.main",
|
||||||
|
"numpy.typing.mypy_plugin",
|
||||||
|
]
|
||||||
|
check_untyped_defs = true
|
||||||
|
disallow_any_generics = true
|
||||||
|
disallow_incomplete_defs = true
|
||||||
|
disallow_untyped_defs = true
|
||||||
|
warn_redundant_casts = true
|
||||||
|
warn_unused_ignores = true
|
||||||
|
|
||||||
|
[tool.uv]
|
||||||
|
required-version = ">=0.5.14"
|
||||||
|
package = false
|
||||||
|
environments = [
|
||||||
|
"sys_platform == 'darwin'",
|
||||||
|
"sys_platform == 'linux'",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
# Markers are chosen to select these almost exclusively when building the Docker image
|
||||||
|
psycopg-c = [
|
||||||
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
||||||
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
|
||||||
|
]
|
||||||
|
zxing-cpp = [
|
||||||
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
||||||
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.django-stubs]
|
||||||
|
django_settings_module = "paperless.settings"
|
@ -12,4 +12,13 @@ module.exports = {
|
|||||||
'^src/(.*)': '<rootDir>/src/$1',
|
'^src/(.*)': '<rootDir>/src/$1',
|
||||||
},
|
},
|
||||||
workerIdleMemoryLimit: '512MB',
|
workerIdleMemoryLimit: '512MB',
|
||||||
|
reporters: [
|
||||||
|
'default',
|
||||||
|
[
|
||||||
|
'jest-junit',
|
||||||
|
{
|
||||||
|
classNameTemplate: '{filepath}/{classname}: {title}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
@ -545,7 +545,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">364</context>
|
<context context-type="linenumber">360</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||||
@ -1034,8 +1034,8 @@
|
|||||||
<context context-type="linenumber">188</context>
|
<context context-type="linenumber">188</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7791089127901960679" datatype="html">
|
<trans-unit id="2678648946508279627" datatype="html">
|
||||||
<source>Default zoom:</source>
|
<source>Default zoom</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">194</context>
|
<context context-type="linenumber">194</context>
|
||||||
@ -1076,11 +1076,79 @@
|
|||||||
<context context-type="linenumber">213</context>
|
<context context-type="linenumber">213</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="6760166989231109310" datatype="html">
|
||||||
|
<source>Global search</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">217</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.ts</context>
|
||||||
|
<context context-type="linenumber">120</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="2818183879511244335" datatype="html">
|
||||||
|
<source>Do not include advanced search results</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">220</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="3969258421469113318" datatype="html">
|
||||||
|
<source>Full search links to</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">226</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6631288852577115923" datatype="html">
|
||||||
|
<source>Title and content search</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">230</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="1010505078885609376" datatype="html">
|
||||||
|
<source>Advanced search</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">231</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.html</context>
|
||||||
|
<context context-type="linenumber">24</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||||
|
<context context-type="linenumber">173</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="8508424367627989968" datatype="html">
|
||||||
|
<source>Bulk editing</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">236</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="8158899674926420054" datatype="html">
|
||||||
|
<source>Show confirmation dialogs</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">239</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="290238406234356122" datatype="html">
|
||||||
|
<source>Apply on close</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">240</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="8104421162933956065" datatype="html">
|
<trans-unit id="8104421162933956065" datatype="html">
|
||||||
<source>Notes</source>
|
<source>Notes</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">217</context>
|
<context context-type="linenumber">244</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||||
@ -1097,84 +1165,16 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="293524471897878391" datatype="html">
|
<trans-unit id="293524471897878391" datatype="html">
|
||||||
<source>Enable notes</source>
|
<source>Enable notes</source>
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">220</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="8508424367627989968" datatype="html">
|
|
||||||
<source>Bulk editing</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">224</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="8158899674926420054" datatype="html">
|
|
||||||
<source>Show confirmation dialogs</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">227</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="290238406234356122" datatype="html">
|
|
||||||
<source>Apply on close</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">228</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="6760166989231109310" datatype="html">
|
|
||||||
<source>Global search</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">232</context>
|
|
||||||
</context-group>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.ts</context>
|
|
||||||
<context context-type="linenumber">120</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="2818183879511244335" datatype="html">
|
|
||||||
<source>Do not include advanced search results</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">235</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="3969258421469113318" datatype="html">
|
|
||||||
<source>Full search links to</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">243</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="6631288852577115923" datatype="html">
|
|
||||||
<source>Title and content search</source>
|
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">247</context>
|
<context context-type="linenumber">247</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1010505078885609376" datatype="html">
|
|
||||||
<source>Advanced search</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">248</context>
|
|
||||||
</context-group>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.html</context>
|
|
||||||
<context context-type="linenumber">24</context>
|
|
||||||
</context-group>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
|
||||||
<context context-type="linenumber">173</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="7314814725704332646" datatype="html">
|
<trans-unit id="7314814725704332646" datatype="html">
|
||||||
<source>Permissions</source>
|
<source>Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">262</context>
|
<context context-type="linenumber">258</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html</context>
|
||||||
@ -1237,28 +1237,28 @@
|
|||||||
<source>Default Permissions</source>
|
<source>Default Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">265</context>
|
<context context-type="linenumber">261</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8222269449891326545" datatype="html">
|
<trans-unit id="6544153565064275581" datatype="html">
|
||||||
<source> Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI </source>
|
<source> Settings apply to this user account for objects (Tags, Mail Rules, etc. but not documents) created via the web UI. </source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">269,271</context>
|
<context context-type="linenumber">265,267</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4292903881380648974" datatype="html">
|
<trans-unit id="4292903881380648974" datatype="html">
|
||||||
<source>Default Owner</source>
|
<source>Default Owner</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">276</context>
|
<context context-type="linenumber">272</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="734147282056744882" datatype="html">
|
<trans-unit id="734147282056744882" datatype="html">
|
||||||
<source>Objects without an owner can be viewed and edited by all users</source>
|
<source>Objects without an owner can be viewed and edited by all users</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">280</context>
|
<context context-type="linenumber">276</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
||||||
@ -1269,18 +1269,18 @@
|
|||||||
<source>Default View Permissions</source>
|
<source>Default View Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">285</context>
|
<context context-type="linenumber">281</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2191775412581217688" datatype="html">
|
<trans-unit id="2191775412581217688" datatype="html">
|
||||||
<source>Users:</source>
|
<source>Users:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">290</context>
|
<context context-type="linenumber">286</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">317</context>
|
<context context-type="linenumber">313</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
@ -1311,11 +1311,11 @@
|
|||||||
<source>Groups:</source>
|
<source>Groups:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">300</context>
|
<context context-type="linenumber">296</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">327</context>
|
<context context-type="linenumber">323</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
@ -1346,14 +1346,14 @@
|
|||||||
<source>Default Edit Permissions</source>
|
<source>Default Edit Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">312</context>
|
<context context-type="linenumber">308</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3728984448750213892" datatype="html">
|
<trans-unit id="3728984448750213892" datatype="html">
|
||||||
<source>Edit permissions also grant viewing permissions</source>
|
<source>Edit permissions also grant viewing permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">336</context>
|
<context context-type="linenumber">332</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
@ -1372,7 +1372,7 @@
|
|||||||
<source>Notifications</source>
|
<source>Notifications</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">344</context>
|
<context context-type="linenumber">340</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.html</context>
|
<context context-type="sourcefile">src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.html</context>
|
||||||
@ -1383,49 +1383,49 @@
|
|||||||
<source>Document processing</source>
|
<source>Document processing</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">347</context>
|
<context context-type="linenumber">343</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3656786776644872398" datatype="html">
|
<trans-unit id="3656786776644872398" datatype="html">
|
||||||
<source>Show notifications when new documents are detected</source>
|
<source>Show notifications when new documents are detected</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">351</context>
|
<context context-type="linenumber">347</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6057053428592387613" datatype="html">
|
<trans-unit id="6057053428592387613" datatype="html">
|
||||||
<source>Show notifications when document processing completes successfully</source>
|
<source>Show notifications when document processing completes successfully</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">352</context>
|
<context context-type="linenumber">348</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="370315664367425513" datatype="html">
|
<trans-unit id="370315664367425513" datatype="html">
|
||||||
<source>Show notifications when document processing fails</source>
|
<source>Show notifications when document processing fails</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">353</context>
|
<context context-type="linenumber">349</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6838309441164918531" datatype="html">
|
<trans-unit id="6838309441164918531" datatype="html">
|
||||||
<source>Suppress notifications on dashboard</source>
|
<source>Suppress notifications on dashboard</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">354</context>
|
<context context-type="linenumber">350</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2741919327232918179" datatype="html">
|
<trans-unit id="2741919327232918179" datatype="html">
|
||||||
<source>This will suppress all messages about document processing status on the dashboard.</source>
|
<source>This will suppress all messages about document processing status on the dashboard.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">354</context>
|
<context context-type="linenumber">350</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2159130950882492111" datatype="html">
|
<trans-unit id="2159130950882492111" datatype="html">
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">365</context>
|
<context context-type="linenumber">361</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/confirm-dialog/confirm-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/confirm-dialog/confirm-dialog.component.ts</context>
|
||||||
|
245
src-ui/package-lock.json
generated
245
src-ui/package-lock.json
generated
@ -44,23 +44,24 @@
|
|||||||
"@angular-devkit/build-angular": "^19.0.4",
|
"@angular-devkit/build-angular": "^19.0.4",
|
||||||
"@angular-devkit/core": "^19.2.0",
|
"@angular-devkit/core": "^19.2.0",
|
||||||
"@angular-devkit/schematics": "^19.2.0",
|
"@angular-devkit/schematics": "^19.2.0",
|
||||||
"@angular-eslint/builder": "19.1.0",
|
"@angular-eslint/builder": "19.2.0",
|
||||||
"@angular-eslint/eslint-plugin": "19.1.0",
|
"@angular-eslint/eslint-plugin": "19.2.0",
|
||||||
"@angular-eslint/eslint-plugin-template": "19.1.0",
|
"@angular-eslint/eslint-plugin-template": "19.2.0",
|
||||||
"@angular-eslint/schematics": "19.1.0",
|
"@angular-eslint/schematics": "19.2.0",
|
||||||
"@angular-eslint/template-parser": "19.1.0",
|
"@angular-eslint/template-parser": "19.2.0",
|
||||||
"@angular/cli": "~19.2.0",
|
"@angular/cli": "~19.2.0",
|
||||||
"@angular/compiler-cli": "~19.2.0",
|
"@angular/compiler-cli": "~19.2.0",
|
||||||
"@codecov/webpack-plugin": "^1.9.0",
|
"@codecov/webpack-plugin": "^1.9.0",
|
||||||
"@playwright/test": "^1.50.1",
|
"@playwright/test": "^1.50.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.13.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.25.0",
|
"@typescript-eslint/eslint-plugin": "^8.26.0",
|
||||||
"@typescript-eslint/parser": "^8.25.0",
|
"@typescript-eslint/parser": "^8.26.0",
|
||||||
"@typescript-eslint/utils": "^8.0.0",
|
"@typescript-eslint/utils": "^8.0.0",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"jest-junit": "^16.0.0",
|
||||||
"jest-preset-angular": "^14.5.3",
|
"jest-preset-angular": "^14.5.3",
|
||||||
"jest-websocket-mock": "^2.5.0",
|
"jest-websocket-mock": "^2.5.0",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
@ -1148,9 +1149,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/builder": {
|
"node_modules/@angular-eslint/builder": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-19.2.0.tgz",
|
||||||
"integrity": "sha512-LWdQMTES/7GySlpTNFJn3k33ZGmjjWlHI/+IHV7B3xHQ9hj4MPK4ACmE/PNOAIQ9LwQm7sKS+3cTMxOZQ/cvSg==",
|
"integrity": "sha512-8Lx24MrMJT8RlgDtwqfiLiJo4DzSaktjco6RmELUdWO2chJgRe9y+2iIgOeB2pmyD9UCsubwsfjBXlrnV/MPhQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1163,21 +1164,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/bundled-angular-compiler": {
|
"node_modules/@angular-eslint/bundled-angular-compiler": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-19.2.0.tgz",
|
||||||
"integrity": "sha512-HUJyukRvnh8Z9lIdxdblBRuBaPYEVv4iAYZMw3d+dn4rrM27Nt5oh3/zkwYrrPkt36tZdeXdDWrOuz9jgjVN5w==",
|
"integrity": "sha512-hmmAogTpYGbBvnJ0j7DNLi8YQ+YEEuwFdx0heU8XjTpZlRoSRIP7MJJVlaQCt+ZT5f5XwdGtqi9lOXqqcyGHLA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/eslint-plugin": {
|
"node_modules/@angular-eslint/eslint-plugin": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-19.2.0.tgz",
|
||||||
"integrity": "sha512-TDO0+Ry+oNkxnaLHogKp1k2aey6IkJef5d7hathE4UFT6owjRizltWaRoX6bGw7Qu1yagVLL8L2Se8SddxSPAQ==",
|
"integrity": "sha512-QQWWDrTdJ22tBd7RLFG/FdPwNyYEhg7YwWgn29z6XcdnV00ZFtf7FRbv/te1kqVNPvfjtht7bvtHcPQ432aUdQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-eslint/bundled-angular-compiler": "19.1.0",
|
"@angular-eslint/bundled-angular-compiler": "19.2.0",
|
||||||
"@angular-eslint/utils": "19.1.0"
|
"@angular-eslint/utils": "19.2.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
|
"@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
|
||||||
@ -1186,14 +1187,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/eslint-plugin-template": {
|
"node_modules/@angular-eslint/eslint-plugin-template": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-19.2.0.tgz",
|
||||||
"integrity": "sha512-bIUizkCY40mnU8oAO1tLV7uN2H/cHf1evLlhpqlb9JYwc5dT2moiEhNDo61OtOgkJmDGNuThAeO9Xk9hGQc7nA==",
|
"integrity": "sha512-lUSzmk5/Dr0bNc2Omb5CZDu3zQZh70bJyuXnN5MKd00V1b3u90eqvMSveFzWFJ6Eot8Hh8+FxtiozPwGqOE+Og==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-eslint/bundled-angular-compiler": "19.1.0",
|
"@angular-eslint/bundled-angular-compiler": "19.2.0",
|
||||||
"@angular-eslint/utils": "19.1.0",
|
"@angular-eslint/utils": "19.2.0",
|
||||||
"aria-query": "5.3.2",
|
"aria-query": "5.3.2",
|
||||||
"axobject-query": "4.1.0"
|
"axobject-query": "4.1.0"
|
||||||
},
|
},
|
||||||
@ -1204,17 +1205,47 @@
|
|||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular-eslint/eslint-plugin-template/node_modules/@angular-eslint/utils": {
|
||||||
|
"version": "19.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.2.0.tgz",
|
||||||
|
"integrity": "sha512-1XQXzIqYadKUxcAgW1DPev56SVbR8Uld6TthgolU7rfIX23RYMIIRtQlrQCk7zoXLXm5fzcGqjTR4wHfoD+iWg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@angular-eslint/bundled-angular-compiler": "19.2.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
|
||||||
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
|
"typescript": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular-eslint/eslint-plugin/node_modules/@angular-eslint/utils": {
|
||||||
|
"version": "19.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.2.0.tgz",
|
||||||
|
"integrity": "sha512-1XQXzIqYadKUxcAgW1DPev56SVbR8Uld6TthgolU7rfIX23RYMIIRtQlrQCk7zoXLXm5fzcGqjTR4wHfoD+iWg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@angular-eslint/bundled-angular-compiler": "19.2.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
|
||||||
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
|
"typescript": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular-eslint/schematics": {
|
"node_modules/@angular-eslint/schematics": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-19.2.0.tgz",
|
||||||
"integrity": "sha512-6S1FjmM7rZxc0u0W0KjqWYOkFQ0q89IGyjPkdUt1a8NwRnWg3VoXp4WYfeuZOjda/FEYuBS/E6rckLAMp0h6Aw==",
|
"integrity": "sha512-SQfbKgPEJNkK5TVXRsdnWp6TjvVZOczvf8lELF1n+I/Uwmp7ulUjTRgTo59ZQnXoPSs2qCPgS4gAOVR6CD91zQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-devkit/core": ">= 19.0.0 < 20.0.0",
|
"@angular-devkit/core": ">= 19.0.0 < 20.0.0",
|
||||||
"@angular-devkit/schematics": ">= 19.0.0 < 20.0.0",
|
"@angular-devkit/schematics": ">= 19.0.0 < 20.0.0",
|
||||||
"@angular-eslint/eslint-plugin": "19.1.0",
|
"@angular-eslint/eslint-plugin": "19.2.0",
|
||||||
"@angular-eslint/eslint-plugin-template": "19.1.0",
|
"@angular-eslint/eslint-plugin-template": "19.2.0",
|
||||||
"ignore": "7.0.3",
|
"ignore": "7.0.3",
|
||||||
"semver": "7.7.1",
|
"semver": "7.7.1",
|
||||||
"strip-json-comments": "3.1.1"
|
"strip-json-comments": "3.1.1"
|
||||||
@ -1231,13 +1262,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/template-parser": {
|
"node_modules/@angular-eslint/template-parser": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.2.0.tgz",
|
||||||
"integrity": "sha512-wbMi7adlC+uYqZo7NHNBShpNhFJRZsXLqihqvFpAUt1Ei6uDX8HR6MyMEDZ9tUnlqtPVW5nmbedPyLVG7HkjAA==",
|
"integrity": "sha512-VqgvFrILhoMe0GHZrx+Bjy8kx7/LJfJTd+x/wzE/X1cCChSU81MBZFMVeFMnoI75OOQUf4fwaaKrtUhUvAkVyw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-eslint/bundled-angular-compiler": "19.1.0",
|
"@angular-eslint/bundled-angular-compiler": "19.2.0",
|
||||||
"eslint-scope": "^8.0.2"
|
"eslint-scope": "^8.0.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@ -1245,21 +1276,6 @@
|
|||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/utils": {
|
|
||||||
"version": "19.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.1.0.tgz",
|
|
||||||
"integrity": "sha512-mcb7hPMH/u6wwUwvsewrmgb9y9NWN6ZacvpUvKlTOxF/jOtTdsu0XfV4YB43sp2A8NWzYzX0Str4c8K1xSmuBQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@angular-eslint/bundled-angular-compiler": "19.1.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
|
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
|
||||||
"typescript": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@angular/cdk": {
|
"node_modules/@angular/cdk": {
|
||||||
"version": "19.2.1",
|
"version": "19.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.2.1.tgz",
|
||||||
@ -7150,9 +7166,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.13.5",
|
"version": "22.13.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz",
|
||||||
"integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==",
|
"integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -7271,17 +7287,17 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.0.tgz",
|
||||||
"integrity": "sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA==",
|
"integrity": "sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.25.0",
|
"@typescript-eslint/scope-manager": "8.26.0",
|
||||||
"@typescript-eslint/type-utils": "8.25.0",
|
"@typescript-eslint/type-utils": "8.26.0",
|
||||||
"@typescript-eslint/utils": "8.25.0",
|
"@typescript-eslint/utils": "8.26.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.25.0",
|
"@typescript-eslint/visitor-keys": "8.26.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^5.3.1",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
@ -7297,20 +7313,20 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
|
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.0.tgz",
|
||||||
"integrity": "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==",
|
"integrity": "sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.25.0",
|
"@typescript-eslint/scope-manager": "8.26.0",
|
||||||
"@typescript-eslint/types": "8.25.0",
|
"@typescript-eslint/types": "8.26.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.25.0",
|
"@typescript-eslint/typescript-estree": "8.26.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.25.0",
|
"@typescript-eslint/visitor-keys": "8.26.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -7322,18 +7338,18 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.0.tgz",
|
||||||
"integrity": "sha512-6PPeiKIGbgStEyt4NNXa2ru5pMzQ8OYKO1hX1z53HMomrmiSB+R5FmChgQAP1ro8jMtNawz+TRQo/cSXrauTpg==",
|
"integrity": "sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.25.0",
|
"@typescript-eslint/types": "8.26.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.25.0"
|
"@typescript-eslint/visitor-keys": "8.26.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -7344,14 +7360,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.0.tgz",
|
||||||
"integrity": "sha512-d77dHgHWnxmXOPJuDWO4FDWADmGQkN5+tt6SFRZz/RtCWl4pHgFl3+WdYCn16+3teG09DY6XtEpf3gGD0a186g==",
|
"integrity": "sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.25.0",
|
"@typescript-eslint/typescript-estree": "8.26.0",
|
||||||
"@typescript-eslint/utils": "8.25.0",
|
"@typescript-eslint/utils": "8.26.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^2.0.1"
|
"ts-api-utils": "^2.0.1"
|
||||||
},
|
},
|
||||||
@ -7364,13 +7380,13 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.0.tgz",
|
||||||
"integrity": "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw==",
|
"integrity": "sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -7382,14 +7398,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.0.tgz",
|
||||||
"integrity": "sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q==",
|
"integrity": "sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.25.0",
|
"@typescript-eslint/types": "8.26.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.25.0",
|
"@typescript-eslint/visitor-keys": "8.26.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
@ -7405,7 +7421,7 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||||
@ -7435,16 +7451,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.0.tgz",
|
||||||
"integrity": "sha512-syqRbrEv0J1wywiLsK60XzHnQe/kRViI3zwFALrNEgnntn1l24Ra2KvOAWwWbWZ1lBZxZljPDGOq967dsl6fkA==",
|
"integrity": "sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"@typescript-eslint/scope-manager": "8.25.0",
|
"@typescript-eslint/scope-manager": "8.26.0",
|
||||||
"@typescript-eslint/types": "8.25.0",
|
"@typescript-eslint/types": "8.26.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.25.0"
|
"@typescript-eslint/typescript-estree": "8.26.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -7455,17 +7471,17 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.0.tgz",
|
||||||
"integrity": "sha512-kCYXKAum9CecGVHGij7muybDfTS2sD3t0L4bJsEZLkyrXUImiCTq1M3LG2SRtOhiHFwMR9wAFplpT6XHYjTkwQ==",
|
"integrity": "sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.25.0",
|
"@typescript-eslint/types": "8.26.0",
|
||||||
"eslint-visitor-keys": "^4.2.0"
|
"eslint-visitor-keys": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -12403,6 +12419,32 @@
|
|||||||
"fsevents": "^2.3.2"
|
"fsevents": "^2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-junit": {
|
||||||
|
"version": "16.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz",
|
||||||
|
"integrity": "sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": "^1.0.4",
|
||||||
|
"strip-ansi": "^6.0.1",
|
||||||
|
"uuid": "^8.3.2",
|
||||||
|
"xml": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-junit/node_modules/uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-leak-detector": {
|
"node_modules/jest-leak-detector": {
|
||||||
"version": "29.7.0",
|
"version": "29.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
|
||||||
@ -19250,6 +19292,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xml": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/xml-name-validator": {
|
"node_modules/xml-name-validator": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
|
||||||
|
@ -46,23 +46,24 @@
|
|||||||
"@angular-devkit/build-angular": "^19.0.4",
|
"@angular-devkit/build-angular": "^19.0.4",
|
||||||
"@angular-devkit/core": "^19.2.0",
|
"@angular-devkit/core": "^19.2.0",
|
||||||
"@angular-devkit/schematics": "^19.2.0",
|
"@angular-devkit/schematics": "^19.2.0",
|
||||||
"@angular-eslint/builder": "19.1.0",
|
"@angular-eslint/builder": "19.2.0",
|
||||||
"@angular-eslint/eslint-plugin": "19.1.0",
|
"@angular-eslint/eslint-plugin": "19.2.0",
|
||||||
"@angular-eslint/eslint-plugin-template": "19.1.0",
|
"@angular-eslint/eslint-plugin-template": "19.2.0",
|
||||||
"@angular-eslint/schematics": "19.1.0",
|
"@angular-eslint/schematics": "19.2.0",
|
||||||
"@angular-eslint/template-parser": "19.1.0",
|
"@angular-eslint/template-parser": "19.2.0",
|
||||||
"@angular/cli": "~19.2.0",
|
"@angular/cli": "~19.2.0",
|
||||||
"@angular/compiler-cli": "~19.2.0",
|
"@angular/compiler-cli": "~19.2.0",
|
||||||
"@codecov/webpack-plugin": "^1.9.0",
|
"@codecov/webpack-plugin": "^1.9.0",
|
||||||
"@playwright/test": "^1.50.1",
|
"@playwright/test": "^1.50.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.13.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.25.0",
|
"@typescript-eslint/eslint-plugin": "^8.26.0",
|
||||||
"@typescript-eslint/parser": "^8.25.0",
|
"@typescript-eslint/parser": "^8.26.0",
|
||||||
"@typescript-eslint/utils": "^8.0.0",
|
"@typescript-eslint/utils": "^8.0.0",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"jest-junit": "^16.0.0",
|
||||||
"jest-preset-angular": "^14.5.3",
|
"jest-preset-angular": "^14.5.3",
|
||||||
"jest-websocket-mock": "^2.5.0",
|
"jest-websocket-mock": "^2.5.0",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
|
@ -118,7 +118,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
<span i18n>Sidebar</span>
|
<span i18n>Sidebar</span>
|
||||||
</div>
|
</div>
|
||||||
@ -129,7 +129,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
<span i18n>Dark mode</span>
|
<span i18n>Dark mode</span>
|
||||||
</div>
|
</div>
|
||||||
@ -165,7 +165,7 @@
|
|||||||
<p i18n>
|
<p i18n>
|
||||||
Update checking works by pinging the public GitHub API for the latest release to determine whether a new version is available. Actual updating of the app must still be performed manually.
|
Update checking works by pinging the public GitHub API for the latest release to determine whether a new version is available. Actual updating of the app must still be performed manually.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p class="mb-0">
|
||||||
<em i18n>No tracking data is collected by the app in any way.</em>
|
<em i18n>No tracking data is collected by the app in any way.</em>
|
||||||
</p>
|
</p>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -173,7 +173,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 class="mt-3" i18n>Saved Views</h5>
|
<h5 class="mt-3" i18n>Saved Views</h5>
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
@ -183,15 +183,15 @@
|
|||||||
<div class="col-xl-6 ps-xl-5">
|
<div class="col-xl-6 ps-xl-5">
|
||||||
<h5 class="mt-3 mt-md-0" i18n>Document editing</h5>
|
<h5 class="mt-3 mt-md-0" i18n>Document editing</h5>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Use PDF viewer provided by the browser" i18n-hint hint="This is usually faster for displaying large PDF documents, but it might not work on some browsers." formControlName="useNativePdfViewer"></pngx-input-check>
|
<pngx-input-check i18n-title title="Use PDF viewer provided by the browser" i18n-hint hint="This is usually faster for displaying large PDF documents, but it might not work on some browsers." formControlName="useNativePdfViewer"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col-2">
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
<span i18n>Default zoom:</span>
|
<span i18n>Default zoom</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<select class="form-select" formControlName="pdfViewerDefaultZoom">
|
<select class="form-select" formControlName="pdfViewerDefaultZoom">
|
||||||
@ -202,7 +202,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Automatically remove inbox tag(s) on save" formControlName="documentEditingRemoveInboxTags"></pngx-input-check>
|
<pngx-input-check i18n-title title="Automatically remove inbox tag(s) on save" formControlName="documentEditingRemoveInboxTags"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
@ -214,10 +214,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 class="mt-3" i18n>Notes</h5>
|
<h5 class="mt-3" i18n>Global search</h5>
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Enable notes" formControlName="notesEnabled"></pngx-input-check>
|
<pngx-input-check i18n-title title="Do not include advanced search results" formControlName="searchDbOnly"></pngx-input-check>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Full search links to</span>
|
||||||
|
</div>
|
||||||
|
<div class="col mb-3">
|
||||||
|
<select class="form-select" formControlName="searchLink">
|
||||||
|
<option [ngValue]="GlobalSearchType.TITLE_CONTENT" i18n>Title and content search</option>
|
||||||
|
<option [ngValue]="GlobalSearchType.ADVANCED" i18n>Advanced search</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -229,26 +241,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 class="mt-3" i18n>Global search</h5>
|
<h5 class="mt-3" i18n>Notes</h5>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Do not include advanced search results" formControlName="searchDbOnly"></pngx-input-check>
|
<pngx-input-check i18n-title title="Enable notes" formControlName="notesEnabled"></pngx-input-check>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>Full search links to</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<select class="form-select" formControlName="searchLink">
|
|
||||||
<option [ngValue]="GlobalSearchType.TITLE_CONTENT" i18n>Title and content search</option>
|
|
||||||
<option [ngValue]="GlobalSearchType.ADVANCED" i18n>Advanced search</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -267,7 +263,7 @@
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<p i18n>
|
<p i18n>
|
||||||
Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI
|
Settings apply to this user account for objects (Tags, Mail Rules, etc. but not documents) created via the web UI.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -307,7 +303,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
<span i18n>Default Edit Permissions</span>
|
<span i18n>Default Edit Permissions</span>
|
||||||
</div>
|
</div>
|
||||||
@ -346,7 +342,7 @@
|
|||||||
|
|
||||||
<h5 i18n>Document processing</h5>
|
<h5 i18n>Document processing</h5>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Show notifications when new documents are detected" formControlName="notificationsConsumerNewDocument"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show notifications when new documents are detected" formControlName="notificationsConsumerNewDocument"></pngx-input-check>
|
||||||
<pngx-input-check i18n-title title="Show notifications when document processing completes successfully" formControlName="notificationsConsumerSuccess"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show notifications when document processing completes successfully" formControlName="notificationsConsumerSuccess"></pngx-input-check>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
<div class="scroll-list">
|
<div class="scroll-list">
|
||||||
@for (toast of toasts; track toast.id) {
|
@for (toast of toasts; track toast.id) {
|
||||||
<pngx-toast [autohide]="false" [toast]="toast" (hidden)="onHidden(toast)" (close)="toastService.closeToast(toast)"></pngx-toast>
|
<pngx-toast [autohide]="false" [toast]="toast" (hidden)="onHidden(toast)" (closed)="toastService.closeToast(toast)"></pngx-toast>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,6 +51,6 @@
|
|||||||
<p class="mb-0 mt-2"><button class="btn btn-sm btn-outline-secondary" (click)="close.emit(toast); toast.action()">{{toast.actionName}}</button></p>
|
<p class="mb-0 mt-2"><button class="btn btn-sm btn-outline-secondary" (click)="close.emit(toast); toast.action()">{{toast.actionName}}</button></p>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn-close ms-auto flex-shrink-0" data-bs-dismiss="toast" aria-label="Close" (click)="close.emit(toast);"></button>
|
<button type="button" class="btn-close ms-auto flex-shrink-0" data-bs-dismiss="toast" aria-label="Close" (click)="closed.emit(toast);"></button>
|
||||||
</div>
|
</div>
|
||||||
</ngb-toast>
|
</ngb-toast>
|
||||||
|
@ -27,7 +27,7 @@ export class ToastComponent {
|
|||||||
|
|
||||||
@Output() hidden: EventEmitter<Toast> = new EventEmitter<Toast>()
|
@Output() hidden: EventEmitter<Toast> = new EventEmitter<Toast>()
|
||||||
|
|
||||||
@Output() close: EventEmitter<Toast> = new EventEmitter<Toast>()
|
@Output() closed: EventEmitter<Toast> = new EventEmitter<Toast>()
|
||||||
|
|
||||||
public copied: boolean = false
|
public copied: boolean = false
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
@for (toast of toasts; track toast.id) {
|
@for (toast of toasts; track toast.id) {
|
||||||
<pngx-toast [toast]="toast" [autohide]="true" (close)="closeToast()"></pngx-toast>
|
<pngx-toast [toast]="toast" [autohide]="true" (closed)="closeToast()"></pngx-toast>
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ from documents.models import CustomField
|
|||||||
from documents.models import CustomFieldInstance
|
from documents.models import CustomFieldInstance
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import FileInfo
|
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
from documents.models import Tag
|
from documents.models import Tag
|
||||||
from documents.models import WorkflowTrigger
|
from documents.models import WorkflowTrigger
|
||||||
@ -705,8 +704,6 @@ class ConsumerPlugin(
|
|||||||
) -> Document:
|
) -> Document:
|
||||||
# If someone gave us the original filename, use it instead of doc.
|
# If someone gave us the original filename, use it instead of doc.
|
||||||
|
|
||||||
file_info = FileInfo.from_filename(self.filename)
|
|
||||||
|
|
||||||
self.log.debug("Saving record to database")
|
self.log.debug("Saving record to database")
|
||||||
|
|
||||||
if self.metadata.created is not None:
|
if self.metadata.created is not None:
|
||||||
@ -714,9 +711,6 @@ class ConsumerPlugin(
|
|||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Creation date from post_documents parameter: {create_date}",
|
f"Creation date from post_documents parameter: {create_date}",
|
||||||
)
|
)
|
||||||
elif file_info.created is not None:
|
|
||||||
create_date = file_info.created
|
|
||||||
self.log.debug(f"Creation date from FileInfo: {create_date}")
|
|
||||||
elif date is not None:
|
elif date is not None:
|
||||||
create_date = date
|
create_date = date
|
||||||
self.log.debug(f"Creation date from parse_date: {create_date}")
|
self.log.debug(f"Creation date from parse_date: {create_date}")
|
||||||
@ -729,7 +723,11 @@ class ConsumerPlugin(
|
|||||||
|
|
||||||
storage_type = Document.STORAGE_TYPE_UNENCRYPTED
|
storage_type = Document.STORAGE_TYPE_UNENCRYPTED
|
||||||
|
|
||||||
title = file_info.title
|
if self.metadata.filename:
|
||||||
|
title = Path(self.metadata.filename).stem
|
||||||
|
else:
|
||||||
|
title = self.input_doc.original_file.stem
|
||||||
|
|
||||||
if self.metadata.title is not None:
|
if self.metadata.title is not None:
|
||||||
try:
|
try:
|
||||||
title = self._parse_title_placeholders(self.metadata.title)
|
title = self._parse_title_placeholders(self.metadata.title)
|
||||||
|
@ -36,7 +36,6 @@ from documents.models import CustomField
|
|||||||
from documents.models import CustomFieldInstance
|
from documents.models import CustomFieldInstance
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import Log
|
|
||||||
from documents.models import PaperlessTask
|
from documents.models import PaperlessTask
|
||||||
from documents.models import ShareLink
|
from documents.models import ShareLink
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
@ -761,12 +760,6 @@ class DocumentFilterSet(FilterSet):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LogFilterSet(FilterSet):
|
|
||||||
class Meta:
|
|
||||||
model = Log
|
|
||||||
fields = {"level": INT_KWARGS, "created": DATE_KWARGS, "group": ID_KWARGS}
|
|
||||||
|
|
||||||
|
|
||||||
class ShareLinkFilterSet(FilterSet):
|
class ShareLinkFilterSet(FilterSet):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ShareLink
|
model = ShareLink
|
||||||
|
15
src/documents/migrations/1064_delete_log.py
Normal file
15
src/documents/migrations/1064_delete_log.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-28 15:19
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("documents", "1063_paperlesstask_type_alter_paperlesstask_task_name_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name="Log",
|
||||||
|
),
|
||||||
|
]
|
@ -6,7 +6,7 @@ from django.db import models
|
|||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("documents", "1063_paperlesstask_type_alter_paperlesstask_task_name_and_more"),
|
("documents", "1064_delete_log"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
@ -18,7 +18,7 @@ class Migration(migrations.Migration):
|
|||||||
help_text="Optional values to assign to the custom fields.",
|
help_text="Optional values to assign to the custom fields.",
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name="custom field values",
|
verbose_name="custom field values",
|
||||||
default={},
|
default=dict,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
@ -1,12 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
from collections import OrderedDict
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
import dateutil.parser
|
|
||||||
import pathvalidate
|
import pathvalidate
|
||||||
from celery import states
|
from celery import states
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -379,36 +374,6 @@ class Document(SoftDeleteModel, ModelWithOwner):
|
|||||||
return timezone.localdate(self.created)
|
return timezone.localdate(self.created)
|
||||||
|
|
||||||
|
|
||||||
class Log(models.Model):
|
|
||||||
LEVELS = (
|
|
||||||
(logging.DEBUG, _("debug")),
|
|
||||||
(logging.INFO, _("information")),
|
|
||||||
(logging.WARNING, _("warning")),
|
|
||||||
(logging.ERROR, _("error")),
|
|
||||||
(logging.CRITICAL, _("critical")),
|
|
||||||
)
|
|
||||||
|
|
||||||
group = models.UUIDField(_("group"), blank=True, null=True)
|
|
||||||
|
|
||||||
message = models.TextField(_("message"))
|
|
||||||
|
|
||||||
level = models.PositiveIntegerField(
|
|
||||||
_("level"),
|
|
||||||
choices=LEVELS,
|
|
||||||
default=logging.INFO,
|
|
||||||
)
|
|
||||||
|
|
||||||
created = models.DateTimeField(_("created"), auto_now_add=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ("-created",)
|
|
||||||
verbose_name = _("log")
|
|
||||||
verbose_name_plural = _("logs")
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.message
|
|
||||||
|
|
||||||
|
|
||||||
class SavedView(ModelWithOwner):
|
class SavedView(ModelWithOwner):
|
||||||
class DisplayMode(models.TextChoices):
|
class DisplayMode(models.TextChoices):
|
||||||
TABLE = ("table", _("Table"))
|
TABLE = ("table", _("Table"))
|
||||||
@ -548,91 +513,6 @@ class SavedViewFilterRule(models.Model):
|
|||||||
return f"SavedViewFilterRule: {self.rule_type} : {self.value}"
|
return f"SavedViewFilterRule: {self.rule_type} : {self.value}"
|
||||||
|
|
||||||
|
|
||||||
# TODO: why is this in the models file?
|
|
||||||
# TODO: how about, what is this and where is it documented?
|
|
||||||
# It appears to parsing JSON from an environment variable to get a title and date from
|
|
||||||
# the filename, if possible, as a higher priority than either document filename or
|
|
||||||
# content parsing
|
|
||||||
class FileInfo:
|
|
||||||
REGEXES = OrderedDict(
|
|
||||||
[
|
|
||||||
(
|
|
||||||
"created-title",
|
|
||||||
re.compile(
|
|
||||||
r"^(?P<created>\d{8}(\d{6})?Z) - (?P<title>.*)$",
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("title", re.compile(r"(?P<title>.*)$", flags=re.IGNORECASE)),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
created=None,
|
|
||||||
correspondent=None,
|
|
||||||
title=None,
|
|
||||||
tags=(),
|
|
||||||
extension=None,
|
|
||||||
):
|
|
||||||
self.created = created
|
|
||||||
self.title = title
|
|
||||||
self.extension = extension
|
|
||||||
self.correspondent = correspondent
|
|
||||||
self.tags = tags
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_created(cls, created):
|
|
||||||
try:
|
|
||||||
return dateutil.parser.parse(f"{created[:-1]:0<14}Z")
|
|
||||||
except ValueError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_title(cls, title):
|
|
||||||
return title
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _mangle_property(cls, properties, name):
|
|
||||||
if name in properties:
|
|
||||||
properties[name] = getattr(cls, f"_get_{name}")(properties[name])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_filename(cls, filename) -> "FileInfo":
|
|
||||||
# Mutate filename in-place before parsing its components
|
|
||||||
# by applying at most one of the configured transformations.
|
|
||||||
for pattern, repl in settings.FILENAME_PARSE_TRANSFORMS:
|
|
||||||
(filename, count) = pattern.subn(repl, filename)
|
|
||||||
if count:
|
|
||||||
break
|
|
||||||
|
|
||||||
# do this after the transforms so that the transforms can do whatever
|
|
||||||
# with the file extension.
|
|
||||||
filename_no_ext = os.path.splitext(filename)[0]
|
|
||||||
|
|
||||||
if filename_no_ext == filename and filename.startswith("."):
|
|
||||||
# This is a very special case where there is no text before the
|
|
||||||
# file type.
|
|
||||||
# TODO: this should be handled better. The ext is not removed
|
|
||||||
# because usually, files like '.pdf' are just hidden files
|
|
||||||
# with the name pdf, but in our case, its more likely that
|
|
||||||
# there's just no name to begin with.
|
|
||||||
filename = ""
|
|
||||||
# This isn't too bad either, since we'll just not match anything
|
|
||||||
# and return an empty title. TODO: actually, this is kinda bad.
|
|
||||||
else:
|
|
||||||
filename = filename_no_ext
|
|
||||||
|
|
||||||
# Parse filename components.
|
|
||||||
for regex in cls.REGEXES.values():
|
|
||||||
m = regex.match(filename)
|
|
||||||
if m:
|
|
||||||
properties = m.groupdict()
|
|
||||||
cls._mangle_property(properties, "created")
|
|
||||||
cls._mangle_property(properties, "title")
|
|
||||||
return cls(**properties)
|
|
||||||
|
|
||||||
|
|
||||||
# Extending User Model Using a One-To-One Link
|
# Extending User Model Using a One-To-One Link
|
||||||
class UiSettings(models.Model):
|
class UiSettings(models.Model):
|
||||||
user = models.OneToOneField(
|
user = models.OneToOneField(
|
||||||
@ -1398,7 +1278,7 @@ class WorkflowAction(models.Model):
|
|||||||
help_text=_(
|
help_text=_(
|
||||||
"Optional values to assign to the custom fields.",
|
"Optional values to assign to the custom fields.",
|
||||||
),
|
),
|
||||||
default={},
|
default=dict,
|
||||||
)
|
)
|
||||||
|
|
||||||
remove_tags = models.ManyToManyField(
|
remove_tags = models.ManyToManyField(
|
||||||
|
@ -632,7 +632,7 @@ def send_webhook(
|
|||||||
else:
|
else:
|
||||||
httpx.post(
|
httpx.post(
|
||||||
url,
|
url,
|
||||||
data=data,
|
content=data,
|
||||||
files=files,
|
files=files,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
).raise_for_status()
|
).raise_for_status()
|
||||||
@ -1019,7 +1019,8 @@ def run_workflows(
|
|||||||
added = timezone.localtime(timezone.now())
|
added = timezone.localtime(timezone.now())
|
||||||
created = timezone.localtime(overrides.created)
|
created = timezone.localtime(overrides.created)
|
||||||
|
|
||||||
subject = parse_w_workflow_placeholders(
|
subject = (
|
||||||
|
parse_w_workflow_placeholders(
|
||||||
action.email.subject,
|
action.email.subject,
|
||||||
correspondent,
|
correspondent,
|
||||||
document_type,
|
document_type,
|
||||||
@ -1031,7 +1032,11 @@ def run_workflows(
|
|||||||
title,
|
title,
|
||||||
doc_url,
|
doc_url,
|
||||||
)
|
)
|
||||||
body = parse_w_workflow_placeholders(
|
if action.email.subject
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
body = (
|
||||||
|
parse_w_workflow_placeholders(
|
||||||
action.email.body,
|
action.email.body,
|
||||||
correspondent,
|
correspondent,
|
||||||
document_type,
|
document_type,
|
||||||
@ -1043,6 +1048,9 @@ def run_workflows(
|
|||||||
title,
|
title,
|
||||||
doc_url,
|
doc_url,
|
||||||
)
|
)
|
||||||
|
if action.email.body
|
||||||
|
else ""
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
n_messages = send_email(
|
n_messages = send_email(
|
||||||
subject=subject,
|
subject=subject,
|
||||||
@ -1122,7 +1130,7 @@ def run_workflows(
|
|||||||
f"Error occurred parsing webhook params: {e}",
|
f"Error occurred parsing webhook params: {e}",
|
||||||
extra={"group": logging_group},
|
extra={"group": logging_group},
|
||||||
)
|
)
|
||||||
else:
|
elif action.webhook.body:
|
||||||
data = parse_w_workflow_placeholders(
|
data = parse_w_workflow_placeholders(
|
||||||
action.webhook.body,
|
action.webhook.body,
|
||||||
correspondent,
|
correspondent,
|
||||||
|
@ -8,6 +8,7 @@ from pathlib import PurePath
|
|||||||
import pathvalidate
|
import pathvalidate
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.dateparse import parse_date
|
from django.utils.dateparse import parse_date
|
||||||
|
from django.utils.text import slugify as django_slugify
|
||||||
from jinja2 import StrictUndefined
|
from jinja2 import StrictUndefined
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
from jinja2 import TemplateSyntaxError
|
from jinja2 import TemplateSyntaxError
|
||||||
@ -100,6 +101,8 @@ def format_datetime(value: str | datetime, format: str) -> str:
|
|||||||
|
|
||||||
_template_environment.filters["datetime"] = format_datetime
|
_template_environment.filters["datetime"] = format_datetime
|
||||||
|
|
||||||
|
_template_environment.filters["slugify"] = django_slugify
|
||||||
|
|
||||||
|
|
||||||
def create_dummy_document():
|
def create_dummy_document():
|
||||||
"""
|
"""
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
import tempfile
|
import tempfile
|
||||||
import zoneinfo
|
import zoneinfo
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest import TestCase as UnittestTestCase
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
@ -26,7 +24,6 @@ from documents.models import Correspondent
|
|||||||
from documents.models import CustomField
|
from documents.models import CustomField
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import FileInfo
|
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
from documents.models import Tag
|
from documents.models import Tag
|
||||||
from documents.parsers import DocumentParser
|
from documents.parsers import DocumentParser
|
||||||
@ -40,143 +37,6 @@ from paperless_mail.models import MailRule
|
|||||||
from paperless_mail.parsers import MailDocumentParser
|
from paperless_mail.parsers import MailDocumentParser
|
||||||
|
|
||||||
|
|
||||||
class TestAttributes(UnittestTestCase):
|
|
||||||
TAGS = ("tag1", "tag2", "tag3")
|
|
||||||
|
|
||||||
def _test_guess_attributes_from_name(self, filename, sender, title, tags):
|
|
||||||
file_info = FileInfo.from_filename(filename)
|
|
||||||
|
|
||||||
if sender:
|
|
||||||
self.assertEqual(file_info.correspondent.name, sender, filename)
|
|
||||||
else:
|
|
||||||
self.assertIsNone(file_info.correspondent, filename)
|
|
||||||
|
|
||||||
self.assertEqual(file_info.title, title, filename)
|
|
||||||
|
|
||||||
self.assertEqual(tuple(t.name for t in file_info.tags), tags, filename)
|
|
||||||
|
|
||||||
def test_guess_attributes_from_name_when_title_starts_with_dash(self):
|
|
||||||
self._test_guess_attributes_from_name(
|
|
||||||
"- weird but should not break.pdf",
|
|
||||||
None,
|
|
||||||
"- weird but should not break",
|
|
||||||
(),
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_guess_attributes_from_name_when_title_ends_with_dash(self):
|
|
||||||
self._test_guess_attributes_from_name(
|
|
||||||
"weird but should not break -.pdf",
|
|
||||||
None,
|
|
||||||
"weird but should not break -",
|
|
||||||
(),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestFieldPermutations(TestCase):
|
|
||||||
valid_dates = (
|
|
||||||
"20150102030405Z",
|
|
||||||
"20150102Z",
|
|
||||||
)
|
|
||||||
valid_correspondents = ["timmy", "Dr. McWheelie", "Dash Gor-don", "o Θεpμaoτής", ""]
|
|
||||||
valid_titles = ["title", "Title w Spaces", "Title a-dash", "Tίτλoς", ""]
|
|
||||||
valid_tags = ["tag", "tig,tag", "tag1,tag2,tag-3"]
|
|
||||||
|
|
||||||
def _test_guessed_attributes(
|
|
||||||
self,
|
|
||||||
filename,
|
|
||||||
created=None,
|
|
||||||
correspondent=None,
|
|
||||||
title=None,
|
|
||||||
tags=None,
|
|
||||||
):
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
|
|
||||||
# Created
|
|
||||||
if created is None:
|
|
||||||
self.assertIsNone(info.created, filename)
|
|
||||||
else:
|
|
||||||
self.assertEqual(info.created.year, int(created[:4]), filename)
|
|
||||||
self.assertEqual(info.created.month, int(created[4:6]), filename)
|
|
||||||
self.assertEqual(info.created.day, int(created[6:8]), filename)
|
|
||||||
|
|
||||||
# Correspondent
|
|
||||||
if correspondent:
|
|
||||||
self.assertEqual(info.correspondent.name, correspondent, filename)
|
|
||||||
else:
|
|
||||||
self.assertEqual(info.correspondent, None, filename)
|
|
||||||
|
|
||||||
# Title
|
|
||||||
self.assertEqual(info.title, title, filename)
|
|
||||||
|
|
||||||
# Tags
|
|
||||||
if tags is None:
|
|
||||||
self.assertEqual(info.tags, (), filename)
|
|
||||||
else:
|
|
||||||
self.assertEqual([t.name for t in info.tags], tags.split(","), filename)
|
|
||||||
|
|
||||||
def test_just_title(self):
|
|
||||||
template = "{title}.pdf"
|
|
||||||
for title in self.valid_titles:
|
|
||||||
spec = dict(title=title)
|
|
||||||
filename = template.format(**spec)
|
|
||||||
self._test_guessed_attributes(filename, **spec)
|
|
||||||
|
|
||||||
def test_created_and_title(self):
|
|
||||||
template = "{created} - {title}.pdf"
|
|
||||||
|
|
||||||
for created in self.valid_dates:
|
|
||||||
for title in self.valid_titles:
|
|
||||||
spec = {"created": created, "title": title}
|
|
||||||
self._test_guessed_attributes(template.format(**spec), **spec)
|
|
||||||
|
|
||||||
def test_invalid_date_format(self):
|
|
||||||
info = FileInfo.from_filename("06112017Z - title.pdf")
|
|
||||||
self.assertEqual(info.title, "title")
|
|
||||||
self.assertIsNone(info.created)
|
|
||||||
|
|
||||||
def test_filename_parse_transforms(self):
|
|
||||||
filename = "tag1,tag2_20190908_180610_0001.pdf"
|
|
||||||
all_patt = re.compile("^.*$")
|
|
||||||
none_patt = re.compile("$a")
|
|
||||||
re.compile("^([a-z0-9,]+)_(\\d{8})_(\\d{6})_([0-9]+)\\.")
|
|
||||||
|
|
||||||
# No transformations configured (= default)
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
self.assertEqual(info.title, "tag1,tag2_20190908_180610_0001")
|
|
||||||
self.assertEqual(info.tags, ())
|
|
||||||
self.assertIsNone(info.created)
|
|
||||||
|
|
||||||
# Pattern doesn't match (filename unaltered)
|
|
||||||
with self.settings(FILENAME_PARSE_TRANSFORMS=[(none_patt, "none.gif")]):
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
self.assertEqual(info.title, "tag1,tag2_20190908_180610_0001")
|
|
||||||
|
|
||||||
# Simple transformation (match all)
|
|
||||||
with self.settings(FILENAME_PARSE_TRANSFORMS=[(all_patt, "all.gif")]):
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
self.assertEqual(info.title, "all")
|
|
||||||
|
|
||||||
# Multiple transformations configured (first pattern matches)
|
|
||||||
with self.settings(
|
|
||||||
FILENAME_PARSE_TRANSFORMS=[
|
|
||||||
(all_patt, "all.gif"),
|
|
||||||
(all_patt, "anotherall.gif"),
|
|
||||||
],
|
|
||||||
):
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
self.assertEqual(info.title, "all")
|
|
||||||
|
|
||||||
# Multiple transformations configured (second pattern matches)
|
|
||||||
with self.settings(
|
|
||||||
FILENAME_PARSE_TRANSFORMS=[
|
|
||||||
(none_patt, "none.gif"),
|
|
||||||
(all_patt, "anotherall.gif"),
|
|
||||||
],
|
|
||||||
):
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
self.assertEqual(info.title, "anotherall")
|
|
||||||
|
|
||||||
|
|
||||||
class _BaseTestParser(DocumentParser):
|
class _BaseTestParser(DocumentParser):
|
||||||
def get_settings(self):
|
def get_settings(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1517,3 +1517,63 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
generate_filename(doc_a),
|
generate_filename(doc_a),
|
||||||
"2024-10-01/Some Title.pdf",
|
"2024-10-01/Some Title.pdf",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_slugify_filter(self):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- Filename format with slugify filter
|
||||||
|
WHEN:
|
||||||
|
- Filepath for a document with this format is called
|
||||||
|
THEN:
|
||||||
|
- The slugify filter properly converts strings to URL-friendly slugs
|
||||||
|
"""
|
||||||
|
doc = Document.objects.create(
|
||||||
|
title="Some Title! With @ Special # Characters",
|
||||||
|
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
||||||
|
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
||||||
|
mime_type="application/pdf",
|
||||||
|
pk=2,
|
||||||
|
checksum="2",
|
||||||
|
archive_serial_number=25,
|
||||||
|
)
|
||||||
|
|
||||||
|
with override_settings(
|
||||||
|
FILENAME_FORMAT="{{ title | slugify }}",
|
||||||
|
):
|
||||||
|
self.assertEqual(
|
||||||
|
generate_filename(doc),
|
||||||
|
"some-title-with-special-characters.pdf",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test with correspondent name containing spaces and special chars
|
||||||
|
doc.correspondent = Correspondent.objects.create(
|
||||||
|
name="John's @ Office / Workplace",
|
||||||
|
)
|
||||||
|
doc.save()
|
||||||
|
|
||||||
|
with override_settings(
|
||||||
|
FILENAME_FORMAT="{{ correspondent | slugify }}/{{ title | slugify }}",
|
||||||
|
):
|
||||||
|
self.assertEqual(
|
||||||
|
generate_filename(doc),
|
||||||
|
"johns-office-workplace/some-title-with-special-characters.pdf",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test with custom fields
|
||||||
|
cf = CustomField.objects.create(
|
||||||
|
name="Location",
|
||||||
|
data_type=CustomField.FieldDataType.STRING,
|
||||||
|
)
|
||||||
|
CustomFieldInstance.objects.create(
|
||||||
|
document=doc,
|
||||||
|
field=cf,
|
||||||
|
value_text="Brussels @ Belgium!",
|
||||||
|
)
|
||||||
|
|
||||||
|
with override_settings(
|
||||||
|
FILENAME_FORMAT="{{ custom_fields | get_cf_value('Location') | slugify }}/{{ title | slugify }}",
|
||||||
|
):
|
||||||
|
self.assertEqual(
|
||||||
|
generate_filename(doc),
|
||||||
|
"brussels-belgium/some-title-with-special-characters.pdf",
|
||||||
|
)
|
||||||
|
@ -2615,7 +2615,7 @@ class TestWorkflows(
|
|||||||
|
|
||||||
mock_post.assert_called_once_with(
|
mock_post.assert_called_once_with(
|
||||||
"http://paperless-ngx.com",
|
"http://paperless-ngx.com",
|
||||||
data="Test message",
|
content="Test message",
|
||||||
headers={},
|
headers={},
|
||||||
files=None,
|
files=None,
|
||||||
)
|
)
|
||||||
|
@ -3,7 +3,6 @@ import json
|
|||||||
import math
|
import math
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import tempfile
|
import tempfile
|
||||||
from os import PathLike
|
from os import PathLike
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -1089,11 +1088,6 @@ FILENAME_DATE_ORDER = os.getenv("PAPERLESS_FILENAME_DATE_ORDER")
|
|||||||
# fewer dates shown.
|
# fewer dates shown.
|
||||||
NUMBER_OF_SUGGESTED_DATES = __get_int("PAPERLESS_NUMBER_OF_SUGGESTED_DATES", 3)
|
NUMBER_OF_SUGGESTED_DATES = __get_int("PAPERLESS_NUMBER_OF_SUGGESTED_DATES", 3)
|
||||||
|
|
||||||
# Transformations applied before filename parsing
|
|
||||||
FILENAME_PARSE_TRANSFORMS = []
|
|
||||||
for t in json.loads(os.getenv("PAPERLESS_FILENAME_PARSE_TRANSFORMS", "[]")):
|
|
||||||
FILENAME_PARSE_TRANSFORMS.append((re.compile(t["pattern"]), t["repl"]))
|
|
||||||
|
|
||||||
# Specify the filename format for out files
|
# Specify the filename format for out files
|
||||||
FILENAME_FORMAT = os.getenv("PAPERLESS_FILENAME_FORMAT")
|
FILENAME_FORMAT = os.getenv("PAPERLESS_FILENAME_FORMAT")
|
||||||
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
[tool:pytest]
|
|
||||||
DJANGO_SETTINGS_MODULE = paperless.settings
|
|
||||||
addopts = --pythonwarnings=all --cov --cov-report=html --cov-report=xml --numprocesses auto --maxprocesses=16 --quiet --durations=50
|
|
||||||
env =
|
|
||||||
PAPERLESS_DISABLE_DBHANDLER=true
|
|
||||||
PAPERLESS_CACHE_BACKEND=django.core.cache.backends.locmem.LocMemCache
|
|
||||||
norecursedirs = locale/*
|
|
||||||
|
|
||||||
[coverage:run]
|
|
||||||
source =
|
|
||||||
./
|
|
||||||
omit =
|
|
||||||
*/tests/*
|
|
||||||
manage.py
|
|
||||||
paperless/workers.py
|
|
||||||
paperless/wsgi.py
|
|
||||||
paperless/auth.py
|
|
||||||
|
|
||||||
[coverage:report]
|
|
||||||
exclude_also =
|
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
|
||||||
if AUDIT_LOG_ENABLED:
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
|
|
||||||
[mypy]
|
|
||||||
plugins = mypy_django_plugin.main, mypy_drf_plugin.main, numpy.typing.mypy_plugin
|
|
||||||
check_untyped_defs = true
|
|
||||||
disallow_any_generics = true
|
|
||||||
disallow_incomplete_defs = true
|
|
||||||
disallow_untyped_defs = true
|
|
||||||
warn_redundant_casts = true
|
|
||||||
warn_unused_ignores = true
|
|
||||||
|
|
||||||
[mypy.plugins.django-stubs]
|
|
||||||
django_settings_module = "paperless.settings"
|
|
@ -6,7 +6,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
Granian(
|
Granian(
|
||||||
"paperless.asgi:application",
|
"paperless.asgi:application",
|
||||||
interface=Interfaces.ASGI,
|
interface=Interfaces.ASGINL,
|
||||||
address=os.getenv("GRANIAN_HOST") or os.getenv("PAPERLESS_BIND_ADDR", "::"),
|
address=os.getenv("GRANIAN_HOST") or os.getenv("PAPERLESS_BIND_ADDR", "::"),
|
||||||
port=int(os.getenv("GRANIAN_PORT") or os.getenv("PAPERLESS_PORT") or 8000),
|
port=int(os.getenv("GRANIAN_PORT") or os.getenv("PAPERLESS_PORT") or 8000),
|
||||||
workers=int(
|
workers=int(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user