mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-28 18:24:38 -05:00
Merge remote-tracking branch 'upstream/dev' into feature-consume-eml
This commit is contained in:
@@ -76,7 +76,7 @@
|
||||
</g>
|
||||
</svg>
|
||||
<h6 class="m-auto">{% translate "Paperless-ngx is loading..." %}</h6>
|
||||
<p class="warning m-auto mt-3 small fade hide">{% translate "Still here?! Hmm, something might be wrong." %} <a href="https://paperless-ngx.readthedocs.io/en/latest/">{% translate "Here's a link to the docs." %}</a></p>
|
||||
<p class="warning m-auto mt-3 small fade hide">{% translate "Still here?! Hmm, something might be wrong." %} <a href="https://docs.paperless-ngx.com">{% translate "Here's a link to the docs." %}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</app-root>
|
||||
|
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: paperless-ngx\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-09 21:50+0000\n"
|
||||
"PO-Revision-Date: 2022-11-09 23:11\n"
|
||||
"PO-Revision-Date: 2022-11-29 08:29\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Norwegian\n"
|
||||
"Language: no_NO\n"
|
||||
@@ -184,11 +184,11 @@ msgstr "Gjeldende arkiv filnavn i lagring"
|
||||
|
||||
#: documents/models.py:221
|
||||
msgid "original filename"
|
||||
msgstr ""
|
||||
msgstr "opprinnelig filnavn"
|
||||
|
||||
#: documents/models.py:227
|
||||
msgid "The original name of the file when it was uploaded"
|
||||
msgstr ""
|
||||
msgstr "Det opprinnelige filnavnet da den ble lastet opp"
|
||||
|
||||
#: documents/models.py:231
|
||||
msgid "archive serial number"
|
||||
@@ -368,15 +368,15 @@ msgstr "har tags i"
|
||||
|
||||
#: documents/models.py:410
|
||||
msgid "ASN greater than"
|
||||
msgstr ""
|
||||
msgstr "ASN større enn"
|
||||
|
||||
#: documents/models.py:411
|
||||
msgid "ASN less than"
|
||||
msgstr ""
|
||||
msgstr "ASN mindre enn"
|
||||
|
||||
#: documents/models.py:412
|
||||
msgid "storage path is"
|
||||
msgstr ""
|
||||
msgstr "lagringssti er"
|
||||
|
||||
#: documents/models.py:422
|
||||
msgid "rule type"
|
||||
@@ -396,31 +396,31 @@ msgstr "filtrer regler"
|
||||
|
||||
#: documents/models.py:536
|
||||
msgid "Task ID"
|
||||
msgstr ""
|
||||
msgstr "Oppgave ID"
|
||||
|
||||
#: documents/models.py:537
|
||||
msgid "Celery ID for the Task that was run"
|
||||
msgstr ""
|
||||
msgstr "Celery ID for oppgaven som ble kjørt"
|
||||
|
||||
#: documents/models.py:542
|
||||
msgid "Acknowledged"
|
||||
msgstr ""
|
||||
msgstr "Bekreftet"
|
||||
|
||||
#: documents/models.py:543
|
||||
msgid "If the task is acknowledged via the frontend or API"
|
||||
msgstr ""
|
||||
msgstr "Hvis oppgaven bekreftes via frontend eller API"
|
||||
|
||||
#: documents/models.py:549 documents/models.py:556
|
||||
msgid "Task Name"
|
||||
msgstr ""
|
||||
msgstr "Oppgavenavn"
|
||||
|
||||
#: documents/models.py:550
|
||||
msgid "Name of the file which the Task was run for"
|
||||
msgstr ""
|
||||
msgstr "Navn på filen som oppgaven ble kjørt for"
|
||||
|
||||
#: documents/models.py:557
|
||||
msgid "Name of the Task which was run"
|
||||
msgstr ""
|
||||
msgstr "Navn på Oppgaven som ble kjørt"
|
||||
|
||||
#: documents/models.py:562
|
||||
msgid "Task Positional Arguments"
|
||||
|
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: paperless-ngx\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-09 21:50+0000\n"
|
||||
"PO-Revision-Date: 2022-11-09 23:11\n"
|
||||
"PO-Revision-Date: 2022-11-28 16:30\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Polish\n"
|
||||
"Language: pl_PL\n"
|
||||
@@ -184,11 +184,11 @@ msgstr "Aktualna nazwa pliku archiwum w pamięci"
|
||||
|
||||
#: documents/models.py:221
|
||||
msgid "original filename"
|
||||
msgstr ""
|
||||
msgstr "oryginalna nazwa pliku"
|
||||
|
||||
#: documents/models.py:227
|
||||
msgid "The original name of the file when it was uploaded"
|
||||
msgstr ""
|
||||
msgstr "Oryginalna nazwa pliku, gdy został przesłany"
|
||||
|
||||
#: documents/models.py:231
|
||||
msgid "archive serial number"
|
||||
@@ -368,15 +368,15 @@ msgstr "ma znaczniki w"
|
||||
|
||||
#: documents/models.py:410
|
||||
msgid "ASN greater than"
|
||||
msgstr ""
|
||||
msgstr "ASN większy niż"
|
||||
|
||||
#: documents/models.py:411
|
||||
msgid "ASN less than"
|
||||
msgstr ""
|
||||
msgstr "ASN mniejszy niż"
|
||||
|
||||
#: documents/models.py:412
|
||||
msgid "storage path is"
|
||||
msgstr ""
|
||||
msgstr "ścieżką przechowywania jest"
|
||||
|
||||
#: documents/models.py:422
|
||||
msgid "rule type"
|
||||
@@ -396,23 +396,23 @@ msgstr "reguły filtrowania"
|
||||
|
||||
#: documents/models.py:536
|
||||
msgid "Task ID"
|
||||
msgstr ""
|
||||
msgstr "ID zadania"
|
||||
|
||||
#: documents/models.py:537
|
||||
msgid "Celery ID for the Task that was run"
|
||||
msgstr ""
|
||||
msgstr "ID Celery dla zadania, które zostało uruchomione"
|
||||
|
||||
#: documents/models.py:542
|
||||
msgid "Acknowledged"
|
||||
msgstr ""
|
||||
msgstr "Potwierdzono"
|
||||
|
||||
#: documents/models.py:543
|
||||
msgid "If the task is acknowledged via the frontend or API"
|
||||
msgstr ""
|
||||
msgstr "Jeśli zadanie jest potwierdzone przez frontend lub API"
|
||||
|
||||
#: documents/models.py:549 documents/models.py:556
|
||||
msgid "Task Name"
|
||||
msgstr ""
|
||||
msgstr "Nazwa zadania"
|
||||
|
||||
#: documents/models.py:550
|
||||
msgid "Name of the file which the Task was run for"
|
||||
@@ -440,7 +440,7 @@ msgstr ""
|
||||
|
||||
#: documents/models.py:578
|
||||
msgid "Task State"
|
||||
msgstr ""
|
||||
msgstr "Stan zadania"
|
||||
|
||||
#: documents/models.py:579
|
||||
msgid "Current state of the task being run"
|
||||
@@ -476,19 +476,19 @@ msgstr ""
|
||||
|
||||
#: documents/models.py:604
|
||||
msgid "The data returned by the task"
|
||||
msgstr ""
|
||||
msgstr "Dane zwrócone przez zadanie"
|
||||
|
||||
#: documents/models.py:613
|
||||
msgid "Comment for the document"
|
||||
msgstr ""
|
||||
msgstr "Komentarz do dokumentu"
|
||||
|
||||
#: documents/models.py:642
|
||||
msgid "comment"
|
||||
msgstr ""
|
||||
msgstr "komentarz"
|
||||
|
||||
#: documents/models.py:643
|
||||
msgid "comments"
|
||||
msgstr ""
|
||||
msgstr "komentarze"
|
||||
|
||||
#: documents/serialisers.py:72
|
||||
#, python-format
|
||||
|
@@ -8,6 +8,7 @@ import tempfile
|
||||
from typing import Final
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
from typing import Tuple
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from celery.schedules import crontab
|
||||
@@ -65,6 +66,44 @@ def __get_path(key: str, default: str) -> str:
|
||||
return os.path.abspath(os.path.normpath(os.environ.get(key, default)))
|
||||
|
||||
|
||||
def _parse_redis_url(env_redis: Optional[str]) -> Tuple[str]:
|
||||
"""
|
||||
Gets the Redis information from the environment or a default and handles
|
||||
converting from incompatible django_channels and celery formats.
|
||||
|
||||
Returns a tuple of (celery_url, channels_url)
|
||||
"""
|
||||
|
||||
# Not set, return a compatible default
|
||||
if env_redis is None:
|
||||
return ("redis://localhost:6379", "redis://localhost:6379")
|
||||
|
||||
if "unix" in env_redis.lower():
|
||||
# channels_redis socket format, looks like:
|
||||
# "unix:///path/to/redis.sock"
|
||||
_, path = env_redis.split(":")
|
||||
# Optionally setting a db number
|
||||
if "?db=" in env_redis:
|
||||
path, number = path.split("?db=")
|
||||
return (f"redis+socket:{path}?virtual_host={number}", env_redis)
|
||||
else:
|
||||
return (f"redis+socket:{path}", env_redis)
|
||||
|
||||
elif "+socket" in env_redis.lower():
|
||||
# celery socket style, looks like:
|
||||
# "redis+socket:///path/to/redis.sock"
|
||||
_, path = env_redis.split(":")
|
||||
if "?virtual_host=" in env_redis:
|
||||
# Virtual host (aka db number)
|
||||
path, number = path.split("?virtual_host=")
|
||||
return (env_redis, f"unix:{path}?db={number}")
|
||||
else:
|
||||
return (env_redis, f"unix:{path}")
|
||||
|
||||
# Not a socket
|
||||
return (env_redis, env_redis)
|
||||
|
||||
|
||||
# NEVER RUN WITH DEBUG IN PRODUCTION.
|
||||
DEBUG = __get_boolean("PAPERLESS_DEBUG", "NO")
|
||||
|
||||
@@ -182,7 +221,9 @@ ASGI_APPLICATION = "paperless.asgi.application"
|
||||
STATIC_URL = os.getenv("PAPERLESS_STATIC_URL", BASE_URL + "static/")
|
||||
WHITENOISE_STATIC_PREFIX = "/static/"
|
||||
|
||||
_REDIS_URL = os.getenv("PAPERLESS_REDIS", "redis://localhost:6379")
|
||||
_CELERY_REDIS_URL, _CHANNELS_REDIS_URL = _parse_redis_url(
|
||||
os.getenv("PAPERLESS_REDIS", None),
|
||||
)
|
||||
|
||||
# TODO: what is this used for?
|
||||
TEMPLATES = [
|
||||
@@ -205,7 +246,7 @@ CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "channels_redis.core.RedisChannelLayer",
|
||||
"CONFIG": {
|
||||
"hosts": [_REDIS_URL],
|
||||
"hosts": [_CHANNELS_REDIS_URL],
|
||||
"capacity": 2000, # default 100
|
||||
"expiry": 15, # default 60
|
||||
},
|
||||
@@ -468,7 +509,7 @@ TASK_WORKERS = __get_int("PAPERLESS_TASK_WORKERS", 1)
|
||||
|
||||
WORKER_TIMEOUT: Final[int] = __get_int("PAPERLESS_WORKER_TIMEOUT", 1800)
|
||||
|
||||
CELERY_BROKER_URL = _REDIS_URL
|
||||
CELERY_BROKER_URL = _CELERY_REDIS_URL
|
||||
CELERY_TIMEZONE = TIME_ZONE
|
||||
|
||||
CELERY_WORKER_HIJACK_ROOT_LOGGER = False
|
||||
@@ -513,7 +554,7 @@ CELERY_BEAT_SCHEDULE_FILENAME = os.path.join(DATA_DIR, "celerybeat-schedule.db")
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.redis.RedisCache",
|
||||
"LOCATION": _REDIS_URL,
|
||||
"LOCATION": _CHANNELS_REDIS_URL,
|
||||
},
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,7 @@ from unittest import mock
|
||||
from unittest import TestCase
|
||||
|
||||
from paperless.settings import _parse_ignore_dates
|
||||
from paperless.settings import _parse_redis_url
|
||||
from paperless.settings import default_threads_per_worker
|
||||
|
||||
|
||||
@@ -82,3 +83,59 @@ class TestIgnoreDateParsing(TestCase):
|
||||
self.assertGreaterEqual(default_threads, 1)
|
||||
|
||||
self.assertLessEqual(default_workers * default_threads, i)
|
||||
|
||||
def test_redis_socket_parsing(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- Various Redis connection URI formats
|
||||
WHEN:
|
||||
- The URI is parsed
|
||||
THEN:
|
||||
- Socket based URIs are translated
|
||||
- Non-socket URIs are unchanged
|
||||
- None provided uses default
|
||||
"""
|
||||
|
||||
for input, expected in [
|
||||
# Nothing is set
|
||||
(None, ("redis://localhost:6379", "redis://localhost:6379")),
|
||||
# celery style
|
||||
(
|
||||
"redis+socket:///run/redis/redis.sock",
|
||||
(
|
||||
"redis+socket:///run/redis/redis.sock",
|
||||
"unix:///run/redis/redis.sock",
|
||||
),
|
||||
),
|
||||
# redis-py / channels-redis style
|
||||
(
|
||||
"unix:///run/redis/redis.sock",
|
||||
(
|
||||
"redis+socket:///run/redis/redis.sock",
|
||||
"unix:///run/redis/redis.sock",
|
||||
),
|
||||
),
|
||||
# celery style with db
|
||||
(
|
||||
"redis+socket:///run/redis/redis.sock?virtual_host=5",
|
||||
(
|
||||
"redis+socket:///run/redis/redis.sock?virtual_host=5",
|
||||
"unix:///run/redis/redis.sock?db=5",
|
||||
),
|
||||
),
|
||||
# redis-py / channels-redis style with db
|
||||
(
|
||||
"unix:///run/redis/redis.sock?db=10",
|
||||
(
|
||||
"redis+socket:///run/redis/redis.sock?virtual_host=10",
|
||||
"unix:///run/redis/redis.sock?db=10",
|
||||
),
|
||||
),
|
||||
# Just a host with a port
|
||||
(
|
||||
"redis://myredishost:6379",
|
||||
("redis://myredishost:6379", "redis://myredishost:6379"),
|
||||
),
|
||||
]:
|
||||
result = _parse_redis_url(input)
|
||||
self.assertTupleEqual(expected, result)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
from typing import Final
|
||||
from typing import Tuple
|
||||
|
||||
__version__: Final[Tuple[int, int, int]] = (1, 10, 0)
|
||||
__version__: Final[Tuple[int, int, int]] = (1, 10, 2)
|
||||
# Version string like X.Y.Z
|
||||
__full_version_str__: Final[str] = ".".join(map(str, __version__))
|
||||
# Version string like X.Y
|
||||
|
@@ -16,8 +16,7 @@ def get_tesseract_langs():
|
||||
# Decode bytes to string, split on newlines, trim out the header
|
||||
proc_lines = proc.stdout.decode("utf8", errors="ignore").strip().split("\n")[1:]
|
||||
|
||||
# Replace _ with - to convert two part languages to the expected code
|
||||
return [x.replace("_", "-") for x in proc_lines]
|
||||
return [x.strip() for x in proc_lines]
|
||||
|
||||
|
||||
@register()
|
||||
|
@@ -27,3 +27,40 @@ class TestChecks(TestCase):
|
||||
msgs = check_default_language_available(None)
|
||||
self.assertEqual(len(msgs), 1)
|
||||
self.assertEqual(msgs[0].level, ERROR)
|
||||
|
||||
@override_settings(OCR_LANGUAGE="chi_sim")
|
||||
@mock.patch("paperless_tesseract.checks.get_tesseract_langs")
|
||||
def test_multi_part_language(self, m):
|
||||
"""
|
||||
GIVEN:
|
||||
- An OCR language which is multi part (ie chi-sim)
|
||||
- The language is correctly formatted
|
||||
WHEN:
|
||||
- Installed packages are checked
|
||||
THEN:
|
||||
- No errors are reported
|
||||
"""
|
||||
m.return_value = ["chi_sim", "eng"]
|
||||
|
||||
msgs = check_default_language_available(None)
|
||||
|
||||
self.assertEqual(len(msgs), 0)
|
||||
|
||||
@override_settings(OCR_LANGUAGE="chi-sim")
|
||||
@mock.patch("paperless_tesseract.checks.get_tesseract_langs")
|
||||
def test_multi_part_language_bad_format(self, m):
|
||||
"""
|
||||
GIVEN:
|
||||
- An OCR language which is multi part (ie chi-sim)
|
||||
- The language is correctly NOT formatted
|
||||
WHEN:
|
||||
- Installed packages are checked
|
||||
THEN:
|
||||
- No errors are reported
|
||||
"""
|
||||
m.return_value = ["chi_sim", "eng"]
|
||||
|
||||
msgs = check_default_language_available(None)
|
||||
|
||||
self.assertEqual(len(msgs), 1)
|
||||
self.assertEqual(msgs[0].level, ERROR)
|
||||
|
Reference in New Issue
Block a user