mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-10-31 08:45:21 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d394ead1e4 | ||
|
|
8568543516 | ||
|
|
321e333711 | ||
|
|
853d64328e | ||
|
|
e5bb3f7dbb | ||
|
|
3bc1ee5826 | ||
|
|
46d0a5b202 | ||
|
|
0707fc2718 | ||
|
|
6b2a9101c4 | ||
|
|
0bfb3af886 | ||
|
|
d091906315 |
No files matched your search
@@ -1,91 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## paperless-ngx 3.4.0
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- Enhancement: support custom fields in workflow title templates [@shamoon](https://github.com/shamoon) ([#14564](https://github.com/paperless-ngx/paperless-ngx/pull/14564))
|
||||
- Enhancement: relative date custom field queries [@shamoon](https://github.com/shamoon) ([#14549](https://github.com/paperless-ngx/paperless-ngx/pull/14549))
|
||||
- Performance: reduce tasks badge load times [@JanKleine](https://github.com/JanKleine) ([#14552](https://github.com/paperless-ngx/paperless-ngx/pull/14552))
|
||||
- Enhancement: skip local 2FA for trusted OIDC AMR [@shamoon](https://github.com/shamoon) ([#14403](https://github.com/paperless-ngx/paperless-ngx/pull/14403))
|
||||
- Feature: Generate PDF thumbnails via poppler utils [@stumpylog](https://github.com/stumpylog) ([#14420](https://github.com/paperless-ngx/paperless-ngx/pull/14420))
|
||||
- Enhancement: add upload count to button, link to tray, reduce notifications [@shamoon](https://github.com/shamoon) ([#14501](https://github.com/paperless-ngx/paperless-ngx/pull/14501))
|
||||
- Enhancement: notifications improvements [@shamoon](https://github.com/shamoon) ([#14500](https://github.com/paperless-ngx/paperless-ngx/pull/14500))
|
||||
- Enhancement: doc details ux improvements [@shamoon](https://github.com/shamoon) ([#14485](https://github.com/paperless-ngx/paperless-ngx/pull/14485))
|
||||
- Enhancement (QoL): allow independently toggling child + parent tags in WF tag fields [@shamoon](https://github.com/shamoon) ([#14400](https://github.com/paperless-ngx/paperless-ngx/pull/14400))
|
||||
- Tweak: drop the user dropdown caret [@shamoon](https://github.com/shamoon) ([#14444](https://github.com/paperless-ngx/paperless-ngx/pull/14444))
|
||||
- Enhancement: navbar upload button [@shamoon](https://github.com/shamoon) ([#14442](https://github.com/paperless-ngx/paperless-ngx/pull/14442))
|
||||
- Enhancement: AI chat UX tweaks [@shamoon](https://github.com/shamoon) ([#14415](https://github.com/paperless-ngx/paperless-ngx/pull/14415))
|
||||
- Enhancement: support markdown in AI chat [@shamoon](https://github.com/shamoon) ([#14409](https://github.com/paperless-ngx/paperless-ngx/pull/14409))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: truncate long version labels in dropdown [@shamoon](https://github.com/shamoon) ([#14578](https://github.com/paperless-ngx/paperless-ngx/pull/14578))
|
||||
- Fixhancement: show better result for revoked / expired tasks [@shamoon](https://github.com/shamoon) ([#14566](https://github.com/paperless-ngx/paperless-ngx/pull/14566))
|
||||
- Fix: move re-index after workflow runs on doc edit [@shamoon](https://github.com/shamoon) ([#14558](https://github.com/paperless-ngx/paperless-ngx/pull/14558))
|
||||
- Fix: folder export with --delete fails on a stale manifest.json.tmp [@benpetty](https://github.com/benpetty) ([#14526](https://github.com/paperless-ngx/paperless-ngx/pull/14526))
|
||||
- Fix: fix list type for api schema document object filters [@shamoon](https://github.com/shamoon) ([#14524](https://github.com/paperless-ngx/paperless-ngx/pull/14524))
|
||||
- Fix: standardize some dropdown sizing [@shamoon](https://github.com/shamoon) ([#14510](https://github.com/paperless-ngx/paperless-ngx/pull/14510))
|
||||
- Fix: prevent doc permissions queries on schema view [@shamoon](https://github.com/shamoon) ([#14503](https://github.com/paperless-ngx/paperless-ngx/pull/14503))
|
||||
- Fix: dont defer tag assignment when barcode will not result in a split [@shamoon](https://github.com/shamoon) ([#14497](https://github.com/paperless-ngx/paperless-ngx/pull/14497))
|
||||
|
||||
### Dependencies
|
||||
|
||||
<details>
|
||||
<summary>6 changes</summary>
|
||||
|
||||
- Chore: bump some frontend deps for security fixes [@shamoon](https://github.com/shamoon) ([#14581](https://github.com/paperless-ngx/paperless-ngx/pull/14581))
|
||||
- Chore: bump brotli, djangorestframework, filelock, msgpack, setuptools for security fixes [@stumpylog](https://github.com/stumpylog) ([#14575](https://github.com/paperless-ngx/paperless-ngx/pull/14575))
|
||||
- Chore: bump django-allauth to 65.20 and oauthlib to 4.0 [@stumpylog](https://github.com/stumpylog) ([#14570](https://github.com/paperless-ngx/paperless-ngx/pull/14570))
|
||||
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14517](https://github.com/paperless-ngx/paperless-ngx/pull/14517))
|
||||
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14493](https://github.com/paperless-ngx/paperless-ngx/pull/14493))
|
||||
- Chore: Drop ImageMagik as a dependency [@stumpylog](https://github.com/stumpylog) ([#14440](https://github.com/paperless-ngx/paperless-ngx/pull/14440))
|
||||
|
||||
</details>
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>35 changes</summary>
|
||||
|
||||
- Chore: Consolidate test sample files [@stumpylog](https://github.com/stumpylog) ([#14430](https://github.com/paperless-ngx/paperless-ngx/pull/14430))
|
||||
- Chore: bump some frontend deps for security fixes [@shamoon](https://github.com/shamoon) ([#14581](https://github.com/paperless-ngx/paperless-ngx/pull/14581))
|
||||
- Enhancement: support custom fields in workflow title templates [@shamoon](https://github.com/shamoon) ([#14564](https://github.com/paperless-ngx/paperless-ngx/pull/14564))
|
||||
- Enhancement: relative date custom field queries [@shamoon](https://github.com/shamoon) ([#14549](https://github.com/paperless-ngx/paperless-ngx/pull/14549))
|
||||
- Fix: truncate long version labels in dropdown [@shamoon](https://github.com/shamoon) ([#14578](https://github.com/paperless-ngx/paperless-ngx/pull/14578))
|
||||
- Chore: bump brotli, djangorestframework, filelock, msgpack, setuptools for security fixes [@stumpylog](https://github.com/stumpylog) ([#14575](https://github.com/paperless-ngx/paperless-ngx/pull/14575))
|
||||
- Chore: bump django-allauth to 65.20 and oauthlib to 4.0 [@stumpylog](https://github.com/stumpylog) ([#14570](https://github.com/paperless-ngx/paperless-ngx/pull/14570))
|
||||
- Fixhancement: show better result for revoked / expired tasks [@shamoon](https://github.com/shamoon) ([#14566](https://github.com/paperless-ngx/paperless-ngx/pull/14566))
|
||||
- Fix: move re-index after workflow runs on doc edit [@shamoon](https://github.com/shamoon) ([#14558](https://github.com/paperless-ngx/paperless-ngx/pull/14558))
|
||||
- Performance: reduce tasks badge load times [@JanKleine](https://github.com/JanKleine) ([#14552](https://github.com/paperless-ngx/paperless-ngx/pull/14552))
|
||||
- Enhancement: skip local 2FA for trusted OIDC AMR [@shamoon](https://github.com/shamoon) ([#14403](https://github.com/paperless-ngx/paperless-ngx/pull/14403))
|
||||
- Chore: consolidate direct permission grant lookups into one helper [@stumpylog](https://github.com/stumpylog) ([#14456](https://github.com/paperless-ngx/paperless-ngx/pull/14456))
|
||||
- Chore: drop get\_groups\_with\_only\_permission [@stumpylog](https://github.com/stumpylog) ([#14454](https://github.com/paperless-ngx/paperless-ngx/pull/14454))
|
||||
- Chore: Consolidate to a single way to set permission on an object [@stumpylog](https://github.com/stumpylog) ([#14453](https://github.com/paperless-ngx/paperless-ngx/pull/14453))
|
||||
- Chore: Refactor bulk edit to remove PDF knowledge [@stumpylog](https://github.com/stumpylog) ([#14445](https://github.com/paperless-ngx/paperless-ngx/pull/14445))
|
||||
- Fix: folder export with --delete fails on a stale manifest.json.tmp [@benpetty](https://github.com/benpetty) ([#14526](https://github.com/paperless-ngx/paperless-ngx/pull/14526))
|
||||
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14517](https://github.com/paperless-ngx/paperless-ngx/pull/14517))
|
||||
- Fix: fix list type for api schema document object filters [@shamoon](https://github.com/shamoon) ([#14524](https://github.com/paperless-ngx/paperless-ngx/pull/14524))
|
||||
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14493](https://github.com/paperless-ngx/paperless-ngx/pull/14493))
|
||||
- Chore: drop get\_objects\_for\_user\_owner\_aware [@stumpylog](https://github.com/stumpylog) ([#14457](https://github.com/paperless-ngx/paperless-ngx/pull/14457))
|
||||
- Chore: update Angular components to 22.2.2 [@shamoon](https://github.com/shamoon) ([#14515](https://github.com/paperless-ngx/paperless-ngx/pull/14515))
|
||||
- Chore: share one base class for the method-mapped permission classes [@stumpylog](https://github.com/stumpylog) ([#14458](https://github.com/paperless-ngx/paperless-ngx/pull/14458))
|
||||
- Fix: standardize some dropdown sizing [@shamoon](https://github.com/shamoon) ([#14510](https://github.com/paperless-ngx/paperless-ngx/pull/14510))
|
||||
- Chore: Drop ImageMagik as a dependency [@stumpylog](https://github.com/stumpylog) ([#14440](https://github.com/paperless-ngx/paperless-ngx/pull/14440))
|
||||
- Feature: Generate PDF thumbnails via poppler utils [@stumpylog](https://github.com/stumpylog) ([#14420](https://github.com/paperless-ngx/paperless-ngx/pull/14420))
|
||||
- Fix: prevent doc permissions queries on schema view [@shamoon](https://github.com/shamoon) ([#14503](https://github.com/paperless-ngx/paperless-ngx/pull/14503))
|
||||
- Enhancement: add upload count to button, link to tray, reduce notifications [@shamoon](https://github.com/shamoon) ([#14501](https://github.com/paperless-ngx/paperless-ngx/pull/14501))
|
||||
- Enhancement: notifications improvements [@shamoon](https://github.com/shamoon) ([#14500](https://github.com/paperless-ngx/paperless-ngx/pull/14500))
|
||||
- Enhancement: doc details ux improvements [@shamoon](https://github.com/shamoon) ([#14485](https://github.com/paperless-ngx/paperless-ngx/pull/14485))
|
||||
- Enhancement (QoL): allow independently toggling child + parent tags in WF tag fields [@shamoon](https://github.com/shamoon) ([#14400](https://github.com/paperless-ngx/paperless-ngx/pull/14400))
|
||||
- Tweak: drop the user dropdown caret [@shamoon](https://github.com/shamoon) ([#14444](https://github.com/paperless-ngx/paperless-ngx/pull/14444))
|
||||
- Enhancement: navbar upload button [@shamoon](https://github.com/shamoon) ([#14442](https://github.com/paperless-ngx/paperless-ngx/pull/14442))
|
||||
- Enhancement: AI chat UX tweaks [@shamoon](https://github.com/shamoon) ([#14415](https://github.com/paperless-ngx/paperless-ngx/pull/14415))
|
||||
- Enhancement: support markdown in AI chat [@shamoon](https://github.com/shamoon) ([#14409](https://github.com/paperless-ngx/paperless-ngx/pull/14409))
|
||||
- Fix: dont defer tag assignment when barcode will not result in a split [@shamoon](https://github.com/shamoon) ([#14497](https://github.com/paperless-ngx/paperless-ngx/pull/14497))
|
||||
|
||||
</details>
|
||||
|
||||
## paperless-ngx 3.3.4
|
||||
|
||||
### All App Changes
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "paperless-ngx"
|
||||
version = "3.4.0"
|
||||
version = "3.3.4"
|
||||
description = """\
|
||||
A community-supported supercharged document management system: scan, index and archive all your physical documents\
|
||||
"""
|
||||
@@ -256,8 +256,8 @@ isort.force-single-line = true
|
||||
[tool.codespell]
|
||||
ignore-words-list = "criterias,afterall,valeu,ureue,equest,ure,assertIn,Oktober,commitish,NIN,nin,reprot"
|
||||
skip = """\
|
||||
src-ui/src/locale/*,src-ui/pnpm-lock.yaml,src-ui/e2e/*,src/paperless/tests/samples/mail/*,src/documents/tests/samples\
|
||||
/*,src/paperless_testing/sample_files/*,*.po,*.json\
|
||||
src-ui/src/locale/*,src-ui/pnpm-lock.yaml,src-ui/e2e/*,src/paperless_mail/tests/samples/*,src/paperless/tests/samples\
|
||||
/mail/*,src/documents/tests/samples/*,*.po,*.json\
|
||||
"""
|
||||
write-changes = true
|
||||
|
||||
|
||||
@@ -134,9 +134,7 @@ def seed_database() -> None:
|
||||
originals.mkdir(parents=True)
|
||||
thumbnails = Path(os.environ["PAPERLESS_MEDIA_ROOT"]) / "documents" / "thumbnails"
|
||||
thumbnails.mkdir(parents=True)
|
||||
sample_pdf = (
|
||||
SOURCE_ROOT / "paperless_testing" / "sample_files" / "simple-digital.pdf"
|
||||
)
|
||||
sample_pdf = SOURCE_ROOT / "documents" / "tests" / "samples" / "simple.pdf"
|
||||
for document in documents:
|
||||
shutil.copyfile(sample_pdf, originals / document.filename)
|
||||
shutil.copyfile(
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "paperless-ngx-ui",
|
||||
"version": "3.4.0",
|
||||
"version": "3.3.4",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"ng": "ng",
|
||||
|
||||
Generated
+35
-228
@@ -4,10 +4,6 @@ settings:
|
||||
autoInstallPeers: false
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
vite@>=8.3.0 <8.3.3: ^8.3.3
|
||||
zod@<4.6.6: ^4.6.6
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -1654,9 +1650,6 @@ packages:
|
||||
'@oxc-project/types@0.150.0':
|
||||
resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==}
|
||||
|
||||
'@oxc-project/types@0.155.0':
|
||||
resolution: {integrity: sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==}
|
||||
|
||||
'@parcel/watcher-android-arm64@2.6.0':
|
||||
resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
@@ -1755,85 +1748,42 @@ packages:
|
||||
'@popperjs/core@2.11.8':
|
||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||
|
||||
'@rolldown/binding-android-arm-eabi@1.2.15':
|
||||
resolution: {integrity: sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@rolldown/binding-android-arm-eabi@1.2.8':
|
||||
resolution: {integrity: sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@rolldown/binding-android-arm64@1.2.15':
|
||||
resolution: {integrity: sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rolldown/binding-android-arm64@1.2.8':
|
||||
resolution: {integrity: sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.2.15':
|
||||
resolution: {integrity: sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.2.8':
|
||||
resolution: {integrity: sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.2.15':
|
||||
resolution: {integrity: sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.2.8':
|
||||
resolution: {integrity: sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.2.15':
|
||||
resolution: {integrity: sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.2.8':
|
||||
resolution: {integrity: sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.2.15':
|
||||
resolution: {integrity: sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.2.8':
|
||||
resolution: {integrity: sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.2.15':
|
||||
resolution: {integrity: sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.2.8':
|
||||
resolution: {integrity: sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -1841,13 +1791,6 @@ packages:
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.2.15':
|
||||
resolution: {integrity: sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.2.8':
|
||||
resolution: {integrity: sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -1855,13 +1798,6 @@ packages:
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.2.15':
|
||||
resolution: {integrity: sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.2.8':
|
||||
resolution: {integrity: sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -1869,13 +1805,6 @@ packages:
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.2.15':
|
||||
resolution: {integrity: sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.2.8':
|
||||
resolution: {integrity: sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -1883,13 +1812,6 @@ packages:
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.2.15':
|
||||
resolution: {integrity: sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.2.8':
|
||||
resolution: {integrity: sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -1897,13 +1819,6 @@ packages:
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.2.15':
|
||||
resolution: {integrity: sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.2.8':
|
||||
resolution: {integrity: sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -1911,36 +1826,18 @@ packages:
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.2.15':
|
||||
resolution: {integrity: sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.2.8':
|
||||
resolution: {integrity: sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.2.15':
|
||||
resolution: {integrity: sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.2.8':
|
||||
resolution: {integrity: sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.2.15':
|
||||
resolution: {integrity: sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.2.8':
|
||||
resolution: {integrity: sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -2235,7 +2132,7 @@ packages:
|
||||
resolution: {integrity: sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
peerDependencies:
|
||||
vite: ^8.3.3
|
||||
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
|
||||
acorn-jsx@5.3.2:
|
||||
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
||||
@@ -2364,8 +2261,8 @@ packages:
|
||||
peerDependencies:
|
||||
'@popperjs/core': ^2.11.8
|
||||
|
||||
brace-expansion@2.1.7:
|
||||
resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==}
|
||||
brace-expansion@2.1.4:
|
||||
resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}
|
||||
|
||||
brace-expansion@5.0.12:
|
||||
resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==}
|
||||
@@ -2700,8 +2597,8 @@ packages:
|
||||
fast-string-width@3.0.2:
|
||||
resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==}
|
||||
|
||||
fast-uri@3.1.9:
|
||||
resolution: {integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==}
|
||||
fast-uri@3.1.8:
|
||||
resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==}
|
||||
|
||||
fast-wrap-ansi@0.2.2:
|
||||
resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==}
|
||||
@@ -3132,8 +3029,8 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@3.15.2:
|
||||
resolution: {integrity: sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==}
|
||||
js-yaml@3.15.1:
|
||||
resolution: {integrity: sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==}
|
||||
hasBin: true
|
||||
|
||||
jsdom@26.1.0:
|
||||
@@ -3383,11 +3280,6 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
nanoid@3.3.20:
|
||||
resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
napi-postinstall@0.3.4:
|
||||
resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==}
|
||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||
@@ -3618,10 +3510,6 @@ packages:
|
||||
resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
postcss@8.5.31:
|
||||
resolution: {integrity: sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
prelude-ls@1.2.1:
|
||||
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -3713,11 +3601,6 @@ packages:
|
||||
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
rolldown@1.2.15:
|
||||
resolution: {integrity: sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
|
||||
rolldown@1.2.8:
|
||||
resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -3890,8 +3773,8 @@ packages:
|
||||
resolution: {integrity: sha512-aBY19bn/XA+hKOKX0qL7Z0UfdBsbQvc9hn993U8ALUjzxCvDcuZqLoRXjGJrUARWAlwMnsRVD9sw2AFNOYvalA==}
|
||||
engines: {node: '>=22'}
|
||||
|
||||
source-map-js@1.2.2:
|
||||
resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==}
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
@@ -4122,13 +4005,13 @@ packages:
|
||||
resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
||||
vite@8.3.5:
|
||||
resolution: {integrity: sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==}
|
||||
vite@8.3.0:
|
||||
resolution: {integrity: sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^20.19.0 || >=22.12.0
|
||||
'@vitejs/devtools': ^0.7.1 || ^0.8.0
|
||||
'@vitejs/devtools': ^0.7.1
|
||||
esbuild: ^0.27.0 || ^0.28.0
|
||||
jiti: '>=1.21.0'
|
||||
less: ^4.0.0
|
||||
@@ -4285,8 +4168,8 @@ packages:
|
||||
resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
zod@4.6.7:
|
||||
resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==}
|
||||
zod@4.6.5:
|
||||
resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==}
|
||||
|
||||
snapshots:
|
||||
|
||||
@@ -4486,7 +4369,7 @@ snapshots:
|
||||
'@babel/core': 8.0.5
|
||||
'@inquirer/confirm': 6.3.2(@types/node@26.6.2)
|
||||
'@parcel/watcher': 2.6.0
|
||||
'@vitejs/plugin-basic-ssl': 2.3.0(vite@8.3.5(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1))
|
||||
'@vitejs/plugin-basic-ssl': 2.3.0(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1))
|
||||
beasties: 0.5.4
|
||||
browserslist: 4.29.1
|
||||
chokidar: 5.0.0
|
||||
@@ -4508,7 +4391,7 @@ snapshots:
|
||||
tinyglobby: 0.2.17
|
||||
tslib: 2.8.1
|
||||
typescript: 6.0.3
|
||||
vite: 8.3.5(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1)
|
||||
vite: 8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1)
|
||||
xxhash-wasm: 1.1.0
|
||||
optionalDependencies:
|
||||
'@angular/core': 22.2.2(@angular/compiler@22.2.2)(rxjs@7.8.2)
|
||||
@@ -4554,7 +4437,7 @@ snapshots:
|
||||
parse5-html-rewriting-stream: 8.0.1
|
||||
semver: 7.8.5
|
||||
yargs: 18.1.0
|
||||
zod: 4.6.7
|
||||
zod: 4.6.5
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- chokidar
|
||||
@@ -4598,7 +4481,7 @@ snapshots:
|
||||
'@standard-schema/spec': 1.1.0
|
||||
rxjs: 7.8.2
|
||||
tslib: 2.8.1
|
||||
zod: 4.6.7
|
||||
zod: 4.6.5
|
||||
|
||||
'@angular/localize@22.2.2(@angular/compiler-cli@22.2.2(@angular/compiler@22.2.2)(typescript@6.0.3))(@angular/compiler@22.2.2)':
|
||||
dependencies:
|
||||
@@ -5235,7 +5118,7 @@ snapshots:
|
||||
camelcase: 5.3.1
|
||||
find-up: 4.1.0
|
||||
get-package-type: 0.1.0
|
||||
js-yaml: 3.15.2
|
||||
js-yaml: 3.15.1
|
||||
resolve-from: 5.0.0
|
||||
|
||||
'@istanbuljs/schema@0.1.6': {}
|
||||
@@ -5529,12 +5412,12 @@ snapshots:
|
||||
|
||||
'@modelcontextprotocol/core@2.3.1':
|
||||
dependencies:
|
||||
zod: 4.6.7
|
||||
zod: 4.6.5
|
||||
|
||||
'@modelcontextprotocol/server@2.3.1':
|
||||
dependencies:
|
||||
'@modelcontextprotocol/core': 2.3.1
|
||||
zod: 4.6.7
|
||||
zod: 4.6.5
|
||||
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4':
|
||||
optional: true
|
||||
@@ -5769,8 +5652,6 @@ snapshots:
|
||||
|
||||
'@oxc-project/types@0.150.0': {}
|
||||
|
||||
'@oxc-project/types@0.155.0': {}
|
||||
|
||||
'@parcel/watcher-android-arm64@2.6.0':
|
||||
optional: true
|
||||
|
||||
@@ -5838,93 +5719,48 @@ snapshots:
|
||||
|
||||
'@popperjs/core@2.11.8': {}
|
||||
|
||||
'@rolldown/binding-android-arm-eabi@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-android-arm-eabi@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-android-arm64@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-android-arm64@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.2.8':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.2.15':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.2.8':
|
||||
optional: true
|
||||
|
||||
@@ -6215,9 +6051,9 @@ snapshots:
|
||||
'@unrs/resolver-binding-win32-x64-msvc@1.12.2':
|
||||
optional: true
|
||||
|
||||
'@vitejs/plugin-basic-ssl@2.3.0(vite@8.3.5(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1))':
|
||||
'@vitejs/plugin-basic-ssl@2.3.0(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1))':
|
||||
dependencies:
|
||||
vite: 8.3.5(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1)
|
||||
vite: 8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1)
|
||||
|
||||
acorn-jsx@5.3.2(acorn@8.18.0):
|
||||
dependencies:
|
||||
@@ -6247,7 +6083,7 @@ snapshots:
|
||||
ajv@8.20.0:
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
fast-uri: 3.1.9
|
||||
fast-uri: 3.1.8
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
@@ -6362,7 +6198,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@popperjs/core': 2.11.8
|
||||
|
||||
brace-expansion@2.1.7:
|
||||
brace-expansion@2.1.4:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
|
||||
@@ -6707,7 +6543,7 @@ snapshots:
|
||||
dependencies:
|
||||
fast-string-truncated-width: 3.0.3
|
||||
|
||||
fast-uri@3.1.9: {}
|
||||
fast-uri@3.1.8: {}
|
||||
|
||||
fast-wrap-ansi@0.2.2:
|
||||
dependencies:
|
||||
@@ -7357,7 +7193,7 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@3.15.2:
|
||||
js-yaml@3.15.1:
|
||||
dependencies:
|
||||
argparse: 1.0.10
|
||||
esprima: 4.0.1
|
||||
@@ -7557,7 +7393,7 @@ snapshots:
|
||||
|
||||
minimatch@9.0.9:
|
||||
dependencies:
|
||||
brace-expansion: 2.1.7
|
||||
brace-expansion: 2.1.4
|
||||
|
||||
minimist@1.2.8: {}
|
||||
|
||||
@@ -7592,8 +7428,6 @@ snapshots:
|
||||
|
||||
nanoid@3.3.18: {}
|
||||
|
||||
nanoid@3.3.20: {}
|
||||
|
||||
napi-postinstall@0.3.4: {}
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
@@ -7840,13 +7674,7 @@ snapshots:
|
||||
dependencies:
|
||||
nanoid: 3.3.18
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.2
|
||||
|
||||
postcss@8.5.31:
|
||||
dependencies:
|
||||
nanoid: 3.3.20
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
prelude-ls@1.2.1: {}
|
||||
|
||||
@@ -7914,27 +7742,6 @@ snapshots:
|
||||
onetime: 7.0.0
|
||||
signal-exit: 4.1.0
|
||||
|
||||
rolldown@1.2.15:
|
||||
dependencies:
|
||||
'@oxc-project/types': 0.155.0
|
||||
'@rolldown/pluginutils': 1.0.1
|
||||
optionalDependencies:
|
||||
'@rolldown/binding-android-arm-eabi': 1.2.15
|
||||
'@rolldown/binding-android-arm64': 1.2.15
|
||||
'@rolldown/binding-darwin-arm64': 1.2.15
|
||||
'@rolldown/binding-darwin-x64': 1.2.15
|
||||
'@rolldown/binding-freebsd-x64': 1.2.15
|
||||
'@rolldown/binding-linux-arm-gnueabihf': 1.2.15
|
||||
'@rolldown/binding-linux-arm64-gnu': 1.2.15
|
||||
'@rolldown/binding-linux-arm64-musl': 1.2.15
|
||||
'@rolldown/binding-linux-ppc64-gnu': 1.2.15
|
||||
'@rolldown/binding-linux-s390x-gnu': 1.2.15
|
||||
'@rolldown/binding-linux-x64-gnu': 1.2.15
|
||||
'@rolldown/binding-linux-x64-musl': 1.2.15
|
||||
'@rolldown/binding-openharmony-arm64': 1.2.15
|
||||
'@rolldown/binding-win32-arm64-msvc': 1.2.15
|
||||
'@rolldown/binding-win32-x64-msvc': 1.2.15
|
||||
|
||||
rolldown@1.2.8:
|
||||
dependencies:
|
||||
'@oxc-project/types': 0.149.0
|
||||
@@ -8055,7 +7862,7 @@ snapshots:
|
||||
dependencies:
|
||||
chokidar: 5.0.0
|
||||
immutable: 5.1.9
|
||||
source-map-js: 1.2.2
|
||||
source-map-js: 1.2.1
|
||||
optionalDependencies:
|
||||
'@parcel/watcher': 2.6.0
|
||||
|
||||
@@ -8084,7 +7891,7 @@ snapshots:
|
||||
ansi-styles: 6.2.3
|
||||
is-fullwidth-code-point: 5.1.0
|
||||
|
||||
source-map-js@1.2.2: {}
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
dependencies:
|
||||
@@ -8311,12 +8118,12 @@ snapshots:
|
||||
|
||||
verkit@0.3.2: {}
|
||||
|
||||
vite@8.3.5(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1):
|
||||
vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1):
|
||||
dependencies:
|
||||
lightningcss: 1.33.0
|
||||
picomatch: 4.0.7
|
||||
postcss: 8.5.31
|
||||
rolldown: 1.2.15
|
||||
postcss: 8.5.28
|
||||
rolldown: 1.2.8
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
'@types/node': 26.6.2
|
||||
@@ -8434,4 +8241,4 @@ snapshots:
|
||||
|
||||
yoctocolors@2.2.0: {}
|
||||
|
||||
zod@4.6.7: {}
|
||||
zod@4.6.5: {}
|
||||
@@ -24,6 +24,3 @@ onlyBuiltDependencies:
|
||||
ignoredBuiltDependencies:
|
||||
- normalize-diacritics
|
||||
- unrs-resolver
|
||||
overrides:
|
||||
"vite@>=8.3.0 <8.3.3": "^8.3.3"
|
||||
"zod@<4.6.6": "^4.6.6"
|
||||
+7
@@ -1,3 +1,10 @@
|
||||
.custom-fields-dropdown {
|
||||
min-width: 300px;
|
||||
|
||||
// correct position on mobile
|
||||
@media (max-width: 575.98px) {
|
||||
&.show {
|
||||
margin-left: -245px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,7 +308,7 @@
|
||||
</tr>
|
||||
<tr class="align-middle">
|
||||
<td i18n>Original SHA256 checksum</td>
|
||||
<td><span class="badge bg-light text-dark font-monospace text-wrap text-break text-start">{{metadata()?.original_checksum}}</span></td>
|
||||
<td><span class="badge bg-light font-monospace text-wrap text-break text-start">{{metadata()?.original_checksum}}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n>Original file size</td>
|
||||
|
||||
@@ -24,7 +24,7 @@ export interface SystemStatus {
|
||||
status: SystemStatusItemStatus
|
||||
error?: string
|
||||
migration_status: {
|
||||
latest_migration: string
|
||||
latest_migration: string | null
|
||||
unapplied_migrations: string[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export const environment = {
|
||||
apiVersion: '10', // match src/paperless/settings.py
|
||||
appTitle: DEFAULT_APP_TITLE,
|
||||
tag: 'prod',
|
||||
version: '3.4.0',
|
||||
version: '3.3.4',
|
||||
webSocketHost: window.location.host,
|
||||
webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:',
|
||||
webSocketBaseUrl: base_url.pathname + 'ws/',
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -11,8 +11,6 @@ from typing import TYPE_CHECKING
|
||||
|
||||
import pytest
|
||||
|
||||
from paperless_testing import samples
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Generator
|
||||
from pathlib import Path
|
||||
@@ -77,30 +75,6 @@ def _clear_django_caches() -> None:
|
||||
cache.clear()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _keep_connection_open_in_atomic(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Make ``close_old_connections()`` a no-op inside the test's transaction.
|
||||
|
||||
The Celery signal handlers call it, as a worker would between tasks. Tests
|
||||
run inside an open atomic block, where Postgres really closes the
|
||||
connection, taking the test transaction and every later test on the
|
||||
worker with it. SQLite in-memory ignores the close, which hid this.
|
||||
"""
|
||||
from django.db.backends.base.base import BaseDatabaseWrapper
|
||||
|
||||
original = BaseDatabaseWrapper.close_if_unusable_or_obsolete
|
||||
|
||||
def close_unless_atomic(self: BaseDatabaseWrapper) -> None:
|
||||
if not self.in_atomic_block:
|
||||
original(self)
|
||||
|
||||
monkeypatch.setattr(
|
||||
BaseDatabaseWrapper,
|
||||
"close_if_unusable_or_obsolete",
|
||||
close_unless_atomic,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def paperless_dirs(
|
||||
tmp_path: Path,
|
||||
@@ -175,39 +149,3 @@ def fake_progress_manager(
|
||||
|
||||
monkeypatch.setattr("documents.tasks.ProgressManager", FakeProgressManager)
|
||||
return FakeProgressManager
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def shared_samples_dir() -> Path:
|
||||
"""Directory of sample files used by more than one app's tests."""
|
||||
return samples.SHARED_SAMPLES_DIR
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def simple_digital_pdf_file() -> Path:
|
||||
"""One-page PDF with a text layer."""
|
||||
return samples.SIMPLE_DIGITAL_PDF
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def multi_page_digital_pdf_file() -> Path:
|
||||
"""Three-page PDF with a text layer."""
|
||||
return samples.MULTI_PAGE_DIGITAL_PDF
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def with_form_pdf_file() -> Path:
|
||||
"""PDF containing a fillable form."""
|
||||
return samples.WITH_FORM_PDF
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def multi_page_images_pdf_file() -> Path:
|
||||
"""Multi-page PDF of scanned images, no text layer."""
|
||||
return samples.MULTI_PAGE_IMAGES_PDF
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def thumbnail_webp_file() -> Path:
|
||||
"""Small WebP thumbnail."""
|
||||
return samples.THUMBNAIL_WEBP
|
||||
@@ -476,13 +476,13 @@ def update_filename_and_move_files(
|
||||
return
|
||||
|
||||
with FileLock(settings.MEDIA_LOCK):
|
||||
try:
|
||||
# If this was waiting for the lock, the filename or archive_filename
|
||||
# of this document may have been updated. This happens if multiple updates
|
||||
# get queued from the UI for the same document
|
||||
# So freshen up the data before doing anything
|
||||
instance.refresh_from_db()
|
||||
# If this was waiting for the lock, the filename or archive_filename
|
||||
# of this document may have been updated. This happens if multiple updates
|
||||
# get queued from the UI for the same document
|
||||
# So freshen up the data before doing anything
|
||||
instance.refresh_from_db()
|
||||
|
||||
try:
|
||||
old_filename = instance.filename
|
||||
old_source_path = instance.source_path
|
||||
move_original = False
|
||||
|
||||
@@ -44,7 +44,10 @@ def localize_date(value: date | datetime | str, format: str, locale: str) -> str
|
||||
|
||||
def format_datetime(value: str | datetime, format: str) -> str:
|
||||
if isinstance(value, str):
|
||||
value = parse_date(value)
|
||||
parsed = parse_datetime(value) or parse_date(value)
|
||||
if parsed is None:
|
||||
raise ValueError(f"Invalid date or datetime string: {value!r}")
|
||||
return parsed.strftime(format=format)
|
||||
return value.strftime(format=format)
|
||||
|
||||
|
||||
|
||||
@@ -12,25 +12,29 @@ if TYPE_CHECKING:
|
||||
from paperless_testing.dirs import PaperlessDirs
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def document_samples_dir() -> Path:
|
||||
"""Path to the shared test sample documents."""
|
||||
return Path(__file__).parent / "samples" / "documents"
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def sample_doc(
|
||||
paperless_dirs: "PaperlessDirs",
|
||||
simple_digital_pdf_file: Path,
|
||||
multi_page_images_pdf_file: Path,
|
||||
thumbnail_webp_file: Path,
|
||||
document_samples_dir: Path,
|
||||
) -> "Document":
|
||||
"""Create a document with valid files and matching checksums."""
|
||||
with filelock.FileLock(paperless_dirs.media_lock):
|
||||
shutil.copy(
|
||||
simple_digital_pdf_file,
|
||||
document_samples_dir / "originals" / "0000001.pdf",
|
||||
paperless_dirs.originals_dir / "0000001.pdf",
|
||||
)
|
||||
shutil.copy(
|
||||
multi_page_images_pdf_file,
|
||||
document_samples_dir / "archive" / "0000001.pdf",
|
||||
paperless_dirs.archive_dir / "0000001.pdf",
|
||||
)
|
||||
shutil.copy(
|
||||
thumbnail_webp_file,
|
||||
document_samples_dir / "thumbnails" / "0000001.webp",
|
||||
paperless_dirs.thumbnail_dir / "0000001.webp",
|
||||
)
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This is a test file.
|
||||
@@ -53,7 +53,7 @@ class TestBulkDownload(DirectoriesMixin, SampleDirMixin, APITestCase):
|
||||
archive_checksum="D",
|
||||
)
|
||||
|
||||
shutil.copy(self.SIMPLE_PDF, self.doc2.source_path)
|
||||
shutil.copy(self.SAMPLE_DIR / "simple.pdf", self.doc2.source_path)
|
||||
shutil.copy(self.SAMPLE_DIR / "simple.png", self.doc2b.source_path)
|
||||
shutil.copy(self.SAMPLE_DIR / "simple.jpg", self.doc3.source_path)
|
||||
shutil.copy(self.SAMPLE_DIR / "test_with_bom.pdf", self.doc3.archive_path)
|
||||
|
||||
@@ -1062,7 +1062,7 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
|
||||
"custom_fields": [
|
||||
{
|
||||
"field": custom_field_doclink.id,
|
||||
"value": [doc2.id, doc3.id, doc4.id],
|
||||
"value": [2, 3, 4],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -1073,13 +1073,13 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
|
||||
self.assertEqual(CustomFieldInstance.objects.count(), 4)
|
||||
_cf_5 = doc2.custom_fields.first()
|
||||
assert _cf_5 is not None
|
||||
self.assertEqual(_cf_5.value, [doc1.id])
|
||||
self.assertEqual(_cf_5.value, [1])
|
||||
_cf_6 = doc3.custom_fields.first()
|
||||
assert _cf_6 is not None
|
||||
self.assertEqual(_cf_6.value, [doc1.id])
|
||||
self.assertEqual(_cf_6.value, [1])
|
||||
_cf_7 = doc4.custom_fields.first()
|
||||
assert _cf_7 is not None
|
||||
self.assertEqual(_cf_7.value, [doc1.id])
|
||||
self.assertEqual(_cf_7.value, [1])
|
||||
|
||||
# Add links appends if necessary
|
||||
resp = self.client.patch(
|
||||
@@ -1088,7 +1088,7 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
|
||||
"custom_fields": [
|
||||
{
|
||||
"field": custom_field_doclink.id,
|
||||
"value": [doc1.id, doc4.id],
|
||||
"value": [1, 4],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -1098,7 +1098,7 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
|
||||
self.assertEqual(resp.status_code, status.HTTP_200_OK)
|
||||
_cf_8 = doc4.custom_fields.first()
|
||||
assert _cf_8 is not None
|
||||
self.assertEqual(_cf_8.value, [doc1.id, doc3.id])
|
||||
self.assertEqual(_cf_8.value, [1, 3])
|
||||
|
||||
# Remove one of the links, removed on other doc
|
||||
resp = self.client.patch(
|
||||
@@ -1107,7 +1107,7 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
|
||||
"custom_fields": [
|
||||
{
|
||||
"field": custom_field_doclink.id,
|
||||
"value": [doc2.id, doc3.id],
|
||||
"value": [2, 3],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -1117,13 +1117,13 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
|
||||
self.assertEqual(resp.status_code, status.HTTP_200_OK)
|
||||
_cf_9 = doc2.custom_fields.first()
|
||||
assert _cf_9 is not None
|
||||
self.assertEqual(_cf_9.value, [doc1.id])
|
||||
self.assertEqual(_cf_9.value, [1])
|
||||
_cf_10 = doc3.custom_fields.first()
|
||||
assert _cf_10 is not None
|
||||
self.assertEqual(_cf_10.value, [doc1.id, doc4.id])
|
||||
self.assertEqual(_cf_10.value, [1, 4])
|
||||
_cf_11 = doc4.custom_fields.first()
|
||||
assert _cf_11 is not None
|
||||
self.assertEqual(_cf_11.value, [doc3.id])
|
||||
self.assertEqual(_cf_11.value, [3])
|
||||
|
||||
# Removes the field entirely
|
||||
resp = self.client.patch(
|
||||
@@ -1140,10 +1140,10 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
|
||||
self.assertEqual(_cf_12.value, [])
|
||||
_cf_13 = doc3.custom_fields.first()
|
||||
assert _cf_13 is not None
|
||||
self.assertEqual(_cf_13.value, [doc4.id])
|
||||
self.assertEqual(_cf_13.value, [4])
|
||||
_cf_14 = doc4.custom_fields.first()
|
||||
assert _cf_14 is not None
|
||||
self.assertEqual(_cf_14.value, [doc3.id])
|
||||
self.assertEqual(_cf_14.value, [3])
|
||||
|
||||
# If field exists on target doc but value is None
|
||||
doc5 = Document.objects.create(
|
||||
@@ -1170,7 +1170,7 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
|
||||
self.assertEqual(resp.status_code, status.HTTP_200_OK)
|
||||
_cf_15 = doc5.custom_fields.first()
|
||||
assert _cf_15 is not None
|
||||
self.assertEqual(_cf_15.value, [doc1.id])
|
||||
self.assertEqual(_cf_15.value, [1])
|
||||
|
||||
def test_documentlink_patch_requires_change_permission_on_target_documents(
|
||||
self,
|
||||
|
||||
@@ -57,8 +57,6 @@ from paperless_testing.http import read_streaming_response
|
||||
from paperless_testing.permissions import grant_all_global
|
||||
from paperless_testing.permissions import grant_global
|
||||
from paperless_testing.permissions import grant_object
|
||||
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
|
||||
from paperless_testing.samples import THUMBNAIL_WEBP
|
||||
|
||||
|
||||
class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
@@ -1919,11 +1917,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
id=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
with SimpleUploadedFile(
|
||||
"simple.pdf",
|
||||
SIMPLE_DIGITAL_PDF.read_bytes(),
|
||||
content_type="application/pdf",
|
||||
) as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f},
|
||||
@@ -1951,7 +1945,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
|
||||
payload = SimpleUploadedFile(
|
||||
"../../outside.pdf",
|
||||
SIMPLE_DIGITAL_PDF.read_bytes(),
|
||||
(Path(__file__).parent / "samples" / "simple.pdf").read_bytes(),
|
||||
content_type="application/pdf",
|
||||
)
|
||||
|
||||
@@ -1980,7 +1974,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
id=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
pdf_bytes = SIMPLE_DIGITAL_PDF.read_bytes()
|
||||
pdf_bytes = (Path(__file__).parent / "samples" / "simple.pdf").read_bytes()
|
||||
boundary = "paperless-boundary"
|
||||
payload = (
|
||||
(
|
||||
@@ -2056,7 +2050,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
def test_upload_insufficient_permissions(self) -> None:
|
||||
self.client.force_authenticate(user=UserFactory(username="testuser2"))
|
||||
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f},
|
||||
@@ -2069,11 +2063,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
id=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
with SimpleUploadedFile(
|
||||
"simple.pdf",
|
||||
SIMPLE_DIGITAL_PDF.read_bytes(),
|
||||
content_type="application/pdf",
|
||||
) as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{
|
||||
@@ -2106,7 +2096,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
id=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"documenst": f},
|
||||
@@ -2132,7 +2122,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
id=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "title": "my custom title"},
|
||||
@@ -2152,7 +2142,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
)
|
||||
|
||||
c = Correspondent.objects.create(name="test-corres")
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "correspondent": c.id},
|
||||
@@ -2171,7 +2161,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
id=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "correspondent": 3456},
|
||||
@@ -2186,7 +2176,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
)
|
||||
|
||||
dt = DocumentType.objects.create(name="invoice")
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "document_type": dt.id},
|
||||
@@ -2205,7 +2195,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
id=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "document_type": 34578},
|
||||
@@ -2220,7 +2210,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
)
|
||||
|
||||
sp = StoragePath.objects.create(name="invoices")
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "storage_path": sp.id},
|
||||
@@ -2239,7 +2229,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
id=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "storage_path": 34578},
|
||||
@@ -2255,7 +2245,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
|
||||
t1 = Tag.objects.create(name="tag1")
|
||||
t2 = Tag.objects.create(name="tag2")
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "tags": [t2.id, t1.id]},
|
||||
@@ -2276,7 +2266,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
|
||||
t1 = Tag.objects.create(name="tag1")
|
||||
t2 = Tag.objects.create(name="tag2")
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "tags": [t2.id, t1.id, 734563]},
|
||||
@@ -2300,7 +2290,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
0,
|
||||
tzinfo=zoneinfo.ZoneInfo("America/Los_Angeles"),
|
||||
)
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "created": created},
|
||||
@@ -2316,11 +2306,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
id=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
with SimpleUploadedFile(
|
||||
"simple.pdf",
|
||||
SIMPLE_DIGITAL_PDF.read_bytes(),
|
||||
content_type="application/pdf",
|
||||
) as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "archive_serial_number": 500},
|
||||
@@ -2347,11 +2333,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
data_type=CustomField.FieldDataType.STRING,
|
||||
)
|
||||
|
||||
with SimpleUploadedFile(
|
||||
"simple.pdf",
|
||||
SIMPLE_DIGITAL_PDF.read_bytes(),
|
||||
content_type="application/pdf",
|
||||
) as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{
|
||||
@@ -2406,7 +2388,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
w1.actions.add(action1)
|
||||
w1.save()
|
||||
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{
|
||||
@@ -2447,11 +2429,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
data_type=CustomField.FieldDataType.INT,
|
||||
)
|
||||
|
||||
with SimpleUploadedFile(
|
||||
"simple.pdf",
|
||||
SIMPLE_DIGITAL_PDF.read_bytes(),
|
||||
content_type="application/pdf",
|
||||
) as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{
|
||||
@@ -2500,7 +2478,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
]
|
||||
|
||||
for payload in error_payloads:
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
data = {"document": f, **payload}
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
@@ -2558,7 +2536,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
id=str(uuid.uuid4()),
|
||||
)
|
||||
|
||||
with SIMPLE_DIGITAL_PDF.open("rb") as f:
|
||||
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||
response = self.client.post(
|
||||
"/api/documents/post_document/",
|
||||
{"document": f, "from_webui": True},
|
||||
@@ -2597,8 +2575,14 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
archive_filename="archive.pdf",
|
||||
)
|
||||
|
||||
source_file: Path = THUMBNAIL_WEBP
|
||||
archive_file: Path = SIMPLE_DIGITAL_PDF
|
||||
source_file: Path = (
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "thumbnails"
|
||||
/ "0000001.webp"
|
||||
)
|
||||
archive_file: Path = Path(__file__).parent / "samples" / "simple.pdf"
|
||||
|
||||
shutil.copy(source_file, doc.source_path)
|
||||
shutil.copy(archive_file, doc.archive_path)
|
||||
@@ -2631,7 +2615,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
mime_type="application/pdf",
|
||||
)
|
||||
|
||||
shutil.copy(SIMPLE_DIGITAL_PDF, doc.source_path)
|
||||
shutil.copy(Path(__file__).parent / "samples" / "simple.pdf", doc.source_path)
|
||||
|
||||
response = self.client.get(f"/api/documents/{doc.pk}/metadata/")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
@@ -4397,8 +4381,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
||||
filename="test2.pdf",
|
||||
)
|
||||
|
||||
archive_file = SIMPLE_DIGITAL_PDF
|
||||
source_file = SIMPLE_DIGITAL_PDF
|
||||
archive_file = Path(__file__).parent / "samples" / "simple.pdf"
|
||||
source_file = Path(__file__).parent / "samples" / "simple.pdf"
|
||||
|
||||
shutil.copy(archive_file, doc.archive_path)
|
||||
shutil.copy(source_file, doc2.source_path)
|
||||
|
||||
@@ -12,9 +12,6 @@ from documents.tests.utils import SampleDirMixin
|
||||
from paperless_testing.dirs import DirectoriesMixin
|
||||
from paperless_testing.factories import UserFactory
|
||||
from paperless_testing.permissions import grant_global
|
||||
from paperless_testing.samples import MULTI_PAGE_DIGITAL_PDF
|
||||
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
|
||||
from paperless_testing.samples import WITH_FORM_PDF
|
||||
|
||||
|
||||
class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
|
||||
@@ -45,15 +42,15 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
|
||||
|
||||
# Copy sample files to document paths (using different files to distinguish versions)
|
||||
shutil.copy(
|
||||
SIMPLE_DIGITAL_PDF,
|
||||
self.SAMPLE_DIR / "documents" / "originals" / "0000001.pdf",
|
||||
self.doc1.archive_path,
|
||||
)
|
||||
shutil.copy(
|
||||
MULTI_PAGE_DIGITAL_PDF,
|
||||
self.SAMPLE_DIR / "documents" / "originals" / "0000002.pdf",
|
||||
self.doc1.source_path,
|
||||
)
|
||||
shutil.copy(
|
||||
WITH_FORM_PDF,
|
||||
self.SAMPLE_DIR / "documents" / "originals" / "0000003.pdf",
|
||||
self.doc2.source_path,
|
||||
)
|
||||
|
||||
@@ -380,7 +377,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
|
||||
checksum="3",
|
||||
filename="test3.pdf",
|
||||
)
|
||||
shutil.copy(self.SIMPLE_PDF, doc3.source_path)
|
||||
shutil.copy(self.SAMPLE_DIR / "simple.pdf", doc3.source_path)
|
||||
|
||||
doc4 = Document.objects.create(
|
||||
title="test1",
|
||||
@@ -389,7 +386,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
|
||||
checksum="4",
|
||||
filename="test4.pdf",
|
||||
)
|
||||
shutil.copy(self.SIMPLE_PDF, doc4.source_path)
|
||||
shutil.copy(self.SAMPLE_DIR / "simple.pdf", doc4.source_path)
|
||||
|
||||
response = self.client.post(
|
||||
self.ENDPOINT,
|
||||
|
||||
@@ -628,23 +628,22 @@ class TestCustomFieldsSearch(DirectoriesMixin, APITestCase):
|
||||
# Subset check (document link field only) #
|
||||
# ==========================================================#
|
||||
def test_document_link_contains(self) -> None:
|
||||
linked = {self.documents[1].id, self.documents[2].id}
|
||||
# Document link field "contains" performs a subset check.
|
||||
self._assert_query_match_predicate(
|
||||
["documentlink_field", "contains", sorted(linked)],
|
||||
["documentlink_field", "contains", [1, 2]],
|
||||
lambda document: (
|
||||
"documentlink_field" in document
|
||||
and document["documentlink_field"] is not None
|
||||
and set(document["documentlink_field"]) >= linked
|
||||
and set(document["documentlink_field"]) >= {1, 2}
|
||||
),
|
||||
)
|
||||
# The order of IDs don't matter - this is the same as above.
|
||||
self._assert_query_match_predicate(
|
||||
["documentlink_field", "contains", sorted(linked, reverse=True)],
|
||||
["documentlink_field", "contains", [2, 1]],
|
||||
lambda document: (
|
||||
"documentlink_field" in document
|
||||
and document["documentlink_field"] is not None
|
||||
and set(document["documentlink_field"]) >= linked
|
||||
and set(document["documentlink_field"]) >= {1, 2}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -944,7 +944,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
results = response.data["results"]
|
||||
self.assertEqual({r["id"] for r in results}, {d1.id, d2.id})
|
||||
self.assertEqual({r["id"] for r in results}, {1, 2})
|
||||
|
||||
@mock.patch("documents.search._backend.parse_user_query")
|
||||
def test_search_parser_bug_surfaces_as_500_not_400(self, m) -> None:
|
||||
@@ -1801,7 +1801,6 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
|
||||
def test_search_sorting(self) -> None:
|
||||
u1 = UserFactory(username="user1")
|
||||
u2 = UserFactory(username="user2")
|
||||
u3 = UserFactory(username="user3")
|
||||
c1 = Correspondent.objects.create(name="corres Ax")
|
||||
c2 = Correspondent.objects.create(name="corres Cx")
|
||||
c3 = Correspondent.objects.create(name="corres Bx")
|
||||
@@ -1827,7 +1826,6 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
|
||||
content="test",
|
||||
archive_serial_number=1,
|
||||
title="1",
|
||||
owner=u3,
|
||||
)
|
||||
Note.objects.create(
|
||||
note="This is a note.",
|
||||
@@ -1880,14 +1878,14 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
|
||||
search_query("&ordering=-num_notes"),
|
||||
[d1.id, d3.id, d2.id],
|
||||
)
|
||||
# owner sort: ORM orders by owner_id (integer)
|
||||
# owner sort: ORM orders by owner_id (integer); NULLs first in SQLite ASC
|
||||
self.assertListEqual(
|
||||
search_query("&ordering=owner"),
|
||||
[d1.id, d2.id, d3.id],
|
||||
[d3.id, d1.id, d2.id],
|
||||
)
|
||||
self.assertListEqual(
|
||||
search_query("&ordering=-owner"),
|
||||
[d3.id, d2.id, d1.id],
|
||||
[d2.id, d1.id, d3.id],
|
||||
)
|
||||
|
||||
def test_search_ordering_by_score(self) -> None:
|
||||
|
||||
@@ -5,7 +5,6 @@ from datetime import timedelta
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
from django.db import connection
|
||||
from django.test import override_settings
|
||||
from django.utils import timezone
|
||||
from rest_framework import status
|
||||
@@ -69,7 +68,7 @@ class TestSystemStatus(APITestCase):
|
||||
self.assertEqual(response.data["install_type"], "bare-metal")
|
||||
self.assertIsNotNone(response.data["storage"]["total"])
|
||||
self.assertIsNotNone(response.data["storage"]["available"])
|
||||
self.assertEqual(response.data["database"]["type"], connection.vendor)
|
||||
self.assertEqual(response.data["database"]["type"], "sqlite")
|
||||
self.assertIsNotNone(response.data["database"]["url"])
|
||||
self.assertEqual(response.data["database"]["status"], "OK")
|
||||
self.assertIsNone(response.data["database"]["error"])
|
||||
@@ -540,3 +539,50 @@ class TestSystemStatus(APITestCase):
|
||||
self.assertEqual(response.data["tasks"]["summary"]["success_count"], 1)
|
||||
self.assertEqual(response.data["tasks"]["summary"]["failure_count"], 1)
|
||||
self.assertEqual(response.data["tasks"]["summary"]["pending_count"], 1)
|
||||
|
||||
def test_system_status_reports_database_error(self) -> None:
|
||||
"""
|
||||
GIVEN:
|
||||
- Reading the migration state fails
|
||||
WHEN:
|
||||
- The system status is requested
|
||||
THEN:
|
||||
- The response reports the database error rather than a server error
|
||||
"""
|
||||
self.client.force_login(self.user)
|
||||
with mock.patch(
|
||||
"documents.views.MigrationLoader",
|
||||
side_effect=Exception("database is down"),
|
||||
):
|
||||
response = self.client.get(self.ENDPOINT)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
database = response.data["database"]
|
||||
self.assertEqual(database["status"], "ERROR")
|
||||
self.assertIsNotNone(database["error"])
|
||||
self.assertEqual(
|
||||
database["migration_status"],
|
||||
{"latest_migration": None, "unapplied_migrations": []},
|
||||
)
|
||||
|
||||
def test_system_status_migration_query_error(self) -> None:
|
||||
"""
|
||||
GIVEN:
|
||||
- The migration graph loads but reading the applied migrations fails
|
||||
WHEN:
|
||||
- The system status is requested
|
||||
THEN:
|
||||
- No migrations are reported as unapplied, since the state is unknown
|
||||
"""
|
||||
self.client.force_login(self.user)
|
||||
with mock.patch("documents.views.MigrationRecorder") as recorder:
|
||||
recorder.Migration.objects.all.side_effect = Exception("query failed")
|
||||
response = self.client.get(self.ENDPOINT)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
database = response.data["database"]
|
||||
self.assertEqual(database["status"], "ERROR")
|
||||
self.assertEqual(
|
||||
database["migration_status"],
|
||||
{"latest_migration": None, "unapplied_migrations": []},
|
||||
)
|
||||
@@ -169,7 +169,7 @@ class TestBarcode(
|
||||
- No barcodes detected
|
||||
- No pages to split on
|
||||
"""
|
||||
test_file = self.SIMPLE_PDF
|
||||
test_file = self.SAMPLE_DIR / "simple.pdf"
|
||||
with self.get_reader(test_file) as reader:
|
||||
reader.detect()
|
||||
separator_page_numbers = reader.get_separation_pages()
|
||||
@@ -461,7 +461,7 @@ class TestBarcode(
|
||||
THEN:
|
||||
- Nothing happens
|
||||
"""
|
||||
test_file = self.SIMPLE_PDF
|
||||
test_file = self.SAMPLE_DIR / "simple.pdf"
|
||||
|
||||
with self.get_reader(test_file) as reader:
|
||||
try:
|
||||
|
||||
@@ -29,9 +29,6 @@ from documents.permissions import set_permissions_for_objects
|
||||
from paperless_testing.dirs import DirectoriesMixin
|
||||
from paperless_testing.factories import DocumentFactory
|
||||
from paperless_testing.permissions import grant_object
|
||||
from paperless_testing.samples import MULTI_PAGE_DIGITAL_PDF
|
||||
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
|
||||
from paperless_testing.samples import WITH_FORM_PDF
|
||||
|
||||
|
||||
class TestBulkEdit(DirectoriesMixin, TestCase):
|
||||
@@ -709,27 +706,47 @@ class TestPDFActions(DirectoriesMixin, TestCase):
|
||||
super().setUp()
|
||||
sample1 = self.dirs.scratch_dir / "sample.pdf"
|
||||
shutil.copy(
|
||||
SIMPLE_DIGITAL_PDF,
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000001.pdf",
|
||||
sample1,
|
||||
)
|
||||
sample1_archive = self.dirs.archive_dir / "sample_archive.pdf"
|
||||
shutil.copy(
|
||||
SIMPLE_DIGITAL_PDF,
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000001.pdf",
|
||||
sample1_archive,
|
||||
)
|
||||
sample2 = self.dirs.scratch_dir / "sample2.pdf"
|
||||
shutil.copy(
|
||||
MULTI_PAGE_DIGITAL_PDF,
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000002.pdf",
|
||||
sample2,
|
||||
)
|
||||
sample2_archive = self.dirs.archive_dir / "sample2_archive.pdf"
|
||||
shutil.copy(
|
||||
MULTI_PAGE_DIGITAL_PDF,
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000002.pdf",
|
||||
sample2_archive,
|
||||
)
|
||||
sample3 = self.dirs.scratch_dir / "sample3.pdf"
|
||||
shutil.copy(
|
||||
WITH_FORM_PDF,
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000003.pdf",
|
||||
sample3,
|
||||
)
|
||||
self.doc1 = Document.objects.create(
|
||||
@@ -763,7 +780,11 @@ class TestPDFActions(DirectoriesMixin, TestCase):
|
||||
)
|
||||
img_doc_archive = self.dirs.archive_dir / "sample_image.pdf"
|
||||
shutil.copy(
|
||||
SIMPLE_DIGITAL_PDF,
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000001.pdf",
|
||||
img_doc_archive,
|
||||
)
|
||||
self.img_doc = Document.objects.create(
|
||||
@@ -1031,7 +1052,9 @@ class TestPDFActions(DirectoriesMixin, TestCase):
|
||||
with self.assertLogs("paperless.bulk_edit", level="ERROR") as cm:
|
||||
bulk_edit.merge(doc_ids)
|
||||
error_str = cm.output[0]
|
||||
expected_str = f"Error merging document {self.doc2.id}, it will not be included in the merge"
|
||||
expected_str = (
|
||||
"Error merging document 2, it will not be included in the merge"
|
||||
)
|
||||
self.assertIn(expected_str, error_str)
|
||||
|
||||
mock_consume_file.assert_not_called()
|
||||
@@ -1191,7 +1214,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
|
||||
with self.assertLogs("paperless.bulk_edit", level="ERROR") as cm:
|
||||
bulk_edit.split(doc_ids, pages)
|
||||
error_str = cm.output[0]
|
||||
expected_str = f"Error splitting document {self.doc2.id}"
|
||||
expected_str = "Error splitting document 2"
|
||||
self.assertIn(expected_str, error_str)
|
||||
|
||||
mock_consume_file.assert_not_called()
|
||||
|
||||
@@ -39,9 +39,6 @@ from paperless_testing.factories import TagFactory
|
||||
from paperless_testing.factories import UserFactory
|
||||
from paperless_testing.fakes.progress import FakeProgressManager
|
||||
from paperless_testing.permissions import grant_object
|
||||
from paperless_testing.samples import MULTI_PAGE_DIGITAL_PDF
|
||||
from paperless_testing.samples import MULTI_PAGE_IMAGES_PDF
|
||||
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
|
||||
|
||||
|
||||
class _BaseNewStyleParser:
|
||||
@@ -118,7 +115,9 @@ class _BaseNewStyleParser:
|
||||
|
||||
|
||||
class DummyParser(_BaseNewStyleParser):
|
||||
_ARCHIVE_SRC = MULTI_PAGE_IMAGES_PDF
|
||||
_ARCHIVE_SRC = (
|
||||
Path(__file__).parent / "samples" / "documents" / "archive" / "0000001.pdf"
|
||||
)
|
||||
|
||||
def parse(self, document_path, mime_type, *, produce_archive: bool = True) -> None:
|
||||
self._text = "The Text"
|
||||
@@ -204,19 +203,33 @@ class TestConsumer(
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def get_test_file(self):
|
||||
src = SIMPLE_DIGITAL_PDF
|
||||
src = (
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000001.pdf"
|
||||
)
|
||||
dst = self.dirs.scratch_dir / "sample.pdf"
|
||||
shutil.copy(src, dst)
|
||||
return dst
|
||||
|
||||
def get_test_file2(self):
|
||||
src = MULTI_PAGE_DIGITAL_PDF
|
||||
src = (
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000002.pdf"
|
||||
)
|
||||
dst = self.dirs.scratch_dir / "sample2.pdf"
|
||||
shutil.copy(src, dst)
|
||||
return dst
|
||||
|
||||
def get_test_archive_file(self):
|
||||
src = MULTI_PAGE_IMAGES_PDF
|
||||
src = (
|
||||
Path(__file__).parent / "samples" / "documents" / "archive" / "0000001.pdf"
|
||||
)
|
||||
dst = self.dirs.scratch_dir / "sample_archive.pdf"
|
||||
shutil.copy(src, dst)
|
||||
return dst
|
||||
@@ -248,8 +261,8 @@ class TestConsumer(
|
||||
)
|
||||
self.assertIsNone(document.correspondent)
|
||||
self.assertIsNone(document.document_type)
|
||||
self.assertEqual(document.filename, f"{document.pk:07}.pdf")
|
||||
self.assertEqual(document.archive_filename, f"{document.pk:07}.pdf")
|
||||
self.assertEqual(document.filename, "0000001.pdf")
|
||||
self.assertEqual(document.archive_filename, "0000001.pdf")
|
||||
|
||||
self.assertIsFile(document.source_path)
|
||||
|
||||
@@ -1111,7 +1124,7 @@ class TestConsumer(
|
||||
@mock.patch("documents.consumer.get_parser_registry")
|
||||
def test_similar_filenames(self, m) -> None:
|
||||
shutil.copy(
|
||||
SIMPLE_DIGITAL_PDF,
|
||||
Path(__file__).parent / "samples" / "simple.pdf",
|
||||
settings.CONSUMPTION_DIR / "simple.pdf",
|
||||
)
|
||||
shutil.copy(
|
||||
@@ -1127,17 +1140,17 @@ class TestConsumer(
|
||||
with self.get_consumer(settings.CONSUMPTION_DIR / "simple.png") as consumer:
|
||||
consumer.run()
|
||||
|
||||
doc1 = Document.objects.latest("pk")
|
||||
doc1 = Document.objects.filter(pk=1).first()
|
||||
|
||||
with self.get_consumer(settings.CONSUMPTION_DIR / "simple.pdf") as consumer:
|
||||
consumer.run()
|
||||
|
||||
doc2 = Document.objects.latest("pk")
|
||||
doc2 = Document.objects.filter(pk=2).first()
|
||||
|
||||
with self.get_consumer(settings.CONSUMPTION_DIR / "simple.png.pdf") as consumer:
|
||||
consumer.run()
|
||||
|
||||
doc3 = Document.objects.latest("pk")
|
||||
doc3 = Document.objects.filter(pk=3).first()
|
||||
|
||||
self.assertEqual(doc1.filename, "simple.png")
|
||||
self.assertEqual(doc1.archive_filename, "simple.pdf")
|
||||
@@ -1652,7 +1665,13 @@ class TestConsumerRemoteOCR(
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def _consume(self, *, overrides: DocumentMetadataOverrides | None = None) -> bool:
|
||||
src = SIMPLE_DIGITAL_PDF
|
||||
src = (
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000001.pdf"
|
||||
)
|
||||
dst = self.dirs.scratch_dir / "sample.pdf"
|
||||
shutil.copy(src, dst)
|
||||
|
||||
|
||||
@@ -37,15 +37,12 @@ class TestDoubleSided(
|
||||
self.double_sided_dir.mkdir()
|
||||
self.staging_file = self.dirs.scratch_dir / STAGING_FILE_NAME
|
||||
|
||||
def _sample(self, name: str) -> Path:
|
||||
return self.SIMPLE_PDF if name == "simple.pdf" else self.SAMPLE_DIR / name
|
||||
|
||||
def consume_file(self, srcname, dstname: str | Path = "foo.pdf"):
|
||||
"""
|
||||
Starts the consume process and also ensures the
|
||||
destination file does not exist afterwards
|
||||
"""
|
||||
src = self._sample(srcname)
|
||||
src = self.SAMPLE_DIR / srcname
|
||||
dst = self.double_sided_dir / dstname
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy(src, dst)
|
||||
@@ -60,7 +57,7 @@ class TestDoubleSided(
|
||||
return msg
|
||||
|
||||
def create_staging_file(self, src="double-sided-odd.pdf", datetime=None) -> None:
|
||||
shutil.copy(self._sample(src), self.staging_file)
|
||||
shutil.copy(self.SAMPLE_DIR / src, self.staging_file)
|
||||
if datetime is None:
|
||||
datetime = dt.datetime.now()
|
||||
os.utime(str(self.staging_file), (datetime.timestamp(),) * 2)
|
||||
|
||||
@@ -31,6 +31,7 @@ from documents.serialisers import DocumentSerializer
|
||||
from documents.tasks import empty_trash
|
||||
from paperless_testing.assertions import FileSystemAssertsMixin
|
||||
from paperless_testing.dirs import DirectoriesMixin
|
||||
from paperless_testing.dirs import PaperlessDirs
|
||||
from paperless_testing.factories import DocumentFactory
|
||||
from paperless_testing.factories import UserFactory
|
||||
|
||||
@@ -760,9 +761,6 @@ class TestFileHandlingWithArchive(DirectoriesMixin, FileSystemAssertsMixin, Test
|
||||
archive_checksum="B",
|
||||
)
|
||||
|
||||
# Without a format the files are renamed to match the primary key
|
||||
original = doc.source_path
|
||||
archive = doc.archive_path
|
||||
self.assertIsFile(original)
|
||||
self.assertIsFile(archive)
|
||||
self.assertIsFile(doc.source_path)
|
||||
@@ -981,9 +979,6 @@ class TestFileHandlingWithArchive(DirectoriesMixin, FileSystemAssertsMixin, Test
|
||||
archive_filename="0000001.pdf",
|
||||
)
|
||||
|
||||
# Without a format the files are renamed to match the primary key
|
||||
original = doc.source_path
|
||||
archive = doc.archive_path
|
||||
self.assertIsFile(original)
|
||||
self.assertIsFile(archive)
|
||||
self.assertIsFile(doc.source_path)
|
||||
@@ -2140,3 +2135,28 @@ class TestPathDateLocalization:
|
||||
with override_settings(FILENAME_FORMAT=filename_format):
|
||||
filename = generate_filename(document)
|
||||
assert filename == Path(expected_filename)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_refresh_failure_surfaces_database_error(
|
||||
paperless_dirs: PaperlessDirs,
|
||||
) -> None:
|
||||
"""
|
||||
GIVEN:
|
||||
- A document with a stored filename
|
||||
WHEN:
|
||||
- Saving it triggers a filename update and refreshing it from the database fails
|
||||
THEN:
|
||||
- The database error is raised, not an unbound variable error from the rollback
|
||||
"""
|
||||
document = DocumentFactory(filename="some.pdf")
|
||||
|
||||
with (
|
||||
mock.patch.object(
|
||||
Document,
|
||||
"refresh_from_db",
|
||||
side_effect=DatabaseError("database is locked"),
|
||||
),
|
||||
pytest.raises(DatabaseError, match="database is locked"),
|
||||
):
|
||||
document.save()
|
||||
@@ -4,6 +4,7 @@ from typing import Literal
|
||||
|
||||
import pytest
|
||||
|
||||
from documents.templating.filters import format_datetime
|
||||
from documents.templating.filters import localize_date
|
||||
|
||||
|
||||
@@ -294,3 +295,23 @@ class TestDateLocalization:
|
||||
Tests `localize_date` with `date` string across different locales and formats.
|
||||
"""
|
||||
assert localize_date(value, format_style, locale_str) == expected_output
|
||||
|
||||
|
||||
class TestFormatDatetime:
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
pytest.param("2026-09-21", "2026-09-21 00:00", id="date"),
|
||||
pytest.param(
|
||||
"2026-09-21T13:45:00+00:00",
|
||||
"2026-09-21 13:45",
|
||||
id="datetime",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_parses_iso_strings(self, value: str, expected: str) -> None:
|
||||
assert format_datetime(value, "%Y-%m-%d %H:%M") == expected
|
||||
|
||||
def test_rejects_unparseable_string(self) -> None:
|
||||
with pytest.raises(ValueError, match="not a date"):
|
||||
format_datetime("not a date", "%Y")
|
||||
@@ -23,9 +23,8 @@ from documents.tasks import update_document_content_maybe_archive_file
|
||||
from paperless_testing.assertions import FileSystemAssertsMixin
|
||||
from paperless_testing.dirs import DirectoriesMixin
|
||||
from paperless_testing.factories import DocumentFactory
|
||||
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
|
||||
|
||||
sample_file: Path = SIMPLE_DIGITAL_PDF
|
||||
sample_file: Path = Path(__file__).parent / "samples" / "simple.pdf"
|
||||
|
||||
|
||||
@pytest.mark.management
|
||||
|
||||
@@ -59,7 +59,6 @@ from paperless_testing.dirs import paperless_environment
|
||||
from paperless_testing.factories import DocumentBarcodeFactory
|
||||
from paperless_testing.factories import DocumentFactory
|
||||
from paperless_testing.permissions import grant_object
|
||||
from paperless_testing.samples import install_document_samples
|
||||
|
||||
|
||||
@pytest.mark.management
|
||||
@@ -151,20 +150,6 @@ class TestExportImport(
|
||||
|
||||
super().setUp()
|
||||
|
||||
def _install_sample_files(self) -> None:
|
||||
documents_dir = Path(self.dirs.media_dir) / "documents"
|
||||
shutil.rmtree(documents_dir)
|
||||
install_document_samples(documents_dir)
|
||||
# Thumbnails are located by primary key, which the database sequence
|
||||
# does not reset between tests on PostgreSQL
|
||||
for number, document in enumerate(
|
||||
(self.d1, self.d2, self.d3, self.d4),
|
||||
start=1,
|
||||
):
|
||||
source = documents_dir / "thumbnails" / f"{number:07}.webp"
|
||||
if source != document.thumbnail_path:
|
||||
shutil.copy(source, document.thumbnail_path)
|
||||
|
||||
def _get_document_from_manifest(self, manifest, id):
|
||||
f = list(
|
||||
filter(
|
||||
@@ -218,7 +203,11 @@ class TestExportImport(
|
||||
return manifest
|
||||
|
||||
def test_exporter(self, *, use_filename_format=False) -> None:
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
num_permission_objects = Permission.objects.count()
|
||||
|
||||
@@ -321,7 +310,11 @@ class TestExportImport(
|
||||
self.assertEqual(messages.total_issue_count, 0)
|
||||
|
||||
def test_exporter_with_filename_format(self) -> None:
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
with override_settings(
|
||||
FILENAME_FORMAT="{created_year}/{correspondent}/{title}",
|
||||
@@ -329,7 +322,11 @@ class TestExportImport(
|
||||
self.test_exporter(use_filename_format=True)
|
||||
|
||||
def test_exporter_includes_share_links_and_bundles(self) -> None:
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
share_link = ShareLink.objects.create(
|
||||
slug="share-link-slug",
|
||||
@@ -427,7 +424,11 @@ class TestExportImport(
|
||||
)
|
||||
|
||||
def test_update_export_changed_time(self) -> None:
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
self._do_export()
|
||||
self.assertIsFile(self.target / "manifest.json")
|
||||
@@ -462,7 +463,11 @@ class TestExportImport(
|
||||
self.assertEqual(st_mtime_3, st_mtime_4)
|
||||
|
||||
def test_update_export_changed_checksum(self) -> None:
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
self._do_export()
|
||||
|
||||
@@ -488,7 +493,11 @@ class TestExportImport(
|
||||
self.assertIsFile(self.target / "manifest.json")
|
||||
|
||||
def test_update_export_deleted_document(self) -> None:
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
manifest = self._do_export()
|
||||
|
||||
@@ -519,7 +528,11 @@ class TestExportImport(
|
||||
|
||||
@override_settings(FILENAME_FORMAT="{title}/{correspondent}")
|
||||
def test_update_export_changed_location(self) -> None:
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
self._do_export(use_filename_format=True)
|
||||
self.assertIsFile(self.target / "wow1" / "c.pdf")
|
||||
@@ -560,7 +573,11 @@ class TestExportImport(
|
||||
- Zipfile is created
|
||||
- Zipfile contains exported files
|
||||
"""
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
args = ["document_exporter", self.target, "--zip"]
|
||||
|
||||
@@ -588,7 +605,11 @@ class TestExportImport(
|
||||
- Zipfile is created
|
||||
- Zipfile contains exported files
|
||||
"""
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
args = ["document_exporter", self.target, "--zip", "--use-filename-format"]
|
||||
|
||||
@@ -623,7 +644,11 @@ class TestExportImport(
|
||||
- Zipfile contains exported files
|
||||
- The existing file and directory in target are removed
|
||||
"""
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
# Create stuff in target directory
|
||||
existing_file = self.target / "test.txt"
|
||||
@@ -718,7 +743,11 @@ class TestExportImport(
|
||||
- Manifest.json doesn't contain information about archive files
|
||||
- Documents can be imported again
|
||||
"""
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
manifest = self._do_export()
|
||||
has_archive = False
|
||||
@@ -760,7 +789,11 @@ class TestExportImport(
|
||||
- Manifest.json doesn't contain information about thumbnails
|
||||
- Documents can be imported again
|
||||
"""
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
manifest = self._do_export()
|
||||
has_thumbnail = False
|
||||
@@ -804,7 +837,11 @@ class TestExportImport(
|
||||
- Main manifest.json file doesn't contain information about documents
|
||||
- Documents can be imported again
|
||||
"""
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
manifest = self._do_export(split_manifest=True)
|
||||
has_document = False
|
||||
@@ -828,7 +865,11 @@ class TestExportImport(
|
||||
self.assertEqual(CustomFieldInstance.objects.count(), 1)
|
||||
|
||||
def _export_import_barcodes(self, *, split_manifest: bool) -> None:
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
DocumentBarcodeFactory(document=self.d1, value="https://example.com")
|
||||
DocumentBarcodeFactory(document=self.d2, page=2, value="DE8937")
|
||||
|
||||
@@ -863,7 +904,11 @@ class TestExportImport(
|
||||
THEN:
|
||||
- Documents can be imported again
|
||||
"""
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
self._do_export(use_folder_prefix=True)
|
||||
|
||||
@@ -889,7 +934,11 @@ class TestExportImport(
|
||||
THEN:
|
||||
- Documents can be imported again
|
||||
"""
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
self._do_export(use_folder_prefix=True, split_manifest=True)
|
||||
|
||||
@@ -915,7 +964,11 @@ class TestExportImport(
|
||||
- ContentType & Permission objects are not deleted, db transaction rolled back
|
||||
"""
|
||||
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
num_content_type_objects = ContentType.objects.count()
|
||||
num_permission_objects = Permission.objects.count()
|
||||
@@ -951,7 +1004,11 @@ class TestExportImport(
|
||||
self.assertEqual(Permission.objects.count(), num_permission_objects + 1)
|
||||
|
||||
def test_exporter_with_auditlog_disabled(self) -> None:
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
with override_settings(
|
||||
AUDIT_LOG_ENABLED=False,
|
||||
@@ -970,7 +1027,11 @@ class TestExportImport(
|
||||
- The soft-deleted document, note, and custom field instance
|
||||
survive the round-trip with deleted_at preserved
|
||||
"""
|
||||
self._install_sample_files()
|
||||
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
|
||||
shutil.copytree(
|
||||
Path(__file__).parent / "samples" / "documents",
|
||||
Path(self.dirs.media_dir) / "documents",
|
||||
)
|
||||
|
||||
# d1 has self.note and self.cfi1 attached via setUp
|
||||
self.d1.delete()
|
||||
@@ -1014,7 +1075,10 @@ class TestExportImport(
|
||||
"""
|
||||
|
||||
shutil.rmtree(self.dirs.media_dir / "documents")
|
||||
install_document_samples(self.dirs.media_dir / "documents")
|
||||
shutil.copytree(
|
||||
self.SAMPLE_DIR / "documents",
|
||||
self.dirs.media_dir / "documents",
|
||||
)
|
||||
|
||||
_ = self._do_export(data_only=True)
|
||||
|
||||
|
||||
@@ -19,10 +19,6 @@ class TestFuzzyMatchCommand(TestCase):
|
||||
call_command(
|
||||
"document_fuzzy_match",
|
||||
"--no-progress-bar",
|
||||
# The default scales with the CPU count and forks, which closes
|
||||
# the test's database connection on PostgreSQL
|
||||
"--processes",
|
||||
"1",
|
||||
*args,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
|
||||
@@ -425,29 +425,26 @@ class TestRetaggerStoragePath(DirectoriesMixin):
|
||||
@pytest.mark.management
|
||||
@pytest.mark.django_db
|
||||
class TestRetaggerIdRange(DirectoriesMixin):
|
||||
@pytest.mark.usefixtures("documents")
|
||||
@pytest.mark.parametrize(
|
||||
("range_end", "expected_count"),
|
||||
("id_range_args", "expected_count"),
|
||||
[
|
||||
pytest.param("second", 1, id="narrow_range_limits_scope"),
|
||||
pytest.param("extra", 2, id="wide_range_tags_all_matches"),
|
||||
pytest.param(["1", "2"], 1, id="narrow_range_limits_scope"),
|
||||
pytest.param(["1", "9999"], 2, id="wide_range_tags_all_matches"),
|
||||
],
|
||||
)
|
||||
def test_id_range_limits_scope(
|
||||
self,
|
||||
tags: TagTuple,
|
||||
documents: DocumentTuple,
|
||||
range_end: str,
|
||||
id_range_args: list[str],
|
||||
expected_count: int,
|
||||
) -> None:
|
||||
first, second, *_ = documents
|
||||
extra = DocumentFactory(content="NOT the first document")
|
||||
end = {"second": second, "extra": extra}[range_end]
|
||||
DocumentFactory(content="NOT the first document")
|
||||
call_command(
|
||||
"document_retagger",
|
||||
"--tags",
|
||||
"--id-range",
|
||||
str(first.pk),
|
||||
str(end.pk),
|
||||
*id_range_args,
|
||||
skip_checks=True,
|
||||
)
|
||||
tag_first, *_ = tags
|
||||
|
||||
@@ -11,7 +11,6 @@ from documents.models import Document
|
||||
from documents.parsers import get_default_thumbnail
|
||||
from paperless_testing.assertions import FileSystemAssertsMixin
|
||||
from paperless_testing.dirs import DirectoriesMixin
|
||||
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
|
||||
|
||||
|
||||
@pytest.mark.management
|
||||
@@ -25,7 +24,7 @@ class TestMakeThumbnails(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
|
||||
filename="test.pdf",
|
||||
)
|
||||
shutil.copy(
|
||||
SIMPLE_DIGITAL_PDF,
|
||||
Path(__file__).parent / "samples" / "simple.pdf",
|
||||
self.d1.source_path,
|
||||
)
|
||||
|
||||
@@ -37,7 +36,7 @@ class TestMakeThumbnails(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
|
||||
filename="test2.pdf",
|
||||
)
|
||||
shutil.copy(
|
||||
SIMPLE_DIGITAL_PDF,
|
||||
Path(__file__).parent / "samples" / "simple.pdf",
|
||||
self.d2.source_path,
|
||||
)
|
||||
|
||||
|
||||
@@ -15,11 +15,10 @@ import pytest
|
||||
|
||||
from documents import pdf_ops
|
||||
from documents.pdf_ops import PageSpec
|
||||
from paperless_testing.samples import MULTI_PAGE_DIGITAL_PDF
|
||||
|
||||
SRC_ROOT = Path(__file__).parents[2]
|
||||
SAMPLES = Path(__file__).parent / "samples"
|
||||
THREE_PAGES = MULTI_PAGE_DIGITAL_PDF
|
||||
THREE_PAGES = SAMPLES / "documents" / "originals" / "0000002.pdf"
|
||||
TWELVE_PAGES = SAMPLES / "barcodes" / "split-by-asn-2.pdf"
|
||||
ENCRYPTED = SAMPLES / "password-is-test.pdf"
|
||||
SIGNED = SRC_ROOT / "paperless" / "tests" / "samples" / "tesseract" / "signed.pdf"
|
||||
|
||||
@@ -21,7 +21,6 @@ from documents.tests.helpers import dummy_preprocess
|
||||
from paperless_testing.assertions import FileSystemAssertsMixin
|
||||
from paperless_testing.dirs import DirectoriesMixin
|
||||
from paperless_testing.factories import DocumentFactory
|
||||
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -230,12 +229,20 @@ class TestUpdateContent(DirectoriesMixin, TestCase):
|
||||
"""
|
||||
sample1 = self.dirs.scratch_dir / "sample.pdf"
|
||||
shutil.copy(
|
||||
SIMPLE_DIGITAL_PDF,
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000001.pdf",
|
||||
sample1,
|
||||
)
|
||||
sample1_archive = self.dirs.archive_dir / "sample_archive.pdf"
|
||||
shutil.copy(
|
||||
SIMPLE_DIGITAL_PDF,
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000001.pdf",
|
||||
sample1_archive,
|
||||
)
|
||||
doc = Document.objects.create(
|
||||
@@ -263,7 +270,11 @@ class TestUpdateContent(DirectoriesMixin, TestCase):
|
||||
"""
|
||||
sample1 = self.dirs.scratch_dir / "sample.pdf"
|
||||
shutil.copy(
|
||||
SIMPLE_DIGITAL_PDF,
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000001.pdf",
|
||||
sample1,
|
||||
)
|
||||
doc = Document.objects.create(
|
||||
@@ -279,7 +290,14 @@ class TestUpdateContent(DirectoriesMixin, TestCase):
|
||||
|
||||
def _create_root_with_version(self) -> tuple[Document, Document]:
|
||||
sample1 = self.dirs.scratch_dir / "sample.pdf"
|
||||
shutil.copy(SIMPLE_DIGITAL_PDF, sample1)
|
||||
shutil.copy(
|
||||
Path(__file__).parent
|
||||
/ "samples"
|
||||
/ "documents"
|
||||
/ "originals"
|
||||
/ "0000001.pdf",
|
||||
sample1,
|
||||
)
|
||||
root = DocumentFactory(content="root content", mime_type="application/pdf")
|
||||
version = DocumentFactory(
|
||||
content="my document",
|
||||
|
||||
@@ -174,11 +174,11 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
self.assertEqual(w.__str__(), "Workflow: Workflow 1")
|
||||
self.assertEqual(trigger.__str__(), f"WorkflowTrigger {trigger.pk}")
|
||||
self.assertEqual(action.__str__(), f"WorkflowAction {action.pk}")
|
||||
self.assertEqual(trigger.__str__(), "WorkflowTrigger 1")
|
||||
self.assertEqual(action.__str__(), "WorkflowAction 1")
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -290,7 +290,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -409,7 +409,7 @@ class TestWorkflows(
|
||||
w2.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -433,7 +433,7 @@ class TestWorkflows(
|
||||
list(document.tags.all()),
|
||||
[self.t1, self.t2, self.t3],
|
||||
)
|
||||
self.assertCountEqual(
|
||||
self.assertEqual(
|
||||
list(
|
||||
get_users_with_perms(
|
||||
document,
|
||||
@@ -478,7 +478,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -531,7 +531,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -608,7 +608,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -687,7 +687,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -765,7 +765,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -876,7 +876,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -928,7 +928,7 @@ class TestWorkflows(
|
||||
generated = generate_unique_filename(doc)
|
||||
destination = (settings.ORIGINALS_DIR / generated).resolve()
|
||||
create_source_path_directory(destination)
|
||||
shutil.copy(self.SIMPLE_PDF, destination)
|
||||
shutil.copy(self.SAMPLE_DIR / "simple.pdf", destination)
|
||||
Document.objects.filter(pk=doc.pk).update(filename=generated.as_posix())
|
||||
doc.refresh_from_db()
|
||||
|
||||
@@ -2067,7 +2067,7 @@ class TestWorkflows(
|
||||
superuser = UserFactory(username="superuser", superuser=True)
|
||||
self.client.force_authenticate(user=superuser)
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
tasks.consume_file(
|
||||
@@ -3053,7 +3053,7 @@ class TestWorkflows(
|
||||
generated = generate_unique_filename(doc)
|
||||
destination = (settings.ORIGINALS_DIR / generated).resolve()
|
||||
create_source_path_directory(destination)
|
||||
shutil.copy(self.SIMPLE_PDF, destination)
|
||||
shutil.copy(self.SAMPLE_DIR / "simple.pdf", destination)
|
||||
Document.objects.filter(pk=doc.pk).update(filename=generated.as_posix())
|
||||
doc.refresh_from_db()
|
||||
doc.tags.set([self.t1, self.t2])
|
||||
@@ -3117,7 +3117,7 @@ class TestWorkflows(
|
||||
generated = generate_unique_filename(doc)
|
||||
destination = (settings.ORIGINALS_DIR / generated).resolve()
|
||||
create_source_path_directory(destination)
|
||||
shutil.copy(self.SIMPLE_PDF, destination)
|
||||
shutil.copy(self.SAMPLE_DIR / "simple.pdf", destination)
|
||||
Document.objects.filter(pk=doc.pk).update(filename=generated.as_posix())
|
||||
doc.refresh_from_db()
|
||||
doc.tags.set([self.t1])
|
||||
@@ -3266,7 +3266,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -3390,7 +3390,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -3540,7 +3540,7 @@ class TestWorkflows(
|
||||
workflow.actions.set([assignment_action, email_action])
|
||||
|
||||
temp_working_copy = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "working-copy.pdf",
|
||||
)
|
||||
|
||||
@@ -3640,7 +3640,7 @@ class TestWorkflows(
|
||||
|
||||
# move the file
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -3748,7 +3748,7 @@ class TestWorkflows(
|
||||
generated = generate_unique_filename(doc)
|
||||
destination = (settings.ORIGINALS_DIR / generated).resolve()
|
||||
create_source_path_directory(destination)
|
||||
shutil.copy(self.SIMPLE_PDF, destination)
|
||||
shutil.copy(self.SAMPLE_DIR / "simple.pdf", destination)
|
||||
Document.objects.filter(pk=doc.pk).update(filename=generated.as_posix())
|
||||
|
||||
run_workflows(WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, doc)
|
||||
@@ -3959,7 +3959,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -4080,7 +4080,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -4431,7 +4431,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -4879,7 +4879,7 @@ class TestWorkflows(
|
||||
w.save()
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -5071,7 +5071,7 @@ class TestWorkflows(
|
||||
|
||||
# Create a test file to be consumed
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
test_file_path = Path(test_file)
|
||||
@@ -5135,7 +5135,7 @@ class TestWorkflows(
|
||||
|
||||
# Create a test file to be consumed
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple2.pdf",
|
||||
)
|
||||
test_file_path = Path(test_file)
|
||||
@@ -5639,7 +5639,7 @@ class TestDateWorkflowLocalization(
|
||||
)
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
tmp_path / "simple.pdf",
|
||||
)
|
||||
|
||||
@@ -5706,7 +5706,7 @@ class TestRemoteOCRWorkflowAction(DirectoriesMixin, SampleDirMixin, APITestCase)
|
||||
self._make_workflow(WorkflowTrigger.WorkflowTriggerType.CONSUMPTION)
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
overrides = DocumentMetadataOverrides()
|
||||
@@ -5924,7 +5924,7 @@ class TestApplyAISuggestionsWorkflowAction(
|
||||
)
|
||||
|
||||
test_file = shutil.copy(
|
||||
self.SIMPLE_PDF,
|
||||
self.SAMPLE_DIR / "simple.pdf",
|
||||
self.dirs.scratch_dir / "simple.pdf",
|
||||
)
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ from documents.data_models import ConsumableDocument
|
||||
from documents.data_models import DocumentMetadataOverrides
|
||||
from documents.data_models import DocumentSource
|
||||
from paperless_testing.fakes.progress import FakeProgressManager
|
||||
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
|
||||
|
||||
|
||||
class ConsumeTaskMixin:
|
||||
@@ -54,8 +53,6 @@ class SampleDirMixin:
|
||||
|
||||
BARCODE_SAMPLE_DIR = SAMPLE_DIR / "barcodes"
|
||||
|
||||
SIMPLE_PDF = SIMPLE_DIGITAL_PDF
|
||||
|
||||
|
||||
class GetConsumerMixin:
|
||||
@contextmanager
|
||||
|
||||
+10
-3
@@ -5267,7 +5267,9 @@ class CustomFieldViewSet(PermissionsAwareDocumentCountMixin, ModelViewSet[Custom
|
||||
"migration_status": inline_serializer(
|
||||
name="MigrationStatus",
|
||||
fields={
|
||||
"latest_migration": serializers.CharField(),
|
||||
"latest_migration": serializers.CharField(
|
||||
allow_null=True,
|
||||
),
|
||||
"unapplied_migrations": serializers.ListSerializer(
|
||||
child=serializers.CharField(),
|
||||
),
|
||||
@@ -5342,6 +5344,8 @@ class SystemStatusView(PassUserMixin):
|
||||
db_conn = connections["default"]
|
||||
db_url = str(db_conn.settings_dict["NAME"])
|
||||
db_error = None
|
||||
all_migrations: list[str] = []
|
||||
applied_migrations: list[str] = []
|
||||
|
||||
try:
|
||||
db_conn.ensure_connection()
|
||||
@@ -5352,7 +5356,8 @@ class SystemStatusView(PassUserMixin):
|
||||
f"{m.app}.{m.name}"
|
||||
for m in MigrationRecorder.Migration.objects.all().order_by("id")
|
||||
]
|
||||
except Exception as e: # pragma: no cover
|
||||
except Exception as e:
|
||||
all_migrations = []
|
||||
applied_migrations = []
|
||||
db_status = "ERROR"
|
||||
logger.exception(
|
||||
@@ -5548,7 +5553,9 @@ class SystemStatusView(PassUserMixin):
|
||||
"status": db_status,
|
||||
"error": db_error,
|
||||
"migration_status": {
|
||||
"latest_migration": applied_migrations[-1],
|
||||
"latest_migration": (
|
||||
applied_migrations[-1] if applied_migrations else None
|
||||
),
|
||||
"unapplied_migrations": [
|
||||
m for m in all_migrations if m not in applied_migrations
|
||||
],
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: paperless-ngx\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-10-29 17:25+0000\n"
|
||||
"PO-Revision-Date: 2026-10-29 17:27\n"
|
||||
"POT-Creation-Date: 2026-10-19 15:40+0000\n"
|
||||
"PO-Revision-Date: 2026-10-19 15:42\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Afrikaans\n"
|
||||
"Language: af_ZA\n"
|
||||
@@ -21,43 +21,39 @@ msgstr ""
|
||||
msgid "Documents"
|
||||
msgstr "Dokumente"
|
||||
|
||||
#: documents/filters.py:337
|
||||
msgid "Expected a date or a relative date such as \"today\" or \"in 30 days\"."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:513
|
||||
#: documents/filters.py:464
|
||||
msgid "Value must be valid JSON."
|
||||
msgstr "Waarde moet geldige JSON wees."
|
||||
|
||||
#: documents/filters.py:534
|
||||
#: documents/filters.py:483
|
||||
msgid "Invalid custom field query expression"
|
||||
msgstr "Ongeldige gepasmaakte veldnavraaguitdrukking"
|
||||
|
||||
#: documents/filters.py:544
|
||||
#: documents/filters.py:493
|
||||
msgid "Invalid expression list. Must be nonempty."
|
||||
msgstr "Ongeldige uitdrukking lys. Moet nie leeg wees nie."
|
||||
|
||||
#: documents/filters.py:565
|
||||
#: documents/filters.py:514
|
||||
msgid "Invalid logical operator {op!r}"
|
||||
msgstr "Ongeldige logiese uitdrukking {op!r}"
|
||||
|
||||
#: documents/filters.py:579
|
||||
#: documents/filters.py:528
|
||||
msgid "Maximum number of query conditions exceeded."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:643
|
||||
#: documents/filters.py:592
|
||||
msgid "{name!r} is not a valid custom field."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:680
|
||||
#: documents/filters.py:629
|
||||
msgid "{data_type} does not support query expr {expr!r}."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:801 documents/models.py:137
|
||||
#: documents/filters.py:748 documents/models.py:137
|
||||
msgid "Maximum nesting depth exceeded."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:1178
|
||||
#: documents/filters.py:1125
|
||||
msgid "Custom field not found"
|
||||
msgstr ""
|
||||
|
||||
@@ -1639,48 +1635,48 @@ msgstr ""
|
||||
msgid "workflow runs"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:483 documents/serialisers.py:840
|
||||
#: documents/serialisers.py:2872 documents/views.py:360 documents/views.py:2730
|
||||
#: documents/serialisers.py:515 documents/serialisers.py:872
|
||||
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
|
||||
#: paperless_mail/serialisers.py:156
|
||||
msgid "Insufficient permissions."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:676
|
||||
#: documents/serialisers.py:708
|
||||
msgid "Invalid color."
|
||||
msgstr "Ongeldige kleur."
|
||||
|
||||
#: documents/serialisers.py:2339
|
||||
#: documents/serialisers.py:2369
|
||||
#, python-format
|
||||
msgid "File type %(type)s not supported"
|
||||
msgstr "Lêertipe %(type)s word nie ondersteun nie"
|
||||
|
||||
#: documents/serialisers.py:2383
|
||||
#: documents/serialisers.py:2413
|
||||
#, python-format
|
||||
msgid "Custom field id must be an integer: %(id)s"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2390
|
||||
#: documents/serialisers.py:2420
|
||||
#, python-format
|
||||
msgid "Custom field with id %(id)s does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2407 documents/serialisers.py:2417
|
||||
#: documents/serialisers.py:2437 documents/serialisers.py:2447
|
||||
msgid "Custom fields must be a list of integers or an object mapping ids to values."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2412
|
||||
#: documents/serialisers.py:2442
|
||||
msgid "Some custom fields don't exist or were specified twice."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2559
|
||||
#: documents/serialisers.py:2589
|
||||
msgid "Invalid variable detected."
|
||||
msgstr "Ongeldige veranderlike bespeur."
|
||||
|
||||
#: documents/serialisers.py:2928
|
||||
#: documents/serialisers.py:2958
|
||||
msgid "Duplicate document identifiers are not allowed."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2958 documents/views.py:4780
|
||||
#: documents/serialisers.py:2988 documents/views.py:4816
|
||||
#, python-format
|
||||
msgid "Documents not found: %(ids)s"
|
||||
msgstr ""
|
||||
@@ -1920,40 +1916,40 @@ msgstr ""
|
||||
msgid ", "
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:315
|
||||
#: documents/views.py:318
|
||||
msgid "Specify only one of text, title_search, query, or more_like_id."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:353 documents/views.py:2727
|
||||
#: documents/views.py:356 documents/views.py:2765
|
||||
msgid "Invalid more_like_id"
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1665
|
||||
#: documents/views.py:1703
|
||||
msgid "Invalid AI configuration."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1676
|
||||
#: documents/views.py:1714
|
||||
msgid "AI backend request timed out."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1688
|
||||
#: documents/views.py:1726
|
||||
msgid "AI backend rejected the request. Check logs for details."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4796
|
||||
#: documents/views.py:4832
|
||||
#, python-format
|
||||
msgid "Insufficient permissions to share document %(id)s."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4843
|
||||
#: documents/views.py:4879
|
||||
msgid "Bundle is already being processed."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4909
|
||||
#: documents/views.py:4945
|
||||
msgid "The share link bundle is still being prepared. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4923
|
||||
#: documents/views.py:4959
|
||||
msgid "The share link bundle is unavailable."
|
||||
msgstr ""
|
||||
|
||||
@@ -2245,151 +2241,151 @@ msgstr ""
|
||||
msgid "paperless application settings"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:566
|
||||
#: paperless/settings/__init__.py:562
|
||||
msgid "English (US)"
|
||||
msgstr "Engels (VS)"
|
||||
|
||||
#: paperless/settings/__init__.py:567
|
||||
#: paperless/settings/__init__.py:563
|
||||
msgid "Arabic"
|
||||
msgstr "Arabies"
|
||||
|
||||
#: paperless/settings/__init__.py:568
|
||||
#: paperless/settings/__init__.py:564
|
||||
msgid "Afrikaans"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:569
|
||||
#: paperless/settings/__init__.py:565
|
||||
msgid "Belarusian"
|
||||
msgstr "Belorussies"
|
||||
|
||||
#: paperless/settings/__init__.py:570
|
||||
#: paperless/settings/__init__.py:566
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:571
|
||||
#: paperless/settings/__init__.py:567
|
||||
msgid "Catalan"
|
||||
msgstr "Katalaans"
|
||||
|
||||
#: paperless/settings/__init__.py:572
|
||||
#: paperless/settings/__init__.py:568
|
||||
msgid "Czech"
|
||||
msgstr "Tsjeggies"
|
||||
|
||||
#: paperless/settings/__init__.py:573
|
||||
#: paperless/settings/__init__.py:569
|
||||
msgid "Danish"
|
||||
msgstr "Deens"
|
||||
|
||||
#: paperless/settings/__init__.py:574
|
||||
#: paperless/settings/__init__.py:570
|
||||
msgid "German"
|
||||
msgstr "Duits"
|
||||
|
||||
#: paperless/settings/__init__.py:575
|
||||
#: paperless/settings/__init__.py:571
|
||||
msgid "Greek"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:576
|
||||
#: paperless/settings/__init__.py:572
|
||||
msgid "English (GB)"
|
||||
msgstr "Engels (GB)"
|
||||
|
||||
#: paperless/settings/__init__.py:577
|
||||
#: paperless/settings/__init__.py:573
|
||||
msgid "Spanish"
|
||||
msgstr "Spaans"
|
||||
|
||||
#: paperless/settings/__init__.py:578
|
||||
#: paperless/settings/__init__.py:574
|
||||
msgid "Persian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:579
|
||||
#: paperless/settings/__init__.py:575
|
||||
msgid "Finnish"
|
||||
msgstr "Fins"
|
||||
|
||||
#: paperless/settings/__init__.py:580
|
||||
#: paperless/settings/__init__.py:576
|
||||
msgid "French"
|
||||
msgstr "Frans"
|
||||
|
||||
#: paperless/settings/__init__.py:581
|
||||
#: paperless/settings/__init__.py:577
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:582
|
||||
#: paperless/settings/__init__.py:578
|
||||
msgid "Indonesian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:583
|
||||
#: paperless/settings/__init__.py:579
|
||||
msgid "Italian"
|
||||
msgstr "Italiaans"
|
||||
|
||||
#: paperless/settings/__init__.py:584
|
||||
#: paperless/settings/__init__.py:580
|
||||
msgid "Japanese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:585
|
||||
#: paperless/settings/__init__.py:581
|
||||
msgid "Korean"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:586
|
||||
#: paperless/settings/__init__.py:582
|
||||
msgid "Luxembourgish"
|
||||
msgstr "Luxemburgs"
|
||||
|
||||
#: paperless/settings/__init__.py:587
|
||||
#: paperless/settings/__init__.py:583
|
||||
msgid "Norwegian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:588
|
||||
#: paperless/settings/__init__.py:584
|
||||
msgid "Dutch"
|
||||
msgstr "Nederlands"
|
||||
|
||||
#: paperless/settings/__init__.py:589
|
||||
#: paperless/settings/__init__.py:585
|
||||
msgid "Polish"
|
||||
msgstr "Pools"
|
||||
|
||||
#: paperless/settings/__init__.py:590
|
||||
#: paperless/settings/__init__.py:586
|
||||
msgid "Portuguese (Brazil)"
|
||||
msgstr "Portugees (Brasilië)"
|
||||
|
||||
#: paperless/settings/__init__.py:591
|
||||
#: paperless/settings/__init__.py:587
|
||||
msgid "Portuguese"
|
||||
msgstr "Portugees"
|
||||
|
||||
#: paperless/settings/__init__.py:592
|
||||
#: paperless/settings/__init__.py:588
|
||||
msgid "Romanian"
|
||||
msgstr "Roemeens"
|
||||
|
||||
#: paperless/settings/__init__.py:593
|
||||
#: paperless/settings/__init__.py:589
|
||||
msgid "Russian"
|
||||
msgstr "Russies"
|
||||
|
||||
#: paperless/settings/__init__.py:594
|
||||
#: paperless/settings/__init__.py:590
|
||||
msgid "Slovak"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:595
|
||||
#: paperless/settings/__init__.py:591
|
||||
msgid "Slovenian"
|
||||
msgstr "Sloweens"
|
||||
|
||||
#: paperless/settings/__init__.py:596
|
||||
#: paperless/settings/__init__.py:592
|
||||
msgid "Serbian"
|
||||
msgstr "Serwies"
|
||||
|
||||
#: paperless/settings/__init__.py:597
|
||||
#: paperless/settings/__init__.py:593
|
||||
msgid "Swedish"
|
||||
msgstr "Sweeds"
|
||||
|
||||
#: paperless/settings/__init__.py:598
|
||||
#: paperless/settings/__init__.py:594
|
||||
msgid "Turkish"
|
||||
msgstr "Turks"
|
||||
|
||||
#: paperless/settings/__init__.py:599
|
||||
#: paperless/settings/__init__.py:595
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:600
|
||||
#: paperless/settings/__init__.py:596
|
||||
msgid "Vietnamese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:601
|
||||
#: paperless/settings/__init__.py:597
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "Vereenvoudigde Sjinees"
|
||||
|
||||
#: paperless/settings/__init__.py:602
|
||||
#: paperless/settings/__init__.py:598
|
||||
msgid "Chinese Traditional"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: paperless-ngx\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-10-29 17:25+0000\n"
|
||||
"PO-Revision-Date: 2026-10-29 17:27\n"
|
||||
"POT-Creation-Date: 2026-10-19 15:40+0000\n"
|
||||
"PO-Revision-Date: 2026-10-19 15:42\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Amharic\n"
|
||||
"Language: am_ET\n"
|
||||
@@ -21,43 +21,39 @@ msgstr ""
|
||||
msgid "Documents"
|
||||
msgstr "መዝገባት"
|
||||
|
||||
#: documents/filters.py:337
|
||||
msgid "Expected a date or a relative date such as \"today\" or \"in 30 days\"."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:513
|
||||
#: documents/filters.py:464
|
||||
msgid "Value must be valid JSON."
|
||||
msgstr "የሚሰራው እሴት \"JSON\" መሆን አለበት"
|
||||
|
||||
#: documents/filters.py:534
|
||||
#: documents/filters.py:483
|
||||
msgid "Invalid custom field query expression"
|
||||
msgstr "ልክ ያልሆነ የተወሰነ የቦታ መጠይቅ አገላለጽ"
|
||||
|
||||
#: documents/filters.py:544
|
||||
#: documents/filters.py:493
|
||||
msgid "Invalid expression list. Must be nonempty."
|
||||
msgstr "ልክ ያልሆነ የመግለጫ ዝርዝር። ባዶ መሆን የለበትም።"
|
||||
|
||||
#: documents/filters.py:565
|
||||
#: documents/filters.py:514
|
||||
msgid "Invalid logical operator {op!r}"
|
||||
msgstr "ልክ ያልሆነ የሎጂክ ኦፕሬተር {op!r}"
|
||||
|
||||
#: documents/filters.py:579
|
||||
#: documents/filters.py:528
|
||||
msgid "Maximum number of query conditions exceeded."
|
||||
msgstr "ከፍተኛው የጥያቄ ሁኔታዎች/መጠን ብዛት አልፏል።"
|
||||
|
||||
#: documents/filters.py:643
|
||||
#: documents/filters.py:592
|
||||
msgid "{name!r} is not a valid custom field."
|
||||
msgstr "{name!r} ይሄ ታዐማኒነት ያለው ልማድ አይደለም።"
|
||||
|
||||
#: documents/filters.py:680
|
||||
#: documents/filters.py:629
|
||||
msgid "{data_type} does not support query expr {expr!r}."
|
||||
msgstr "ጥያቄን አይደግፍም expr {expr!r}."
|
||||
|
||||
#: documents/filters.py:801 documents/models.py:137
|
||||
#: documents/filters.py:748 documents/models.py:137
|
||||
msgid "Maximum nesting depth exceeded."
|
||||
msgstr "ከፍተኛው የጥገኝነት ጥልቀት አልፏል።"
|
||||
|
||||
#: documents/filters.py:1178
|
||||
#: documents/filters.py:1125
|
||||
msgid "Custom field not found"
|
||||
msgstr "ይህ ልማድ አልተገኘም"
|
||||
|
||||
@@ -1639,48 +1635,48 @@ msgstr ""
|
||||
msgid "workflow runs"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:483 documents/serialisers.py:840
|
||||
#: documents/serialisers.py:2872 documents/views.py:360 documents/views.py:2730
|
||||
#: documents/serialisers.py:515 documents/serialisers.py:872
|
||||
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
|
||||
#: paperless_mail/serialisers.py:156
|
||||
msgid "Insufficient permissions."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:676
|
||||
#: documents/serialisers.py:708
|
||||
msgid "Invalid color."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2339
|
||||
#: documents/serialisers.py:2369
|
||||
#, python-format
|
||||
msgid "File type %(type)s not supported"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2383
|
||||
#: documents/serialisers.py:2413
|
||||
#, python-format
|
||||
msgid "Custom field id must be an integer: %(id)s"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2390
|
||||
#: documents/serialisers.py:2420
|
||||
#, python-format
|
||||
msgid "Custom field with id %(id)s does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2407 documents/serialisers.py:2417
|
||||
#: documents/serialisers.py:2437 documents/serialisers.py:2447
|
||||
msgid "Custom fields must be a list of integers or an object mapping ids to values."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2412
|
||||
#: documents/serialisers.py:2442
|
||||
msgid "Some custom fields don't exist or were specified twice."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2559
|
||||
#: documents/serialisers.py:2589
|
||||
msgid "Invalid variable detected."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2928
|
||||
#: documents/serialisers.py:2958
|
||||
msgid "Duplicate document identifiers are not allowed."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2958 documents/views.py:4780
|
||||
#: documents/serialisers.py:2988 documents/views.py:4816
|
||||
#, python-format
|
||||
msgid "Documents not found: %(ids)s"
|
||||
msgstr ""
|
||||
@@ -1920,40 +1916,40 @@ msgstr ""
|
||||
msgid ", "
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:315
|
||||
#: documents/views.py:318
|
||||
msgid "Specify only one of text, title_search, query, or more_like_id."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:353 documents/views.py:2727
|
||||
#: documents/views.py:356 documents/views.py:2765
|
||||
msgid "Invalid more_like_id"
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1665
|
||||
#: documents/views.py:1703
|
||||
msgid "Invalid AI configuration."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1676
|
||||
#: documents/views.py:1714
|
||||
msgid "AI backend request timed out."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1688
|
||||
#: documents/views.py:1726
|
||||
msgid "AI backend rejected the request. Check logs for details."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4796
|
||||
#: documents/views.py:4832
|
||||
#, python-format
|
||||
msgid "Insufficient permissions to share document %(id)s."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4843
|
||||
#: documents/views.py:4879
|
||||
msgid "Bundle is already being processed."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4909
|
||||
#: documents/views.py:4945
|
||||
msgid "The share link bundle is still being prepared. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4923
|
||||
#: documents/views.py:4959
|
||||
msgid "The share link bundle is unavailable."
|
||||
msgstr ""
|
||||
|
||||
@@ -2245,151 +2241,151 @@ msgstr ""
|
||||
msgid "paperless application settings"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:566
|
||||
#: paperless/settings/__init__.py:562
|
||||
msgid "English (US)"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:567
|
||||
#: paperless/settings/__init__.py:563
|
||||
msgid "Arabic"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:568
|
||||
#: paperless/settings/__init__.py:564
|
||||
msgid "Afrikaans"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:569
|
||||
#: paperless/settings/__init__.py:565
|
||||
msgid "Belarusian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:570
|
||||
#: paperless/settings/__init__.py:566
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:571
|
||||
#: paperless/settings/__init__.py:567
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:572
|
||||
#: paperless/settings/__init__.py:568
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:573
|
||||
#: paperless/settings/__init__.py:569
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:574
|
||||
#: paperless/settings/__init__.py:570
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:575
|
||||
#: paperless/settings/__init__.py:571
|
||||
msgid "Greek"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:576
|
||||
#: paperless/settings/__init__.py:572
|
||||
msgid "English (GB)"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:577
|
||||
#: paperless/settings/__init__.py:573
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:578
|
||||
#: paperless/settings/__init__.py:574
|
||||
msgid "Persian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:579
|
||||
#: paperless/settings/__init__.py:575
|
||||
msgid "Finnish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:580
|
||||
#: paperless/settings/__init__.py:576
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:581
|
||||
#: paperless/settings/__init__.py:577
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:582
|
||||
#: paperless/settings/__init__.py:578
|
||||
msgid "Indonesian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:583
|
||||
#: paperless/settings/__init__.py:579
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:584
|
||||
#: paperless/settings/__init__.py:580
|
||||
msgid "Japanese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:585
|
||||
#: paperless/settings/__init__.py:581
|
||||
msgid "Korean"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:586
|
||||
#: paperless/settings/__init__.py:582
|
||||
msgid "Luxembourgish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:587
|
||||
#: paperless/settings/__init__.py:583
|
||||
msgid "Norwegian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:588
|
||||
#: paperless/settings/__init__.py:584
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:589
|
||||
#: paperless/settings/__init__.py:585
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:590
|
||||
#: paperless/settings/__init__.py:586
|
||||
msgid "Portuguese (Brazil)"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:591
|
||||
#: paperless/settings/__init__.py:587
|
||||
msgid "Portuguese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:592
|
||||
#: paperless/settings/__init__.py:588
|
||||
msgid "Romanian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:593
|
||||
#: paperless/settings/__init__.py:589
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:594
|
||||
#: paperless/settings/__init__.py:590
|
||||
msgid "Slovak"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:595
|
||||
#: paperless/settings/__init__.py:591
|
||||
msgid "Slovenian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:596
|
||||
#: paperless/settings/__init__.py:592
|
||||
msgid "Serbian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:597
|
||||
#: paperless/settings/__init__.py:593
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:598
|
||||
#: paperless/settings/__init__.py:594
|
||||
msgid "Turkish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:599
|
||||
#: paperless/settings/__init__.py:595
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:600
|
||||
#: paperless/settings/__init__.py:596
|
||||
msgid "Vietnamese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:601
|
||||
#: paperless/settings/__init__.py:597
|
||||
msgid "Chinese Simplified"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:602
|
||||
#: paperless/settings/__init__.py:598
|
||||
msgid "Chinese Traditional"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: paperless-ngx\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-10-29 17:25+0000\n"
|
||||
"PO-Revision-Date: 2026-10-29 17:27\n"
|
||||
"POT-Creation-Date: 2026-10-19 15:40+0000\n"
|
||||
"PO-Revision-Date: 2026-10-19 15:42\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Arabic\n"
|
||||
"Language: ar_SA\n"
|
||||
@@ -21,43 +21,39 @@ msgstr ""
|
||||
msgid "Documents"
|
||||
msgstr "المستندات"
|
||||
|
||||
#: documents/filters.py:337
|
||||
msgid "Expected a date or a relative date such as \"today\" or \"in 30 days\"."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:513
|
||||
#: documents/filters.py:464
|
||||
msgid "Value must be valid JSON."
|
||||
msgstr "يجب أن تكون القيمة JSON."
|
||||
|
||||
#: documents/filters.py:534
|
||||
#: documents/filters.py:483
|
||||
msgid "Invalid custom field query expression"
|
||||
msgstr "تعبير استعلام غير صالح للحقول المخصصة"
|
||||
|
||||
#: documents/filters.py:544
|
||||
#: documents/filters.py:493
|
||||
msgid "Invalid expression list. Must be nonempty."
|
||||
msgstr "قائمة عبارة خاطئة."
|
||||
|
||||
#: documents/filters.py:565
|
||||
#: documents/filters.py:514
|
||||
msgid "Invalid logical operator {op!r}"
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:579
|
||||
#: documents/filters.py:528
|
||||
msgid "Maximum number of query conditions exceeded."
|
||||
msgstr "تجاوز الحد الأقصى لعدد شروط الاستعلام."
|
||||
|
||||
#: documents/filters.py:643
|
||||
#: documents/filters.py:592
|
||||
msgid "{name!r} is not a valid custom field."
|
||||
msgstr "{name!r} حقل مخصص غير صالح."
|
||||
|
||||
#: documents/filters.py:680
|
||||
#: documents/filters.py:629
|
||||
msgid "{data_type} does not support query expr {expr!r}."
|
||||
msgstr "{data_type} لا يدعم تعبير الاستعلام {expr!r}."
|
||||
|
||||
#: documents/filters.py:801 documents/models.py:137
|
||||
#: documents/filters.py:748 documents/models.py:137
|
||||
msgid "Maximum nesting depth exceeded."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:1178
|
||||
#: documents/filters.py:1125
|
||||
msgid "Custom field not found"
|
||||
msgstr "لم يتم العثور على حقل مخصص"
|
||||
|
||||
@@ -1639,48 +1635,48 @@ msgstr ""
|
||||
msgid "workflow runs"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:483 documents/serialisers.py:840
|
||||
#: documents/serialisers.py:2872 documents/views.py:360 documents/views.py:2730
|
||||
#: documents/serialisers.py:515 documents/serialisers.py:872
|
||||
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
|
||||
#: paperless_mail/serialisers.py:156
|
||||
msgid "Insufficient permissions."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:676
|
||||
#: documents/serialisers.py:708
|
||||
msgid "Invalid color."
|
||||
msgstr "لون خاطئ."
|
||||
|
||||
#: documents/serialisers.py:2339
|
||||
#: documents/serialisers.py:2369
|
||||
#, python-format
|
||||
msgid "File type %(type)s not supported"
|
||||
msgstr "نوع الملف %(type)s غير مدعوم"
|
||||
|
||||
#: documents/serialisers.py:2383
|
||||
#: documents/serialisers.py:2413
|
||||
#, python-format
|
||||
msgid "Custom field id must be an integer: %(id)s"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2390
|
||||
#: documents/serialisers.py:2420
|
||||
#, python-format
|
||||
msgid "Custom field with id %(id)s does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2407 documents/serialisers.py:2417
|
||||
#: documents/serialisers.py:2437 documents/serialisers.py:2447
|
||||
msgid "Custom fields must be a list of integers or an object mapping ids to values."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2412
|
||||
#: documents/serialisers.py:2442
|
||||
msgid "Some custom fields don't exist or were specified twice."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2559
|
||||
#: documents/serialisers.py:2589
|
||||
msgid "Invalid variable detected."
|
||||
msgstr "اكتشاف متغير خاطئ."
|
||||
|
||||
#: documents/serialisers.py:2928
|
||||
#: documents/serialisers.py:2958
|
||||
msgid "Duplicate document identifiers are not allowed."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2958 documents/views.py:4780
|
||||
#: documents/serialisers.py:2988 documents/views.py:4816
|
||||
#, python-format
|
||||
msgid "Documents not found: %(ids)s"
|
||||
msgstr ""
|
||||
@@ -1921,40 +1917,40 @@ msgstr "كخطوة أخيرة، يرجى ملء النموذج التالي:"
|
||||
msgid ", "
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:315
|
||||
#: documents/views.py:318
|
||||
msgid "Specify only one of text, title_search, query, or more_like_id."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:353 documents/views.py:2727
|
||||
#: documents/views.py:356 documents/views.py:2765
|
||||
msgid "Invalid more_like_id"
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1665
|
||||
#: documents/views.py:1703
|
||||
msgid "Invalid AI configuration."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1676
|
||||
#: documents/views.py:1714
|
||||
msgid "AI backend request timed out."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1688
|
||||
#: documents/views.py:1726
|
||||
msgid "AI backend rejected the request. Check logs for details."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4796
|
||||
#: documents/views.py:4832
|
||||
#, python-format
|
||||
msgid "Insufficient permissions to share document %(id)s."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4843
|
||||
#: documents/views.py:4879
|
||||
msgid "Bundle is already being processed."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4909
|
||||
#: documents/views.py:4945
|
||||
msgid "The share link bundle is still being prepared. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4923
|
||||
#: documents/views.py:4959
|
||||
msgid "The share link bundle is unavailable."
|
||||
msgstr ""
|
||||
|
||||
@@ -2246,151 +2242,151 @@ msgstr ""
|
||||
msgid "paperless application settings"
|
||||
msgstr "إعدادات التطبيق paperless"
|
||||
|
||||
#: paperless/settings/__init__.py:566
|
||||
#: paperless/settings/__init__.py:562
|
||||
msgid "English (US)"
|
||||
msgstr "الإنجليزية (الولايات المتحدة)"
|
||||
|
||||
#: paperless/settings/__init__.py:567
|
||||
#: paperless/settings/__init__.py:563
|
||||
msgid "Arabic"
|
||||
msgstr "العربية"
|
||||
|
||||
#: paperless/settings/__init__.py:568
|
||||
#: paperless/settings/__init__.py:564
|
||||
msgid "Afrikaans"
|
||||
msgstr "اللغة الأفريقانية"
|
||||
|
||||
#: paperless/settings/__init__.py:569
|
||||
#: paperless/settings/__init__.py:565
|
||||
msgid "Belarusian"
|
||||
msgstr "البيلاروسية"
|
||||
|
||||
#: paperless/settings/__init__.py:570
|
||||
#: paperless/settings/__init__.py:566
|
||||
msgid "Bulgarian"
|
||||
msgstr "البلغارية"
|
||||
|
||||
#: paperless/settings/__init__.py:571
|
||||
#: paperless/settings/__init__.py:567
|
||||
msgid "Catalan"
|
||||
msgstr "اللغة الكتالونية"
|
||||
|
||||
#: paperless/settings/__init__.py:572
|
||||
#: paperless/settings/__init__.py:568
|
||||
msgid "Czech"
|
||||
msgstr "التشيكية"
|
||||
|
||||
#: paperless/settings/__init__.py:573
|
||||
#: paperless/settings/__init__.py:569
|
||||
msgid "Danish"
|
||||
msgstr "الدانماركية"
|
||||
|
||||
#: paperless/settings/__init__.py:574
|
||||
#: paperless/settings/__init__.py:570
|
||||
msgid "German"
|
||||
msgstr "الألمانية"
|
||||
|
||||
#: paperless/settings/__init__.py:575
|
||||
#: paperless/settings/__init__.py:571
|
||||
msgid "Greek"
|
||||
msgstr "اليونانية"
|
||||
|
||||
#: paperless/settings/__init__.py:576
|
||||
#: paperless/settings/__init__.py:572
|
||||
msgid "English (GB)"
|
||||
msgstr "الإنجليزية (المملكة المتحدة)"
|
||||
|
||||
#: paperless/settings/__init__.py:577
|
||||
#: paperless/settings/__init__.py:573
|
||||
msgid "Spanish"
|
||||
msgstr "الإسبانية"
|
||||
|
||||
#: paperless/settings/__init__.py:578
|
||||
#: paperless/settings/__init__.py:574
|
||||
msgid "Persian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:579
|
||||
#: paperless/settings/__init__.py:575
|
||||
msgid "Finnish"
|
||||
msgstr "الفنلندية"
|
||||
|
||||
#: paperless/settings/__init__.py:580
|
||||
#: paperless/settings/__init__.py:576
|
||||
msgid "French"
|
||||
msgstr "الفرنسية"
|
||||
|
||||
#: paperless/settings/__init__.py:581
|
||||
#: paperless/settings/__init__.py:577
|
||||
msgid "Hungarian"
|
||||
msgstr "المجرية"
|
||||
|
||||
#: paperless/settings/__init__.py:582
|
||||
#: paperless/settings/__init__.py:578
|
||||
msgid "Indonesian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:583
|
||||
#: paperless/settings/__init__.py:579
|
||||
msgid "Italian"
|
||||
msgstr "الإيطالية"
|
||||
|
||||
#: paperless/settings/__init__.py:584
|
||||
#: paperless/settings/__init__.py:580
|
||||
msgid "Japanese"
|
||||
msgstr "اليابانية"
|
||||
|
||||
#: paperless/settings/__init__.py:585
|
||||
#: paperless/settings/__init__.py:581
|
||||
msgid "Korean"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:586
|
||||
#: paperless/settings/__init__.py:582
|
||||
msgid "Luxembourgish"
|
||||
msgstr "اللوكسمبرجية"
|
||||
|
||||
#: paperless/settings/__init__.py:587
|
||||
#: paperless/settings/__init__.py:583
|
||||
msgid "Norwegian"
|
||||
msgstr "النرويجية"
|
||||
|
||||
#: paperless/settings/__init__.py:588
|
||||
#: paperless/settings/__init__.py:584
|
||||
msgid "Dutch"
|
||||
msgstr "الهولندية"
|
||||
|
||||
#: paperless/settings/__init__.py:589
|
||||
#: paperless/settings/__init__.py:585
|
||||
msgid "Polish"
|
||||
msgstr "البولندية"
|
||||
|
||||
#: paperless/settings/__init__.py:590
|
||||
#: paperless/settings/__init__.py:586
|
||||
msgid "Portuguese (Brazil)"
|
||||
msgstr "البرتغالية (البرازيل)"
|
||||
|
||||
#: paperless/settings/__init__.py:591
|
||||
#: paperless/settings/__init__.py:587
|
||||
msgid "Portuguese"
|
||||
msgstr "البرتغالية"
|
||||
|
||||
#: paperless/settings/__init__.py:592
|
||||
#: paperless/settings/__init__.py:588
|
||||
msgid "Romanian"
|
||||
msgstr "الرومانية"
|
||||
|
||||
#: paperless/settings/__init__.py:593
|
||||
#: paperless/settings/__init__.py:589
|
||||
msgid "Russian"
|
||||
msgstr "الروسية"
|
||||
|
||||
#: paperless/settings/__init__.py:594
|
||||
#: paperless/settings/__init__.py:590
|
||||
msgid "Slovak"
|
||||
msgstr "السلوفاكية"
|
||||
|
||||
#: paperless/settings/__init__.py:595
|
||||
#: paperless/settings/__init__.py:591
|
||||
msgid "Slovenian"
|
||||
msgstr "السلوفانية"
|
||||
|
||||
#: paperless/settings/__init__.py:596
|
||||
#: paperless/settings/__init__.py:592
|
||||
msgid "Serbian"
|
||||
msgstr "الصربية"
|
||||
|
||||
#: paperless/settings/__init__.py:597
|
||||
#: paperless/settings/__init__.py:593
|
||||
msgid "Swedish"
|
||||
msgstr "السويدية"
|
||||
|
||||
#: paperless/settings/__init__.py:598
|
||||
#: paperless/settings/__init__.py:594
|
||||
msgid "Turkish"
|
||||
msgstr "التركية"
|
||||
|
||||
#: paperless/settings/__init__.py:599
|
||||
#: paperless/settings/__init__.py:595
|
||||
msgid "Ukrainian"
|
||||
msgstr "الأوكرانية"
|
||||
|
||||
#: paperless/settings/__init__.py:600
|
||||
#: paperless/settings/__init__.py:596
|
||||
msgid "Vietnamese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:601
|
||||
#: paperless/settings/__init__.py:597
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "الصينية المبسطة"
|
||||
|
||||
#: paperless/settings/__init__.py:602
|
||||
#: paperless/settings/__init__.py:598
|
||||
msgid "Chinese Traditional"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: paperless-ngx\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-10-29 17:25+0000\n"
|
||||
"PO-Revision-Date: 2026-10-29 17:27\n"
|
||||
"POT-Creation-Date: 2026-10-19 15:40+0000\n"
|
||||
"PO-Revision-Date: 2026-10-19 15:42\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Belarusian\n"
|
||||
"Language: be_BY\n"
|
||||
@@ -21,43 +21,39 @@ msgstr ""
|
||||
msgid "Documents"
|
||||
msgstr "Дакументы"
|
||||
|
||||
#: documents/filters.py:337
|
||||
msgid "Expected a date or a relative date such as \"today\" or \"in 30 days\"."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:513
|
||||
#: documents/filters.py:464
|
||||
msgid "Value must be valid JSON."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:534
|
||||
#: documents/filters.py:483
|
||||
msgid "Invalid custom field query expression"
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:544
|
||||
#: documents/filters.py:493
|
||||
msgid "Invalid expression list. Must be nonempty."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:565
|
||||
#: documents/filters.py:514
|
||||
msgid "Invalid logical operator {op!r}"
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:579
|
||||
#: documents/filters.py:528
|
||||
msgid "Maximum number of query conditions exceeded."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:643
|
||||
#: documents/filters.py:592
|
||||
msgid "{name!r} is not a valid custom field."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:680
|
||||
#: documents/filters.py:629
|
||||
msgid "{data_type} does not support query expr {expr!r}."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:801 documents/models.py:137
|
||||
#: documents/filters.py:748 documents/models.py:137
|
||||
msgid "Maximum nesting depth exceeded."
|
||||
msgstr ""
|
||||
|
||||
#: documents/filters.py:1178
|
||||
#: documents/filters.py:1125
|
||||
msgid "Custom field not found"
|
||||
msgstr ""
|
||||
|
||||
@@ -1639,48 +1635,48 @@ msgstr ""
|
||||
msgid "workflow runs"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:483 documents/serialisers.py:840
|
||||
#: documents/serialisers.py:2872 documents/views.py:360 documents/views.py:2730
|
||||
#: documents/serialisers.py:515 documents/serialisers.py:872
|
||||
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
|
||||
#: paperless_mail/serialisers.py:156
|
||||
msgid "Insufficient permissions."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:676
|
||||
#: documents/serialisers.py:708
|
||||
msgid "Invalid color."
|
||||
msgstr "Няправільны колер."
|
||||
|
||||
#: documents/serialisers.py:2339
|
||||
#: documents/serialisers.py:2369
|
||||
#, python-format
|
||||
msgid "File type %(type)s not supported"
|
||||
msgstr "Тып файла %(type)s не падтрымліваецца"
|
||||
|
||||
#: documents/serialisers.py:2383
|
||||
#: documents/serialisers.py:2413
|
||||
#, python-format
|
||||
msgid "Custom field id must be an integer: %(id)s"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2390
|
||||
#: documents/serialisers.py:2420
|
||||
#, python-format
|
||||
msgid "Custom field with id %(id)s does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2407 documents/serialisers.py:2417
|
||||
#: documents/serialisers.py:2437 documents/serialisers.py:2447
|
||||
msgid "Custom fields must be a list of integers or an object mapping ids to values."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2412
|
||||
#: documents/serialisers.py:2442
|
||||
msgid "Some custom fields don't exist or were specified twice."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2559
|
||||
#: documents/serialisers.py:2589
|
||||
msgid "Invalid variable detected."
|
||||
msgstr "Выяўлена няправільная зменная."
|
||||
|
||||
#: documents/serialisers.py:2928
|
||||
#: documents/serialisers.py:2958
|
||||
msgid "Duplicate document identifiers are not allowed."
|
||||
msgstr ""
|
||||
|
||||
#: documents/serialisers.py:2958 documents/views.py:4780
|
||||
#: documents/serialisers.py:2988 documents/views.py:4816
|
||||
#, python-format
|
||||
msgid "Documents not found: %(ids)s"
|
||||
msgstr ""
|
||||
@@ -1920,40 +1916,40 @@ msgstr ""
|
||||
msgid ", "
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:315
|
||||
#: documents/views.py:318
|
||||
msgid "Specify only one of text, title_search, query, or more_like_id."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:353 documents/views.py:2727
|
||||
#: documents/views.py:356 documents/views.py:2765
|
||||
msgid "Invalid more_like_id"
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1665
|
||||
#: documents/views.py:1703
|
||||
msgid "Invalid AI configuration."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1676
|
||||
#: documents/views.py:1714
|
||||
msgid "AI backend request timed out."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:1688
|
||||
#: documents/views.py:1726
|
||||
msgid "AI backend rejected the request. Check logs for details."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4796
|
||||
#: documents/views.py:4832
|
||||
#, python-format
|
||||
msgid "Insufficient permissions to share document %(id)s."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4843
|
||||
#: documents/views.py:4879
|
||||
msgid "Bundle is already being processed."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4909
|
||||
#: documents/views.py:4945
|
||||
msgid "The share link bundle is still being prepared. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
#: documents/views.py:4923
|
||||
#: documents/views.py:4959
|
||||
msgid "The share link bundle is unavailable."
|
||||
msgstr ""
|
||||
|
||||
@@ -2245,151 +2241,151 @@ msgstr ""
|
||||
msgid "paperless application settings"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:566
|
||||
#: paperless/settings/__init__.py:562
|
||||
msgid "English (US)"
|
||||
msgstr "Англійская (ЗША)"
|
||||
|
||||
#: paperless/settings/__init__.py:567
|
||||
#: paperless/settings/__init__.py:563
|
||||
msgid "Arabic"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:568
|
||||
#: paperless/settings/__init__.py:564
|
||||
msgid "Afrikaans"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:569
|
||||
#: paperless/settings/__init__.py:565
|
||||
msgid "Belarusian"
|
||||
msgstr "Беларуская"
|
||||
|
||||
#: paperless/settings/__init__.py:570
|
||||
#: paperless/settings/__init__.py:566
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:571
|
||||
#: paperless/settings/__init__.py:567
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:572
|
||||
#: paperless/settings/__init__.py:568
|
||||
msgid "Czech"
|
||||
msgstr "Чэшская"
|
||||
|
||||
#: paperless/settings/__init__.py:573
|
||||
#: paperless/settings/__init__.py:569
|
||||
msgid "Danish"
|
||||
msgstr "Дацкая"
|
||||
|
||||
#: paperless/settings/__init__.py:574
|
||||
#: paperless/settings/__init__.py:570
|
||||
msgid "German"
|
||||
msgstr "Нямецкая"
|
||||
|
||||
#: paperless/settings/__init__.py:575
|
||||
#: paperless/settings/__init__.py:571
|
||||
msgid "Greek"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:576
|
||||
#: paperless/settings/__init__.py:572
|
||||
msgid "English (GB)"
|
||||
msgstr "Англійская (Вялікабрытанія)"
|
||||
|
||||
#: paperless/settings/__init__.py:577
|
||||
#: paperless/settings/__init__.py:573
|
||||
msgid "Spanish"
|
||||
msgstr "Іспанская"
|
||||
|
||||
#: paperless/settings/__init__.py:578
|
||||
#: paperless/settings/__init__.py:574
|
||||
msgid "Persian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:579
|
||||
#: paperless/settings/__init__.py:575
|
||||
msgid "Finnish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:580
|
||||
#: paperless/settings/__init__.py:576
|
||||
msgid "French"
|
||||
msgstr "Французская"
|
||||
|
||||
#: paperless/settings/__init__.py:581
|
||||
#: paperless/settings/__init__.py:577
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:582
|
||||
#: paperless/settings/__init__.py:578
|
||||
msgid "Indonesian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:583
|
||||
#: paperless/settings/__init__.py:579
|
||||
msgid "Italian"
|
||||
msgstr "Італьянская"
|
||||
|
||||
#: paperless/settings/__init__.py:584
|
||||
#: paperless/settings/__init__.py:580
|
||||
msgid "Japanese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:585
|
||||
#: paperless/settings/__init__.py:581
|
||||
msgid "Korean"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:586
|
||||
#: paperless/settings/__init__.py:582
|
||||
msgid "Luxembourgish"
|
||||
msgstr "Люксембургская"
|
||||
|
||||
#: paperless/settings/__init__.py:587
|
||||
#: paperless/settings/__init__.py:583
|
||||
msgid "Norwegian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:588
|
||||
#: paperless/settings/__init__.py:584
|
||||
msgid "Dutch"
|
||||
msgstr "Нідэрландская"
|
||||
|
||||
#: paperless/settings/__init__.py:589
|
||||
#: paperless/settings/__init__.py:585
|
||||
msgid "Polish"
|
||||
msgstr "Польская"
|
||||
|
||||
#: paperless/settings/__init__.py:590
|
||||
#: paperless/settings/__init__.py:586
|
||||
msgid "Portuguese (Brazil)"
|
||||
msgstr "Партугальская (Бразілія)"
|
||||
|
||||
#: paperless/settings/__init__.py:591
|
||||
#: paperless/settings/__init__.py:587
|
||||
msgid "Portuguese"
|
||||
msgstr "Партугальская"
|
||||
|
||||
#: paperless/settings/__init__.py:592
|
||||
#: paperless/settings/__init__.py:588
|
||||
msgid "Romanian"
|
||||
msgstr "Румынская"
|
||||
|
||||
#: paperless/settings/__init__.py:593
|
||||
#: paperless/settings/__init__.py:589
|
||||
msgid "Russian"
|
||||
msgstr "Руская"
|
||||
|
||||
#: paperless/settings/__init__.py:594
|
||||
#: paperless/settings/__init__.py:590
|
||||
msgid "Slovak"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:595
|
||||
#: paperless/settings/__init__.py:591
|
||||
msgid "Slovenian"
|
||||
msgstr "Славенская"
|
||||
|
||||
#: paperless/settings/__init__.py:596
|
||||
#: paperless/settings/__init__.py:592
|
||||
msgid "Serbian"
|
||||
msgstr "Сербская"
|
||||
|
||||
#: paperless/settings/__init__.py:597
|
||||
#: paperless/settings/__init__.py:593
|
||||
msgid "Swedish"
|
||||
msgstr "Шведская"
|
||||
|
||||
#: paperless/settings/__init__.py:598
|
||||
#: paperless/settings/__init__.py:594
|
||||
msgid "Turkish"
|
||||
msgstr "Турэцкая"
|
||||
|
||||
#: paperless/settings/__init__.py:599
|
||||
#: paperless/settings/__init__.py:595
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:600
|
||||
#: paperless/settings/__init__.py:596
|
||||
msgid "Vietnamese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings/__init__.py:601
|
||||
#: paperless/settings/__init__.py:597
|
||||
msgid "Chinese Simplified"
|
||||
msgstr "Кітайская спрошчаная"
|
||||
|
||||
#: paperless/settings/__init__.py:602
|
||||
#: paperless/settings/__init__.py:598
|
||||
msgid "Chinese Traditional"
|
||||
msgstr ""
|
||||
|
||||
|
||||
Loaded 100 of 166 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user