mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-05-01 11:19:32 -05:00
Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1b3a91f63f | ||
![]() |
924a13f724 | ||
![]() |
7236b8b682 | ||
![]() |
a3b85c64ca | ||
![]() |
0cefdc8475 | ||
![]() |
915584551c | ||
![]() |
855c9aa28d | ||
![]() |
e277a8e1ea | ||
![]() |
6d8c507169 | ||
![]() |
00050f7c7b | ||
![]() |
5a278381e3 | ||
![]() |
af95963c5f | ||
![]() |
c724436fc4 | ||
![]() |
e288824963 | ||
![]() |
312bb743b9 | ||
![]() |
7436a97684 | ||
![]() |
cbaceb95af | ||
![]() |
2abf38f98e | ||
![]() |
b40e1f7d01 | ||
![]() |
15d4ac8ba2 | ||
![]() |
fcb7349e8e | ||
![]() |
648cfd9d05 | ||
![]() |
c3df7d3439 | ||
![]() |
a5cd545a1b | ||
![]() |
96227f785a | ||
![]() |
7f98c4b794 | ||
![]() |
d914a82dea |
@ -21,14 +21,12 @@
|
|||||||
# This file is intended only to be used through VSCOde devcontainers. See README.md
|
# This file is intended only to be used through VSCOde devcontainers. See README.md
|
||||||
# in the folder .devcontainer.
|
# in the folder .devcontainer.
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:7
|
image: docker.io/library/redis:7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ./redisdata:/data
|
- ./redisdata:/data
|
||||||
|
|
||||||
# No ports need to be exposed; the VSCode DevContainer plugin manages them.
|
# No ports need to be exposed; the VSCode DevContainer plugin manages them.
|
||||||
paperless-development:
|
paperless-development:
|
||||||
image: paperless-ngx
|
image: paperless-ngx
|
||||||
@ -60,25 +58,20 @@ services:
|
|||||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||||
PAPERLESS_STATICDIR: ./src/documents/static
|
PAPERLESS_STATICDIR: ./src/documents/static
|
||||||
PAPERLESS_DEBUG: true
|
PAPERLESS_DEBUG: true
|
||||||
|
|
||||||
# Overrides default command so things don't shut down after the process ends.
|
# Overrides default command so things don't shut down after the process ends.
|
||||||
command: /bin/sh -c "chown -R paperless:paperless /usr/src/paperless/paperless-ngx/src/documents/static/frontend && chown -R paperless:paperless /usr/src/paperless/paperless-ngx/.ruff_cache && while sleep 1000; do :; done"
|
command: /bin/sh -c "chown -R paperless:paperless /usr/src/paperless/paperless-ngx/src/documents/static/frontend && chown -R paperless:paperless /usr/src/paperless/paperless-ngx/.ruff_cache && while sleep 1000; do :; done"
|
||||||
|
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.17
|
image: docker.io/gotenberg/gotenberg:8.17
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# The Gotenberg Chromium route is used to convert .eml files. We do not
|
# The Gotenberg Chromium route is used to convert .eml files. We do not
|
||||||
# want to allow external content like tracking pixels or even JavaScript.
|
# want to allow external content like tracking pixels or even JavaScript.
|
||||||
command:
|
command:
|
||||||
- "gotenberg"
|
- "gotenberg"
|
||||||
- "--chromium-disable-javascript=true"
|
- "--chromium-disable-javascript=true"
|
||||||
- "--chromium-allow-list=file:///tmp/.*"
|
- "--chromium-allow-list=file:///tmp/.*"
|
||||||
|
|
||||||
tika:
|
tika:
|
||||||
image: docker.io/apache/tika:latest
|
image: docker.io/apache/tika:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
@ -5,7 +5,6 @@ version: 2
|
|||||||
# Required for uv support for now
|
# Required for uv support for now
|
||||||
enable-beta-ecosystems: true
|
enable-beta-ecosystems: true
|
||||||
updates:
|
updates:
|
||||||
|
|
||||||
# Enable version updates for pnpm
|
# Enable version updates for pnpm
|
||||||
- package-ecosystem: "npm"
|
- package-ecosystem: "npm"
|
||||||
target-branch: "dev"
|
target-branch: "dev"
|
||||||
@ -35,7 +34,6 @@ updates:
|
|||||||
patterns:
|
patterns:
|
||||||
- "@typescript-eslint*"
|
- "@typescript-eslint*"
|
||||||
- "eslint"
|
- "eslint"
|
||||||
|
|
||||||
# Enable version updates for Python
|
# Enable version updates for Python
|
||||||
- package-ecosystem: "uv"
|
- package-ecosystem: "uv"
|
||||||
target-branch: "dev"
|
target-branch: "dev"
|
||||||
@ -59,6 +57,7 @@ updates:
|
|||||||
django:
|
django:
|
||||||
patterns:
|
patterns:
|
||||||
- "*django*"
|
- "*django*"
|
||||||
|
- "drf-*"
|
||||||
major-versions:
|
major-versions:
|
||||||
update-types:
|
update-types:
|
||||||
- "major"
|
- "major"
|
||||||
@ -70,7 +69,6 @@ updates:
|
|||||||
patterns:
|
patterns:
|
||||||
- psycopg*
|
- psycopg*
|
||||||
- zxing-cpp
|
- zxing-cpp
|
||||||
|
|
||||||
# Enable updates for GitHub Actions
|
# Enable updates for GitHub Actions
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
target-branch: "dev"
|
target-branch: "dev"
|
||||||
@ -90,7 +88,6 @@ updates:
|
|||||||
- "major"
|
- "major"
|
||||||
- "minor"
|
- "minor"
|
||||||
- "patch"
|
- "patch"
|
||||||
|
|
||||||
# Update Dockerfile in root directory
|
# Update Dockerfile in root directory
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
@ -100,12 +97,10 @@ updates:
|
|||||||
reviewers:
|
reviewers:
|
||||||
- "paperless-ngx/ci-cd"
|
- "paperless-ngx/ci-cd"
|
||||||
labels:
|
labels:
|
||||||
- "ci-cd"
|
|
||||||
- "dependencies"
|
- "dependencies"
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "docker"
|
prefix: "docker"
|
||||||
include: "scope"
|
include: "scope"
|
||||||
|
|
||||||
# Update Docker Compose files in docker/compose directory
|
# Update Docker Compose files in docker/compose directory
|
||||||
- package-ecosystem: "docker-compose"
|
- package-ecosystem: "docker-compose"
|
||||||
directory: "/docker/compose/"
|
directory: "/docker/compose/"
|
||||||
@ -115,7 +110,6 @@ updates:
|
|||||||
reviewers:
|
reviewers:
|
||||||
- "paperless-ngx/ci-cd"
|
- "paperless-ngx/ci-cd"
|
||||||
labels:
|
labels:
|
||||||
- "ci-cd"
|
|
||||||
- "dependencies"
|
- "dependencies"
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "docker-compose"
|
prefix: "docker-compose"
|
||||||
|
19
.github/labeler.yml
vendored
Normal file
19
.github/labeler.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
backend:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'src/**'
|
||||||
|
- 'pyproject.toml'
|
||||||
|
- 'uv.lock'
|
||||||
|
- 'requirements.txt'
|
||||||
|
frontend:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'src-ui/**'
|
||||||
|
documentation:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'docs/**'
|
||||||
|
ci-cd:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- '.github/**'
|
230
.github/workflows/ci.yml
vendored
230
.github/workflows/ci.yml
vendored
@ -1,5 +1,4 @@
|
|||||||
name: ci
|
name: ci
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
@ -12,72 +11,57 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'translations**'
|
- 'translations**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEFAULT_UV_VERSION: "0.6.x"
|
DEFAULT_UV_VERSION: "0.6.x"
|
||||||
# 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"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-commit:
|
pre-commit:
|
||||||
# We want to run on external PRs, but not on our own internal PRs as they'll be run
|
# We want to run on external PRs, but not on our own internal PRs as they'll be run
|
||||||
# by the push to the branch. Without this if check, checks are duplicated since
|
# by the push to the branch. Without this if check, checks are duplicated since
|
||||||
# internal PRs match both the push and pull_request events.
|
# internal PRs match both the push and pull_request events.
|
||||||
if:
|
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
|
||||||
github.event_name == 'push' || github.event.pull_request.head.repo.full_name !=
|
|
||||||
github.repository
|
|
||||||
|
|
||||||
name: Linting Checks
|
name: Linting Checks
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Checkout repository
|
||||||
name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
-
|
- name: Install python
|
||||||
name: Install 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 }}
|
||||||
-
|
- name: Check files
|
||||||
name: Check files
|
|
||||||
uses: pre-commit/action@v3.0.1
|
uses: pre-commit/action@v3.0.1
|
||||||
|
|
||||||
documentation:
|
documentation:
|
||||||
name: "Build & Deploy Documentation"
|
name: "Build & Deploy Documentation"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- pre-commit
|
- pre-commit
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Checkout
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
-
|
- name: Set up Python
|
||||||
name: Set up Python
|
|
||||||
id: setup-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 }}
|
||||||
-
|
- name: Install uv
|
||||||
name: Install uv
|
|
||||||
uses: astral-sh/setup-uv@v5
|
uses: astral-sh/setup-uv@v5
|
||||||
with:
|
with:
|
||||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
-
|
- name: Install Python dependencies
|
||||||
name: Install Python dependencies
|
|
||||||
run: |
|
run: |
|
||||||
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
||||||
-
|
- name: Make documentation
|
||||||
name: Make documentation
|
|
||||||
run: |
|
run: |
|
||||||
uv run \
|
uv run \
|
||||||
--python ${{ steps.setup-python.outputs.python-version }} \
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
--dev \
|
--dev \
|
||||||
--frozen \
|
--frozen \
|
||||||
mkdocs build --config-file ./mkdocs.yml
|
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'
|
||||||
run: |
|
run: |
|
||||||
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
|
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
|
||||||
@ -88,14 +72,12 @@ jobs:
|
|||||||
--dev \
|
--dev \
|
||||||
--frozen \
|
--frozen \
|
||||||
mkdocs gh-deploy --force --no-history
|
mkdocs gh-deploy --force --no-history
|
||||||
-
|
- name: Upload artifact
|
||||||
name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: documentation
|
name: documentation
|
||||||
path: site/
|
path: site/
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
tests-backend:
|
tests-backend:
|
||||||
name: "Backend Tests (Python ${{ matrix.python-version }})"
|
name: "Backend Tests (Python ${{ matrix.python-version }})"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -106,49 +88,40 @@ jobs:
|
|||||||
python-version: ['3.10', '3.11', '3.12']
|
python-version: ['3.10', '3.11', '3.12']
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Checkout
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
-
|
- name: Start containers
|
||||||
name: Start containers
|
|
||||||
run: |
|
run: |
|
||||||
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml pull --quiet
|
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml pull --quiet
|
||||||
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml up --detach
|
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml up --detach
|
||||||
-
|
- name: Set up Python
|
||||||
name: Set up Python
|
|
||||||
id: setup-python
|
id: setup-python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "${{ matrix.python-version }}"
|
python-version: "${{ matrix.python-version }}"
|
||||||
-
|
- name: Install uv
|
||||||
name: Install uv
|
|
||||||
uses: astral-sh/setup-uv@v5
|
uses: astral-sh/setup-uv@v5
|
||||||
with:
|
with:
|
||||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ steps.setup-python.outputs.python-version }}
|
python-version: ${{ steps.setup-python.outputs.python-version }}
|
||||||
-
|
- name: Install system dependencies
|
||||||
name: Install system dependencies
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -qq
|
sudo apt-get update -qq
|
||||||
sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript libzbar0 poppler-utils
|
sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript libzbar0 poppler-utils
|
||||||
-
|
- name: Configure ImageMagick
|
||||||
name: Configure ImageMagick
|
|
||||||
run: |
|
run: |
|
||||||
sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml
|
sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml
|
||||||
-
|
- name: Install Python dependencies
|
||||||
name: Install Python dependencies
|
|
||||||
run: |
|
run: |
|
||||||
uv sync \
|
uv sync \
|
||||||
--python ${{ steps.setup-python.outputs.python-version }} \
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
--group testing \
|
--group testing \
|
||||||
--frozen
|
--frozen
|
||||||
-
|
- name: List installed Python dependencies
|
||||||
name: List installed Python dependencies
|
|
||||||
run: |
|
run: |
|
||||||
uv pip list
|
uv pip list
|
||||||
-
|
- name: Tests
|
||||||
name: Tests
|
|
||||||
env:
|
env:
|
||||||
PAPERLESS_CI_TEST: 1
|
PAPERLESS_CI_TEST: 1
|
||||||
# Enable paperless_mail testing against real server
|
# Enable paperless_mail testing against real server
|
||||||
@ -161,28 +134,24 @@ jobs:
|
|||||||
--dev \
|
--dev \
|
||||||
--frozen \
|
--frozen \
|
||||||
pytest
|
pytest
|
||||||
-
|
- name: Upload backend test results to Codecov
|
||||||
name: Upload backend test results to Codecov
|
|
||||||
if: always()
|
if: always()
|
||||||
uses: codecov/test-results-action@v1
|
uses: codecov/test-results-action@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
flags: backend-python-${{ matrix.python-version }}
|
flags: backend-python-${{ matrix.python-version }}
|
||||||
files: junit.xml
|
files: junit.xml
|
||||||
-
|
- name: Upload backend coverage to Codecov
|
||||||
name: Upload backend coverage to Codecov
|
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
flags: backend-python-${{ matrix.python-version }}
|
flags: backend-python-${{ matrix.python-version }}
|
||||||
files: coverage.xml
|
files: coverage.xml
|
||||||
-
|
- name: Stop containers
|
||||||
name: Stop containers
|
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs
|
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs
|
||||||
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down
|
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down
|
||||||
|
|
||||||
install-frontend-dependencies:
|
install-frontend-dependencies:
|
||||||
name: "Install Frontend Dependencies"
|
name: "Install Frontend Dependencies"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -194,8 +163,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
-
|
- name: Use Node.js 20
|
||||||
name: Use Node.js 20
|
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
@ -209,15 +177,12 @@ jobs:
|
|||||||
~/.pnpm-store
|
~/.pnpm-store
|
||||||
~/.cache
|
~/.cache
|
||||||
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
||||||
-
|
- name: Install dependencies
|
||||||
name: Install dependencies
|
|
||||||
if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
|
if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
|
||||||
run: cd src-ui && pnpm install
|
run: cd src-ui && pnpm install
|
||||||
-
|
- name: Install Playwright
|
||||||
name: Install Playwright
|
|
||||||
if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
|
if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
|
||||||
run: cd src-ui && pnpm playwright install --with-deps
|
run: cd src-ui && pnpm playwright install --with-deps
|
||||||
|
|
||||||
tests-frontend:
|
tests-frontend:
|
||||||
name: "Frontend Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})"
|
name: "Frontend Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -235,8 +200,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
-
|
- name: Use Node.js 20
|
||||||
name: Use Node.js 20
|
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
@ -252,31 +216,25 @@ jobs:
|
|||||||
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
||||||
- name: Re-link Angular cli
|
- name: Re-link Angular cli
|
||||||
run: cd src-ui && pnpm link @angular/cli
|
run: cd src-ui && pnpm link @angular/cli
|
||||||
-
|
- name: Linting checks
|
||||||
name: Linting checks
|
|
||||||
run: cd src-ui && pnpm run lint
|
run: cd src-ui && pnpm run lint
|
||||||
-
|
- name: Run Jest unit tests
|
||||||
name: Run Jest unit tests
|
|
||||||
run: cd src-ui && pnpm run test --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
run: cd src-ui && pnpm run test --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||||
-
|
- name: Run Playwright e2e tests
|
||||||
name: Run Playwright e2e tests
|
|
||||||
run: cd src-ui && pnpm exec playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
run: cd src-ui && pnpm exec playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||||
-
|
- name: Upload frontend test results to Codecov
|
||||||
name: Upload frontend test results to Codecov
|
|
||||||
uses: codecov/test-results-action@v1
|
uses: codecov/test-results-action@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
flags: frontend-node-${{ matrix.node-version }}
|
flags: frontend-node-${{ matrix.node-version }}
|
||||||
directory: src-ui/
|
directory: src-ui/
|
||||||
-
|
- name: Upload frontend coverage to Codecov
|
||||||
name: Upload frontend coverage to Codecov
|
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
flags: frontend-node-${{ matrix.node-version }}
|
flags: frontend-node-${{ matrix.node-version }}
|
||||||
directory: src-ui/coverage/
|
directory: src-ui/coverage/
|
||||||
|
|
||||||
frontend-bundle-analysis:
|
frontend-bundle-analysis:
|
||||||
name: "Frontend Bundle Analysis"
|
name: "Frontend Bundle Analysis"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -284,20 +242,17 @@ jobs:
|
|||||||
- tests-frontend
|
- tests-frontend
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
-
|
- name: Install pnpm
|
||||||
name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
-
|
- name: Use Node.js 20
|
||||||
name: Use Node.js 20
|
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
cache-dependency-path: 'src-ui/pnpm-lock.yaml'
|
cache-dependency-path: 'src-ui/pnpm-lock.yaml'
|
||||||
-
|
- name: Cache frontend dependencies
|
||||||
name: Cache frontend dependencies
|
|
||||||
id: cache-frontend-deps
|
id: cache-frontend-deps
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
@ -305,15 +260,12 @@ jobs:
|
|||||||
~/.pnpm-store
|
~/.pnpm-store
|
||||||
~/.cache
|
~/.cache
|
||||||
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }}
|
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }}
|
||||||
-
|
- name: Re-link Angular cli
|
||||||
name: Re-link Angular cli
|
|
||||||
run: cd src-ui && pnpm link @angular/cli
|
run: cd src-ui && pnpm link @angular/cli
|
||||||
-
|
- name: Build frontend and upload analysis
|
||||||
name: Build frontend and upload analysis
|
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
run: cd src-ui && pnpm run build --configuration=production
|
run: cd src-ui && pnpm run build --configuration=production
|
||||||
|
|
||||||
build-docker-image:
|
build-docker-image:
|
||||||
name: Build Docker image for ${{ github.ref_name }}
|
name: Build Docker image for ${{ github.ref_name }}
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -325,8 +277,7 @@ jobs:
|
|||||||
- tests-backend
|
- tests-backend
|
||||||
- tests-frontend
|
- tests-frontend
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Check pushing to Docker Hub
|
||||||
name: Check pushing to Docker Hub
|
|
||||||
id: push-other-places
|
id: push-other-places
|
||||||
# Only push to Dockerhub from the main repo AND the ref is either:
|
# Only push to Dockerhub from the main repo AND the ref is either:
|
||||||
# main
|
# main
|
||||||
@ -342,15 +293,13 @@ jobs:
|
|||||||
echo "Not pushing to DockerHub"
|
echo "Not pushing to DockerHub"
|
||||||
echo "enable=false" >> $GITHUB_OUTPUT
|
echo "enable=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
-
|
- name: Set ghcr repository name
|
||||||
name: Set ghcr repository name
|
|
||||||
id: set-ghcr-repository
|
id: set-ghcr-repository
|
||||||
run: |
|
run: |
|
||||||
ghcr_name=$(echo "${{ github.repository }}" | awk '{ print tolower($0) }')
|
ghcr_name=$(echo "${{ github.repository }}" | awk '{ print tolower($0) }')
|
||||||
echo "Name is ${ghcr_name}"
|
echo "Name is ${ghcr_name}"
|
||||||
echo "ghcr-repository=${ghcr_name}" >> $GITHUB_OUTPUT
|
echo "ghcr-repository=${ghcr_name}" >> $GITHUB_OUTPUT
|
||||||
-
|
- name: Gather Docker metadata
|
||||||
name: Gather Docker metadata
|
|
||||||
id: docker-meta
|
id: docker-meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
@ -365,37 +314,31 @@ jobs:
|
|||||||
# For a tag x.y.z or vX.Y.Z, output an x.y.z and x.y image tag
|
# For a tag x.y.z or vX.Y.Z, output an x.y.z and x.y image tag
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
-
|
- name: Checkout
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
# If https://github.com/docker/buildx/issues/1044 is resolved,
|
# If https://github.com/docker/buildx/issues/1044 is resolved,
|
||||||
# the append input with a native arm64 arch could be used to
|
# the append input with a native arm64 arch could be used to
|
||||||
# significantly speed up building
|
# significantly speed up building
|
||||||
-
|
- name: Set up Docker Buildx
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
-
|
- name: Set up QEMU
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
with:
|
with:
|
||||||
platforms: arm64
|
platforms: arm64
|
||||||
-
|
- name: Login to GitHub Container Registry
|
||||||
name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
-
|
- name: Login to Docker Hub
|
||||||
name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
# Don't attempt to login if not pushing to Docker Hub
|
# Don't attempt to login if not pushing to Docker Hub
|
||||||
if: steps.push-other-places.outputs.enable == 'true'
|
if: steps.push-other-places.outputs.enable == 'true'
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
-
|
- name: Login to Quay.io
|
||||||
name: Login to Quay.io
|
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
# Don't attempt to login if not pushing to Quay.io
|
# Don't attempt to login if not pushing to Quay.io
|
||||||
if: steps.push-other-places.outputs.enable == 'true'
|
if: steps.push-other-places.outputs.enable == 'true'
|
||||||
@ -403,8 +346,7 @@ jobs:
|
|||||||
registry: quay.io
|
registry: quay.io
|
||||||
username: ${{ secrets.QUAY_USERNAME }}
|
username: ${{ secrets.QUAY_USERNAME }}
|
||||||
password: ${{ secrets.QUAY_ROBOT_TOKEN }}
|
password: ${{ secrets.QUAY_ROBOT_TOKEN }}
|
||||||
-
|
- name: Build and push
|
||||||
name: Build and push
|
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
@ -422,23 +364,19 @@ jobs:
|
|||||||
type=registry,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:dev
|
type=registry,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:dev
|
||||||
cache-to: |
|
cache-to: |
|
||||||
type=registry,mode=max,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
type=registry,mode=max,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
||||||
-
|
- name: Inspect image
|
||||||
name: Inspect image
|
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools inspect ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
docker buildx imagetools inspect ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
||||||
-
|
- name: Export frontend artifact from docker
|
||||||
name: Export frontend artifact from docker
|
|
||||||
run: |
|
run: |
|
||||||
docker create --name frontend-extract ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
docker create --name frontend-extract ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
||||||
docker cp frontend-extract:/usr/src/paperless/src/documents/static/frontend src/documents/static/frontend/
|
docker cp frontend-extract:/usr/src/paperless/src/documents/static/frontend src/documents/static/frontend/
|
||||||
-
|
- name: Upload frontend artifact
|
||||||
name: Upload frontend artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: frontend-compiled
|
name: frontend-compiled
|
||||||
path: src/documents/static/frontend/
|
path: src/documents/static/frontend/
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
build-release:
|
build-release:
|
||||||
name: "Build Release"
|
name: "Build Release"
|
||||||
needs:
|
needs:
|
||||||
@ -446,63 +384,52 @@ jobs:
|
|||||||
- documentation
|
- documentation
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Checkout
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
-
|
- name: Set up Python
|
||||||
name: Set up Python
|
|
||||||
id: setup-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 }}
|
||||||
-
|
- name: Install uv
|
||||||
name: Install uv
|
|
||||||
uses: astral-sh/setup-uv@v5
|
uses: astral-sh/setup-uv@v5
|
||||||
with:
|
with:
|
||||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ steps.setup-python.outputs.python-version }}
|
python-version: ${{ steps.setup-python.outputs.python-version }}
|
||||||
-
|
- name: Install Python dependencies
|
||||||
name: Install Python dependencies
|
|
||||||
run: |
|
run: |
|
||||||
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
||||||
-
|
- name: Install system dependencies
|
||||||
name: Install system dependencies
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -qq
|
sudo apt-get update -qq
|
||||||
sudo apt-get install -qq --no-install-recommends gettext liblept5
|
sudo apt-get install -qq --no-install-recommends gettext liblept5
|
||||||
-
|
- name: Download frontend artifact
|
||||||
name: Download frontend artifact
|
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: frontend-compiled
|
name: frontend-compiled
|
||||||
path: src/documents/static/frontend/
|
path: src/documents/static/frontend/
|
||||||
-
|
- name: Download documentation artifact
|
||||||
name: Download documentation artifact
|
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: documentation
|
name: documentation
|
||||||
path: docs/_build/html/
|
path: docs/_build/html/
|
||||||
-
|
- name: Generate requirements file
|
||||||
name: Generate requirements file
|
|
||||||
run: |
|
run: |
|
||||||
uv export --quiet --no-dev --all-extras --format requirements-txt --output-file 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/
|
||||||
uv run \
|
uv run \
|
||||||
--python ${{ steps.setup-python.outputs.python-version }} \
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
manage.py compilemessages
|
manage.py compilemessages
|
||||||
-
|
- name: Collect static files
|
||||||
name: Collect static files
|
|
||||||
run: |
|
run: |
|
||||||
cd src/
|
cd src/
|
||||||
uv run \
|
uv run \
|
||||||
--python ${{ steps.setup-python.outputs.python-version }} \
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
manage.py collectstatic --no-input
|
manage.py collectstatic --no-input
|
||||||
-
|
- name: Move files
|
||||||
name: Move files
|
|
||||||
run: |
|
run: |
|
||||||
echo "Making dist folders"
|
echo "Making dist folders"
|
||||||
for directory in dist \
|
for directory in dist \
|
||||||
@ -539,21 +466,18 @@ jobs:
|
|||||||
cp --recursive docs/_build/html/ dist/paperless-ngx/docs
|
cp --recursive docs/_build/html/ dist/paperless-ngx/docs
|
||||||
|
|
||||||
mv --verbose static dist/paperless-ngx
|
mv --verbose static dist/paperless-ngx
|
||||||
-
|
- name: Make release package
|
||||||
name: Make release package
|
|
||||||
run: |
|
run: |
|
||||||
echo "Creating release archive"
|
echo "Creating release archive"
|
||||||
cd dist
|
cd dist
|
||||||
sudo chown -R 1000:1000 paperless-ngx/
|
sudo chown -R 1000:1000 paperless-ngx/
|
||||||
tar -cJf paperless-ngx.tar.xz paperless-ngx/
|
tar -cJf paperless-ngx.tar.xz paperless-ngx/
|
||||||
-
|
- name: Upload release artifact
|
||||||
name: Upload release artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release
|
name: release
|
||||||
path: dist/paperless-ngx.tar.xz
|
path: dist/paperless-ngx.tar.xz
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
publish-release:
|
publish-release:
|
||||||
name: "Publish Release"
|
name: "Publish Release"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -565,14 +489,12 @@ jobs:
|
|||||||
- build-release
|
- build-release
|
||||||
if: github.ref_type == 'tag' && (startsWith(github.ref_name, 'v') || contains(github.ref_name, '-beta.rc'))
|
if: github.ref_type == 'tag' && (startsWith(github.ref_name, 'v') || contains(github.ref_name, '-beta.rc'))
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Download release artifact
|
||||||
name: Download release artifact
|
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release
|
name: release
|
||||||
path: ./
|
path: ./
|
||||||
-
|
- name: Get version
|
||||||
name: Get version
|
|
||||||
id: get_version
|
id: get_version
|
||||||
run: |
|
run: |
|
||||||
echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
||||||
@ -581,8 +503,7 @@ jobs:
|
|||||||
else
|
else
|
||||||
echo "prerelease=false" >> $GITHUB_OUTPUT
|
echo "prerelease=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
-
|
- name: Create Release and Changelog
|
||||||
name: Create Release and Changelog
|
|
||||||
id: create-release
|
id: create-release
|
||||||
uses: release-drafter/release-drafter@v6
|
uses: release-drafter/release-drafter@v6
|
||||||
with:
|
with:
|
||||||
@ -593,8 +514,7 @@ jobs:
|
|||||||
publish: true # ensures release is not marked as draft
|
publish: true # ensures release is not marked as draft
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
-
|
- name: Upload release archive
|
||||||
name: Upload release archive
|
|
||||||
id: upload-release-asset
|
id: upload-release-asset
|
||||||
uses: shogo82148/actions-upload-release-asset@v1
|
uses: shogo82148/actions-upload-release-asset@v1
|
||||||
with:
|
with:
|
||||||
@ -603,7 +523,6 @@ jobs:
|
|||||||
asset_path: ./paperless-ngx.tar.xz
|
asset_path: ./paperless-ngx.tar.xz
|
||||||
asset_name: paperless-ngx-${{ steps.get_version.outputs.version }}.tar.xz
|
asset_name: paperless-ngx-${{ steps.get_version.outputs.version }}.tar.xz
|
||||||
asset_content_type: application/x-xz
|
asset_content_type: application/x-xz
|
||||||
|
|
||||||
append-changelog:
|
append-changelog:
|
||||||
name: "Append Changelog"
|
name: "Append Changelog"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -611,26 +530,22 @@ jobs:
|
|||||||
- publish-release
|
- publish-release
|
||||||
if: needs.publish-release.outputs.prerelease == 'false'
|
if: needs.publish-release.outputs.prerelease == 'false'
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Checkout
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
-
|
- name: Set up Python
|
||||||
name: Set up Python
|
|
||||||
id: setup-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 }}
|
||||||
-
|
- name: Install uv
|
||||||
name: Install uv
|
|
||||||
uses: astral-sh/setup-uv@v5
|
uses: astral-sh/setup-uv@v5
|
||||||
with:
|
with:
|
||||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
-
|
- name: Append Changelog to docs
|
||||||
name: Append Changelog to docs
|
|
||||||
id: append-Changelog
|
id: append-Changelog
|
||||||
working-directory: docs
|
working-directory: docs
|
||||||
run: |
|
run: |
|
||||||
@ -652,8 +567,7 @@ jobs:
|
|||||||
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"
|
||||||
git push origin ${{ needs.publish-release.outputs.version }}-changelog
|
git push origin ${{ needs.publish-release.outputs.version }}-changelog
|
||||||
-
|
- name: Create Pull Request
|
||||||
name: Create Pull Request
|
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
|
10
.github/workflows/cleanup-tags.yml
vendored
10
.github/workflows/cleanup-tags.yml
vendored
@ -6,17 +6,14 @@
|
|||||||
# This workflow will not trigger runs on forked repos.
|
# This workflow will not trigger runs on forked repos.
|
||||||
|
|
||||||
name: Cleanup Image Tags
|
name: Cleanup Image Tags
|
||||||
|
|
||||||
on:
|
on:
|
||||||
delete:
|
delete:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/cleanup-tags.yml"
|
- ".github/workflows/cleanup-tags.yml"
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: registry-tags-cleanup
|
group: registry-tags-cleanup
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cleanup-images:
|
cleanup-images:
|
||||||
name: Cleanup Image Tags for ${{ matrix.primary-name }}
|
name: Cleanup Image Tags for ${{ matrix.primary-name }}
|
||||||
@ -30,8 +27,7 @@ jobs:
|
|||||||
# Requires a personal access token with the OAuth scope delete:packages
|
# Requires a personal access token with the OAuth scope delete:packages
|
||||||
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Clean temporary images
|
||||||
name: Clean temporary images
|
|
||||||
if: "${{ env.TOKEN != '' }}"
|
if: "${{ env.TOKEN != '' }}"
|
||||||
uses: stumpylog/image-cleaner-action/ephemeral@v0.10.0
|
uses: stumpylog/image-cleaner-action/ephemeral@v0.10.0
|
||||||
with:
|
with:
|
||||||
@ -43,7 +39,6 @@ jobs:
|
|||||||
repo_name: "paperless-ngx"
|
repo_name: "paperless-ngx"
|
||||||
match_regex: "(feature|fix)"
|
match_regex: "(feature|fix)"
|
||||||
do_delete: "true"
|
do_delete: "true"
|
||||||
|
|
||||||
cleanup-untagged-images:
|
cleanup-untagged-images:
|
||||||
name: Cleanup Untagged Images Tags for ${{ matrix.primary-name }}
|
name: Cleanup Untagged Images Tags for ${{ matrix.primary-name }}
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
@ -58,8 +53,7 @@ jobs:
|
|||||||
# Requires a personal access token with the OAuth scope delete:packages
|
# Requires a personal access token with the OAuth scope delete:packages
|
||||||
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Clean untagged images
|
||||||
name: Clean untagged images
|
|
||||||
if: "${{ env.TOKEN != '' }}"
|
if: "${{ env.TOKEN != '' }}"
|
||||||
uses: stumpylog/image-cleaner-action/untagged@v0.10.0
|
uses: stumpylog/image-cleaner-action/untagged@v0.10.0
|
||||||
with:
|
with:
|
||||||
|
12
.github/workflows/codeql-analysis.yml
vendored
12
.github/workflows/codeql-analysis.yml
vendored
@ -10,16 +10,14 @@
|
|||||||
# supported CodeQL languages.
|
# supported CodeQL languages.
|
||||||
#
|
#
|
||||||
name: "CodeQL"
|
name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main, dev ]
|
branches: [main, dev]
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: [ dev ]
|
branches: [dev]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '28 13 * * 5'
|
- cron: '28 13 * * 5'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
@ -28,18 +26,15 @@ jobs:
|
|||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
security-events: write
|
security-events: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
language: [ 'javascript', 'python' ]
|
language: ['javascript', 'python']
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@v3
|
||||||
@ -49,6 +44,5 @@ jobs:
|
|||||||
# By default, queries listed here will override any specified in a config file.
|
# By default, queries listed here will override any specified in a config file.
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v3
|
||||||
|
11
.github/workflows/crowdin.yml
vendored
11
.github/workflows/crowdin.yml
vendored
@ -1,23 +1,16 @@
|
|||||||
name: Crowdin Action
|
name: Crowdin Action
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '2 */12 * * *'
|
- cron: '2 */12 * * *'
|
||||||
push:
|
push:
|
||||||
paths: [
|
paths: ['src/locale/**', 'src-ui/messages.xlf', 'src-ui/src/locale/**']
|
||||||
'src/locale/**',
|
branches: [dev]
|
||||||
'src-ui/messages.xlf',
|
|
||||||
'src-ui/src/locale/**'
|
|
||||||
]
|
|
||||||
branches: [ dev ]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
synchronize-with-crowdin:
|
synchronize-with-crowdin:
|
||||||
name: Crowdin Sync
|
name: Crowdin Sync
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
86
.github/workflows/pr-bot.yml
vendored
Normal file
86
.github/workflows/pr-bot.yml
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
name: PR Bot
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
jobs:
|
||||||
|
pr-bot:
|
||||||
|
name: Automated PR Bot
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Label by file path
|
||||||
|
uses: actions/labeler@v5
|
||||||
|
with:
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Label by size
|
||||||
|
uses: Gascon1/pr-size-labeler@v1.3.0
|
||||||
|
with:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
xs_label: 'small-change'
|
||||||
|
xs_diff: '9'
|
||||||
|
s_label: 'non-trivial'
|
||||||
|
s_diff: '99999'
|
||||||
|
fail_if_xl: 'false'
|
||||||
|
excluded_files: /\.lock$/ /\.txt$/ ^src-ui/pnpm-lock\.yaml$ ^src-ui/messages\.xlf$ ^src/locale/en_US/LC_MESSAGES/django\.po$
|
||||||
|
- name: Label bot-generated PRs
|
||||||
|
if: ${{ contains(github.actor, 'dependabot') || contains(github.actor, 'crowdin-bot') }}
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const pr = context.payload.pull_request;
|
||||||
|
const user = pr.user.login.toLowerCase();
|
||||||
|
const labels = [];
|
||||||
|
|
||||||
|
if (user.includes('dependabot')) {
|
||||||
|
labels.push('dependencies');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.includes('crowdin-bot')) {
|
||||||
|
labels.push('translation', 'skip-changelog');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (labels.length) {
|
||||||
|
await github.rest.issues.addLabels({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: pr.number,
|
||||||
|
labels,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
- name: Welcome comment
|
||||||
|
if: ${{ !contains(github.actor, 'bot') }}
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const pr = context.payload.pull_request;
|
||||||
|
const user = pr.user.login;
|
||||||
|
|
||||||
|
const { data: members } = await github.rest.orgs.listMembers({
|
||||||
|
org: 'paperless-ngx',
|
||||||
|
});
|
||||||
|
|
||||||
|
const memberLogins = members.map(m => m.login.toLowerCase());
|
||||||
|
if (memberLogins.includes(user.toLowerCase())) {
|
||||||
|
core.info('Skipping comment: user is org member');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body =
|
||||||
|
"Hello @" + user + ",\n\n" +
|
||||||
|
"Thank you very much for submitting this PR to us!\n\n" +
|
||||||
|
"This is what will happen next:\n\n" +
|
||||||
|
"1. CI tests will run against your PR to ensure quality and consistency.\n" +
|
||||||
|
"2. Next, human contributors from paperless-ngx review your changes.\n" +
|
||||||
|
"3. Please address any issues that come up during the review as soon as you are able to.\n" +
|
||||||
|
"4. If accepted, your pull request will be merged into the `dev` branch and changes there will be tested further.\n" +
|
||||||
|
"5. Eventually, changes from you and other contributors will be merged into `main` and a new release will be made.\n\n" +
|
||||||
|
"You'll be hearing from us soon, and thank you again for contributing to our project.";
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
issue_number: pr.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
body,
|
||||||
|
});
|
3
.github/workflows/project-actions.yml
vendored
3
.github/workflows/project-actions.yml
vendored
@ -1,5 +1,4 @@
|
|||||||
name: Project Automations
|
name: Project Automations
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_target: #_target allows access to secrets
|
pull_request_target: #_target allows access to secrets
|
||||||
types:
|
types:
|
||||||
@ -8,10 +7,8 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- dev
|
- dev
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pr_opened_or_reopened:
|
pr_opened_or_reopened:
|
||||||
name: pr_opened_or_reopened
|
name: pr_opened_or_reopened
|
||||||
|
27
.github/workflows/repo-maintenance.yml
vendored
27
.github/workflows/repo-maintenance.yml
vendored
@ -1,18 +1,14 @@
|
|||||||
name: 'Repository Maintenance'
|
name: 'Repository Maintenance'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 3 * * *'
|
- cron: '0 3 * * *'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
discussions: write
|
discussions: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: lock
|
group: lock
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
name: 'Stale'
|
name: 'Stale'
|
||||||
@ -27,9 +23,8 @@ jobs:
|
|||||||
stale-issue-label: stale
|
stale-issue-label: stale
|
||||||
stale-pr-label: stale
|
stale-pr-label: stale
|
||||||
stale-issue-message: >
|
stale-issue-message: >
|
||||||
This issue has been automatically marked as stale because it has not had
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
||||||
recent activity. It will be closed if no further activity occurs. Thank you
|
|
||||||
for your contributions. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
|
||||||
lock-threads:
|
lock-threads:
|
||||||
name: 'Lock Old Threads'
|
name: 'Lock Old Threads'
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
@ -42,20 +37,14 @@ jobs:
|
|||||||
discussion-inactive-days: '30'
|
discussion-inactive-days: '30'
|
||||||
log-output: true
|
log-output: true
|
||||||
issue-comment: >
|
issue-comment: >
|
||||||
This issue has been automatically locked since there
|
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new discussion or issue for related concerns. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
||||||
has not been any recent activity after it was closed.
|
|
||||||
Please open a new discussion or issue for related concerns.
|
|
||||||
See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
|
||||||
pr-comment: >
|
pr-comment: >
|
||||||
This pull request has been automatically locked since there
|
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new discussion or issue for related concerns. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
||||||
has not been any recent activity after it was closed.
|
|
||||||
Please open a new discussion or issue for related concerns.
|
|
||||||
See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
|
||||||
discussion-comment: >
|
discussion-comment: >
|
||||||
This discussion has been automatically locked since there
|
This discussion has been automatically locked since there has not been any recent activity after it was closed. Please open a new discussion for related concerns. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
||||||
has not been any recent activity after it was closed.
|
|
||||||
Please open a new discussion for related concerns.
|
|
||||||
See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
|
||||||
close-answered-discussions:
|
close-answered-discussions:
|
||||||
name: 'Close Answered Discussions'
|
name: 'Close Answered Discussions'
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
|
69
.github/workflows/translate-strings.yml
vendored
Normal file
69
.github/workflows/translate-strings.yml
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
name: Generate Translation Strings
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
jobs:
|
||||||
|
generate-translate-strings:
|
||||||
|
name: Generate Translation Strings
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.PNGX_BOT_PAT }}
|
||||||
|
ref: ${{ github.head_ref }}
|
||||||
|
- name: Set up Python
|
||||||
|
id: setup-python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -qq --no-install-recommends gettext
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v5
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
- name: Install backend python dependencies
|
||||||
|
run: |
|
||||||
|
uv sync \
|
||||||
|
--group dev \
|
||||||
|
--frozen
|
||||||
|
- name: Generate backend translation strings
|
||||||
|
run: cd src/ && uv run manage.py makemessages -l en_US -i "samples*"
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 10
|
||||||
|
- name: Use Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
cache: 'pnpm'
|
||||||
|
cache-dependency-path: 'src-ui/pnpm-lock.yaml'
|
||||||
|
- name: Cache frontend dependencies
|
||||||
|
id: cache-frontend-deps
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.pnpm-store
|
||||||
|
~/.cache
|
||||||
|
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
|
||||||
|
run: cd src-ui && pnpm install
|
||||||
|
- name: Re-link Angular cli
|
||||||
|
run: cd src-ui && pnpm link @angular/cli
|
||||||
|
- name: Generate frontend translation strings
|
||||||
|
run: |
|
||||||
|
cd src-ui
|
||||||
|
pnpm run ng extract-i18n
|
||||||
|
- name: Commit changes
|
||||||
|
uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
|
with:
|
||||||
|
file_pattern: 'src-ui/messages.xlf src/locale/en_US/LC_MESSAGES/django.po'
|
||||||
|
commit_message: "Auto translate strings"
|
||||||
|
commit_user_name: "GitHub Actions"
|
||||||
|
commit_author: "GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>"
|
@ -76,3 +76,8 @@ repos:
|
|||||||
rev: "v0.10.0.1"
|
rev: "v0.10.0.1"
|
||||||
hooks:
|
hooks:
|
||||||
- id: shellcheck
|
- id: shellcheck
|
||||||
|
- repo: https://github.com/google/yamlfmt
|
||||||
|
rev: v0.14.0
|
||||||
|
hooks:
|
||||||
|
- id: yamlfmt
|
||||||
|
exclude: "^src-ui/pnpm-lock.yaml"
|
||||||
|
@ -32,7 +32,7 @@ RUN set -eux \
|
|||||||
# 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 ghcr.io/astral-sh/uv:0.6.13-python3.12-bookworm-slim AS s6-overlay-base
|
FROM ghcr.io/astral-sh/uv:0.6.16-python3.12-bookworm-slim AS s6-overlay-base
|
||||||
|
|
||||||
WORKDIR /usr/src/s6
|
WORKDIR /usr/src/s6
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.19
|
image: docker.io/gotenberg/gotenberg:8.20
|
||||||
hostname: gotenberg
|
hostname: gotenberg
|
||||||
container_name: gotenberg
|
container_name: gotenberg
|
||||||
network_mode: host
|
network_mode: host
|
||||||
|
@ -36,7 +36,6 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/mariadb:11
|
image: docker.io/library/mariadb:11
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -48,7 +47,6 @@ services:
|
|||||||
MARIADB_USER: paperless
|
MARIADB_USER: paperless
|
||||||
MARIADB_PASSWORD: paperless
|
MARIADB_PASSWORD: paperless
|
||||||
MARIADB_ROOT_PASSWORD: paperless
|
MARIADB_ROOT_PASSWORD: paperless
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -75,9 +73,8 @@ services:
|
|||||||
PAPERLESS_TIKA_ENABLED: 1
|
PAPERLESS_TIKA_ENABLED: 1
|
||||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
||||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||||
|
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.19
|
image: docker.io/gotenberg/gotenberg:8.20
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
# The gotenberg chromium route is used to convert .eml files. We do not
|
# The gotenberg chromium route is used to convert .eml files. We do not
|
||||||
# want to allow external content like tracking pixels or even javascript.
|
# want to allow external content like tracking pixels or even javascript.
|
||||||
@ -85,11 +82,9 @@ services:
|
|||||||
- "gotenberg"
|
- "gotenberg"
|
||||||
- "--chromium-disable-javascript=true"
|
- "--chromium-disable-javascript=true"
|
||||||
- "--chromium-allow-list=file:///tmp/.*"
|
- "--chromium-allow-list=file:///tmp/.*"
|
||||||
|
|
||||||
tika:
|
tika:
|
||||||
image: docker.io/apache/tika:latest
|
image: docker.io/apache/tika:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -31,7 +31,6 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/mariadb:11
|
image: docker.io/library/mariadb:11
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -43,7 +42,6 @@ services:
|
|||||||
MARIADB_USER: paperless
|
MARIADB_USER: paperless
|
||||||
MARIADB_PASSWORD: paperless
|
MARIADB_PASSWORD: paperless
|
||||||
MARIADB_ROOT_PASSWORD: paperless
|
MARIADB_ROOT_PASSWORD: paperless
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -65,7 +63,6 @@ services:
|
|||||||
PAPERLESS_DBUSER: paperless # only needed if non-default username
|
PAPERLESS_DBUSER: paperless # only needed if non-default username
|
||||||
PAPERLESS_DBPASS: paperless # only needed if non-default password
|
PAPERLESS_DBPASS: paperless # only needed if non-default password
|
||||||
PAPERLESS_DBPORT: 3306
|
PAPERLESS_DBPORT: 3306
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -32,7 +32,6 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/postgres:17
|
image: docker.io/library/postgres:17
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -42,7 +41,6 @@ services:
|
|||||||
POSTGRES_DB: paperless
|
POSTGRES_DB: paperless
|
||||||
POSTGRES_USER: paperless
|
POSTGRES_USER: paperless
|
||||||
POSTGRES_PASSWORD: paperless
|
POSTGRES_PASSWORD: paperless
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -61,7 +59,6 @@ services:
|
|||||||
PAPERLESS_DBHOST: db
|
PAPERLESS_DBHOST: db
|
||||||
env_file:
|
env_file:
|
||||||
- stack.env
|
- stack.env
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -35,7 +35,6 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/postgres:17
|
image: docker.io/library/postgres:17
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -45,7 +44,6 @@ services:
|
|||||||
POSTGRES_DB: paperless
|
POSTGRES_DB: paperless
|
||||||
POSTGRES_USER: paperless
|
POSTGRES_USER: paperless
|
||||||
POSTGRES_PASSWORD: paperless
|
POSTGRES_PASSWORD: paperless
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -68,22 +66,18 @@ services:
|
|||||||
PAPERLESS_TIKA_ENABLED: 1
|
PAPERLESS_TIKA_ENABLED: 1
|
||||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
||||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||||
|
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.19
|
image: docker.io/gotenberg/gotenberg:8.20
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# The gotenberg chromium route is used to convert .eml files. We do not
|
# The gotenberg chromium route is used to convert .eml files. We do not
|
||||||
# want to allow external content like tracking pixels or even javascript.
|
# want to allow external content like tracking pixels or even javascript.
|
||||||
command:
|
command:
|
||||||
- "gotenberg"
|
- "gotenberg"
|
||||||
- "--chromium-disable-javascript=true"
|
- "--chromium-disable-javascript=true"
|
||||||
- "--chromium-allow-list=file:///tmp/.*"
|
- "--chromium-allow-list=file:///tmp/.*"
|
||||||
|
|
||||||
tika:
|
tika:
|
||||||
image: docker.io/apache/tika:latest
|
image: docker.io/apache/tika:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -31,7 +31,6 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/postgres:17
|
image: docker.io/library/postgres:17
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -41,7 +40,6 @@ services:
|
|||||||
POSTGRES_DB: paperless
|
POSTGRES_DB: paperless
|
||||||
POSTGRES_USER: paperless
|
POSTGRES_USER: paperless
|
||||||
POSTGRES_PASSWORD: paperless
|
POSTGRES_PASSWORD: paperless
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -59,7 +57,6 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
PAPERLESS_REDIS: redis://broker:6379
|
PAPERLESS_REDIS: redis://broker:6379
|
||||||
PAPERLESS_DBHOST: db
|
PAPERLESS_DBHOST: db
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -35,7 +35,6 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -56,22 +55,18 @@ services:
|
|||||||
PAPERLESS_TIKA_ENABLED: 1
|
PAPERLESS_TIKA_ENABLED: 1
|
||||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
||||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||||
|
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.19
|
image: docker.io/gotenberg/gotenberg:8.20
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# The gotenberg chromium route is used to convert .eml files. We do not
|
# The gotenberg chromium route is used to convert .eml files. We do not
|
||||||
# want to allow external content like tracking pixels or even javascript.
|
# want to allow external content like tracking pixels or even javascript.
|
||||||
command:
|
command:
|
||||||
- "gotenberg"
|
- "gotenberg"
|
||||||
- "--chromium-disable-javascript=true"
|
- "--chromium-disable-javascript=true"
|
||||||
- "--chromium-allow-list=file:///tmp/.*"
|
- "--chromium-allow-list=file:///tmp/.*"
|
||||||
|
|
||||||
tika:
|
tika:
|
||||||
image: docker.io/apache/tika:latest
|
image: docker.io/apache/tika:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -28,7 +28,6 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -44,7 +43,6 @@ services:
|
|||||||
env_file: docker-compose.env
|
env_file: docker-compose.env
|
||||||
environment:
|
environment:
|
||||||
PAPERLESS_REDIS: redis://broker:6379
|
PAPERLESS_REDIS: redis://broker:6379
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -1,5 +1,27 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## paperless-ngx 2.15.3
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: do not try deleting original file that was moved to trash dir [@shamoon](https://github.com/shamoon) ([#9684](https://github.com/paperless-ngx/paperless-ngx/pull/9684))
|
||||||
|
- Fix: preserve non-ASCII filenames in document downloads [@shamoon](https://github.com/shamoon) ([#9702](https://github.com/paperless-ngx/paperless-ngx/pull/9702))
|
||||||
|
- Fix: fix breaking api change to document notes user field [@shamoon](https://github.com/shamoon) ([#9714](https://github.com/paperless-ngx/paperless-ngx/pull/9714))
|
||||||
|
- Fix: another doc link fix [@shamoon](https://github.com/shamoon) ([#9700](https://github.com/paperless-ngx/paperless-ngx/pull/9700))
|
||||||
|
- Fix: correctly handle dict data with webhook [@shamoon](https://github.com/shamoon) ([#9674](https://github.com/paperless-ngx/paperless-ngx/pull/9674))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>5 changes</summary>
|
||||||
|
|
||||||
|
- Fix: do not try deleting original file that was moved to trash dir [@shamoon](https://github.com/shamoon) ([#9684](https://github.com/paperless-ngx/paperless-ngx/pull/9684))
|
||||||
|
- Fix: preserve non-ASCII filenames in document downloads [@shamoon](https://github.com/shamoon) ([#9702](https://github.com/paperless-ngx/paperless-ngx/pull/9702))
|
||||||
|
- Fix: fix breaking api change to document notes user field [@shamoon](https://github.com/shamoon) ([#9714](https://github.com/paperless-ngx/paperless-ngx/pull/9714))
|
||||||
|
- Fix: another doc link fix [@shamoon](https://github.com/shamoon) ([#9700](https://github.com/paperless-ngx/paperless-ngx/pull/9700))
|
||||||
|
- Fix: correctly handle dict data with webhook [@shamoon](https://github.com/shamoon) ([#9674](https://github.com/paperless-ngx/paperless-ngx/pull/9674))
|
||||||
|
</details>
|
||||||
|
|
||||||
## paperless-ngx 2.15.2
|
## paperless-ngx 2.15.2
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
@ -631,6 +631,12 @@ If both the [PAPERLESS_ACCOUNT_DEFAULT_GROUPS](#PAPERLESS_ACCOUNT_DEFAULT_GROUPS
|
|||||||
|
|
||||||
If you do not have a working email server set up you should set this to 'none'.
|
If you do not have a working email server set up you should set this to 'none'.
|
||||||
|
|
||||||
|
#### [`PAPERLESS_ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS=<bool>`](#PAPERLESS_ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS) {#PAPERLESS_ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS}
|
||||||
|
|
||||||
|
: See the relevant [django-allauth documentation](https://docs.allauth.org/en/latest/account/configuration.html)
|
||||||
|
|
||||||
|
Defaults to True (from allauth)
|
||||||
|
|
||||||
#### [`PAPERLESS_DISABLE_REGULAR_LOGIN=<bool>`](#PAPERLESS_DISABLE_REGULAR_LOGIN) {#PAPERLESS_DISABLE_REGULAR_LOGIN}
|
#### [`PAPERLESS_DISABLE_REGULAR_LOGIN=<bool>`](#PAPERLESS_DISABLE_REGULAR_LOGIN) {#PAPERLESS_DISABLE_REGULAR_LOGIN}
|
||||||
|
|
||||||
: Disables the regular frontend username / password login, i.e. once you have setup SSO. Note that this setting does not disable the Django admin login nor logging in with local credentials via the API. To prevent access to the Django admin, consider blocking `/admin/` in your [web server or reverse proxy configuration](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx).
|
: Disables the regular frontend username / password login, i.e. once you have setup SSO. Note that this setting does not disable the Django admin login nor logging in with local credentials via the API. To prevent access to the Django admin, consider blocking `/admin/` in your [web server or reverse proxy configuration](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx).
|
||||||
|
@ -11,14 +11,12 @@ theme:
|
|||||||
toggle:
|
toggle:
|
||||||
icon: material/brightness-auto
|
icon: material/brightness-auto
|
||||||
name: Switch to light mode
|
name: Switch to light mode
|
||||||
|
|
||||||
# Palette toggle for light mode
|
# Palette toggle for light mode
|
||||||
- media: "(prefers-color-scheme: light)"
|
- media: "(prefers-color-scheme: light)"
|
||||||
scheme: default
|
scheme: default
|
||||||
toggle:
|
toggle:
|
||||||
icon: material/brightness-7
|
icon: material/brightness-7
|
||||||
name: Switch to dark mode
|
name: Switch to dark mode
|
||||||
|
|
||||||
# Palette toggle for dark mode
|
# Palette toggle for dark mode
|
||||||
- media: "(prefers-color-scheme: dark)"
|
- media: "(prefers-color-scheme: dark)"
|
||||||
scheme: slate
|
scheme: slate
|
||||||
|
@ -26,10 +26,10 @@ dependencies = [
|
|||||||
"django~=5.1.7",
|
"django~=5.1.7",
|
||||||
"django-allauth[socialaccount,mfa]~=65.4.0",
|
"django-allauth[socialaccount,mfa]~=65.4.0",
|
||||||
"django-auditlog~=3.0.0",
|
"django-auditlog~=3.0.0",
|
||||||
"django-celery-results~=2.5.1",
|
"django-celery-results~=2.6.0",
|
||||||
"django-compression-middleware~=0.5.0",
|
"django-compression-middleware~=0.5.0",
|
||||||
"django-cors-headers~=4.7.0",
|
"django-cors-headers~=4.7.0",
|
||||||
"django-extensions~=3.2.3",
|
"django-extensions~=4.1",
|
||||||
"django-filter~=25.1",
|
"django-filter~=25.1",
|
||||||
"django-guardian~=2.4.0",
|
"django-guardian~=2.4.0",
|
||||||
"django-multiselectfield~=0.1.13",
|
"django-multiselectfield~=0.1.13",
|
||||||
@ -37,11 +37,11 @@ dependencies = [
|
|||||||
"djangorestframework~=3.15",
|
"djangorestframework~=3.15",
|
||||||
"djangorestframework-guardian~=0.3.0",
|
"djangorestframework-guardian~=0.3.0",
|
||||||
"drf-spectacular~=0.28",
|
"drf-spectacular~=0.28",
|
||||||
"drf-spectacular-sidecar~=2025.3.1",
|
"drf-spectacular-sidecar~=2025.4.1",
|
||||||
"drf-writable-nested~=0.7.1",
|
"drf-writable-nested~=0.7.1",
|
||||||
"filelock~=3.17.0",
|
"filelock~=3.18.0",
|
||||||
"flower~=2.0.1",
|
"flower~=2.0.1",
|
||||||
"gotenberg-client~=0.9.0",
|
"gotenberg-client~=0.10.0",
|
||||||
"httpx-oauth~=0.16",
|
"httpx-oauth~=0.16",
|
||||||
"imap-tools~=1.10.0",
|
"imap-tools~=1.10.0",
|
||||||
"inotifyrecursive~=0.3",
|
"inotifyrecursive~=0.3",
|
||||||
@ -52,12 +52,12 @@ dependencies = [
|
|||||||
"pathvalidate~=3.2.3",
|
"pathvalidate~=3.2.3",
|
||||||
"pdf2image~=1.17.0",
|
"pdf2image~=1.17.0",
|
||||||
"python-dateutil~=2.9.0",
|
"python-dateutil~=2.9.0",
|
||||||
"python-dotenv~=1.0.1",
|
"python-dotenv~=1.1.0",
|
||||||
"python-gnupg~=0.5.4",
|
"python-gnupg~=0.5.4",
|
||||||
"python-ipware~=3.0.0",
|
"python-ipware~=3.0.0",
|
||||||
"python-magic~=0.4.27",
|
"python-magic~=0.4.27",
|
||||||
"pyzbar~=0.1.9",
|
"pyzbar~=0.1.9",
|
||||||
"rapidfuzz~=3.12.1",
|
"rapidfuzz~=3.13.0",
|
||||||
"redis[hiredis]~=5.2.1",
|
"redis[hiredis]~=5.2.1",
|
||||||
"scikit-learn~=1.6.1",
|
"scikit-learn~=1.6.1",
|
||||||
"setproctitle~=1.3.4",
|
"setproctitle~=1.3.4",
|
||||||
@ -227,27 +227,9 @@ lint.per-file-ignores."src/documents/tests/test_consumer.py" = [
|
|||||||
lint.per-file-ignores."src/documents/tests/test_file_handling.py" = [
|
lint.per-file-ignores."src/documents/tests/test_file_handling.py" = [
|
||||||
"PTH",
|
"PTH",
|
||||||
] # TODO Enable & remove
|
] # 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_migration_archive_files.py" = [
|
lint.per-file-ignores."src/documents/tests/test_migration_archive_files.py" = [
|
||||||
"PTH",
|
"PTH",
|
||||||
] # TODO Enable & remove
|
] # 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/views.py" = [
|
lint.per-file-ignores."src/documents/views.py" = [
|
||||||
"PTH",
|
"PTH",
|
||||||
] # TODO Enable & remove
|
] # TODO Enable & remove
|
||||||
|
@ -5,14 +5,14 @@
|
|||||||
<trans-unit id="ngb.alert.close" datatype="html">
|
<trans-unit id="ngb.alert.close" datatype="html">
|
||||||
<source>Close</source>
|
<source>Close</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/alert/alert.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/alert/alert.ts</context>
|
||||||
<context context-type="linenumber">51</context>
|
<context context-type="linenumber">51</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.carousel.slide-number" datatype="html">
|
<trans-unit id="ngb.carousel.slide-number" datatype="html">
|
||||||
<source> Slide <x id="INTERPOLATION" equiv-text="ueryList<NgbSli"/> of <x id="INTERPOLATION_1" equiv-text="EventSource = N"/> </source>
|
<source> Slide <x id="INTERPOLATION" equiv-text="ueryList<NgbSli"/> of <x id="INTERPOLATION_1" equiv-text="EventSource = N"/> </source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/carousel/carousel.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/carousel/carousel.ts</context>
|
||||||
<context context-type="linenumber">132,136</context>
|
<context context-type="linenumber">132,136</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">Currently selected slide number read by screen reader</note>
|
<note priority="1" from="description">Currently selected slide number read by screen reader</note>
|
||||||
@ -20,212 +20,212 @@
|
|||||||
<trans-unit id="ngb.carousel.previous" datatype="html">
|
<trans-unit id="ngb.carousel.previous" datatype="html">
|
||||||
<source>Previous</source>
|
<source>Previous</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/carousel/carousel.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/carousel/carousel.ts</context>
|
||||||
<context context-type="linenumber">156,160</context>
|
<context context-type="linenumber">158,160</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.carousel.next" datatype="html">
|
<trans-unit id="ngb.carousel.next" datatype="html">
|
||||||
<source>Next</source>
|
<source>Next</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/carousel/carousel.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/carousel/carousel.ts</context>
|
||||||
<context context-type="linenumber">196,199</context>
|
<context context-type="linenumber">199</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.datepicker.previous-month" datatype="html">
|
<trans-unit id="ngb.datepicker.previous-month" datatype="html">
|
||||||
<source>Previous month</source>
|
<source>Previous month</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||||
<context context-type="linenumber">77,79</context>
|
<context context-type="linenumber">77,79</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||||
<context context-type="linenumber">102</context>
|
<context context-type="linenumber">102</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.datepicker.next-month" datatype="html">
|
<trans-unit id="ngb.datepicker.next-month" datatype="html">
|
||||||
<source>Next month</source>
|
<source>Next month</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||||
<context context-type="linenumber">102</context>
|
<context context-type="linenumber">102</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||||
<context context-type="linenumber">102</context>
|
<context context-type="linenumber">102</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.HH" datatype="html">
|
<trans-unit id="ngb.timepicker.HH" datatype="html">
|
||||||
<source>HH</source>
|
<source>HH</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.toast.close-aria" datatype="html">
|
<trans-unit id="ngb.toast.close-aria" datatype="html">
|
||||||
<source>Close</source>
|
<source>Close</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.datepicker.select-month" datatype="html">
|
<trans-unit id="ngb.datepicker.select-month" datatype="html">
|
||||||
<source>Select month</source>
|
<source>Select month</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.pagination.first" datatype="html">
|
<trans-unit id="ngb.pagination.first" datatype="html">
|
||||||
<source>««</source>
|
<source>««</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.hours" datatype="html">
|
<trans-unit id="ngb.timepicker.hours" datatype="html">
|
||||||
<source>Hours</source>
|
<source>Hours</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.pagination.previous" datatype="html">
|
<trans-unit id="ngb.pagination.previous" datatype="html">
|
||||||
<source>«</source>
|
<source>«</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.MM" datatype="html">
|
<trans-unit id="ngb.timepicker.MM" datatype="html">
|
||||||
<source>MM</source>
|
<source>MM</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.pagination.next" datatype="html">
|
<trans-unit id="ngb.pagination.next" datatype="html">
|
||||||
<source>»</source>
|
<source>»</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.datepicker.select-year" datatype="html">
|
<trans-unit id="ngb.datepicker.select-year" datatype="html">
|
||||||
<source>Select year</source>
|
<source>Select year</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.minutes" datatype="html">
|
<trans-unit id="ngb.timepicker.minutes" datatype="html">
|
||||||
<source>Minutes</source>
|
<source>Minutes</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.pagination.last" datatype="html">
|
<trans-unit id="ngb.pagination.last" datatype="html">
|
||||||
<source>»»</source>
|
<source>»»</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.pagination.first-aria" datatype="html">
|
<trans-unit id="ngb.pagination.first-aria" datatype="html">
|
||||||
<source>First</source>
|
<source>First</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.increment-hours" datatype="html">
|
<trans-unit id="ngb.timepicker.increment-hours" datatype="html">
|
||||||
<source>Increment hours</source>
|
<source>Increment hours</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.pagination.previous-aria" datatype="html">
|
<trans-unit id="ngb.pagination.previous-aria" datatype="html">
|
||||||
<source>Previous</source>
|
<source>Previous</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.decrement-hours" datatype="html">
|
<trans-unit id="ngb.timepicker.decrement-hours" datatype="html">
|
||||||
<source>Decrement hours</source>
|
<source>Decrement hours</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.pagination.next-aria" datatype="html">
|
<trans-unit id="ngb.pagination.next-aria" datatype="html">
|
||||||
<source>Next</source>
|
<source>Next</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.increment-minutes" datatype="html">
|
<trans-unit id="ngb.timepicker.increment-minutes" datatype="html">
|
||||||
<source>Increment minutes</source>
|
<source>Increment minutes</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.pagination.last-aria" datatype="html">
|
<trans-unit id="ngb.pagination.last-aria" datatype="html">
|
||||||
<source>Last</source>
|
<source>Last</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.decrement-minutes" datatype="html">
|
<trans-unit id="ngb.timepicker.decrement-minutes" datatype="html">
|
||||||
<source>Decrement minutes</source>
|
<source>Decrement minutes</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.SS" datatype="html">
|
<trans-unit id="ngb.timepicker.SS" datatype="html">
|
||||||
<source>SS</source>
|
<source>SS</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.seconds" datatype="html">
|
<trans-unit id="ngb.timepicker.seconds" datatype="html">
|
||||||
<source>Seconds</source>
|
<source>Seconds</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.increment-seconds" datatype="html">
|
<trans-unit id="ngb.timepicker.increment-seconds" datatype="html">
|
||||||
<source>Increment seconds</source>
|
<source>Increment seconds</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.decrement-seconds" datatype="html">
|
<trans-unit id="ngb.timepicker.decrement-seconds" datatype="html">
|
||||||
<source>Decrement seconds</source>
|
<source>Decrement seconds</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ngb.timepicker.PM" datatype="html">
|
<trans-unit id="ngb.timepicker.PM" datatype="html">
|
||||||
<source><x id="INTERPOLATION"/></source>
|
<source><x id="INTERPOLATION"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/ngb-config.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/ngb-config.ts</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -233,7 +233,7 @@
|
|||||||
<source><x id="INTERPOLATION" equiv-text="barConfig);
|
<source><x id="INTERPOLATION" equiv-text="barConfig);
|
||||||
pu"/></source>
|
pu"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.4_@angular+core@19.2.4_rxjs@7.8._7cb86cab255ede976b272d2c2294ed3c/node_modules/src/progressbar/progressbar.ts</context>
|
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.7_@angular+core@19.2.7_rxjs@7.8._60030a7752e9525eb53596e6254db077/node_modules/src/progressbar/progressbar.ts</context>
|
||||||
<context context-type="linenumber">41,42</context>
|
<context context-type="linenumber">41,42</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -2537,19 +2537,19 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">966</context>
|
<context context-type="linenumber">986</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1326</context>
|
<context context-type="linenumber">1347</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1365</context>
|
<context context-type="linenumber">1386</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1406</context>
|
<context context-type="linenumber">1427</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@ -3157,7 +3157,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">919</context>
|
<context context-type="linenumber">939</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@ -3406,7 +3406,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1383</context>
|
<context context-type="linenumber">1404</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/guards/dirty-saved-view.guard.ts</context>
|
<context context-type="sourcefile">src/app/guards/dirty-saved-view.guard.ts</context>
|
||||||
@ -6908,39 +6908,39 @@
|
|||||||
<source>Document "<x id="PH" equiv-text="newValues.title"/>" saved successfully.</source>
|
<source>Document "<x id="PH" equiv-text="newValues.title"/>" saved successfully.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">814</context>
|
<context context-type="linenumber">834</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">837</context>
|
<context context-type="linenumber">857</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6626387786259219838" datatype="html">
|
<trans-unit id="6626387786259219838" datatype="html">
|
||||||
<source>Error saving document "<x id="PH" equiv-text="this.document.title"/>"</source>
|
<source>Error saving document "<x id="PH" equiv-text="this.document.title"/>"</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">843</context>
|
<context context-type="linenumber">863</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="448882439049417053" datatype="html">
|
<trans-unit id="448882439049417053" datatype="html">
|
||||||
<source>Error saving document</source>
|
<source>Error saving document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">888</context>
|
<context context-type="linenumber">908</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8410796510716511826" datatype="html">
|
<trans-unit id="8410796510716511826" datatype="html">
|
||||||
<source>Do you really want to move the document "<x id="PH" equiv-text="this.document.title"/>" to the trash?</source>
|
<source>Do you really want to move the document "<x id="PH" equiv-text="this.document.title"/>" to the trash?</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">920</context>
|
<context context-type="linenumber">940</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="282586936710748252" datatype="html">
|
<trans-unit id="282586936710748252" datatype="html">
|
||||||
<source>Documents can be restored prior to permanent deletion.</source>
|
<source>Documents can be restored prior to permanent deletion.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">921</context>
|
<context context-type="linenumber">941</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@ -6951,7 +6951,7 @@
|
|||||||
<source>Move to trash</source>
|
<source>Move to trash</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">923</context>
|
<context context-type="linenumber">943</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@ -6962,14 +6962,14 @@
|
|||||||
<source>Error deleting document</source>
|
<source>Error deleting document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">942</context>
|
<context context-type="linenumber">962</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="619486176823357521" datatype="html">
|
<trans-unit id="619486176823357521" datatype="html">
|
||||||
<source>Reprocess confirm</source>
|
<source>Reprocess confirm</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">962</context>
|
<context context-type="linenumber">982</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@ -6980,77 +6980,77 @@
|
|||||||
<source>This operation will permanently recreate the archive file for this document.</source>
|
<source>This operation will permanently recreate the archive file for this document.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">963</context>
|
<context context-type="linenumber">983</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="302054111564709516" datatype="html">
|
<trans-unit id="302054111564709516" datatype="html">
|
||||||
<source>The archive file will be re-generated with the current settings.</source>
|
<source>The archive file will be re-generated with the current settings.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">964</context>
|
<context context-type="linenumber">984</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8251197608401006898" datatype="html">
|
<trans-unit id="8251197608401006898" datatype="html">
|
||||||
<source>Reprocess operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background. Close and re-open or reload this document after the operation has completed to see new content.</source>
|
<source>Reprocess operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background. Close and re-open or reload this document after the operation has completed to see new content.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">974</context>
|
<context context-type="linenumber">994</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4409560272830824468" datatype="html">
|
<trans-unit id="4409560272830824468" datatype="html">
|
||||||
<source>Error executing operation</source>
|
<source>Error executing operation</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">985</context>
|
<context context-type="linenumber">1005</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6030453331794586802" datatype="html">
|
<trans-unit id="6030453331794586802" datatype="html">
|
||||||
<source>Error downloading document</source>
|
<source>Error downloading document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1034</context>
|
<context context-type="linenumber">1054</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4458954481601077369" datatype="html">
|
<trans-unit id="4458954481601077369" datatype="html">
|
||||||
<source>Page Fit</source>
|
<source>Page Fit</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1113</context>
|
<context context-type="linenumber">1131</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1217563727923422413" datatype="html">
|
<trans-unit id="1217563727923422413" datatype="html">
|
||||||
<source>Split confirm</source>
|
<source>Split confirm</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1324</context>
|
<context context-type="linenumber">1345</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2805304563009985503" datatype="html">
|
<trans-unit id="2805304563009985503" datatype="html">
|
||||||
<source>This operation will split the selected document(s) into new documents.</source>
|
<source>This operation will split the selected document(s) into new documents.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1325</context>
|
<context context-type="linenumber">1346</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7638681545012641321" datatype="html">
|
<trans-unit id="7638681545012641321" datatype="html">
|
||||||
<source>Split operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background.</source>
|
<source>Split operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1341</context>
|
<context context-type="linenumber">1362</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3235014591864339926" datatype="html">
|
<trans-unit id="3235014591864339926" datatype="html">
|
||||||
<source>Error executing split operation</source>
|
<source>Error executing split operation</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1350</context>
|
<context context-type="linenumber">1371</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6555329262222566158" datatype="html">
|
<trans-unit id="6555329262222566158" datatype="html">
|
||||||
<source>Rotate confirm</source>
|
<source>Rotate confirm</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1363</context>
|
<context context-type="linenumber">1384</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@ -7061,60 +7061,60 @@
|
|||||||
<source>This operation will permanently rotate the original version of the current document.</source>
|
<source>This operation will permanently rotate the original version of the current document.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1364</context>
|
<context context-type="linenumber">1385</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3802852336439815451" datatype="html">
|
<trans-unit id="3802852336439815451" datatype="html">
|
||||||
<source>Rotation of "<x id="PH" equiv-text="this.document.title"/>" will begin in the background. Close and re-open the document after the operation has completed to see the changes.</source>
|
<source>Rotation of "<x id="PH" equiv-text="this.document.title"/>" will begin in the background. Close and re-open the document after the operation has completed to see the changes.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1380</context>
|
<context context-type="linenumber">1401</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2962674215361798818" datatype="html">
|
<trans-unit id="2962674215361798818" datatype="html">
|
||||||
<source>Error executing rotate operation</source>
|
<source>Error executing rotate operation</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1392</context>
|
<context context-type="linenumber">1413</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3539261415918606512" datatype="html">
|
<trans-unit id="3539261415918606512" datatype="html">
|
||||||
<source>Delete pages confirm</source>
|
<source>Delete pages confirm</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1404</context>
|
<context context-type="linenumber">1425</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5854352498125813866" datatype="html">
|
<trans-unit id="5854352498125813866" datatype="html">
|
||||||
<source>This operation will permanently delete the selected pages from the original document.</source>
|
<source>This operation will permanently delete the selected pages from the original document.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1405</context>
|
<context context-type="linenumber">1426</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1138505464360427037" datatype="html">
|
<trans-unit id="1138505464360427037" datatype="html">
|
||||||
<source>Delete pages operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background. Close and re-open or reload this document after the operation has completed to see the changes.</source>
|
<source>Delete pages operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background. Close and re-open or reload this document after the operation has completed to see the changes.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1420</context>
|
<context context-type="linenumber">1441</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1249139200486584973" datatype="html">
|
<trans-unit id="1249139200486584973" datatype="html">
|
||||||
<source>Error executing delete pages operation</source>
|
<source>Error executing delete pages operation</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1429</context>
|
<context context-type="linenumber">1450</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6085793215710522488" datatype="html">
|
<trans-unit id="6085793215710522488" datatype="html">
|
||||||
<source>An error occurred loading tiff: <x id="PH" equiv-text="err.toString()"/></source>
|
<source>An error occurred loading tiff: <x id="PH" equiv-text="err.toString()"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1489</context>
|
<context context-type="linenumber">1510</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1493</context>
|
<context context-type="linenumber">1514</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4958946940233632319" datatype="html">
|
<trans-unit id="4958946940233632319" datatype="html">
|
||||||
@ -9854,42 +9854,42 @@
|
|||||||
<source>Document already exists.</source>
|
<source>Document already exists.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">23</context>
|
<context context-type="linenumber">24</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5103087968344279314" datatype="html">
|
<trans-unit id="5103087968344279314" datatype="html">
|
||||||
<source>Document already exists. Note: existing document is in the trash.</source>
|
<source>Document already exists. Note: existing document is in the trash.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">24</context>
|
<context context-type="linenumber">25</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6108404046106249255" datatype="html">
|
<trans-unit id="6108404046106249255" datatype="html">
|
||||||
<source>Document with ASN already exists.</source>
|
<source>Document with ASN already exists.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">25</context>
|
<context context-type="linenumber">26</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="65951081560571094" datatype="html">
|
<trans-unit id="65951081560571094" datatype="html">
|
||||||
<source>Document with ASN already exists. Note: existing document is in the trash.</source>
|
<source>Document with ASN already exists. Note: existing document is in the trash.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">26</context>
|
<context context-type="linenumber">27</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="148389968432135849" datatype="html">
|
<trans-unit id="148389968432135849" datatype="html">
|
||||||
<source>File not found.</source>
|
<source>File not found.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">27</context>
|
<context context-type="linenumber">28</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1520671543092565667" datatype="html">
|
<trans-unit id="1520671543092565667" datatype="html">
|
||||||
<source>Pre-consume script does not exist.</source>
|
<source>Pre-consume script does not exist.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">28</context>
|
<context context-type="linenumber">29</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -9897,7 +9897,7 @@
|
|||||||
<source>Error while executing pre-consume script.</source>
|
<source>Error while executing pre-consume script.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">29</context>
|
<context context-type="linenumber">30</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -9905,7 +9905,7 @@
|
|||||||
<source>Post-consume script does not exist.</source>
|
<source>Post-consume script does not exist.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">30</context>
|
<context context-type="linenumber">31</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -9913,7 +9913,7 @@
|
|||||||
<source>Error while executing post-consume script.</source>
|
<source>Error while executing post-consume script.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">31</context>
|
<context context-type="linenumber">32</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -9921,49 +9921,49 @@
|
|||||||
<source>Received new file.</source>
|
<source>Received new file.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">32</context>
|
<context context-type="linenumber">33</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7337565919209746135" datatype="html">
|
<trans-unit id="7337565919209746135" datatype="html">
|
||||||
<source>File type not supported.</source>
|
<source>File type not supported.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">33</context>
|
<context context-type="linenumber">34</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5002399167376099234" datatype="html">
|
<trans-unit id="5002399167376099234" datatype="html">
|
||||||
<source>Processing document...</source>
|
<source>Processing document...</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">34</context>
|
<context context-type="linenumber">35</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1085975194762600381" datatype="html">
|
<trans-unit id="1085975194762600381" datatype="html">
|
||||||
<source>Generating thumbnail...</source>
|
<source>Generating thumbnail...</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">35</context>
|
<context context-type="linenumber">36</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3280851677698431426" datatype="html">
|
<trans-unit id="3280851677698431426" datatype="html">
|
||||||
<source>Retrieving date from document...</source>
|
<source>Retrieving date from document...</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">36</context>
|
<context context-type="linenumber">37</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7162102384876037296" datatype="html">
|
<trans-unit id="7162102384876037296" datatype="html">
|
||||||
<source>Saving document...</source>
|
<source>Saving document...</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">37</context>
|
<context context-type="linenumber">38</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4550450765009165976" datatype="html">
|
<trans-unit id="4550450765009165976" datatype="html">
|
||||||
<source>Finished.</source>
|
<source>Finished.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">38</context>
|
<context context-type="linenumber">39</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
@ -12,17 +12,17 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/cdk": "^19.2.7",
|
"@angular/cdk": "^19.2.10",
|
||||||
"@angular/common": "~19.2.4",
|
"@angular/common": "~19.2.7",
|
||||||
"@angular/compiler": "~19.2.4",
|
"@angular/compiler": "~19.2.7",
|
||||||
"@angular/core": "~19.2.4",
|
"@angular/core": "~19.2.7",
|
||||||
"@angular/forms": "~19.2.4",
|
"@angular/forms": "~19.2.7",
|
||||||
"@angular/localize": "~19.2.4",
|
"@angular/localize": "~19.2.7",
|
||||||
"@angular/platform-browser": "~19.2.4",
|
"@angular/platform-browser": "~19.2.7",
|
||||||
"@angular/platform-browser-dynamic": "~19.2.4",
|
"@angular/platform-browser-dynamic": "~19.2.7",
|
||||||
"@angular/router": "~19.2.4",
|
"@angular/router": "~19.2.7",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
|
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
|
||||||
"@ng-select/ng-select": "^14.2.6",
|
"@ng-select/ng-select": "^14.7.0",
|
||||||
"@ngneat/dirty-check-forms": "^3.0.3",
|
"@ngneat/dirty-check-forms": "^3.0.3",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
@ -42,30 +42,30 @@
|
|||||||
"zone.js": "^0.15.0"
|
"zone.js": "^0.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-builders/custom-webpack": "^19.0.0",
|
"@angular-builders/custom-webpack": "^19.0.1",
|
||||||
"@angular-builders/jest": "^19.0.0",
|
"@angular-builders/jest": "^19.0.1",
|
||||||
"@angular-devkit/build-angular": "^19.2.5",
|
"@angular-devkit/build-angular": "^19.2.8",
|
||||||
"@angular-devkit/core": "^19.2.5",
|
"@angular-devkit/core": "^19.2.8",
|
||||||
"@angular-devkit/schematics": "^19.2.5",
|
"@angular-devkit/schematics": "^19.2.8",
|
||||||
"@angular-eslint/builder": "19.3.0",
|
"@angular-eslint/builder": "19.3.0",
|
||||||
"@angular-eslint/eslint-plugin": "19.3.0",
|
"@angular-eslint/eslint-plugin": "19.3.0",
|
||||||
"@angular-eslint/eslint-plugin-template": "19.3.0",
|
"@angular-eslint/eslint-plugin-template": "19.3.0",
|
||||||
"@angular-eslint/schematics": "19.3.0",
|
"@angular-eslint/schematics": "19.3.0",
|
||||||
"@angular-eslint/template-parser": "19.3.0",
|
"@angular-eslint/template-parser": "19.3.0",
|
||||||
"@angular/cli": "~19.2.5",
|
"@angular/cli": "~19.2.8",
|
||||||
"@angular/compiler-cli": "~19.2.4",
|
"@angular/compiler-cli": "~19.2.7",
|
||||||
"@codecov/webpack-plugin": "^1.9.0",
|
"@codecov/webpack-plugin": "^1.9.0",
|
||||||
"@playwright/test": "^1.51.1",
|
"@playwright/test": "^1.51.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^22.13.17",
|
"@types/node": "^22.13.17",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.29.0",
|
"@typescript-eslint/eslint-plugin": "^8.31.0",
|
||||||
"@typescript-eslint/parser": "^8.29.0",
|
"@typescript-eslint/parser": "^8.31.0",
|
||||||
"@typescript-eslint/utils": "^8.29.0",
|
"@typescript-eslint/utils": "^8.31.0",
|
||||||
"eslint": "^9.23.0",
|
"eslint": "^9.25.1",
|
||||||
"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-junit": "^16.0.0",
|
||||||
"jest-preset-angular": "^14.5.4",
|
"jest-preset-angular": "^14.5.5",
|
||||||
"jest-websocket-mock": "^2.5.0",
|
"jest-websocket-mock": "^2.5.0",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"prettier-plugin-organize-imports": "^4.1.0",
|
"prettier-plugin-organize-imports": "^4.1.0",
|
||||||
|
1080
src-ui/pnpm-lock.yaml
generated
1080
src-ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -405,7 +405,7 @@ describe('GlobalSearchComponent', () => {
|
|||||||
expect(toastErrorSpy).toHaveBeenCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
|
|
||||||
// succeed
|
// succeed
|
||||||
editDialog.succeeded.emit(true)
|
editDialog.succeeded.emit(object as any)
|
||||||
expect(toastInfoSpy).toHaveBeenCalled()
|
expect(toastInfoSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -456,7 +456,7 @@ describe('GlobalSearchComponent', () => {
|
|||||||
expect(toastErrorSpy).toHaveBeenCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
|
|
||||||
// succeed
|
// succeed
|
||||||
editDialog.succeeded.emit(true)
|
editDialog.succeeded.emit(searchResults.tags[0] as any)
|
||||||
expect(toastInfoSpy).toHaveBeenCalled()
|
expect(toastInfoSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ export abstract class EditDialogComponent<
|
|||||||
object: T
|
object: T
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
succeeded = new EventEmitter()
|
succeeded = new EventEmitter<T>()
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
failed = new EventEmitter()
|
failed = new EventEmitter()
|
||||||
|
@ -586,6 +586,8 @@ export class FilterableDropdownComponent
|
|||||||
this.selectionModel.reset()
|
this.selectionModel.reset()
|
||||||
this.modelIsDirty = false
|
this.modelIsDirty = false
|
||||||
}
|
}
|
||||||
|
this.selectionModel.singleSelect =
|
||||||
|
this.editing && !this.selectionModel.manyToOne
|
||||||
this.opened.next(this)
|
this.opened.next(this)
|
||||||
} else {
|
} else {
|
||||||
if (this.creating) {
|
if (this.creating) {
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-select>
|
</ng-select>
|
||||||
@if (allowCreate && !hideAddButton) {
|
@if (allowCreate && !hideAddButton) {
|
||||||
<button class="btn btn-outline-secondary" type="button" (click)="createTag()" [disabled]="disabled">
|
<button class="btn btn-outline-secondary" type="button" (click)="createTag(null, true)" [disabled]="disabled">
|
||||||
<i-bs width="1.2em" height="1.2em" name="plus"></i-bs>
|
<i-bs width="1.2em" height="1.2em" name="plus"></i-bs>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createTag(name: string = null) {
|
createTag(name: string = null, add: boolean = false) {
|
||||||
var modal = this.modalService.open(TagEditDialogComponent, {
|
var modal = this.modalService.open(TagEditDialogComponent, {
|
||||||
backdrop: 'static',
|
backdrop: 'static',
|
||||||
})
|
})
|
||||||
@ -143,9 +143,10 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
return firstValueFrom(
|
return firstValueFrom(
|
||||||
(modal.componentInstance as TagEditDialogComponent).succeeded.pipe(
|
(modal.componentInstance as TagEditDialogComponent).succeeded.pipe(
|
||||||
first(),
|
first(),
|
||||||
tap(() => {
|
tap((newTag) => {
|
||||||
this.tagService.listAll().subscribe((tags) => {
|
this.tagService.listAll().subscribe((tags) => {
|
||||||
this.tags = tags.results
|
this.tags = tags.results
|
||||||
|
add && this.addTag(newTag.id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
}
|
}
|
||||||
<div class="input-group input-group-sm me-md-5 d-none d-md-flex">
|
<div class="input-group input-group-sm me-md-5 d-none d-md-flex">
|
||||||
<button class="btn btn-outline-secondary" (click)="decreaseZoom()" i18n>-</button>
|
<button class="btn btn-outline-secondary" (click)="decreaseZoom()" i18n>-</button>
|
||||||
<select class="form-select" (change)="setZoom($event.target.value)">
|
<select class="form-select" (change)="setZoom($event.target.value)" [ngModel]="currentZoom">
|
||||||
@for (setting of zoomSettings; track setting) {
|
@for (setting of zoomSettings; track setting) {
|
||||||
<option [value]="setting" [attr.selected]="isZoomSelected(setting) ? 'selected' : null">
|
<option [value]="setting">
|
||||||
{{ getZoomSettingTitle(setting) }}
|
{{ getZoomSettingTitle(setting) }}
|
||||||
</option>
|
</option>
|
||||||
}
|
}
|
||||||
|
@ -456,11 +456,11 @@ describe('DocumentDetailComponent', () => {
|
|||||||
initNormally()
|
initNormally()
|
||||||
component.title = 'Foo Bar'
|
component.title = 'Foo Bar'
|
||||||
const closeSpy = jest.spyOn(component, 'close')
|
const closeSpy = jest.spyOn(component, 'close')
|
||||||
const updateSpy = jest.spyOn(documentService, 'update')
|
const patchSpy = jest.spyOn(documentService, 'patch')
|
||||||
const toastSpy = jest.spyOn(toastService, 'showInfo')
|
const toastSpy = jest.spyOn(toastService, 'showInfo')
|
||||||
updateSpy.mockImplementation((o) => of(doc))
|
patchSpy.mockImplementation((o) => of(doc))
|
||||||
component.save(true)
|
component.save(true)
|
||||||
expect(updateSpy).toHaveBeenCalled()
|
expect(patchSpy).toHaveBeenCalled()
|
||||||
expect(closeSpy).toHaveBeenCalled()
|
expect(closeSpy).toHaveBeenCalled()
|
||||||
expect(toastSpy).toHaveBeenCalledWith(
|
expect(toastSpy).toHaveBeenCalledWith(
|
||||||
'Document "Doc 3" saved successfully.'
|
'Document "Doc 3" saved successfully.'
|
||||||
@ -471,11 +471,11 @@ describe('DocumentDetailComponent', () => {
|
|||||||
initNormally()
|
initNormally()
|
||||||
component.title = 'Foo Bar'
|
component.title = 'Foo Bar'
|
||||||
const closeSpy = jest.spyOn(component, 'close')
|
const closeSpy = jest.spyOn(component, 'close')
|
||||||
const updateSpy = jest.spyOn(documentService, 'update')
|
const patchSpy = jest.spyOn(documentService, 'patch')
|
||||||
const toastSpy = jest.spyOn(toastService, 'showInfo')
|
const toastSpy = jest.spyOn(toastService, 'showInfo')
|
||||||
updateSpy.mockImplementation((o) => of(doc))
|
patchSpy.mockImplementation((o) => of(doc))
|
||||||
component.save()
|
component.save()
|
||||||
expect(updateSpy).toHaveBeenCalled()
|
expect(patchSpy).toHaveBeenCalled()
|
||||||
expect(closeSpy).not.toHaveBeenCalled()
|
expect(closeSpy).not.toHaveBeenCalled()
|
||||||
expect(toastSpy).toHaveBeenCalledWith(
|
expect(toastSpy).toHaveBeenCalledWith(
|
||||||
'Document "Doc 3" saved successfully.'
|
'Document "Doc 3" saved successfully.'
|
||||||
@ -487,12 +487,12 @@ describe('DocumentDetailComponent', () => {
|
|||||||
initNormally()
|
initNormally()
|
||||||
component.title = 'Foo Bar'
|
component.title = 'Foo Bar'
|
||||||
const closeSpy = jest.spyOn(component, 'close')
|
const closeSpy = jest.spyOn(component, 'close')
|
||||||
const updateSpy = jest.spyOn(documentService, 'update')
|
const patchSpy = jest.spyOn(documentService, 'patch')
|
||||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||||
const error = new Error('failed to save')
|
const error = new Error('failed to save')
|
||||||
updateSpy.mockImplementation(() => throwError(() => error))
|
patchSpy.mockImplementation(() => throwError(() => error))
|
||||||
component.save()
|
component.save()
|
||||||
expect(updateSpy).toHaveBeenCalled()
|
expect(patchSpy).toHaveBeenCalled()
|
||||||
expect(closeSpy).not.toHaveBeenCalled()
|
expect(closeSpy).not.toHaveBeenCalled()
|
||||||
expect(toastSpy).toHaveBeenCalledWith(
|
expect(toastSpy).toHaveBeenCalledWith(
|
||||||
'Error saving document "Doc 3"',
|
'Error saving document "Doc 3"',
|
||||||
@ -505,13 +505,13 @@ describe('DocumentDetailComponent', () => {
|
|||||||
initNormally()
|
initNormally()
|
||||||
component.title = 'Foo Bar'
|
component.title = 'Foo Bar'
|
||||||
const closeSpy = jest.spyOn(component, 'close')
|
const closeSpy = jest.spyOn(component, 'close')
|
||||||
const updateSpy = jest.spyOn(documentService, 'update')
|
const patchSpy = jest.spyOn(documentService, 'patch')
|
||||||
const toastSpy = jest.spyOn(toastService, 'showInfo')
|
const toastSpy = jest.spyOn(toastService, 'showInfo')
|
||||||
updateSpy.mockImplementation(() =>
|
patchSpy.mockImplementation(() =>
|
||||||
throwError(() => new Error('failed to save'))
|
throwError(() => new Error('failed to save'))
|
||||||
)
|
)
|
||||||
component.save(true)
|
component.save(true)
|
||||||
expect(updateSpy).toHaveBeenCalled()
|
expect(patchSpy).toHaveBeenCalled()
|
||||||
expect(closeSpy).toHaveBeenCalled()
|
expect(closeSpy).toHaveBeenCalled()
|
||||||
expect(toastSpy).toHaveBeenCalledWith(
|
expect(toastSpy).toHaveBeenCalledWith(
|
||||||
'Document "Doc 3" saved successfully.'
|
'Document "Doc 3" saved successfully.'
|
||||||
@ -522,8 +522,8 @@ describe('DocumentDetailComponent', () => {
|
|||||||
initNormally()
|
initNormally()
|
||||||
const nextDocId = 100
|
const nextDocId = 100
|
||||||
component.title = 'Foo Bar'
|
component.title = 'Foo Bar'
|
||||||
const updateSpy = jest.spyOn(documentService, 'update')
|
const patchSpy = jest.spyOn(documentService, 'patch')
|
||||||
updateSpy.mockReturnValue(of(doc))
|
patchSpy.mockReturnValue(of(doc))
|
||||||
const nextSpy = jest.spyOn(documentListViewService, 'getNext')
|
const nextSpy = jest.spyOn(documentListViewService, 'getNext')
|
||||||
nextSpy.mockReturnValue(of(nextDocId))
|
nextSpy.mockReturnValue(of(nextDocId))
|
||||||
const closeSpy = jest.spyOn(openDocumentsService, 'closeDocument')
|
const closeSpy = jest.spyOn(openDocumentsService, 'closeDocument')
|
||||||
@ -531,7 +531,7 @@ describe('DocumentDetailComponent', () => {
|
|||||||
const navigateSpy = jest.spyOn(router, 'navigate')
|
const navigateSpy = jest.spyOn(router, 'navigate')
|
||||||
|
|
||||||
component.saveEditNext()
|
component.saveEditNext()
|
||||||
expect(updateSpy).toHaveBeenCalled()
|
expect(patchSpy).toHaveBeenCalled()
|
||||||
expect(navigateSpy).toHaveBeenCalledWith(['documents', nextDocId])
|
expect(navigateSpy).toHaveBeenCalledWith(['documents', nextDocId])
|
||||||
expect
|
expect
|
||||||
})
|
})
|
||||||
@ -541,12 +541,12 @@ describe('DocumentDetailComponent', () => {
|
|||||||
initNormally()
|
initNormally()
|
||||||
component.title = 'Foo Bar'
|
component.title = 'Foo Bar'
|
||||||
const closeSpy = jest.spyOn(component, 'close')
|
const closeSpy = jest.spyOn(component, 'close')
|
||||||
const updateSpy = jest.spyOn(documentService, 'update')
|
const patchSpy = jest.spyOn(documentService, 'patch')
|
||||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||||
const error = new Error('failed to save')
|
const error = new Error('failed to save')
|
||||||
updateSpy.mockImplementation(() => throwError(() => error))
|
patchSpy.mockImplementation(() => throwError(() => error))
|
||||||
component.saveEditNext()
|
component.saveEditNext()
|
||||||
expect(updateSpy).toHaveBeenCalled()
|
expect(patchSpy).toHaveBeenCalled()
|
||||||
expect(closeSpy).not.toHaveBeenCalled()
|
expect(closeSpy).not.toHaveBeenCalled()
|
||||||
expect(toastSpy).toHaveBeenCalledWith('Error saving document', error)
|
expect(toastSpy).toHaveBeenCalledWith('Error saving document', error)
|
||||||
})
|
})
|
||||||
@ -791,14 +791,9 @@ describe('DocumentDetailComponent', () => {
|
|||||||
it('should select correct zoom setting in dropdown', () => {
|
it('should select correct zoom setting in dropdown', () => {
|
||||||
initNormally()
|
initNormally()
|
||||||
component.setZoom(ZoomSetting.PageFit)
|
component.setZoom(ZoomSetting.PageFit)
|
||||||
expect(component.isZoomSelected(ZoomSetting.PageFit)).toBeTruthy()
|
expect(component.currentZoom).toEqual(ZoomSetting.PageFit)
|
||||||
expect(component.isZoomSelected(ZoomSetting.One)).toBeFalsy()
|
|
||||||
component.setZoom(ZoomSetting.PageWidth)
|
|
||||||
expect(component.isZoomSelected(ZoomSetting.One)).toBeTruthy()
|
|
||||||
expect(component.isZoomSelected(ZoomSetting.PageFit)).toBeFalsy()
|
|
||||||
component.setZoom(ZoomSetting.Quarter)
|
component.setZoom(ZoomSetting.Quarter)
|
||||||
expect(component.isZoomSelected(ZoomSetting.Quarter)).toBeTruthy()
|
expect(component.currentZoom).toEqual(ZoomSetting.Quarter)
|
||||||
expect(component.isZoomSelected(ZoomSetting.PageFit)).toBeFalsy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should support updating notes dynamically', () => {
|
it('should support updating notes dynamically', () => {
|
||||||
@ -970,10 +965,10 @@ describe('DocumentDetailComponent', () => {
|
|||||||
expect(fixture.debugElement.nativeElement.textContent).toContain(
|
expect(fixture.debugElement.nativeElement.textContent).toContain(
|
||||||
customFields[1].name
|
customFields[1].name
|
||||||
)
|
)
|
||||||
const updateSpy = jest.spyOn(documentService, 'update')
|
const patchSpy = jest.spyOn(documentService, 'patch')
|
||||||
component.save(true)
|
component.save(true)
|
||||||
expect(updateSpy.mock.lastCall[0].custom_fields).toHaveLength(2)
|
expect(patchSpy.mock.lastCall[0].custom_fields).toHaveLength(2)
|
||||||
expect(updateSpy.mock.lastCall[0].custom_fields[1]).toEqual({
|
expect(patchSpy.mock.lastCall[0].custom_fields[1]).toEqual({
|
||||||
field: customFields[1].id,
|
field: customFields[1].id,
|
||||||
value: null,
|
value: null,
|
||||||
})
|
})
|
||||||
@ -990,13 +985,51 @@ describe('DocumentDetailComponent', () => {
|
|||||||
expect(
|
expect(
|
||||||
fixture.debugElement.query(By.css('form')).nativeElement.textContent
|
fixture.debugElement.query(By.css('form')).nativeElement.textContent
|
||||||
).not.toContain('Field 1')
|
).not.toContain('Field 1')
|
||||||
const updateSpy = jest.spyOn(documentService, 'update')
|
const patchSpy = jest.spyOn(documentService, 'patch')
|
||||||
component.save(true)
|
component.save(true)
|
||||||
expect(updateSpy.mock.lastCall[0].custom_fields).toHaveLength(
|
expect(patchSpy.mock.lastCall[0].custom_fields).toHaveLength(
|
||||||
initialLength - 1
|
initialLength - 1
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should correctly determine changed fields', () => {
|
||||||
|
initNormally()
|
||||||
|
expect(component['getChangedFields']()).toEqual({
|
||||||
|
id: doc.id,
|
||||||
|
})
|
||||||
|
component.documentForm.get('title').setValue('Foo Bar')
|
||||||
|
component.documentForm.get('permissions_form').setValue({
|
||||||
|
owner: 1,
|
||||||
|
set_permissions: {
|
||||||
|
view: {
|
||||||
|
users: [2],
|
||||||
|
groups: [],
|
||||||
|
},
|
||||||
|
change: {
|
||||||
|
users: [3],
|
||||||
|
groups: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
component.documentForm.get('title').markAsDirty()
|
||||||
|
component.documentForm.get('permissions_form').markAsDirty()
|
||||||
|
expect(component['getChangedFields']()).toEqual({
|
||||||
|
id: doc.id,
|
||||||
|
title: 'Foo Bar',
|
||||||
|
owner: 1,
|
||||||
|
set_permissions: {
|
||||||
|
view: {
|
||||||
|
users: [2],
|
||||||
|
groups: [],
|
||||||
|
},
|
||||||
|
change: {
|
||||||
|
users: [3],
|
||||||
|
groups: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('should show custom field errors', () => {
|
it('should show custom field errors', () => {
|
||||||
initNormally()
|
initNormally()
|
||||||
component.error = {
|
component.error = {
|
||||||
|
@ -784,6 +784,7 @@ export class DocumentDetailComponent
|
|||||||
this.title = doc.title
|
this.title = doc.title
|
||||||
this.updateFormForCustomFields()
|
this.updateFormForCustomFields()
|
||||||
this.documentForm.patchValue(doc)
|
this.documentForm.patchValue(doc)
|
||||||
|
this.documentForm.markAsPristine()
|
||||||
this.openDocumentService.setDirty(doc, false)
|
this.openDocumentService.setDirty(doc, false)
|
||||||
},
|
},
|
||||||
error: () => {
|
error: () => {
|
||||||
@ -794,11 +795,30 @@ export class DocumentDetailComponent
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getChangedFields(): any {
|
||||||
|
const changes = {
|
||||||
|
id: this.document.id,
|
||||||
|
}
|
||||||
|
Object.keys(this.documentForm.controls).forEach((key) => {
|
||||||
|
if (this.documentForm.get(key).dirty) {
|
||||||
|
if (key === 'permissions_form') {
|
||||||
|
changes['owner'] =
|
||||||
|
this.documentForm.get('permissions_form').value['owner']
|
||||||
|
changes['set_permissions'] =
|
||||||
|
this.documentForm.get('permissions_form').value['set_permissions']
|
||||||
|
} else {
|
||||||
|
changes[key] = this.documentForm.get(key).value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
save(close: boolean = false) {
|
save(close: boolean = false) {
|
||||||
this.networkActive = true
|
this.networkActive = true
|
||||||
;(document.activeElement as HTMLElement)?.dispatchEvent(new Event('change'))
|
;(document.activeElement as HTMLElement)?.dispatchEvent(new Event('change'))
|
||||||
this.documentsService
|
this.documentsService
|
||||||
.update(this.document)
|
.patch(this.getChangedFields())
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (docValues) => {
|
next: (docValues) => {
|
||||||
@ -852,7 +872,7 @@ export class DocumentDetailComponent
|
|||||||
this.networkActive = true
|
this.networkActive = true
|
||||||
this.store.next(this.documentForm.value)
|
this.store.next(this.documentForm.value)
|
||||||
this.documentsService
|
this.documentsService
|
||||||
.update(this.document)
|
.patch(this.getChangedFields())
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((updateResult) => {
|
switchMap((updateResult) => {
|
||||||
return this.documentListViewService
|
return this.documentListViewService
|
||||||
@ -1099,12 +1119,10 @@ export class DocumentDetailComponent
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
isZoomSelected(setting: ZoomSetting): boolean {
|
get currentZoom() {
|
||||||
if (this.previewZoomScale === ZoomSetting.PageFit) {
|
if (this.previewZoomScale === ZoomSetting.PageFit) {
|
||||||
return setting === ZoomSetting.PageFit
|
return ZoomSetting.PageFit
|
||||||
}
|
} else return this.previewZoomSetting
|
||||||
|
|
||||||
return this.previewZoomSetting === setting
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getZoomSettingTitle(setting: ZoomSetting): string {
|
getZoomSettingTitle(setting: ZoomSetting): string {
|
||||||
@ -1305,6 +1323,8 @@ export class DocumentDetailComponent
|
|||||||
created: new Date(),
|
created: new Date(),
|
||||||
})
|
})
|
||||||
this.updateFormForCustomFields(true)
|
this.updateFormForCustomFields(true)
|
||||||
|
this.documentForm.get('custom_fields').markAsDirty()
|
||||||
|
this.documentForm.updateValueAndValidity()
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeField(fieldInstance: CustomFieldInstance) {
|
public removeField(fieldInstance: CustomFieldInstance) {
|
||||||
@ -1313,6 +1333,7 @@ export class DocumentDetailComponent
|
|||||||
1
|
1
|
||||||
)
|
)
|
||||||
this.updateFormForCustomFields(true)
|
this.updateFormForCustomFields(true)
|
||||||
|
this.documentForm.get('custom_fields').markAsDirty()
|
||||||
this.documentForm.updateValueAndValidity()
|
this.documentForm.updateValueAndValidity()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ describe('MailComponent', () => {
|
|||||||
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
||||||
editDialog.failed.emit()
|
editDialog.failed.emit()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toBeCalled()
|
||||||
editDialog.succeeded.emit(mailAccounts[0])
|
editDialog.succeeded.emit(mailAccounts[0] as any)
|
||||||
expect(toastInfoSpy).toHaveBeenCalledWith(
|
expect(toastInfoSpy).toHaveBeenCalledWith(
|
||||||
`Saved account "${mailAccounts[0].name}".`
|
`Saved account "${mailAccounts[0].name}".`
|
||||||
)
|
)
|
||||||
@ -246,7 +246,7 @@ describe('MailComponent', () => {
|
|||||||
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
||||||
editDialog.failed.emit()
|
editDialog.failed.emit()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toBeCalled()
|
||||||
editDialog.succeeded.emit(mailRules[0])
|
editDialog.succeeded.emit(mailRules[0] as any)
|
||||||
expect(toastInfoSpy).toHaveBeenCalledWith(
|
expect(toastInfoSpy).toHaveBeenCalledWith(
|
||||||
`Saved rule "${mailRules[0].name}".`
|
`Saved rule "${mailRules[0].name}".`
|
||||||
)
|
)
|
||||||
|
@ -7,4 +7,6 @@ export interface WebsocketProgressMessage {
|
|||||||
message?: string
|
message?: string
|
||||||
document_id: number
|
document_id: number
|
||||||
owner_id?: number
|
owner_id?: number
|
||||||
|
users_can_view?: number[]
|
||||||
|
groups_can_view?: number[]
|
||||||
}
|
}
|
||||||
|
@ -268,15 +268,15 @@ describe(`DocumentService`, () => {
|
|||||||
expect(req.request.method).toEqual('GET')
|
expect(req.request.method).toEqual('GET')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should pass remove_inbox_tags setting to update', () => {
|
it('should pass remove_inbox_tags setting to patch', () => {
|
||||||
subscription = service.update(documents[0]).subscribe()
|
subscription = service.patch(documents[0]).subscribe()
|
||||||
let req = httpTestingController.expectOne(
|
let req = httpTestingController.expectOne(
|
||||||
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/`
|
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/`
|
||||||
)
|
)
|
||||||
expect(req.request.body.remove_inbox_tags).toEqual(false)
|
expect(req.request.body.remove_inbox_tags).toEqual(false)
|
||||||
|
|
||||||
settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS, true)
|
settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS, true)
|
||||||
subscription = service.update(documents[0]).subscribe()
|
subscription = service.patch(documents[0]).subscribe()
|
||||||
req = httpTestingController.expectOne(
|
req = httpTestingController.expectOne(
|
||||||
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/`
|
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/`
|
||||||
)
|
)
|
||||||
|
@ -189,13 +189,13 @@ export class DocumentService extends AbstractPaperlessService<Document> {
|
|||||||
return this.http.get<number>(this.getResourceUrl(null, 'next_asn'))
|
return this.http.get<number>(this.getResourceUrl(null, 'next_asn'))
|
||||||
}
|
}
|
||||||
|
|
||||||
update(o: Document): Observable<Document> {
|
patch(o: Document): Observable<Document> {
|
||||||
// we want to only set created_date
|
// we want to only set created_date
|
||||||
o.created = undefined
|
delete o.created
|
||||||
o.remove_inbox_tags = !!this.settingsService.get(
|
o.remove_inbox_tags = !!this.settingsService.get(
|
||||||
SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS
|
SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS
|
||||||
)
|
)
|
||||||
return super.update(o)
|
return super.patch(o)
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadDocument(formData) {
|
uploadDocument(formData) {
|
||||||
|
@ -355,6 +355,50 @@ describe('ConsumerStatusService', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should notify user if user can view or is in group', () => {
|
||||||
|
settingsService.currentUser = {
|
||||||
|
id: 1,
|
||||||
|
username: 'testuser',
|
||||||
|
is_superuser: false,
|
||||||
|
groups: [1],
|
||||||
|
}
|
||||||
|
websocketStatusService.connect()
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id: '1234',
|
||||||
|
filename: 'file1.pdf',
|
||||||
|
current_progress: 50,
|
||||||
|
max_progress: 100,
|
||||||
|
docuement_id: 12,
|
||||||
|
owner_id: 2,
|
||||||
|
status: 'WORKING',
|
||||||
|
users_can_view: [1],
|
||||||
|
groups_can_view: [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
||||||
|
1
|
||||||
|
)
|
||||||
|
server.send({
|
||||||
|
type: WebsocketStatusType.STATUS_UPDATE,
|
||||||
|
data: {
|
||||||
|
task_id: '5678',
|
||||||
|
filename: 'file2.pdf',
|
||||||
|
current_progress: 50,
|
||||||
|
max_progress: 100,
|
||||||
|
docuement_id: 13,
|
||||||
|
owner_id: 2,
|
||||||
|
status: 'WORKING',
|
||||||
|
users_can_view: [],
|
||||||
|
groups_can_view: [1],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
||||||
|
2
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('should trigger deleted subject on document deleted', () => {
|
it('should trigger deleted subject on document deleted', () => {
|
||||||
let deleted = false
|
let deleted = false
|
||||||
websocketStatusService.onDocumentDeleted().subscribe(() => {
|
websocketStatusService.onDocumentDeleted().subscribe(() => {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
|
import { User } from '../data/user'
|
||||||
import { WebsocketDocumentsDeletedMessage } from '../data/websocket-documents-deleted-message'
|
import { WebsocketDocumentsDeletedMessage } from '../data/websocket-documents-deleted-message'
|
||||||
import { WebsocketProgressMessage } from '../data/websocket-progress-message'
|
import { WebsocketProgressMessage } from '../data/websocket-progress-message'
|
||||||
import { SettingsService } from './settings.service'
|
import { SettingsService } from './settings.service'
|
||||||
@ -173,13 +174,25 @@ export class WebsocketStatusService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private canViewMessage(messageData: WebsocketProgressMessage): boolean {
|
||||||
|
// see paperless.consumers.StatusConsumer._can_view
|
||||||
|
const user: User = this.settingsService.currentUser
|
||||||
|
return (
|
||||||
|
!messageData.owner_id ||
|
||||||
|
user.is_superuser ||
|
||||||
|
(messageData.owner_id && messageData.owner_id === user.id) ||
|
||||||
|
(messageData.users_can_view &&
|
||||||
|
messageData.users_can_view.includes(user.id)) ||
|
||||||
|
(messageData.groups_can_view &&
|
||||||
|
messageData.groups_can_view.some((groupId) =>
|
||||||
|
user.groups?.includes(groupId)
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
handleProgressUpdate(messageData: WebsocketProgressMessage) {
|
handleProgressUpdate(messageData: WebsocketProgressMessage) {
|
||||||
// fallback if backend didn't restrict message
|
// fallback if backend didn't restrict message
|
||||||
if (
|
if (!this.canViewMessage(messageData)) {
|
||||||
messageData.owner_id &&
|
|
||||||
messageData.owner_id !== this.settingsService.currentUser?.id &&
|
|
||||||
!this.settingsService.currentUser?.is_superuser
|
|
||||||
) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +137,10 @@ class ConsumerPlugin(
|
|||||||
extra_args={
|
extra_args={
|
||||||
"document_id": document_id,
|
"document_id": document_id,
|
||||||
"owner_id": self.metadata.owner_id if self.metadata.owner_id else None,
|
"owner_id": self.metadata.owner_id if self.metadata.owner_id else None,
|
||||||
|
"users_can_view": (self.metadata.view_users or [])
|
||||||
|
+ (self.metadata.change_users or []),
|
||||||
|
"groups_can_view": (self.metadata.view_groups or [])
|
||||||
|
+ (self.metadata.change_groups or []),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -123,14 +123,15 @@ def train_classifier(*, scheduled=True):
|
|||||||
task.result = "Training data unchanged"
|
task.result = "Training data unchanged"
|
||||||
|
|
||||||
task.status = states.SUCCESS
|
task.status = states.SUCCESS
|
||||||
task.date_done = timezone.now()
|
|
||||||
task.save(update_fields=["status", "result", "date_done"])
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("Classifier error: " + str(e))
|
logger.warning("Classifier error: " + str(e))
|
||||||
task.status = states.FAILURE
|
task.status = states.FAILURE
|
||||||
task.result = str(e)
|
task.result = str(e)
|
||||||
|
|
||||||
|
task.date_done = timezone.now()
|
||||||
|
task.save(update_fields=["status", "result", "date_done"])
|
||||||
|
|
||||||
|
|
||||||
@shared_task(bind=True)
|
@shared_task(bind=True)
|
||||||
def consume_file(
|
def consume_file(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import filecmp
|
import filecmp
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
@ -19,7 +18,7 @@ from documents.tasks import update_document_content_maybe_archive_file
|
|||||||
from documents.tests.utils import DirectoriesMixin
|
from documents.tests.utils import DirectoriesMixin
|
||||||
from documents.tests.utils import FileSystemAssertsMixin
|
from documents.tests.utils import FileSystemAssertsMixin
|
||||||
|
|
||||||
sample_file = os.path.join(os.path.dirname(__file__), "samples", "simple.pdf")
|
sample_file: Path = Path(__file__).parent / "samples" / "simple.pdf"
|
||||||
|
|
||||||
|
|
||||||
@override_settings(FILENAME_FORMAT="{correspondent}/{title}")
|
@override_settings(FILENAME_FORMAT="{correspondent}/{title}")
|
||||||
@ -34,19 +33,13 @@ class TestArchiver(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
|
|||||||
|
|
||||||
def test_archiver(self):
|
def test_archiver(self):
|
||||||
doc = self.make_models()
|
doc = self.make_models()
|
||||||
shutil.copy(
|
shutil.copy(sample_file, Path(self.dirs.originals_dir) / f"{doc.id:07}.pdf")
|
||||||
sample_file,
|
|
||||||
os.path.join(self.dirs.originals_dir, f"{doc.id:07}.pdf"),
|
|
||||||
)
|
|
||||||
|
|
||||||
call_command("document_archiver", "--processes", "1")
|
call_command("document_archiver", "--processes", "1")
|
||||||
|
|
||||||
def test_handle_document(self):
|
def test_handle_document(self):
|
||||||
doc = self.make_models()
|
doc = self.make_models()
|
||||||
shutil.copy(
|
shutil.copy(sample_file, Path(self.dirs.originals_dir) / f"{doc.id:07}.pdf")
|
||||||
sample_file,
|
|
||||||
os.path.join(self.dirs.originals_dir, f"{doc.id:07}.pdf"),
|
|
||||||
)
|
|
||||||
|
|
||||||
update_document_content_maybe_archive_file(doc.pk)
|
update_document_content_maybe_archive_file(doc.pk)
|
||||||
|
|
||||||
@ -90,11 +83,8 @@ class TestArchiver(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
|
|||||||
mime_type="application/pdf",
|
mime_type="application/pdf",
|
||||||
filename="document_01.pdf",
|
filename="document_01.pdf",
|
||||||
)
|
)
|
||||||
shutil.copy(sample_file, os.path.join(self.dirs.originals_dir, "document.pdf"))
|
shutil.copy(sample_file, Path(self.dirs.originals_dir) / "document.pdf")
|
||||||
shutil.copy(
|
shutil.copy(sample_file, Path(self.dirs.originals_dir) / "document_01.pdf")
|
||||||
sample_file,
|
|
||||||
os.path.join(self.dirs.originals_dir, "document_01.pdf"),
|
|
||||||
)
|
|
||||||
|
|
||||||
update_document_content_maybe_archive_file(doc2.pk)
|
update_document_content_maybe_archive_file(doc2.pk)
|
||||||
update_document_content_maybe_archive_file(doc1.pk)
|
update_document_content_maybe_archive_file(doc1.pk)
|
||||||
@ -136,22 +126,22 @@ class TestDecryptDocuments(FileSystemAssertsMixin, TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
os.path.join(
|
(
|
||||||
os.path.dirname(__file__),
|
Path(__file__).parent
|
||||||
"samples",
|
/ "samples"
|
||||||
"documents",
|
/ "documents"
|
||||||
"originals",
|
/ "originals"
|
||||||
"0000004.pdf.gpg",
|
/ "0000004.pdf.gpg"
|
||||||
),
|
),
|
||||||
originals_dir / "0000004.pdf.gpg",
|
originals_dir / "0000004.pdf.gpg",
|
||||||
)
|
)
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
os.path.join(
|
(
|
||||||
os.path.dirname(__file__),
|
Path(__file__).parent
|
||||||
"samples",
|
/ "samples"
|
||||||
"documents",
|
/ "documents"
|
||||||
"thumbnails",
|
/ "thumbnails"
|
||||||
"0000004.webp.gpg",
|
/ "0000004.webp.gpg"
|
||||||
),
|
),
|
||||||
thumb_dir / f"{doc.id:07}.webp.gpg",
|
thumb_dir / f"{doc.id:07}.webp.gpg",
|
||||||
)
|
)
|
||||||
@ -162,13 +152,13 @@ class TestDecryptDocuments(FileSystemAssertsMixin, TestCase):
|
|||||||
|
|
||||||
self.assertEqual(doc.storage_type, Document.STORAGE_TYPE_UNENCRYPTED)
|
self.assertEqual(doc.storage_type, Document.STORAGE_TYPE_UNENCRYPTED)
|
||||||
self.assertEqual(doc.filename, "0000004.pdf")
|
self.assertEqual(doc.filename, "0000004.pdf")
|
||||||
self.assertIsFile(os.path.join(originals_dir, "0000004.pdf"))
|
self.assertIsFile(Path(originals_dir) / "0000004.pdf")
|
||||||
self.assertIsFile(doc.source_path)
|
self.assertIsFile(doc.source_path)
|
||||||
self.assertIsFile(os.path.join(thumb_dir, f"{doc.id:07}.webp"))
|
self.assertIsFile(Path(thumb_dir) / f"{doc.id:07}.webp")
|
||||||
self.assertIsFile(doc.thumbnail_path)
|
self.assertIsFile(doc.thumbnail_path)
|
||||||
|
|
||||||
with doc.source_file as f:
|
with doc.source_file as f:
|
||||||
checksum = hashlib.md5(f.read()).hexdigest()
|
checksum: str = hashlib.md5(f.read()).hexdigest()
|
||||||
self.assertEqual(checksum, doc.checksum)
|
self.assertEqual(checksum, doc.checksum)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import filecmp
|
import filecmp
|
||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@ -94,13 +93,13 @@ class ConsumerThreadMixin(DocumentConsumeDelayMixin):
|
|||||||
print("Consumed a perfectly valid file.") # noqa: T201
|
print("Consumed a perfectly valid file.") # noqa: T201
|
||||||
|
|
||||||
def slow_write_file(self, target, *, incomplete=False):
|
def slow_write_file(self, target, *, incomplete=False):
|
||||||
with open(self.sample_file, "rb") as f:
|
with Path(self.sample_file).open("rb") as f:
|
||||||
pdf_bytes = f.read()
|
pdf_bytes = f.read()
|
||||||
|
|
||||||
if incomplete:
|
if incomplete:
|
||||||
pdf_bytes = pdf_bytes[: len(pdf_bytes) - 100]
|
pdf_bytes = pdf_bytes[: len(pdf_bytes) - 100]
|
||||||
|
|
||||||
with open(target, "wb") as f:
|
with Path(target).open("wb") as f:
|
||||||
# this will take 2 seconds, since the file is about 20k.
|
# this will take 2 seconds, since the file is about 20k.
|
||||||
print("Start writing file.") # noqa: T201
|
print("Start writing file.") # noqa: T201
|
||||||
for b in chunked(1000, pdf_bytes):
|
for b in chunked(1000, pdf_bytes):
|
||||||
@ -116,7 +115,7 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase):
|
|||||||
def test_consume_file(self):
|
def test_consume_file(self):
|
||||||
self.t_start()
|
self.t_start()
|
||||||
|
|
||||||
f = Path(os.path.join(self.dirs.consumption_dir, "my_file.pdf"))
|
f = Path(self.dirs.consumption_dir) / "my_file.pdf"
|
||||||
shutil.copy(self.sample_file, f)
|
shutil.copy(self.sample_file, f)
|
||||||
|
|
||||||
self.wait_for_task_mock_call()
|
self.wait_for_task_mock_call()
|
||||||
@ -130,7 +129,7 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase):
|
|||||||
def test_consume_file_invalid_ext(self):
|
def test_consume_file_invalid_ext(self):
|
||||||
self.t_start()
|
self.t_start()
|
||||||
|
|
||||||
f = os.path.join(self.dirs.consumption_dir, "my_file.wow")
|
f = Path(self.dirs.consumption_dir) / "my_file.wow"
|
||||||
shutil.copy(self.sample_file, f)
|
shutil.copy(self.sample_file, f)
|
||||||
|
|
||||||
self.wait_for_task_mock_call()
|
self.wait_for_task_mock_call()
|
||||||
@ -138,7 +137,7 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase):
|
|||||||
self.consume_file_mock.assert_not_called()
|
self.consume_file_mock.assert_not_called()
|
||||||
|
|
||||||
def test_consume_existing_file(self):
|
def test_consume_existing_file(self):
|
||||||
f = Path(os.path.join(self.dirs.consumption_dir, "my_file.pdf"))
|
f = Path(self.dirs.consumption_dir) / "my_file.pdf"
|
||||||
shutil.copy(self.sample_file, f)
|
shutil.copy(self.sample_file, f)
|
||||||
|
|
||||||
self.t_start()
|
self.t_start()
|
||||||
@ -154,7 +153,7 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase):
|
|||||||
|
|
||||||
self.t_start()
|
self.t_start()
|
||||||
|
|
||||||
fname = Path(os.path.join(self.dirs.consumption_dir, "my_file.pdf"))
|
fname = Path(self.dirs.consumption_dir) / "my_file.pdf"
|
||||||
|
|
||||||
self.slow_write_file(fname)
|
self.slow_write_file(fname)
|
||||||
|
|
||||||
@ -174,8 +173,8 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase):
|
|||||||
|
|
||||||
self.t_start()
|
self.t_start()
|
||||||
|
|
||||||
fname = Path(os.path.join(self.dirs.consumption_dir, "my_file.~df"))
|
fname = Path(self.dirs.consumption_dir) / "my_file.~df"
|
||||||
fname2 = Path(os.path.join(self.dirs.consumption_dir, "my_file.pdf"))
|
fname2 = Path(self.dirs.consumption_dir) / "my_file.pdf"
|
||||||
|
|
||||||
self.slow_write_file(fname)
|
self.slow_write_file(fname)
|
||||||
shutil.move(fname, fname2)
|
shutil.move(fname, fname2)
|
||||||
@ -196,7 +195,7 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase):
|
|||||||
|
|
||||||
self.t_start()
|
self.t_start()
|
||||||
|
|
||||||
fname = Path(os.path.join(self.dirs.consumption_dir, "my_file.pdf"))
|
fname = Path(self.dirs.consumption_dir) / "my_file.pdf"
|
||||||
self.slow_write_file(fname, incomplete=True)
|
self.slow_write_file(fname, incomplete=True)
|
||||||
|
|
||||||
self.wait_for_task_mock_call()
|
self.wait_for_task_mock_call()
|
||||||
@ -225,23 +224,23 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase):
|
|||||||
|
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
self.sample_file,
|
self.sample_file,
|
||||||
os.path.join(self.dirs.consumption_dir, ".DS_STORE"),
|
Path(self.dirs.consumption_dir) / ".DS_STORE",
|
||||||
)
|
)
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
self.sample_file,
|
self.sample_file,
|
||||||
os.path.join(self.dirs.consumption_dir, "my_file.pdf"),
|
Path(self.dirs.consumption_dir) / "my_file.pdf",
|
||||||
)
|
)
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
self.sample_file,
|
self.sample_file,
|
||||||
os.path.join(self.dirs.consumption_dir, "._my_file.pdf"),
|
Path(self.dirs.consumption_dir) / "._my_file.pdf",
|
||||||
)
|
)
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
self.sample_file,
|
self.sample_file,
|
||||||
os.path.join(self.dirs.consumption_dir, "my_second_file.pdf"),
|
Path(self.dirs.consumption_dir) / "my_second_file.pdf",
|
||||||
)
|
)
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
self.sample_file,
|
self.sample_file,
|
||||||
os.path.join(self.dirs.consumption_dir, "._my_second_file.pdf"),
|
Path(self.dirs.consumption_dir) / "._my_second_file.pdf",
|
||||||
)
|
)
|
||||||
|
|
||||||
sleep(5)
|
sleep(5)
|
||||||
@ -259,60 +258,66 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase):
|
|||||||
def test_is_ignored(self):
|
def test_is_ignored(self):
|
||||||
test_paths = [
|
test_paths = [
|
||||||
{
|
{
|
||||||
"path": os.path.join(self.dirs.consumption_dir, "foo.pdf"),
|
"path": (Path(self.dirs.consumption_dir) / "foo.pdf").as_posix(),
|
||||||
"ignore": False,
|
"ignore": False,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(self.dirs.consumption_dir, "foo", "bar.pdf"),
|
"path": (
|
||||||
|
Path(self.dirs.consumption_dir) / "foo" / "bar.pdf"
|
||||||
|
).as_posix(),
|
||||||
"ignore": False,
|
"ignore": False,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(self.dirs.consumption_dir, ".DS_STORE"),
|
"path": (Path(self.dirs.consumption_dir) / ".DS_STORE").as_posix(),
|
||||||
"ignore": True,
|
"ignore": True,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(self.dirs.consumption_dir, ".DS_Store"),
|
"path": (Path(self.dirs.consumption_dir) / ".DS_Store").as_posix(),
|
||||||
"ignore": True,
|
"ignore": True,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(self.dirs.consumption_dir, ".stfolder", "foo.pdf"),
|
"path": (
|
||||||
|
Path(self.dirs.consumption_dir) / ".stfolder" / "foo.pdf"
|
||||||
|
).as_posix(),
|
||||||
"ignore": True,
|
"ignore": True,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(self.dirs.consumption_dir, ".stfolder.pdf"),
|
"path": (Path(self.dirs.consumption_dir) / ".stfolder.pdf").as_posix(),
|
||||||
"ignore": False,
|
"ignore": False,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(
|
"path": (
|
||||||
self.dirs.consumption_dir,
|
Path(self.dirs.consumption_dir) / ".stversions" / "foo.pdf"
|
||||||
".stversions",
|
).as_posix(),
|
||||||
"foo.pdf",
|
|
||||||
),
|
|
||||||
"ignore": True,
|
"ignore": True,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(self.dirs.consumption_dir, ".stversions.pdf"),
|
"path": (
|
||||||
|
Path(self.dirs.consumption_dir) / ".stversions.pdf"
|
||||||
|
).as_posix(),
|
||||||
"ignore": False,
|
"ignore": False,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(self.dirs.consumption_dir, "._foo.pdf"),
|
"path": (Path(self.dirs.consumption_dir) / "._foo.pdf").as_posix(),
|
||||||
"ignore": True,
|
"ignore": True,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(self.dirs.consumption_dir, "my_foo.pdf"),
|
"path": (Path(self.dirs.consumption_dir) / "my_foo.pdf").as_posix(),
|
||||||
"ignore": False,
|
"ignore": False,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(self.dirs.consumption_dir, "._foo", "bar.pdf"),
|
"path": (
|
||||||
|
Path(self.dirs.consumption_dir) / "._foo" / "bar.pdf"
|
||||||
|
).as_posix(),
|
||||||
"ignore": True,
|
"ignore": True,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": os.path.join(
|
"path": (
|
||||||
self.dirs.consumption_dir,
|
Path(self.dirs.consumption_dir)
|
||||||
"@eaDir",
|
/ "@eaDir"
|
||||||
"SYNO@.fileindexdb",
|
/ "SYNO@.fileindexdb"
|
||||||
"_1jk.fnm",
|
/ "_1jk.fnm"
|
||||||
),
|
).as_posix(),
|
||||||
"ignore": True,
|
"ignore": True,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -332,7 +337,7 @@ class TestConsumer(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCase):
|
|||||||
|
|
||||||
self.t_start()
|
self.t_start()
|
||||||
|
|
||||||
f = os.path.join(self.dirs.consumption_dir, "my_file.pdf")
|
f = Path(self.dirs.consumption_dir) / "my_file.pdf"
|
||||||
shutil.copy(self.sample_file, f)
|
shutil.copy(self.sample_file, f)
|
||||||
|
|
||||||
self.wait_for_task_mock_call()
|
self.wait_for_task_mock_call()
|
||||||
@ -380,9 +385,9 @@ class TestConsumerTags(DirectoriesMixin, ConsumerThreadMixin, TransactionTestCas
|
|||||||
|
|
||||||
self.t_start()
|
self.t_start()
|
||||||
|
|
||||||
path = os.path.join(self.dirs.consumption_dir, *tag_names)
|
path = Path(self.dirs.consumption_dir) / "/".join(tag_names)
|
||||||
os.makedirs(path, exist_ok=True)
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
f = Path(os.path.join(path, "my_file.pdf"))
|
f = path / "my_file.pdf"
|
||||||
# Wait at least inotify read_delay for recursive watchers
|
# Wait at least inotify read_delay for recursive watchers
|
||||||
# to be created for the new directories
|
# to be created for the new directories
|
||||||
sleep(1)
|
sleep(1)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
@ -183,16 +182,16 @@ class TestExportImport(
|
|||||||
|
|
||||||
call_command(*args)
|
call_command(*args)
|
||||||
|
|
||||||
with open(os.path.join(self.target, "manifest.json")) as f:
|
with (self.target / "manifest.json").open() as f:
|
||||||
manifest = json.load(f)
|
manifest = json.load(f)
|
||||||
|
|
||||||
return manifest
|
return manifest
|
||||||
|
|
||||||
def test_exporter(self, *, use_filename_format=False):
|
def test_exporter(self, *, use_filename_format=False):
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
num_permission_objects = Permission.objects.count()
|
num_permission_objects = Permission.objects.count()
|
||||||
@ -210,7 +209,7 @@ class TestExportImport(
|
|||||||
4,
|
4,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertIsFile(os.path.join(self.target, "manifest.json"))
|
self.assertIsFile((self.target / "manifest.json").as_posix())
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self._get_document_from_manifest(manifest, self.d1.id)["fields"]["title"],
|
self._get_document_from_manifest(manifest, self.d1.id)["fields"]["title"],
|
||||||
@ -231,19 +230,17 @@ class TestExportImport(
|
|||||||
|
|
||||||
for element in manifest:
|
for element in manifest:
|
||||||
if element["model"] == "documents.document":
|
if element["model"] == "documents.document":
|
||||||
fname = os.path.join(
|
fname = (
|
||||||
self.target,
|
self.target / element[document_exporter.EXPORTER_FILE_NAME]
|
||||||
element[document_exporter.EXPORTER_FILE_NAME],
|
).as_posix()
|
||||||
)
|
|
||||||
self.assertIsFile(fname)
|
self.assertIsFile(fname)
|
||||||
self.assertIsFile(
|
self.assertIsFile(
|
||||||
os.path.join(
|
(
|
||||||
self.target,
|
self.target / element[document_exporter.EXPORTER_THUMBNAIL_NAME]
|
||||||
element[document_exporter.EXPORTER_THUMBNAIL_NAME],
|
).as_posix(),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(fname, "rb") as f:
|
with Path(fname).open("rb") as f:
|
||||||
checksum = hashlib.md5(f.read()).hexdigest()
|
checksum = hashlib.md5(f.read()).hexdigest()
|
||||||
self.assertEqual(checksum, element["fields"]["checksum"])
|
self.assertEqual(checksum, element["fields"]["checksum"])
|
||||||
|
|
||||||
@ -253,13 +250,12 @@ class TestExportImport(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if document_exporter.EXPORTER_ARCHIVE_NAME in element:
|
if document_exporter.EXPORTER_ARCHIVE_NAME in element:
|
||||||
fname = os.path.join(
|
fname = (
|
||||||
self.target,
|
self.target / element[document_exporter.EXPORTER_ARCHIVE_NAME]
|
||||||
element[document_exporter.EXPORTER_ARCHIVE_NAME],
|
).as_posix()
|
||||||
)
|
|
||||||
self.assertIsFile(fname)
|
self.assertIsFile(fname)
|
||||||
|
|
||||||
with open(fname, "rb") as f:
|
with Path(fname).open("rb") as f:
|
||||||
checksum = hashlib.md5(f.read()).hexdigest()
|
checksum = hashlib.md5(f.read()).hexdigest()
|
||||||
self.assertEqual(checksum, element["fields"]["archive_checksum"])
|
self.assertEqual(checksum, element["fields"]["archive_checksum"])
|
||||||
|
|
||||||
@ -297,10 +293,10 @@ class TestExportImport(
|
|||||||
self.assertEqual(len(messages), 0)
|
self.assertEqual(len(messages), 0)
|
||||||
|
|
||||||
def test_exporter_with_filename_format(self):
|
def test_exporter_with_filename_format(self):
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
with override_settings(
|
with override_settings(
|
||||||
@ -309,16 +305,16 @@ class TestExportImport(
|
|||||||
self.test_exporter(use_filename_format=True)
|
self.test_exporter(use_filename_format=True)
|
||||||
|
|
||||||
def test_update_export_changed_time(self):
|
def test_update_export_changed_time(self):
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
self._do_export()
|
self._do_export()
|
||||||
self.assertIsFile(os.path.join(self.target, "manifest.json"))
|
self.assertIsFile((self.target / "manifest.json").as_posix())
|
||||||
|
|
||||||
st_mtime_1 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
|
st_mtime_1 = (self.target / "manifest.json").stat().st_mtime
|
||||||
|
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
"documents.management.commands.document_exporter.copy_file_with_basic_stats",
|
"documents.management.commands.document_exporter.copy_file_with_basic_stats",
|
||||||
@ -326,8 +322,8 @@ class TestExportImport(
|
|||||||
self._do_export()
|
self._do_export()
|
||||||
m.assert_not_called()
|
m.assert_not_called()
|
||||||
|
|
||||||
self.assertIsFile(os.path.join(self.target, "manifest.json"))
|
self.assertIsFile((self.target / "manifest.json").as_posix())
|
||||||
st_mtime_2 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
|
st_mtime_2 = (self.target / "manifest.json").stat().st_mtime
|
||||||
|
|
||||||
Path(self.d1.source_path).touch()
|
Path(self.d1.source_path).touch()
|
||||||
|
|
||||||
@ -337,26 +333,26 @@ class TestExportImport(
|
|||||||
self._do_export()
|
self._do_export()
|
||||||
self.assertEqual(m.call_count, 1)
|
self.assertEqual(m.call_count, 1)
|
||||||
|
|
||||||
st_mtime_3 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
|
st_mtime_3 = (self.target / "manifest.json").stat().st_mtime
|
||||||
self.assertIsFile(os.path.join(self.target, "manifest.json"))
|
self.assertIsFile((self.target / "manifest.json").as_posix())
|
||||||
|
|
||||||
self.assertNotEqual(st_mtime_1, st_mtime_2)
|
self.assertNotEqual(st_mtime_1, st_mtime_2)
|
||||||
self.assertNotEqual(st_mtime_2, st_mtime_3)
|
self.assertNotEqual(st_mtime_2, st_mtime_3)
|
||||||
|
|
||||||
self._do_export(compare_json=True)
|
self._do_export(compare_json=True)
|
||||||
st_mtime_4 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
|
st_mtime_4 = (self.target / "manifest.json").stat().st_mtime
|
||||||
self.assertEqual(st_mtime_3, st_mtime_4)
|
self.assertEqual(st_mtime_3, st_mtime_4)
|
||||||
|
|
||||||
def test_update_export_changed_checksum(self):
|
def test_update_export_changed_checksum(self):
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
self._do_export()
|
self._do_export()
|
||||||
|
|
||||||
self.assertIsFile(os.path.join(self.target, "manifest.json"))
|
self.assertIsFile((self.target / "manifest.json").as_posix())
|
||||||
|
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
"documents.management.commands.document_exporter.copy_file_with_basic_stats",
|
"documents.management.commands.document_exporter.copy_file_with_basic_stats",
|
||||||
@ -364,7 +360,7 @@ class TestExportImport(
|
|||||||
self._do_export()
|
self._do_export()
|
||||||
m.assert_not_called()
|
m.assert_not_called()
|
||||||
|
|
||||||
self.assertIsFile(os.path.join(self.target, "manifest.json"))
|
self.assertIsFile((self.target / "manifest.json").as_posix())
|
||||||
|
|
||||||
self.d2.checksum = "asdfasdgf3"
|
self.d2.checksum = "asdfasdgf3"
|
||||||
self.d2.save()
|
self.d2.save()
|
||||||
@ -375,13 +371,13 @@ class TestExportImport(
|
|||||||
self._do_export(compare_checksums=True)
|
self._do_export(compare_checksums=True)
|
||||||
self.assertEqual(m.call_count, 1)
|
self.assertEqual(m.call_count, 1)
|
||||||
|
|
||||||
self.assertIsFile(os.path.join(self.target, "manifest.json"))
|
self.assertIsFile((self.target / "manifest.json").as_posix())
|
||||||
|
|
||||||
def test_update_export_deleted_document(self):
|
def test_update_export_deleted_document(self):
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
manifest = self._do_export()
|
manifest = self._do_export()
|
||||||
@ -389,7 +385,7 @@ class TestExportImport(
|
|||||||
self.assertTrue(len(manifest), 7)
|
self.assertTrue(len(manifest), 7)
|
||||||
doc_from_manifest = self._get_document_from_manifest(manifest, self.d3.id)
|
doc_from_manifest = self._get_document_from_manifest(manifest, self.d3.id)
|
||||||
self.assertIsFile(
|
self.assertIsFile(
|
||||||
os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME]),
|
(self.target / doc_from_manifest[EXPORTER_FILE_NAME]).as_posix(),
|
||||||
)
|
)
|
||||||
self.d3.delete()
|
self.d3.delete()
|
||||||
|
|
||||||
@ -401,39 +397,39 @@ class TestExportImport(
|
|||||||
self.d3.id,
|
self.d3.id,
|
||||||
)
|
)
|
||||||
self.assertIsFile(
|
self.assertIsFile(
|
||||||
os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME]),
|
(self.target / doc_from_manifest[EXPORTER_FILE_NAME]).as_posix(),
|
||||||
)
|
)
|
||||||
|
|
||||||
manifest = self._do_export(delete=True)
|
manifest = self._do_export(delete=True)
|
||||||
self.assertIsNotFile(
|
self.assertIsNotFile(
|
||||||
os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME]),
|
(self.target / doc_from_manifest[EXPORTER_FILE_NAME]).as_posix(),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertTrue(len(manifest), 6)
|
self.assertTrue(len(manifest), 6)
|
||||||
|
|
||||||
@override_settings(FILENAME_FORMAT="{title}/{correspondent}")
|
@override_settings(FILENAME_FORMAT="{title}/{correspondent}")
|
||||||
def test_update_export_changed_location(self):
|
def test_update_export_changed_location(self):
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
self._do_export(use_filename_format=True)
|
self._do_export(use_filename_format=True)
|
||||||
self.assertIsFile(os.path.join(self.target, "wow1", "c.pdf"))
|
self.assertIsFile((self.target / "wow1" / "c.pdf").as_posix())
|
||||||
|
|
||||||
self.assertIsFile(os.path.join(self.target, "manifest.json"))
|
self.assertIsFile((self.target / "manifest.json").as_posix())
|
||||||
|
|
||||||
self.d1.title = "new_title"
|
self.d1.title = "new_title"
|
||||||
self.d1.save()
|
self.d1.save()
|
||||||
self._do_export(use_filename_format=True, delete=True)
|
self._do_export(use_filename_format=True, delete=True)
|
||||||
self.assertIsNotFile(os.path.join(self.target, "wow1", "c.pdf"))
|
self.assertIsNotFile((self.target / "wow1" / "c.pdf").as_posix())
|
||||||
self.assertIsNotDir(os.path.join(self.target, "wow1"))
|
self.assertIsNotDir((self.target / "wow1").as_posix())
|
||||||
self.assertIsFile(os.path.join(self.target, "new_title", "c.pdf"))
|
self.assertIsFile((self.target / "new_title" / "c.pdf").as_posix())
|
||||||
self.assertIsFile(os.path.join(self.target, "manifest.json"))
|
self.assertIsFile((self.target / "manifest.json").as_posix())
|
||||||
self.assertIsFile(os.path.join(self.target, "wow2", "none.pdf"))
|
self.assertIsFile((self.target / "wow2" / "none.pdf").as_posix())
|
||||||
self.assertIsFile(
|
self.assertIsFile(
|
||||||
os.path.join(self.target, "wow2", "none_01.pdf"),
|
(self.target / "wow2" / "none_01.pdf").as_posix(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_export_missing_files(self):
|
def test_export_missing_files(self):
|
||||||
@ -458,20 +454,19 @@ class TestExportImport(
|
|||||||
- Zipfile is created
|
- Zipfile is created
|
||||||
- Zipfile contains exported files
|
- Zipfile contains exported files
|
||||||
"""
|
"""
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
args = ["document_exporter", self.target, "--zip"]
|
args = ["document_exporter", self.target, "--zip"]
|
||||||
|
|
||||||
call_command(*args)
|
call_command(*args)
|
||||||
|
|
||||||
expected_file = os.path.join(
|
expected_file = (
|
||||||
self.target,
|
self.target / f"export-{timezone.localdate().isoformat()}.zip"
|
||||||
f"export-{timezone.localdate().isoformat()}.zip",
|
).as_posix()
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIsFile(expected_file)
|
self.assertIsFile(expected_file)
|
||||||
|
|
||||||
@ -492,10 +487,10 @@ class TestExportImport(
|
|||||||
- Zipfile is created
|
- Zipfile is created
|
||||||
- Zipfile contains exported files
|
- Zipfile contains exported files
|
||||||
"""
|
"""
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
args = ["document_exporter", self.target, "--zip", "--use-filename-format"]
|
args = ["document_exporter", self.target, "--zip", "--use-filename-format"]
|
||||||
@ -505,10 +500,9 @@ class TestExportImport(
|
|||||||
):
|
):
|
||||||
call_command(*args)
|
call_command(*args)
|
||||||
|
|
||||||
expected_file = os.path.join(
|
expected_file = (
|
||||||
self.target,
|
self.target / f"export-{timezone.localdate().isoformat()}.zip"
|
||||||
f"export-{timezone.localdate().isoformat()}.zip",
|
).as_posix()
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIsFile(expected_file)
|
self.assertIsFile(expected_file)
|
||||||
|
|
||||||
@ -533,10 +527,10 @@ class TestExportImport(
|
|||||||
- Zipfile contains exported files
|
- Zipfile contains exported files
|
||||||
- The existing file and directory in target are removed
|
- The existing file and directory in target are removed
|
||||||
"""
|
"""
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create stuff in target directory
|
# Create stuff in target directory
|
||||||
@ -552,10 +546,9 @@ class TestExportImport(
|
|||||||
|
|
||||||
call_command(*args)
|
call_command(*args)
|
||||||
|
|
||||||
expected_file = os.path.join(
|
expected_file = (
|
||||||
self.target,
|
self.target / f"export-{timezone.localdate().isoformat()}.zip"
|
||||||
f"export-{timezone.localdate().isoformat()}.zip",
|
).as_posix()
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIsFile(expected_file)
|
self.assertIsFile(expected_file)
|
||||||
self.assertIsNotFile(existing_file)
|
self.assertIsNotFile(existing_file)
|
||||||
@ -610,7 +603,7 @@ class TestExportImport(
|
|||||||
- Error is raised
|
- Error is raised
|
||||||
"""
|
"""
|
||||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
os.chmod(tmp_dir, 0o000)
|
Path(tmp_dir).chmod(0o000)
|
||||||
|
|
||||||
args = ["document_exporter", tmp_dir]
|
args = ["document_exporter", tmp_dir]
|
||||||
|
|
||||||
@ -629,10 +622,10 @@ class TestExportImport(
|
|||||||
- Manifest.json doesn't contain information about archive files
|
- Manifest.json doesn't contain information about archive files
|
||||||
- Documents can be imported again
|
- Documents can be imported again
|
||||||
"""
|
"""
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
manifest = self._do_export()
|
manifest = self._do_export()
|
||||||
@ -670,10 +663,10 @@ class TestExportImport(
|
|||||||
- Manifest.json doesn't contain information about thumbnails
|
- Manifest.json doesn't contain information about thumbnails
|
||||||
- Documents can be imported again
|
- Documents can be imported again
|
||||||
"""
|
"""
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
manifest = self._do_export()
|
manifest = self._do_export()
|
||||||
@ -713,10 +706,10 @@ class TestExportImport(
|
|||||||
- Main manifest.json file doesn't contain information about documents
|
- Main manifest.json file doesn't contain information about documents
|
||||||
- Documents can be imported again
|
- Documents can be imported again
|
||||||
"""
|
"""
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
manifest = self._do_export(split_manifest=True)
|
manifest = self._do_export(split_manifest=True)
|
||||||
@ -744,10 +737,10 @@ class TestExportImport(
|
|||||||
THEN:
|
THEN:
|
||||||
- Documents can be imported again
|
- Documents can be imported again
|
||||||
"""
|
"""
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
self._do_export(use_folder_prefix=True)
|
self._do_export(use_folder_prefix=True)
|
||||||
@ -769,10 +762,10 @@ class TestExportImport(
|
|||||||
- ContentType & Permission objects are not deleted, db transaction rolled back
|
- ContentType & Permission objects are not deleted, db transaction rolled back
|
||||||
"""
|
"""
|
||||||
|
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
num_content_type_objects = ContentType.objects.count()
|
num_content_type_objects = ContentType.objects.count()
|
||||||
@ -804,10 +797,10 @@ class TestExportImport(
|
|||||||
self.assertEqual(Permission.objects.count(), num_permission_objects + 1)
|
self.assertEqual(Permission.objects.count(), num_permission_objects + 1)
|
||||||
|
|
||||||
def test_exporter_with_auditlog_disabled(self):
|
def test_exporter_with_auditlog_disabled(self):
|
||||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "documents"),
|
Path(__file__).parent / "samples" / "documents",
|
||||||
os.path.join(self.dirs.media_dir, "documents"),
|
Path(self.dirs.media_dir) / "documents",
|
||||||
)
|
)
|
||||||
|
|
||||||
with override_settings(
|
with override_settings(
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -8,11 +7,11 @@ from documents.tests.utils import DirectoriesMixin
|
|||||||
from documents.tests.utils import TestMigrations
|
from documents.tests.utils import TestMigrations
|
||||||
|
|
||||||
|
|
||||||
def source_path_before(self):
|
def source_path_before(self) -> Path:
|
||||||
if self.filename:
|
if self.filename:
|
||||||
fname = str(self.filename)
|
fname = str(self.filename)
|
||||||
|
|
||||||
return os.path.join(settings.ORIGINALS_DIR, fname)
|
return Path(settings.ORIGINALS_DIR) / fname
|
||||||
|
|
||||||
|
|
||||||
class TestMigrateDocumentPageCount(DirectoriesMixin, TestMigrations):
|
class TestMigrateDocumentPageCount(DirectoriesMixin, TestMigrations):
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
@ -20,7 +20,7 @@ def source_path_before(self):
|
|||||||
if self.storage_type == STORAGE_TYPE_GPG:
|
if self.storage_type == STORAGE_TYPE_GPG:
|
||||||
fname += ".gpg"
|
fname += ".gpg"
|
||||||
|
|
||||||
return os.path.join(settings.ORIGINALS_DIR, fname)
|
return (Path(settings.ORIGINALS_DIR) / fname).as_posix()
|
||||||
|
|
||||||
|
|
||||||
def file_type_after(self):
|
def file_type_after(self):
|
||||||
@ -35,7 +35,7 @@ def source_path_after(doc):
|
|||||||
if doc.storage_type == STORAGE_TYPE_GPG:
|
if doc.storage_type == STORAGE_TYPE_GPG:
|
||||||
fname += ".gpg" # pragma: no cover
|
fname += ".gpg" # pragma: no cover
|
||||||
|
|
||||||
return os.path.join(settings.ORIGINALS_DIR, fname)
|
return (Path(settings.ORIGINALS_DIR) / fname).as_posix()
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSPHRASE="test")
|
@override_settings(PASSPHRASE="test")
|
||||||
@ -52,7 +52,7 @@ class TestMigrateMimeType(DirectoriesMixin, TestMigrations):
|
|||||||
)
|
)
|
||||||
self.doc_id = doc.id
|
self.doc_id = doc.id
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
Path(__file__).parent / "samples" / "simple.pdf",
|
||||||
source_path_before(doc),
|
source_path_before(doc),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -63,12 +63,12 @@ class TestMigrateMimeType(DirectoriesMixin, TestMigrations):
|
|||||||
)
|
)
|
||||||
self.doc2_id = doc2.id
|
self.doc2_id = doc2.id
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
os.path.join(
|
(
|
||||||
os.path.dirname(__file__),
|
Path(__file__).parent
|
||||||
"samples",
|
/ "samples"
|
||||||
"documents",
|
/ "documents"
|
||||||
"originals",
|
/ "originals"
|
||||||
"0000004.pdf.gpg",
|
/ "0000004.pdf.gpg"
|
||||||
),
|
),
|
||||||
source_path_before(doc2),
|
source_path_before(doc2),
|
||||||
)
|
)
|
||||||
@ -97,7 +97,7 @@ class TestMigrateMimeTypeBackwards(DirectoriesMixin, TestMigrations):
|
|||||||
)
|
)
|
||||||
self.doc_id = doc.id
|
self.doc_id = doc.id
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
Path(__file__).parent / "samples" / "simple.pdf",
|
||||||
source_path_after(doc),
|
source_path_after(doc),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -17,34 +16,34 @@ class TestSanityCheck(DirectoriesMixin, TestCase):
|
|||||||
with filelock.FileLock(settings.MEDIA_LOCK):
|
with filelock.FileLock(settings.MEDIA_LOCK):
|
||||||
# just make sure that the lockfile is present.
|
# just make sure that the lockfile is present.
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
os.path.join(
|
(
|
||||||
os.path.dirname(__file__),
|
Path(__file__).parent
|
||||||
"samples",
|
/ "samples"
|
||||||
"documents",
|
/ "documents"
|
||||||
"originals",
|
/ "originals"
|
||||||
"0000001.pdf",
|
/ "0000001.pdf"
|
||||||
),
|
),
|
||||||
os.path.join(self.dirs.originals_dir, "0000001.pdf"),
|
Path(self.dirs.originals_dir) / "0000001.pdf",
|
||||||
)
|
)
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
os.path.join(
|
(
|
||||||
os.path.dirname(__file__),
|
Path(__file__).parent
|
||||||
"samples",
|
/ "samples"
|
||||||
"documents",
|
/ "documents"
|
||||||
"archive",
|
/ "archive"
|
||||||
"0000001.pdf",
|
/ "0000001.pdf"
|
||||||
),
|
),
|
||||||
os.path.join(self.dirs.archive_dir, "0000001.pdf"),
|
Path(self.dirs.archive_dir) / "0000001.pdf",
|
||||||
)
|
)
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
os.path.join(
|
(
|
||||||
os.path.dirname(__file__),
|
Path(__file__).parent
|
||||||
"samples",
|
/ "samples"
|
||||||
"documents",
|
/ "documents"
|
||||||
"thumbnails",
|
/ "thumbnails"
|
||||||
"0000001.webp",
|
/ "0000001.webp"
|
||||||
),
|
),
|
||||||
os.path.join(self.dirs.thumbnail_dir, "0000001.webp"),
|
Path(self.dirs.thumbnail_dir) / "0000001.webp",
|
||||||
)
|
)
|
||||||
|
|
||||||
return Document.objects.create(
|
return Document.objects.create(
|
||||||
@ -92,25 +91,25 @@ class TestSanityCheck(DirectoriesMixin, TestCase):
|
|||||||
|
|
||||||
def test_no_thumbnail(self):
|
def test_no_thumbnail(self):
|
||||||
doc = self.make_test_data()
|
doc = self.make_test_data()
|
||||||
os.remove(doc.thumbnail_path)
|
Path(doc.thumbnail_path).unlink()
|
||||||
self.assertSanityError(doc, "Thumbnail of document does not exist")
|
self.assertSanityError(doc, "Thumbnail of document does not exist")
|
||||||
|
|
||||||
def test_thumbnail_no_access(self):
|
def test_thumbnail_no_access(self):
|
||||||
doc = self.make_test_data()
|
doc = self.make_test_data()
|
||||||
os.chmod(doc.thumbnail_path, 0o000)
|
Path(doc.thumbnail_path).chmod(0o000)
|
||||||
self.assertSanityError(doc, "Cannot read thumbnail file of document")
|
self.assertSanityError(doc, "Cannot read thumbnail file of document")
|
||||||
os.chmod(doc.thumbnail_path, 0o777)
|
Path(doc.thumbnail_path).chmod(0o777)
|
||||||
|
|
||||||
def test_no_original(self):
|
def test_no_original(self):
|
||||||
doc = self.make_test_data()
|
doc = self.make_test_data()
|
||||||
os.remove(doc.source_path)
|
Path(doc.source_path).unlink()
|
||||||
self.assertSanityError(doc, "Original of document does not exist.")
|
self.assertSanityError(doc, "Original of document does not exist.")
|
||||||
|
|
||||||
def test_original_no_access(self):
|
def test_original_no_access(self):
|
||||||
doc = self.make_test_data()
|
doc = self.make_test_data()
|
||||||
os.chmod(doc.source_path, 0o000)
|
Path(doc.source_path).chmod(0o000)
|
||||||
self.assertSanityError(doc, "Cannot read original file of document")
|
self.assertSanityError(doc, "Cannot read original file of document")
|
||||||
os.chmod(doc.source_path, 0o777)
|
Path(doc.source_path).chmod(0o777)
|
||||||
|
|
||||||
def test_original_checksum_mismatch(self):
|
def test_original_checksum_mismatch(self):
|
||||||
doc = self.make_test_data()
|
doc = self.make_test_data()
|
||||||
@ -120,14 +119,14 @@ class TestSanityCheck(DirectoriesMixin, TestCase):
|
|||||||
|
|
||||||
def test_no_archive(self):
|
def test_no_archive(self):
|
||||||
doc = self.make_test_data()
|
doc = self.make_test_data()
|
||||||
os.remove(doc.archive_path)
|
Path(doc.archive_path).unlink()
|
||||||
self.assertSanityError(doc, "Archived version of document does not exist.")
|
self.assertSanityError(doc, "Archived version of document does not exist.")
|
||||||
|
|
||||||
def test_archive_no_access(self):
|
def test_archive_no_access(self):
|
||||||
doc = self.make_test_data()
|
doc = self.make_test_data()
|
||||||
os.chmod(doc.archive_path, 0o000)
|
Path(doc.archive_path).chmod(0o000)
|
||||||
self.assertSanityError(doc, "Cannot read archive file of document")
|
self.assertSanityError(doc, "Cannot read archive file of document")
|
||||||
os.chmod(doc.archive_path, 0o777)
|
Path(doc.archive_path).chmod(0o777)
|
||||||
|
|
||||||
def test_archive_checksum_mismatch(self):
|
def test_archive_checksum_mismatch(self):
|
||||||
doc = self.make_test_data()
|
doc = self.make_test_data()
|
||||||
|
@ -2,7 +2,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: paperless-ngx\n"
|
"Project-Id-Version: paperless-ngx\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-19 15:13-0700\n"
|
"POT-Creation-Date: 2025-04-23 16:31+0000\n"
|
||||||
"PO-Revision-Date: 2022-02-17 04:17\n"
|
"PO-Revision-Date: 2022-02-17 04:17\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: English\n"
|
"Language-Team: English\n"
|
||||||
@ -1432,8 +1432,25 @@ msgstr ""
|
|||||||
msgid "As a final step, please complete the following form:"
|
msgid "As a final step, please complete the following form:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: documents/validators.py:24
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Unable to parse URI {value}, missing scheme"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: documents/validators.py:29
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Unable to parse URI {value}, missing net location or path"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: documents/validators.py:36
|
#: documents/validators.py:36
|
||||||
msgid ", "
|
msgid ""
|
||||||
|
"URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '."
|
||||||
|
"join(allowed_schemes)}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: documents/validators.py:45
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Unable to parse URI {value}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/apps.py:11
|
#: paperless/apps.py:11
|
||||||
@ -1584,139 +1601,139 @@ msgstr ""
|
|||||||
msgid "paperless application settings"
|
msgid "paperless application settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:726
|
#: paperless/settings.py:732
|
||||||
msgid "English (US)"
|
msgid "English (US)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:727
|
#: paperless/settings.py:733
|
||||||
msgid "Arabic"
|
msgid "Arabic"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:728
|
#: paperless/settings.py:734
|
||||||
msgid "Afrikaans"
|
msgid "Afrikaans"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:729
|
#: paperless/settings.py:735
|
||||||
msgid "Belarusian"
|
msgid "Belarusian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:730
|
#: paperless/settings.py:736
|
||||||
msgid "Bulgarian"
|
msgid "Bulgarian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:731
|
#: paperless/settings.py:737
|
||||||
msgid "Catalan"
|
msgid "Catalan"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:732
|
#: paperless/settings.py:738
|
||||||
msgid "Czech"
|
msgid "Czech"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:733
|
#: paperless/settings.py:739
|
||||||
msgid "Danish"
|
msgid "Danish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:734
|
#: paperless/settings.py:740
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:735
|
#: paperless/settings.py:741
|
||||||
msgid "Greek"
|
msgid "Greek"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:736
|
#: paperless/settings.py:742
|
||||||
msgid "English (GB)"
|
msgid "English (GB)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:737
|
#: paperless/settings.py:743
|
||||||
msgid "Spanish"
|
msgid "Spanish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:738
|
#: paperless/settings.py:744
|
||||||
msgid "Finnish"
|
msgid "Finnish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:739
|
#: paperless/settings.py:745
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:740
|
#: paperless/settings.py:746
|
||||||
msgid "Hungarian"
|
msgid "Hungarian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:741
|
#: paperless/settings.py:747
|
||||||
msgid "Italian"
|
msgid "Italian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:742
|
#: paperless/settings.py:748
|
||||||
msgid "Japanese"
|
msgid "Japanese"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:743
|
#: paperless/settings.py:749
|
||||||
msgid "Korean"
|
msgid "Korean"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:744
|
#: paperless/settings.py:750
|
||||||
msgid "Luxembourgish"
|
msgid "Luxembourgish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:745
|
#: paperless/settings.py:751
|
||||||
msgid "Norwegian"
|
msgid "Norwegian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:746
|
#: paperless/settings.py:752
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:747
|
#: paperless/settings.py:753
|
||||||
msgid "Polish"
|
msgid "Polish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:748
|
#: paperless/settings.py:754
|
||||||
msgid "Portuguese (Brazil)"
|
msgid "Portuguese (Brazil)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:749
|
#: paperless/settings.py:755
|
||||||
msgid "Portuguese"
|
msgid "Portuguese"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:750
|
#: paperless/settings.py:756
|
||||||
msgid "Romanian"
|
msgid "Romanian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:751
|
#: paperless/settings.py:757
|
||||||
msgid "Russian"
|
msgid "Russian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:752
|
#: paperless/settings.py:758
|
||||||
msgid "Slovak"
|
msgid "Slovak"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:753
|
#: paperless/settings.py:759
|
||||||
msgid "Slovenian"
|
msgid "Slovenian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:754
|
#: paperless/settings.py:760
|
||||||
msgid "Serbian"
|
msgid "Serbian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:755
|
#: paperless/settings.py:761
|
||||||
msgid "Swedish"
|
msgid "Swedish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:756
|
#: paperless/settings.py:762
|
||||||
msgid "Turkish"
|
msgid "Turkish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:757
|
#: paperless/settings.py:763
|
||||||
msgid "Ukrainian"
|
msgid "Ukrainian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:758
|
#: paperless/settings.py:764
|
||||||
msgid "Chinese Simplified"
|
msgid "Chinese Simplified"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:759
|
#: paperless/settings.py:765
|
||||||
msgid "Chinese Traditional"
|
msgid "Chinese Traditional"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -10,14 +10,18 @@ class StatusConsumer(WebsocketConsumer):
|
|||||||
def _authenticated(self):
|
def _authenticated(self):
|
||||||
return "user" in self.scope and self.scope["user"].is_authenticated
|
return "user" in self.scope and self.scope["user"].is_authenticated
|
||||||
|
|
||||||
def _is_owner_or_unowned(self, data):
|
def _can_view(self, data):
|
||||||
|
user = self.scope.get("user") if self.scope.get("user") else None
|
||||||
|
owner_id = data.get("owner_id")
|
||||||
|
users_can_view = data.get("users_can_view", [])
|
||||||
|
groups_can_view = data.get("groups_can_view", [])
|
||||||
return (
|
return (
|
||||||
(
|
user.is_superuser
|
||||||
self.scope["user"].is_superuser
|
or user.id == owner_id
|
||||||
or self.scope["user"].id == data["owner_id"]
|
or user.id in users_can_view
|
||||||
|
or any(
|
||||||
|
user.groups.filter(pk=group_id).exists() for group_id in groups_can_view
|
||||||
)
|
)
|
||||||
if "owner_id" in data and "user" in self.scope
|
|
||||||
else True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
@ -40,7 +44,7 @@ class StatusConsumer(WebsocketConsumer):
|
|||||||
if not self._authenticated():
|
if not self._authenticated():
|
||||||
self.close()
|
self.close()
|
||||||
else:
|
else:
|
||||||
if self._is_owner_or_unowned(event["data"]):
|
if self._can_view(event["data"]):
|
||||||
self.send(json.dumps(event))
|
self.send(json.dumps(event))
|
||||||
|
|
||||||
def documents_deleted(self, event):
|
def documents_deleted(self, event):
|
||||||
|
@ -350,23 +350,6 @@ REST_FRAMEWORK = {
|
|||||||
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
||||||
}
|
}
|
||||||
|
|
||||||
# DRF Spectacular settings
|
|
||||||
SPECTACULAR_SETTINGS = {
|
|
||||||
"TITLE": "Paperless-ngx REST API",
|
|
||||||
"DESCRIPTION": "OpenAPI Spec for Paperless-ngx",
|
|
||||||
"VERSION": "6.0.0",
|
|
||||||
"SERVE_INCLUDE_SCHEMA": False,
|
|
||||||
"SWAGGER_UI_DIST": "SIDECAR",
|
|
||||||
"COMPONENT_SPLIT_REQUEST": True,
|
|
||||||
"EXTERNAL_DOCS": {
|
|
||||||
"description": "Paperless-ngx API Documentation",
|
|
||||||
"url": "https://docs.paperless-ngx.com/api/",
|
|
||||||
},
|
|
||||||
"ENUM_NAME_OVERRIDES": {
|
|
||||||
"MatchingAlgorithm": "documents.models.MatchingModel.MATCHING_ALGORITHMS",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"].append(
|
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"].append(
|
||||||
"paperless.auth.AngularApiAuthenticationOverride",
|
"paperless.auth.AngularApiAuthenticationOverride",
|
||||||
@ -410,6 +393,24 @@ FORCE_SCRIPT_NAME, BASE_URL, LOGIN_URL, LOGIN_REDIRECT_URL, LOGOUT_REDIRECT_URL
|
|||||||
_parse_base_paths()
|
_parse_base_paths()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# DRF Spectacular settings
|
||||||
|
SPECTACULAR_SETTINGS = {
|
||||||
|
"TITLE": "Paperless-ngx REST API",
|
||||||
|
"DESCRIPTION": "OpenAPI Spec for Paperless-ngx",
|
||||||
|
"VERSION": "6.0.0",
|
||||||
|
"SERVE_INCLUDE_SCHEMA": False,
|
||||||
|
"SWAGGER_UI_DIST": "SIDECAR",
|
||||||
|
"COMPONENT_SPLIT_REQUEST": True,
|
||||||
|
"EXTERNAL_DOCS": {
|
||||||
|
"description": "Paperless-ngx API Documentation",
|
||||||
|
"url": "https://docs.paperless-ngx.com/api/",
|
||||||
|
},
|
||||||
|
"ENUM_NAME_OVERRIDES": {
|
||||||
|
"MatchingAlgorithm": "documents.models.MatchingModel.MATCHING_ALGORITHMS",
|
||||||
|
},
|
||||||
|
"SCHEMA_PATH_PREFIX_INSERT": FORCE_SCRIPT_NAME or "",
|
||||||
|
}
|
||||||
|
|
||||||
WSGI_APPLICATION = "paperless.wsgi.application"
|
WSGI_APPLICATION = "paperless.wsgi.application"
|
||||||
ASGI_APPLICATION = "paperless.asgi.application"
|
ASGI_APPLICATION = "paperless.asgi.application"
|
||||||
|
|
||||||
@ -507,6 +508,11 @@ ACCOUNT_EMAIL_VERIFICATION = os.getenv(
|
|||||||
"optional",
|
"optional",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = __get_boolean(
|
||||||
|
"PAPERLESS_ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS",
|
||||||
|
"True",
|
||||||
|
)
|
||||||
|
|
||||||
ACCOUNT_SESSION_REMEMBER = __get_boolean("PAPERLESS_ACCOUNT_SESSION_REMEMBER", "True")
|
ACCOUNT_SESSION_REMEMBER = __get_boolean("PAPERLESS_ACCOUNT_SESSION_REMEMBER", "True")
|
||||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = not ACCOUNT_SESSION_REMEMBER
|
SESSION_EXPIRE_AT_BROWSER_CLOSE = not ACCOUNT_SESSION_REMEMBER
|
||||||
SESSION_COOKIE_AGE = int(
|
SESSION_COOKIE_AGE = int(
|
||||||
|
@ -90,6 +90,52 @@ class TestWebSockets(TestCase):
|
|||||||
|
|
||||||
await communicator.disconnect()
|
await communicator.disconnect()
|
||||||
|
|
||||||
|
async def test_status_update_check_perms(self):
|
||||||
|
communicator = WebsocketCommunicator(application, "/ws/status/")
|
||||||
|
|
||||||
|
communicator.scope["user"] = mock.Mock()
|
||||||
|
communicator.scope["user"].is_authenticated = True
|
||||||
|
communicator.scope["user"].is_superuser = False
|
||||||
|
communicator.scope["user"].id = 1
|
||||||
|
|
||||||
|
connected, subprotocol = await communicator.connect()
|
||||||
|
self.assertTrue(connected)
|
||||||
|
|
||||||
|
# Test as owner
|
||||||
|
message = {"type": "status_update", "data": {"task_id": "test", "owner_id": 1}}
|
||||||
|
channel_layer = get_channel_layer()
|
||||||
|
await channel_layer.group_send(
|
||||||
|
"status_updates",
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
response = await communicator.receive_json_from()
|
||||||
|
self.assertEqual(response, message)
|
||||||
|
|
||||||
|
# Test with a group that the user belongs to
|
||||||
|
communicator.scope["user"].groups.filter.return_value.exists.return_value = True
|
||||||
|
message = {
|
||||||
|
"type": "status_update",
|
||||||
|
"data": {"task_id": "test", "owner_id": 2, "groups_can_view": [1]},
|
||||||
|
}
|
||||||
|
channel_layer = get_channel_layer()
|
||||||
|
await channel_layer.group_send(
|
||||||
|
"status_updates",
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
response = await communicator.receive_json_from()
|
||||||
|
self.assertEqual(response, message)
|
||||||
|
|
||||||
|
# Test with a different owner_id
|
||||||
|
message = {"type": "status_update", "data": {"task_id": "test", "owner_id": 2}}
|
||||||
|
channel_layer = get_channel_layer()
|
||||||
|
await channel_layer.group_send(
|
||||||
|
"status_updates",
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
response = await communicator.receive_nothing()
|
||||||
|
self.assertNotEqual(response, message)
|
||||||
|
await communicator.disconnect()
|
||||||
|
|
||||||
@mock.patch("paperless.consumers.StatusConsumer._authenticated")
|
@mock.patch("paperless.consumers.StatusConsumer._authenticated")
|
||||||
async def test_receive_documents_deleted(self, _authenticated):
|
async def test_receive_documents_deleted(self, _authenticated):
|
||||||
_authenticated.return_value = True
|
_authenticated.return_value = True
|
||||||
|
@ -8,10 +8,10 @@ from django.conf import settings
|
|||||||
from django.utils.timezone import is_naive
|
from django.utils.timezone import is_naive
|
||||||
from django.utils.timezone import make_aware
|
from django.utils.timezone import make_aware
|
||||||
from gotenberg_client import GotenbergClient
|
from gotenberg_client import GotenbergClient
|
||||||
from gotenberg_client.options import MarginType
|
from gotenberg_client.constants import A4
|
||||||
from gotenberg_client.options import MarginUnitType
|
from gotenberg_client.options import Measurement
|
||||||
|
from gotenberg_client.options import MeasurementUnitType
|
||||||
from gotenberg_client.options import PageMarginsType
|
from gotenberg_client.options import PageMarginsType
|
||||||
from gotenberg_client.options import PageSize
|
|
||||||
from gotenberg_client.options import PdfAFormat
|
from gotenberg_client.options import PdfAFormat
|
||||||
from humanize import naturalsize
|
from humanize import naturalsize
|
||||||
from imap_tools import MailAttachment
|
from imap_tools import MailAttachment
|
||||||
@ -370,13 +370,13 @@ class MailDocumentParser(DocumentParser):
|
|||||||
.resource(css_file)
|
.resource(css_file)
|
||||||
.margins(
|
.margins(
|
||||||
PageMarginsType(
|
PageMarginsType(
|
||||||
top=MarginType(0.1, MarginUnitType.Inches),
|
top=Measurement(0.1, MeasurementUnitType.Inches),
|
||||||
bottom=MarginType(0.1, MarginUnitType.Inches),
|
bottom=Measurement(0.1, MeasurementUnitType.Inches),
|
||||||
left=MarginType(0.1, MarginUnitType.Inches),
|
left=Measurement(0.1, MeasurementUnitType.Inches),
|
||||||
right=MarginType(0.1, MarginUnitType.Inches),
|
right=Measurement(0.1, MeasurementUnitType.Inches),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.size(PageSize(height=11.7, width=8.27))
|
.size(A4)
|
||||||
.scale(1.0)
|
.scale(1.0)
|
||||||
.run()
|
.run()
|
||||||
)
|
)
|
||||||
@ -452,14 +452,12 @@ class MailDocumentParser(DocumentParser):
|
|||||||
# Set page size, margins
|
# Set page size, margins
|
||||||
route.margins(
|
route.margins(
|
||||||
PageMarginsType(
|
PageMarginsType(
|
||||||
top=MarginType(0.1, MarginUnitType.Inches),
|
top=Measurement(0.1, MeasurementUnitType.Inches),
|
||||||
bottom=MarginType(0.1, MarginUnitType.Inches),
|
bottom=Measurement(0.1, MeasurementUnitType.Inches),
|
||||||
left=MarginType(0.1, MarginUnitType.Inches),
|
left=Measurement(0.1, MeasurementUnitType.Inches),
|
||||||
right=MarginType(0.1, MarginUnitType.Inches),
|
right=Measurement(0.1, MeasurementUnitType.Inches),
|
||||||
),
|
),
|
||||||
).size(
|
).size(A4).scale(1.0)
|
||||||
PageSize(height=11.7, width=8.27),
|
|
||||||
).scale(1.0)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = route.run()
|
response = route.run()
|
||||||
|
@ -665,7 +665,7 @@ class TestParser:
|
|||||||
assert str(request.url) == "http://localhost:3000/forms/chromium/convert/html"
|
assert str(request.url) == "http://localhost:3000/forms/chromium/convert/html"
|
||||||
|
|
||||||
@pytest.mark.httpx_mock(can_send_already_matched_responses=True)
|
@pytest.mark.httpx_mock(can_send_already_matched_responses=True)
|
||||||
@mock.patch("gotenberg_client._merge.MergeRoute.merge")
|
@mock.patch("gotenberg_client._merge.routes.SyncMergePdfsRoute.merge")
|
||||||
@mock.patch("paperless_mail.models.MailRule.objects.get")
|
@mock.patch("paperless_mail.models.MailRule.objects.get")
|
||||||
def test_generate_pdf_layout_options(
|
def test_generate_pdf_layout_options(
|
||||||
self,
|
self,
|
||||||
|
@ -108,6 +108,7 @@ class RasterisedDocumentParser(DocumentParser):
|
|||||||
"image/bmp",
|
"image/bmp",
|
||||||
"image/gif",
|
"image/gif",
|
||||||
"image/webp",
|
"image/webp",
|
||||||
|
"image/heic",
|
||||||
]
|
]
|
||||||
|
|
||||||
def has_alpha(self, image) -> bool:
|
def has_alpha(self, image) -> bool:
|
||||||
|
@ -16,5 +16,6 @@ def tesseract_consumer_declaration(sender, **kwargs):
|
|||||||
"image/gif": ".gif",
|
"image/gif": ".gif",
|
||||||
"image/bmp": ".bmp",
|
"image/bmp": ".bmp",
|
||||||
"image/webp": ".webp",
|
"image/webp": ".webp",
|
||||||
|
"image/heic": ".heic",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
BIN
src/paperless_tesseract/tests/samples/simple.heic
Normal file
BIN
src/paperless_tesseract/tests/samples/simple.heic
Normal file
Binary file not shown.
@ -880,6 +880,12 @@ class TestParserFileTypes(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
|
|||||||
self.assertIsFile(parser.archive_path)
|
self.assertIsFile(parser.archive_path)
|
||||||
self.assertIn("this is a test document", parser.get_text().lower())
|
self.assertIn("this is a test document", parser.get_text().lower())
|
||||||
|
|
||||||
|
def test_heic(self):
|
||||||
|
parser = RasterisedDocumentParser(None)
|
||||||
|
parser.parse(os.path.join(self.SAMPLE_FILES, "simple.heic"), "image/heic")
|
||||||
|
self.assertIsFile(parser.archive_path)
|
||||||
|
self.assertIn("pizza", parser.get_text().lower())
|
||||||
|
|
||||||
@override_settings(OCR_IMAGE_DPI=200)
|
@override_settings(OCR_IMAGE_DPI=200)
|
||||||
def test_gif(self):
|
def test_gif(self):
|
||||||
parser = RasterisedDocumentParser(None)
|
parser = RasterisedDocumentParser(None)
|
||||||
|
193
uv.lock
generated
193
uv.lock
generated
@ -312,15 +312,15 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "channels"
|
name = "channels"
|
||||||
version = "4.2.0"
|
version = "4.2.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "asgiref", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "asgiref", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/96/e2/10d949dca9eb8a85c5735efefe3309033419e7d4f4193a70f6ede58b2951/channels-4.2.0.tar.gz", hash = "sha256:d9e707487431ba5dbce9af982970dab3b0efd786580fadb99e45dca5e39fdd59", size = 26554 }
|
sdist = { url = "https://files.pythonhosted.org/packages/16/d6/049f93c3c96a88265a52f85da91d2635279261bbd4a924b45caa43b8822e/channels-4.2.2.tar.gz", hash = "sha256:8d7208e48ab8fdb972aaeae8311ce920637d97656ffc7ae5eca4f93f84bcd9a0", size = 26647 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/7e/4e/f36a0e2c04504014385cbc13119a15b8a716e524e8e5ed9480581397691a/channels-4.2.0-py3-none-any.whl", hash = "sha256:6b75bc8d6888fb7236e7e7bf1948520b72d296ad08216a242fc56b1db0ffde1a", size = 30935 },
|
{ url = "https://files.pythonhosted.org/packages/cc/bf/4799809715225d19928147d59fda0d3a4129da055b59a9b3e35aa6223f52/channels-4.2.2-py3-none-any.whl", hash = "sha256:ff36a6e1576cacf40bcdc615fa7aece7a709fc4fdd2dc87f2971f4061ffdaa81", size = 31048 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -626,15 +626,15 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django"
|
name = "django"
|
||||||
version = "5.1.7"
|
version = "5.1.8"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "asgiref", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "asgiref", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
{ name = "sqlparse", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "sqlparse", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/5f/57/11186e493ddc5a5e92cc7924a6363f7d4c2b645f7d7cb04a26a63f9bfb8b/Django-5.1.7.tar.gz", hash = "sha256:30de4ee43a98e5d3da36a9002f287ff400b43ca51791920bfb35f6917bfe041c", size = 10716510 }
|
sdist = { url = "https://files.pythonhosted.org/packages/00/40/45adc1b93435d1b418654a734b68351bb6ce0a0e5e37b2f0e9aeb1a2e233/Django-5.1.8.tar.gz", hash = "sha256:42e92a1dd2810072bcc40a39a212b693f94406d0ba0749e68eb642f31dc770b4", size = 10723602 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/ba/0f/7e042df3d462d39ae01b27a09ee76653692442bc3701fbfa6cb38e12889d/Django-5.1.7-py3-none-any.whl", hash = "sha256:1323617cb624add820cb9611cdcc788312d250824f92ca6048fda8625514af2b", size = 8276912 },
|
{ url = "https://files.pythonhosted.org/packages/ec/0d/e6dd0ed898b920fec35c6eeeb9acbeb831fff19ad21c5e684744df1d4a36/Django-5.1.8-py3-none-any.whl", hash = "sha256:11b28fa4b00e59d0def004e9ee012fefbb1065a5beb39ee838983fd24493ad4f", size = 8277130 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -673,15 +673,15 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-celery-results"
|
name = "django-celery-results"
|
||||||
version = "2.5.1"
|
version = "2.6.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "celery", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "celery", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/75/24/a13ad6a276f62385da6d83a1995ded452d173841b6e0a83a899c3b75062e/django_celery_results-2.5.1.tar.gz", hash = "sha256:3ecb7147f773f34d0381bac6246337ce4cf88a2ea7b82774ed48e518b67bb8fd", size = 80944 }
|
sdist = { url = "https://files.pythonhosted.org/packages/a6/b5/9966c28e31014c228305e09d48b19b35522a8f941fe5af5f81f40dc8fa80/django_celery_results-2.6.0.tar.gz", hash = "sha256:9abcd836ae6b61063779244d8887a88fe80bbfaba143df36d3cb07034671277c", size = 83985 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/05/2e/aa82857354d5227922c2ae6e83e5214537d8f108184bfeea6704d0b045d8/django_celery_results-2.5.1-py3-none-any.whl", hash = "sha256:0da4cd5ecc049333e4524a23fcfc3460dfae91aa0a60f1fae4b6b2889c254e01", size = 36293 },
|
{ url = "https://files.pythonhosted.org/packages/2c/da/70f0f3c5364735344c4bc89e53413bcaae95b4fc1de4e98a7a3b9fb70c88/django_celery_results-2.6.0-py3-none-any.whl", hash = "sha256:b9ccdca2695b98c7cbbb8dea742311ba9a92773d71d7b4944a676e69a7df1c73", size = 38351 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -713,14 +713,14 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-extensions"
|
name = "django-extensions"
|
||||||
version = "3.2.3"
|
version = "4.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/8a/f1/318684c9466968bf9a9c221663128206e460c1a67f595055be4b284cde8a/django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a", size = 277216 }
|
sdist = { url = "https://files.pythonhosted.org/packages/6d/b3/ed0f54ed706ec0b54fd251cc0364a249c6cd6c6ec97f04dc34be5e929eac/django_extensions-4.1.tar.gz", hash = "sha256:7b70a4d28e9b840f44694e3f7feb54f55d495f8b3fa6c5c0e5e12bcb2aa3cdeb", size = 283078 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/a7/7e/ba12b9660642663f5273141018d2bec0a1cae1711f4f6d1093920e157946/django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401", size = 229868 },
|
{ url = "https://files.pythonhosted.org/packages/64/96/d967ca440d6a8e3861120f51985d8e5aec79b9a8bdda16041206adfe7adc/django_extensions-4.1-py3-none-any.whl", hash = "sha256:0699a7af28f2523bf8db309a80278519362cd4b6e1fd0a8cd4bf063e1e023336", size = 232980 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -823,14 +823,14 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "djangorestframework"
|
name = "djangorestframework"
|
||||||
version = "3.15.2"
|
version = "3.16.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/2c/ce/31482eb688bdb4e271027076199e1aa8d02507e530b6d272ab8b4481557c/djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad", size = 1067420 }
|
sdist = { url = "https://files.pythonhosted.org/packages/7d/97/112c5a72e6917949b6d8a18ad6c6e72c46da4290c8f36ee5f1c1dcbc9901/djangorestframework-3.16.0.tar.gz", hash = "sha256:f022ff46613584de994c0c6a4aebbace5fd700555fbe9d33b865ebf173eba6c9", size = 1068408 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/7c/b6/fa99d8f05eff3a9310286ae84c4059b08c301ae4ab33ae32e46e8ef76491/djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20", size = 1071235 },
|
{ url = "https://files.pythonhosted.org/packages/eb/3e/2448e93f4f87fc9a9f35e73e3c05669e0edd0c2526834686e949bb1fd303/djangorestframework-3.16.0-py3-none-any.whl", hash = "sha256:bea7e9f6b96a8584c5224bfb2e4348dfb3f8b5e34edbecb98da258e892089361", size = 1067305 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -888,22 +888,22 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "drf-spectacular-sidecar"
|
name = "drf-spectacular-sidecar"
|
||||||
version = "2025.3.1"
|
version = "2025.4.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/80/5c/85671ba50957e0ab45e2828ca713cd7b5d0d237c398936c1b45bd0286b72/drf_spectacular_sidecar-2025.3.1.tar.gz", hash = "sha256:7425940a409fb68a46c9b024eb504098fab5b4d73d12573efcaef048445d678f", size = 2401986 }
|
sdist = { url = "https://files.pythonhosted.org/packages/5d/b6/ce857d73b65b86a9034d0604b5dc1a002f7fa218e32c4dba479a197acd70/drf_spectacular_sidecar-2025.4.1.tar.gz", hash = "sha256:ea7dc4e674174616589d258b5c9676f3c451ec422e62b79e31234d39db53922d", size = 2402076 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/5e/09/7382eb93a09f33fac72a8af1fc986f43681d15dd5fda750b238a8aace0e8/drf_spectacular_sidecar-2025.3.1-py3-none-any.whl", hash = "sha256:6b9c96204c8e45a06c39928dad704b18536d3253b61591c478540b63db6bdcea", size = 2422301 },
|
{ url = "https://files.pythonhosted.org/packages/cf/c3/d2f31ef748f89d68121aa3d4a71f7dfd44ea54957b84602d70cda2491c43/drf_spectacular_sidecar-2025.4.1-py3-none-any.whl", hash = "sha256:343a24b0d03125fa76d07685072f55779c5c4124d90c10b14e315fdc143ad9b9", size = 2422415 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "drf-writable-nested"
|
name = "drf-writable-nested"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/a0/1e/b27f416a1706a0403076cb3216402c2a66017a0194a52e8c4753635ddc83/drf_writable_nested-0.7.1-py3-none-any.whl", hash = "sha256:d8ddc606dc349e56373810842965712a5789e6a5ca7704729d15429b95f8f2ee", size = 10559 },
|
{ url = "https://files.pythonhosted.org/packages/e8/57/df87d92fbfc3f0f2ef1a49c47f2a83389a4a13b7acf62b8bf7b223627d82/drf_writable_nested-0.7.2-py3-none-any.whl", hash = "sha256:4a3d2737c1cbfafa690e30236b169112e5b23cfe3d288f3992b0651a1b828c4d", size = 10570 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -962,11 +962,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filelock"
|
name = "filelock"
|
||||||
version = "3.17.0"
|
version = "3.18.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 }
|
sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 },
|
{ url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -999,15 +999,15 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gotenberg-client"
|
name = "gotenberg-client"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "httpx", extra = ["http2"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "httpx", extra = ["http2"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
{ name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" },
|
{ name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/20/7f/2bb2ab55a3f00d859649094327e7e3a71776957f40fdf79a53ad68960afe/gotenberg_client-0.9.0.tar.gz", hash = "sha256:bd3c1ed42b74d470a7e192118276f3d91b558b90aa7c0035afbcac04f42179bb", size = 419242 }
|
sdist = { url = "https://files.pythonhosted.org/packages/46/f4/aa1edff06d2da0bd41d05ee1d0f8459a580e8b9fa6658203cd2a37f85101/gotenberg_client-0.10.0.tar.gz", hash = "sha256:27da0ba29eb313d747b8940558d43588bfb816458829e4cb5e2697bfe645732d", size = 1209616 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/39/74/536bf5f66571971e9a7b5babece1c008386c16ee5b7ad6c4a3689f43a999/gotenberg_client-0.9.0-py3-none-any.whl", hash = "sha256:18955453e6b2a29a5015e95d645efff3ef6d000a663bd1550b2c92e9055bdd5b", size = 32696 },
|
{ url = "https://files.pythonhosted.org/packages/95/54/b28368353815f1cc40c19c8bbae4c99b059e15e2771dfbbf00648a72b29e/gotenberg_client-0.10.0-py3-none-any.whl", hash = "sha256:581625accd7fae3514be16294f576a579bc0cbedbd1575ce897786a371d72ea7", size = 50782 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1357,14 +1357,14 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jinja2"
|
name = "jinja2"
|
||||||
version = "3.1.5"
|
version = "3.1.6"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
{ name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 }
|
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 },
|
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2022,10 +2022,10 @@ requires-dist = [
|
|||||||
{ name = "django", specifier = "~=5.1.7" },
|
{ name = "django", specifier = "~=5.1.7" },
|
||||||
{ name = "django-allauth", extras = ["socialaccount", "mfa"], specifier = "~=65.4.0" },
|
{ name = "django-allauth", extras = ["socialaccount", "mfa"], specifier = "~=65.4.0" },
|
||||||
{ name = "django-auditlog", specifier = "~=3.0.0" },
|
{ name = "django-auditlog", specifier = "~=3.0.0" },
|
||||||
{ name = "django-celery-results", specifier = "~=2.5.1" },
|
{ name = "django-celery-results", specifier = "~=2.6.0" },
|
||||||
{ name = "django-compression-middleware", specifier = "~=0.5.0" },
|
{ name = "django-compression-middleware", specifier = "~=0.5.0" },
|
||||||
{ name = "django-cors-headers", specifier = "~=4.7.0" },
|
{ name = "django-cors-headers", specifier = "~=4.7.0" },
|
||||||
{ name = "django-extensions", specifier = "~=3.2.3" },
|
{ name = "django-extensions", specifier = "~=4.1" },
|
||||||
{ name = "django-filter", specifier = "~=25.1" },
|
{ name = "django-filter", specifier = "~=25.1" },
|
||||||
{ name = "django-guardian", specifier = "~=2.4.0" },
|
{ name = "django-guardian", specifier = "~=2.4.0" },
|
||||||
{ name = "django-multiselectfield", specifier = "~=0.1.13" },
|
{ name = "django-multiselectfield", specifier = "~=0.1.13" },
|
||||||
@ -2033,11 +2033,11 @@ requires-dist = [
|
|||||||
{ name = "djangorestframework", specifier = "~=3.15" },
|
{ name = "djangorestframework", specifier = "~=3.15" },
|
||||||
{ name = "djangorestframework-guardian", specifier = "~=0.3.0" },
|
{ name = "djangorestframework-guardian", specifier = "~=0.3.0" },
|
||||||
{ name = "drf-spectacular", specifier = "~=0.28" },
|
{ name = "drf-spectacular", specifier = "~=0.28" },
|
||||||
{ name = "drf-spectacular-sidecar", specifier = "~=2025.3.1" },
|
{ name = "drf-spectacular-sidecar", specifier = "~=2025.4.1" },
|
||||||
{ name = "drf-writable-nested", specifier = "~=0.7.1" },
|
{ name = "drf-writable-nested", specifier = "~=0.7.1" },
|
||||||
{ name = "filelock", specifier = "~=3.17.0" },
|
{ name = "filelock", specifier = "~=3.18.0" },
|
||||||
{ name = "flower", specifier = "~=2.0.1" },
|
{ name = "flower", specifier = "~=2.0.1" },
|
||||||
{ name = "gotenberg-client", specifier = "~=0.9.0" },
|
{ name = "gotenberg-client", specifier = "~=0.10.0" },
|
||||||
{ name = "granian", extras = ["uvloop"], marker = "extra == 'webserver'", specifier = "~=2.2.0" },
|
{ name = "granian", extras = ["uvloop"], marker = "extra == 'webserver'", specifier = "~=2.2.0" },
|
||||||
{ name = "httpx-oauth", specifier = "~=0.16" },
|
{ name = "httpx-oauth", specifier = "~=0.16" },
|
||||||
{ name = "imap-tools", specifier = "~=1.10.0" },
|
{ name = "imap-tools", specifier = "~=1.10.0" },
|
||||||
@ -2054,12 +2054,12 @@ requires-dist = [
|
|||||||
{ name = "psycopg-c", marker = "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'postgres'", url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl" },
|
{ name = "psycopg-c", marker = "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'postgres'", url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl" },
|
||||||
{ name = "psycopg-c", marker = "(python_full_version != '3.12.*' and platform_machine == 'aarch64' and extra == 'postgres') or (python_full_version != '3.12.*' and platform_machine == 'x86_64' and extra == 'postgres') or (platform_machine != 'aarch64' and platform_machine != 'x86_64' and extra == 'postgres') or (sys_platform != 'linux' and extra == 'postgres')", specifier = "==3.2.5" },
|
{ name = "psycopg-c", marker = "(python_full_version != '3.12.*' and platform_machine == 'aarch64' and extra == 'postgres') or (python_full_version != '3.12.*' and platform_machine == 'x86_64' and extra == 'postgres') or (platform_machine != 'aarch64' and platform_machine != 'x86_64' and extra == 'postgres') or (sys_platform != 'linux' and extra == 'postgres')", specifier = "==3.2.5" },
|
||||||
{ name = "python-dateutil", specifier = "~=2.9.0" },
|
{ name = "python-dateutil", specifier = "~=2.9.0" },
|
||||||
{ name = "python-dotenv", specifier = "~=1.0.1" },
|
{ name = "python-dotenv", specifier = "~=1.1.0" },
|
||||||
{ name = "python-gnupg", specifier = "~=0.5.4" },
|
{ name = "python-gnupg", specifier = "~=0.5.4" },
|
||||||
{ name = "python-ipware", specifier = "~=3.0.0" },
|
{ name = "python-ipware", specifier = "~=3.0.0" },
|
||||||
{ name = "python-magic", specifier = "~=0.4.27" },
|
{ name = "python-magic", specifier = "~=0.4.27" },
|
||||||
{ name = "pyzbar", specifier = "~=0.1.9" },
|
{ name = "pyzbar", specifier = "~=0.1.9" },
|
||||||
{ name = "rapidfuzz", specifier = "~=3.12.1" },
|
{ name = "rapidfuzz", specifier = "~=3.13.0" },
|
||||||
{ name = "redis", extras = ["hiredis"], specifier = "~=5.2.1" },
|
{ name = "redis", extras = ["hiredis"], specifier = "~=5.2.1" },
|
||||||
{ name = "scikit-learn", specifier = "~=1.6.1" },
|
{ name = "scikit-learn", specifier = "~=1.6.1" },
|
||||||
{ name = "setproctitle", specifier = "~=1.3.4" },
|
{ name = "setproctitle", specifier = "~=1.3.4" },
|
||||||
@ -2648,11 +2648,11 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 }
|
sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 },
|
{ url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2796,63 +2796,68 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rapidfuzz"
|
name = "rapidfuzz"
|
||||||
version = "3.12.1"
|
version = "3.13.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/df/c300ead8c2962f54ad87872e6372a6836f0181a7f20b433c987bd106bfce/rapidfuzz-3.12.1.tar.gz", hash = "sha256:6a98bbca18b4a37adddf2d8201856441c26e9c981d8895491b5bc857b5f780eb", size = 57907552 }
|
sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/6895abc3a3d056b9698da3199b04c0e56226d530ae44a470edabf8b664f0/rapidfuzz-3.13.0.tar.gz", hash = "sha256:d2eaf3839e52cbcc0accbe9817a67b4b0fcf70aaeb229cfddc1c28061f9ce5d8", size = 57904226 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/29/e6/a56a87edff979559ce1e5486bf148c5f8905c9159ebdb14f217b3a3eeb2b/rapidfuzz-3.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbb7ea2fd786e6d66f225ef6eef1728832314f47e82fee877cb2a793ebda9579", size = 1959669 },
|
{ url = "https://files.pythonhosted.org/packages/de/27/ca10b3166024ae19a7e7c21f73c58dfd4b7fef7420e5497ee64ce6b73453/rapidfuzz-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aafc42a1dc5e1beeba52cd83baa41372228d6d8266f6d803c16dbabbcc156255", size = 1998899 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2e/6d/010a33d3425494f9967025897ad5283a159cf72e4552cc443d5f646cd040/rapidfuzz-3.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ae41361de05762c1eaa3955e5355de7c4c6f30d1ef1ea23d29bf738a35809ab", size = 1433648 },
|
{ url = "https://files.pythonhosted.org/packages/f0/38/c4c404b13af0315483a6909b3a29636e18e1359307fb74a333fdccb3730d/rapidfuzz-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:85c9a131a44a95f9cac2eb6e65531db014e09d89c4f18c7b1fa54979cb9ff1f3", size = 1449949 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/43/a8/2964c7dac65f147098145598e265a434a55a6a6be13ce1bca4c8b822e77f/rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc3c39e0317e7f68ba01bac056e210dd13c7a0abf823e7b6a5fe7e451ddfc496", size = 1423317 },
|
{ url = "https://files.pythonhosted.org/packages/12/ae/15c71d68a6df6b8e24595421fdf5bcb305888318e870b7be8d935a9187ee/rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d7cec4242d30dd521ef91c0df872e14449d1dffc2a6990ede33943b0dae56c3", size = 1424199 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d6/9c/a8d376fcad2f4b48483b5a54a45bd71d75d9401fd12227dae7cfe565f2db/rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69f2520296f1ae1165b724a3aad28c56fd0ac7dd2e4cff101a5d986e840f02d4", size = 5641782 },
|
{ url = "https://files.pythonhosted.org/packages/dc/9a/765beb9e14d7b30d12e2d6019e8b93747a0bedbc1d0cce13184fa3825426/rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e297c09972698c95649e89121e3550cee761ca3640cd005e24aaa2619175464e", size = 5352400 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/98/69/26b21a1c3ccd4960a82493396e90db5e81a73d5fbbad98fc9b913b96e557/rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34dcbf5a7daecebc242f72e2500665f0bde9dd11b779246c6d64d106a7d57c99", size = 1683506 },
|
{ url = "https://files.pythonhosted.org/packages/e2/b8/49479fe6f06b06cd54d6345ed16de3d1ac659b57730bdbe897df1e059471/rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef0f5f03f61b0e5a57b1df7beafd83df993fd5811a09871bad6038d08e526d0d", size = 1652465 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6a/a0/87323883234508bd0ebc599004aab25319c1e296644e73f94c8fbee7c57d/rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:773ab37fccf6e0513891f8eb4393961ddd1053c6eb7e62eaa876e94668fc6d31", size = 1685813 },
|
{ url = "https://files.pythonhosted.org/packages/6f/d8/08823d496b7dd142a7b5d2da04337df6673a14677cfdb72f2604c64ead69/rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8cf5f7cd6e4d5eb272baf6a54e182b2c237548d048e2882258336533f3f02b7", size = 1616590 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/91/ea/e99bea5218805d28a5df7b39a35239e3209e8dce25d0b5a3e1146a9b9d40/rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ecf0e6de84c0bc2c0f48bc03ba23cef2c5f1245db7b26bc860c11c6fd7a097c", size = 3142162 },
|
{ url = "https://files.pythonhosted.org/packages/38/d4/5cfbc9a997e544f07f301c54d42aac9e0d28d457d543169e4ec859b8ce0d/rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9256218ac8f1a957806ec2fb9a6ddfc6c32ea937c0429e88cf16362a20ed8602", size = 3086956 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/da/cd/89751db1dd8b020ccce6d83e59fcf7f4f4090d093900b52552c5561a438c/rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4dc2ebad4adb29d84a661f6a42494df48ad2b72993ff43fad2b9794804f91e45", size = 2339376 },
|
{ url = "https://files.pythonhosted.org/packages/25/1e/06d8932a72fa9576095234a15785136407acf8f9a7dbc8136389a3429da1/rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1bdd2e6d0c5f9706ef7595773a81ca2b40f3b33fd7f9840b726fb00c6c4eb2e", size = 2494220 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/eb/85/b6e46c3d686cc3f53457468d46499e88492980a447e34f12ce1f81fc246d/rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8389d98b9f54cb4f8a95f1fa34bf0ceee639e919807bb931ca479c7a5f2930bf", size = 6941790 },
|
{ url = "https://files.pythonhosted.org/packages/03/16/5acf15df63119d5ca3d9a54b82807866ff403461811d077201ca351a40c3/rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5280be8fd7e2bee5822e254fe0a5763aa0ad57054b85a32a3d9970e9b09bbcbf", size = 7585481 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/54/20/9309eb912ffd701e6a1d1961475b9607f8cd0a793d6011c44a1f0e306f45/rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:165bcdecbfed9978962da1d3ec9c191b2ff9f1ccc2668fbaf0613a975b9aa326", size = 2719567 },
|
{ url = "https://files.pythonhosted.org/packages/e1/cf/ebade4009431ea8e715e59e882477a970834ddaacd1a670095705b86bd0d/rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd742c03885db1fce798a1cd87a20f47f144ccf26d75d52feb6f2bae3d57af05", size = 2894842 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/43/74/449c1680b30f640ed380bef6cdd8837b69b0325e4e9e7a8bc3dd106bd8cb/rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:129d536740ab0048c1a06ccff73c683f282a2347c68069affae8dbc423a37c50", size = 3268295 },
|
{ url = "https://files.pythonhosted.org/packages/a7/bd/0732632bd3f906bf613229ee1b7cbfba77515db714a0e307becfa8a970ae/rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5435fcac94c9ecf0504bf88a8a60c55482c32e18e108d6079a0089c47f3f8cf6", size = 3438517 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/47/71/949eacc7b5a69b5d0aeca27eab295b2a3481116dc26959aa9a063e3876d0/rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b67e390261ffe98ec86c771b89425a78b60ccb610c3b5874660216fcdbded4b", size = 4172971 },
|
{ url = "https://files.pythonhosted.org/packages/83/89/d3bd47ec9f4b0890f62aea143a1e35f78f3d8329b93d9495b4fa8a3cbfc3/rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:93a755266856599be4ab6346273f192acde3102d7aa0735e2f48b456397a041f", size = 4412773 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a3/f2/9146cee62060dfe1de4beebe349fe4c007f5de4611cf3fbfb61e4b61b500/rapidfuzz-3.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6d9afad7b16d01c9e8929b6a205a18163c7e61b6cd9bcf9c81be77d5afc1067a", size = 1960497 },
|
{ url = "https://files.pythonhosted.org/packages/87/17/9be9eff5a3c7dfc831c2511262082c6786dca2ce21aa8194eef1cb71d67a/rapidfuzz-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d395a5cad0c09c7f096433e5fd4224d83b53298d53499945a9b0e5a971a84f3a", size = 1999453 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/3e/54/7fee154f9a00c97b4eb12b223c184ca9be1ec0725b9f9e5e913dc6266c69/rapidfuzz-3.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb424ae7240f2d2f7d8dda66a61ebf603f74d92f109452c63b0dbf400204a437", size = 1434283 },
|
{ url = "https://files.pythonhosted.org/packages/75/67/62e57896ecbabe363f027d24cc769d55dd49019e576533ec10e492fcd8a2/rapidfuzz-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7b3eda607a019169f7187328a8d1648fb9a90265087f6903d7ee3a8eee01805", size = 1450881 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ef/c5/8138e48c1ee31b5bd38facbb78c859e4e58aa306f5f753ffee82166390b7/rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42149e6d13bd6d06437d2a954dae2184dadbbdec0fdb82dafe92860d99f80519", size = 1417803 },
|
{ url = "https://files.pythonhosted.org/packages/96/5c/691c5304857f3476a7b3df99e91efc32428cbe7d25d234e967cc08346c13/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98e0bfa602e1942d542de077baf15d658bd9d5dcfe9b762aff791724c1c38b70", size = 1422990 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/03/0a/be43022744d79f1f0725cb21fe2a9656fb8a509547dbef120b4b335ca9bd/rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:760ac95d788f2964b73da01e0bdffbe1bf2ad8273d0437565ce9092ae6ad1fbc", size = 5620489 },
|
{ url = "https://files.pythonhosted.org/packages/46/81/7a7e78f977496ee2d613154b86b203d373376bcaae5de7bde92f3ad5a192/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bef86df6d59667d9655905b02770a0c776d2853971c0773767d5ef8077acd624", size = 5342309 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/21/d8/fa4b5ce056c4c2e2506706058cb14c44b77de897e70396643ea3bfa75ed0/rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2cf27e8e4bf7bf9d92ef04f3d2b769e91c3f30ba99208c29f5b41e77271a2614", size = 1671236 },
|
{ url = "https://files.pythonhosted.org/packages/51/44/12fdd12a76b190fe94bf38d252bb28ddf0ab7a366b943e792803502901a2/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fedd316c165beed6307bf754dee54d3faca2c47e1f3bcbd67595001dfa11e969", size = 1656881 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/db/21/5b171401ac92189328ba680a1f68c54c89b18a410d8c865794c433839ea1/rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00ceb8ff3c44ab0d6014106c71709c85dee9feedd6890eff77c814aa3798952b", size = 1683376 },
|
{ url = "https://files.pythonhosted.org/packages/27/ae/0d933e660c06fcfb087a0d2492f98322f9348a28b2cc3791a5dbadf6e6fb/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5158da7f2ec02a930be13bac53bb5903527c073c90ee37804090614cab83c29e", size = 1608494 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/1d/ce/f209f437c6df46ba523a6898ebd854b30196650f77dcddf203191f09bf9b/rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b61c558574fbc093d85940c3264c08c2b857b8916f8e8f222e7b86b0bb7d12", size = 3139202 },
|
{ url = "https://files.pythonhosted.org/packages/3d/2c/4b2f8aafdf9400e5599b6ed2f14bc26ca75f5a923571926ccbc998d4246a/rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b6f913ee4618ddb6d6f3e387b76e8ec2fc5efee313a128809fbd44e65c2bbb2", size = 3072160 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/41/3a/6821bddb2af8412b340a7258c89a7519e7ebece58c6b3027859138bb3142/rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:346a2d8f17224e99f9ef988606c83d809d5917d17ad00207237e0965e54f9730", size = 2346575 },
|
{ url = "https://files.pythonhosted.org/packages/60/7d/030d68d9a653c301114101c3003b31ce01cf2c3224034cd26105224cd249/rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d25fdbce6459ccbbbf23b4b044f56fbd1158b97ac50994eaae2a1c0baae78301", size = 2491549 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/44/db/f76a211e050024f11d0d2b0dfca6378e949d6d81f9bdaac15c7c30280942/rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d60d1db1b7e470e71ae096b6456e20ec56b52bde6198e2dbbc5e6769fa6797dc", size = 6944232 },
|
{ url = "https://files.pythonhosted.org/packages/8e/cd/7040ba538fc6a8ddc8816a05ecf46af9988b46c148ddd7f74fb0fb73d012/rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25343ccc589a4579fbde832e6a1e27258bfdd7f2eb0f28cb836d6694ab8591fc", size = 7584142 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/16/a5/670287316f7f3591141c9ab3752f295705547f8075bf1616b76ad8f64069/rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2477da227e266f9c712f11393182c69a99d3c8007ea27f68c5afc3faf401cc43", size = 2722753 },
|
{ url = "https://files.pythonhosted.org/packages/c1/96/85f7536fbceb0aa92c04a1c37a3fc4fcd4e80649e9ed0fb585382df82edc/rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a9ad1f37894e3ffb76bbab76256e8a8b789657183870be11aa64e306bb5228fd", size = 2896234 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ba/68/5be0dfd2b3fc0dfac7f4b251b18121b2809f244f16b2c44a54b0ffa733a6/rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8499c7d963ddea8adb6cffac2861ee39a1053e22ca8a5ee9de1197f8dc0275a5", size = 3262227 },
|
{ url = "https://files.pythonhosted.org/packages/55/fd/460e78438e7019f2462fe9d4ecc880577ba340df7974c8a4cfe8d8d029df/rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5dc71ef23845bb6b62d194c39a97bb30ff171389c9812d83030c1199f319098c", size = 3437420 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/02/c6/a747b4103d3a96b4e5d022326b764d2493190dd5240e4aeb1a791c5a26f9/rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:12802e5c4d8ae104fb6efeeb436098325ce0dca33b461c46e8df015c84fbef26", size = 4175381 },
|
{ url = "https://files.pythonhosted.org/packages/cc/df/c3c308a106a0993befd140a414c5ea78789d201cf1dfffb8fd9749718d4f/rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b7f4c65facdb94f44be759bbd9b6dda1fa54d0d6169cdf1a209a5ab97d311a75", size = 4410860 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/1a/20/6049061411df87f2814a2677db0f15e673bb9795bfeff57dc9708121374d/rapidfuzz-3.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f6235b57ae3faa3f85cb3f90c9fee49b21bd671b76e90fc99e8ca2bdf0b5e4a3", size = 1944328 },
|
{ url = "https://files.pythonhosted.org/packages/13/4b/a326f57a4efed8f5505b25102797a58e37ee11d94afd9d9422cb7c76117e/rapidfuzz-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a1a6a906ba62f2556372282b1ef37b26bca67e3d2ea957277cfcefc6275cca7", size = 1989501 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/25/73/199383c4c21ae3b4b6ea6951c6896ab38e9dc96942462fa01f9d3fb047da/rapidfuzz-3.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af4585e5812632c357fee5ab781c29f00cd06bea58f8882ff244cc4906ba6c9e", size = 1430203 },
|
{ url = "https://files.pythonhosted.org/packages/b7/53/1f7eb7ee83a06c400089ec7cb841cbd581c2edd7a4b21eb2f31030b88daa/rapidfuzz-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fd0975e015b05c79a97f38883a11236f5a24cca83aa992bd2558ceaa5652b26", size = 1445379 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/7b/51/77ebaeec5413c53c3e6d8b800f2b979551adbed7b5efa094d1fad5c5b751/rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5942dc4460e5030c5f9e1d4c9383de2f3564a2503fe25e13e89021bcbfea2f44", size = 1403662 },
|
{ url = "https://files.pythonhosted.org/packages/07/09/de8069a4599cc8e6d194e5fa1782c561151dea7d5e2741767137e2a8c1f0/rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d4e13593d298c50c4f94ce453f757b4b398af3fa0fd2fde693c3e51195b7f69", size = 1405986 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/54/06/1fadd2704db0a7eecf78de812e2f4fab37c4ae105a5ce4578c9fc66bb0c5/rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b31ab59e1a0df5afc21f3109b6cfd77b34040dbf54f1bad3989f885cfae1e60", size = 5555849 },
|
{ url = "https://files.pythonhosted.org/packages/5d/77/d9a90b39c16eca20d70fec4ca377fbe9ea4c0d358c6e4736ab0e0e78aaf6/rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed6f416bda1c9133000009d84d9409823eb2358df0950231cc936e4bf784eb97", size = 5310809 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/19/45/da128c3952bd09cef2935df58db5273fc4eb67f04a69dcbf9e25af9e4432/rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97c885a7a480b21164f57a706418c9bbc9a496ec6da087e554424358cadde445", size = 1655273 },
|
{ url = "https://files.pythonhosted.org/packages/1e/7d/14da291b0d0f22262d19522afaf63bccf39fc027c981233fb2137a57b71f/rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1dc82b6ed01acb536b94a43996a94471a218f4d89f3fdd9185ab496de4b2a981", size = 1629394 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/03/ee/bf2b2a95b5af4e6d36105dd9284dc5335fdcc7f0326186d4ab0b5aa4721e/rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d844c0587d969ce36fbf4b7cbf0860380ffeafc9ac5e17a7cbe8abf528d07bb", size = 1678041 },
|
{ url = "https://files.pythonhosted.org/packages/b7/e4/79ed7e4fa58f37c0f8b7c0a62361f7089b221fe85738ae2dbcfb815e985a/rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9d824de871daa6e443b39ff495a884931970d567eb0dfa213d234337343835f", size = 1600544 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/7f/4f/36ea4d7f306a23e30ea1a6cabf545d2a794e8ca9603d2ee48384314cde3a/rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93c95dce8917bf428064c64024de43ffd34ec5949dd4425780c72bd41f9d969", size = 3137099 },
|
{ url = "https://files.pythonhosted.org/packages/4e/20/e62b4d13ba851b0f36370060025de50a264d625f6b4c32899085ed51f980/rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d18228a2390375cf45726ce1af9d36ff3dc1f11dce9775eae1f1b13ac6ec50f", size = 3052796 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/70/ef/48195d94b018e7340a60c9a642ab0081bf9dc64fb0bd01dfafd93757d2a2/rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:834f6113d538af358f39296604a1953e55f8eeffc20cb4caf82250edbb8bf679", size = 2307388 },
|
{ url = "https://files.pythonhosted.org/packages/cd/8d/55fdf4387dec10aa177fe3df8dbb0d5022224d95f48664a21d6b62a5299d/rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5fe634c9482ec5d4a6692afb8c45d370ae86755e5f57aa6c50bfe4ca2bdd87", size = 2464016 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/e5/cd/53d5dbc4791df3e1a8640fc4ad5e328ebb040cc01c10c66f891aa6b83ed5/rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a940aa71a7f37d7f0daac186066bf6668d4d3b7e7ef464cb50bc7ba89eae1f51", size = 6906504 },
|
{ url = "https://files.pythonhosted.org/packages/9b/be/0872f6a56c0f473165d3b47d4170fa75263dc5f46985755aa9bf2bbcdea1/rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:694eb531889f71022b2be86f625a4209c4049e74be9ca836919b9e395d5e33b3", size = 7556725 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/1b/99/c27e7db1d49cfd77780cb73978f81092682c2bdbc6de75363df6aaa086d6/rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ec9eaf73501c9a7de2c6938cb3050392e2ee0c5ca3921482acf01476b85a7226", size = 2684757 },
|
{ url = "https://files.pythonhosted.org/packages/5d/f3/6c0750e484d885a14840c7a150926f425d524982aca989cdda0bb3bdfa57/rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:11b47b40650e06147dee5e51a9c9ad73bb7b86968b6f7d30e503b9f8dd1292db", size = 2859052 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/02/8c/2474d6282fdd4aae386a6b16272e544a3f9ea2dcdcf2f3b0b286549bc3d5/rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c5ec360694ac14bfaeb6aea95737cf1a6cf805b5fe8ea7fd28814706c7fa838", size = 3229940 },
|
{ url = "https://files.pythonhosted.org/packages/6f/98/5a3a14701b5eb330f444f7883c9840b43fb29c575e292e09c90a270a6e07/rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:98b8107ff14f5af0243f27d236bcc6e1ef8e7e3b3c25df114e91e3a99572da73", size = 3390219 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ac/27/95d5a8ebe5fcc5462dd0fd265553c8a2ec4a770e079afabcff978442bcb3/rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6b5e176524653ac46f1802bdd273a4b44a5f8d0054ed5013a8e8a4b72f254599", size = 4148489 },
|
{ url = "https://files.pythonhosted.org/packages/e9/7d/f4642eaaeb474b19974332f2a58471803448be843033e5740965775760a5/rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b836f486dba0aceb2551e838ff3f514a38ee72b015364f739e526d720fdb823a", size = 4377924 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/62/d2/ceebc2446d1f3d3f2cae2597116982e50c2eed9ff2f5a322a51736981405/rapidfuzz-3.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:97f824c15bc6933a31d6e3cbfa90188ba0e5043cf2b6dd342c2b90ee8b3fd47c", size = 1936794 },
|
{ url = "https://files.pythonhosted.org/packages/0a/76/606e71e4227790750f1646f3c5c873e18d6cfeb6f9a77b2b8c4dec8f0f66/rapidfuzz-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:09e908064d3684c541d312bd4c7b05acb99a2c764f6231bd507d4b4b65226c23", size = 1982282 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/88/38/37f7ea800aa959a4f7a63477fc9ad7f3cd024e46bfadce5d23420af6c7e5/rapidfuzz-3.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a973b3f5cabf931029a3ae4a0f72e3222e53d412ea85fc37ddc49e1774f00fbf", size = 1424155 },
|
{ url = "https://files.pythonhosted.org/packages/0a/f5/d0b48c6b902607a59fd5932a54e3518dae8223814db8349b0176e6e9444b/rapidfuzz-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:57c390336cb50d5d3bfb0cfe1467478a15733703af61f6dffb14b1cd312a6fae", size = 1439274 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/3f/14/409d0aa84430451488177fcc5cba8babcdf5a45cee772a2a265b9b5f4c7e/rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df7880e012228722dec1be02b9ef3898ed023388b8a24d6fa8213d7581932510", size = 1398013 },
|
{ url = "https://files.pythonhosted.org/packages/59/cf/c3ac8c80d8ced6c1f99b5d9674d397ce5d0e9d0939d788d67c010e19c65f/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0da54aa8547b3c2c188db3d1c7eb4d1bb6dd80baa8cdaeaec3d1da3346ec9caa", size = 1399854 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4b/2c/601e3ad0bbe61e65f99e72c8cefed9713606cf4b297cc4c3876051db7722/rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c78582f50e75e6c2bc38c791ed291cb89cf26a3148c47860c1a04d6e5379c8e", size = 5526157 },
|
{ url = "https://files.pythonhosted.org/packages/09/5d/ca8698e452b349c8313faf07bfa84e7d1c2d2edf7ccc67bcfc49bee1259a/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df8e8c21e67afb9d7fbe18f42c6111fe155e801ab103c81109a61312927cc611", size = 5308962 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/97/ce/deb7b00ce6e06713fc4df81336402b7fa062f2393c8a47401c228ee906c3/rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d7d9e6a04d8344b0198c96394c28874086888d0a2b2f605f30d1b27b9377b7d", size = 1648446 },
|
{ url = "https://files.pythonhosted.org/packages/66/0a/bebada332854e78e68f3d6c05226b23faca79d71362509dbcf7b002e33b7/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:461fd13250a2adf8e90ca9a0e1e166515cbcaa5e9c3b1f37545cbbeff9e77f6b", size = 1625016 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ec/6f/2b8eae1748a022290815999594b438dbc1e072c38c76178ea996920a6253/rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5620001fd4d6644a2f56880388179cc8f3767670f0670160fcb97c3b46c828af", size = 1676038 },
|
{ url = "https://files.pythonhosted.org/packages/de/0c/9e58d4887b86d7121d1c519f7050d1be5eb189d8a8075f5417df6492b4f5/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2b3dd5d206a12deca16870acc0d6e5036abeb70e3cad6549c294eff15591527", size = 1600414 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b9/6c/5c831197aca7148ed85c86bbe940e66073fea0fa97f30307bb5850ed8858/rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0666ab4c52e500af7ba5cc17389f5d15c0cdad06412c80312088519fdc25686d", size = 3114137 },
|
{ url = "https://files.pythonhosted.org/packages/9b/df/6096bc669c1311568840bdcbb5a893edc972d1c8d2b4b4325c21d54da5b1/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1343d745fbf4688e412d8f398c6e6d6f269db99a54456873f232ba2e7aeb4939", size = 3053179 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/fc/f2/d66ac185eeb0ee3fc0fe208dab1e72feece2c883bc0ab2097570a8159a7b/rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:27b4d440fa50b50c515a91a01ee17e8ede719dca06eef4c0cccf1a111a4cfad3", size = 2305754 },
|
{ url = "https://files.pythonhosted.org/packages/f9/46/5179c583b75fce3e65a5cd79a3561bd19abd54518cb7c483a89b284bf2b9/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b1b065f370d54551dcc785c6f9eeb5bd517ae14c983d2784c064b3aa525896df", size = 2456856 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6c/61/9bf74d7ea9bebc7a1bed707591617bba7901fce414d346a7c5532ef02dbd/rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83dccfd5a754f2a0e8555b23dde31f0f7920601bfa807aa76829391ea81e7c67", size = 6901746 },
|
{ url = "https://files.pythonhosted.org/packages/6b/64/e9804212e3286d027ac35bbb66603c9456c2bce23f823b67d2f5cabc05c1/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:11b125d8edd67e767b2295eac6eb9afe0b1cdc82ea3d4b9257da4b8e06077798", size = 7567107 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/81/73/d8dddf73e168f723ef21272e8abb7d34d9244da395eb90ed5a617f870678/rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b572b634740e047c53743ed27a1bb3b4f93cf4abbac258cd7af377b2c4a9ba5b", size = 2673947 },
|
{ url = "https://files.pythonhosted.org/packages/8a/f2/7d69e7bf4daec62769b11757ffc31f69afb3ce248947aadbb109fefd9f65/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c33f9c841630b2bb7e69a3fb5c84a854075bb812c47620978bddc591f764da3d", size = 2854192 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2e/31/3c473cea7d76af162819a5b84f5e7bdcf53b9e19568fc37cfbdab4f4512a/rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7fa7b81fb52902d5f78dac42b3d6c835a6633b01ddf9b202a3ca8443be4b2d6a", size = 3233070 },
|
{ url = "https://files.pythonhosted.org/packages/05/21/ab4ad7d7d0f653e6fe2e4ccf11d0245092bef94cdff587a21e534e57bda8/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae4574cb66cf1e85d32bb7e9ec45af5409c5b3970b7ceb8dea90168024127566", size = 3398876 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c0/b7/73227dcbf8586f0ca4a77be2720311367288e2db142ae00a1404f42e712d/rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1d4fbff980cb6baef4ee675963c081f7b5d6580a105d6a4962b20f1f880e1fb", size = 4146828 },
|
{ url = "https://files.pythonhosted.org/packages/0f/a8/45bba94c2489cb1ee0130dcb46e1df4fa2c2b25269e21ffd15240a80322b/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e05752418b24bbd411841b256344c26f57da1148c5509e34ea39c7eb5099ab72", size = 4377077 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/0b/5f/82352d6e68ddd45973cbc9f4c89a2a6b6b93907b0f775b8095f34bef654e/rapidfuzz-3.12.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b7cba636c32a6fc3a402d1cb2c70c6c9f8e6319380aaf15559db09d868a23e56", size = 1858389 },
|
{ url = "https://files.pythonhosted.org/packages/d5/e1/f5d85ae3c53df6f817ca70dbdd37c83f31e64caced5bb867bec6b43d1fdf/rapidfuzz-3.13.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe5790a36d33a5d0a6a1f802aa42ecae282bf29ac6f7506d8e12510847b82a45", size = 1904437 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/05/17/76bab0b29b78171cde746d180258b93aa66a80503291c813b7d8b2a2b927/rapidfuzz-3.12.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b79286738a43e8df8420c4b30a92712dec6247430b130f8e015c3a78b6d61ac2", size = 1368428 },
|
{ url = "https://files.pythonhosted.org/packages/db/d7/ded50603dddc5eb182b7ce547a523ab67b3bf42b89736f93a230a398a445/rapidfuzz-3.13.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:cdb33ee9f8a8e4742c6b268fa6bd739024f34651a06b26913381b1413ebe7590", size = 1383126 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/71/77/0ad39429d25b52e21fa2ecbc1f577e62d77c76c8db562bb93c56fe19ccd3/rapidfuzz-3.12.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dc1937198e7ff67e217e60bfa339f05da268d91bb15fec710452d11fe2fdf60", size = 1364376 },
|
{ url = "https://files.pythonhosted.org/packages/c4/48/6f795e793babb0120b63a165496d64f989b9438efbeed3357d9a226ce575/rapidfuzz-3.13.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c99b76b93f7b495eee7dcb0d6a38fb3ce91e72e99d9f78faa5664a881cb2b7d", size = 1365565 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c0/1d/724670d13222f9959634d3dfa832e7cec889e62fca5f9f4acf65f83fa1d5/rapidfuzz-3.12.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b85817a57cf8db32dd5d2d66ccfba656d299b09eaf86234295f89f91be1a0db2", size = 5486472 },
|
{ url = "https://files.pythonhosted.org/packages/f0/50/0062a959a2d72ed17815824e40e2eefdb26f6c51d627389514510a7875f3/rapidfuzz-3.13.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6af42f2ede8b596a6aaf6d49fdee3066ca578f4856b85ab5c1e2145de367a12d", size = 5251719 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2c/69/e5cb280ce99dea2de60fa1c80ffab2ebc6e38694a98d7c2b25d2337f87eb/rapidfuzz-3.12.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04283c6f3e79f13a784f844cd5b1df4f518ad0f70c789aea733d106c26e1b4fb", size = 3064862 },
|
{ url = "https://files.pythonhosted.org/packages/e7/02/bd8b70cd98b7a88e1621264778ac830c9daa7745cd63e838bd773b1aeebd/rapidfuzz-3.13.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c0efa73afbc5b265aca0d8a467ae2a3f40d6854cbe1481cb442a62b7bf23c99", size = 2991095 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/df/6060c5a9c879b302bd47a73fc012d0db37abf6544c57591bcbc3459673bd/rapidfuzz-3.13.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1ba007f4d35a45ee68656b2eb83b8715e11d0f90e5b9f02d615a8a321ff00c27", size = 1905935 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a2/6c/a0b819b829e20525ef1bd58fc776fb8d07a0c38d819e63ba2b7c311a2ed4/rapidfuzz-3.13.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d7a217310429b43be95b3b8ad7f8fc41aba341109dc91e978cd7c703f928c58f", size = 1383714 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/c1/3da3466cc8a9bfb9cd345ad221fac311143b6a9664b5af4adb95b5e6ce01/rapidfuzz-3.13.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:558bf526bcd777de32b7885790a95a9548ffdcce68f704a81207be4a286c1095", size = 1367329 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/f0/9f2a9043bfc4e66da256b15d728c5fc2d865edf0028824337f5edac36783/rapidfuzz-3.13.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:202a87760f5145140d56153b193a797ae9338f7939eb16652dd7ff96f8faf64c", size = 5251057 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/ff/af2cb1d8acf9777d52487af5c6b34ce9d13381a753f991d95ecaca813407/rapidfuzz-3.13.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcccc08f671646ccb1e413c773bb92e7bba789e3a1796fd49d23c12539fe2e4", size = 2992401 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user