diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5eaca123a..85db2d482 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,7 @@ name: ci on: push: + tags: ng-* branches-ignore: - 'translations**' pull_request: @@ -19,7 +20,7 @@ jobs: name: Set up Python uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: 3.7 - name: Get pip cache dir id: pip-cache @@ -34,8 +35,6 @@ jobs: - name: Install dependencies run: | - sudo apt-get update -qq - sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev pip install --upgrade pipenv pipenv install --system --dev --ignore-pipfile - @@ -50,6 +49,39 @@ jobs: name: documentation path: docs/_build/html/ + codestyle: + runs-on: ubuntu-20.04 + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - + name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + - + name: Persistent Github pip cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip${{ matrix.python-version }} + - + name: Install dependencies + run: | + pip install --upgrade pipenv + pipenv install --system --dev --ignore-pipfile + - + name: Codestyle + run: | + cd src/ + pycodestyle + tests: runs-on: ubuntu-20.04 strategy: @@ -77,10 +109,10 @@ jobs: path: ${{ steps.pip-cache.outputs.dir }} key: ${{ runner.os }}-pip${{ matrix.python-version }} - - name: Prepare tests + name: Install dependencies run: | sudo apt-get update -qq - sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev unpaper tesseract-ocr imagemagick ghostscript optipng + sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript optipng pip install --upgrade pipenv pipenv install --system --dev --ignore-pipfile - @@ -88,11 +120,6 @@ jobs: run: | cd src/ pytest - - - name: Codestyle - run: | - cd src/ - pycodestyle - name: Publish coverage results if: matrix.python-version == '3.8' @@ -113,6 +140,13 @@ jobs: uses: actions/setup-node@v2 with: node-version: '15' + - + name: Configure version on dev branches + if: startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev' + run: | + git_hash=$(git rev-parse --short "$GITHUB_SHA") + git_branch=${GITHUB_REF#refs/heads/} + sed -i -E "s/version: \"(.*)\"/version: \"${git_branch} ${git_hash}\"/g" src-ui/src/environments/environment.prod.ts - name: Build frontend run: ./compile-frontend.sh @@ -124,7 +158,7 @@ jobs: path: src/documents/static/frontend/ build-release: - needs: [frontend, documentation, tests] + needs: [frontend, documentation, tests, codestyle] runs-on: ubuntu-20.04 steps: - @@ -139,7 +173,7 @@ jobs: name: Install dependencies run: | sudo apt-get update -qq - sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev gettext liblept5 + sudo apt-get install -qq --no-install-recommends gettext liblept5 pip3 install -r requirements.txt - name: Download frontend artifact @@ -234,7 +268,7 @@ jobs: build-docker-image: if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/ng-')) runs-on: ubuntu-latest - needs: [frontend, tests] + needs: [frontend, tests, codestyle] steps: - name: Prepare diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a8fb1f8e9..0bbd6c8ea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,11 +9,11 @@ If you want to implement something big: Please start a discussion about that in ## Python -Use python 3.6 for development. Paperless supports python 3.6, 3.7 and 3.8. +Paperless supports python 3.6, 3.7, 3.8 and 3.9. ## Branches -master always reflects the latest release. +master always reflects the latest release. Apart from changes to the documentation or readme, absolutely no functional changes on this branch in between releases. dev contains all changes that will be part of the next release. Use this branch to start making your changes. diff --git a/Dockerfile b/Dockerfile index 6247ef009..c94b7030f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,10 +10,6 @@ RUN ./configure && make FROM python:3.7-slim -WORKDIR /usr/src/paperless/ - -COPY requirements.txt ./ - # Binary dependencies RUN apt-get update \ && apt-get -y --no-install-recommends install \ @@ -49,59 +45,61 @@ RUN apt-get update \ tesseract-ocr-spa \ unpaper \ zlib1g \ - && rm -rf /var/lib/apt/lists/* # This pulls in updated dependencies from bullseye to fix some issues with file type detection. # TODO: Remove this once bullseye releases. -RUN echo "deb http://deb.debian.org/debian bullseye main" > /etc/apt/sources.list.d/bullseye.list \ + && echo "deb http://deb.debian.org/debian bullseye main" > /etc/apt/sources.list.d/bullseye.list \ && apt-get update \ && apt-get install --no-install-recommends -y file libmagic-dev \ && rm -rf /var/lib/apt/lists/* \ && rm /etc/apt/sources.list.d/bullseye.list -# Python dependencies -RUN apt-get update \ - && apt-get -y --no-install-recommends install \ - build-essential \ - libpoppler-cpp-dev \ - libpq-dev \ - libqpdf-dev \ - && python3 -m pip install --upgrade --no-cache-dir supervisor \ - && python3 -m pip install --no-cache-dir -r requirements.txt \ - && apt-get -y purge build-essential libqpdf-dev \ - && apt-get -y autoremove --purge \ - && rm -rf /var/lib/apt/lists/* \ - && mkdir /var/log/supervisord /var/run/supervisord - - -# copy scripts -# this fixes issues with imagemagick and PDF -COPY docker/imagemagick-policy.xml /etc/ImageMagick-6/policy.xml - -COPY gunicorn.conf.py ./ -COPY docker/supervisord.conf /etc/supervisord.conf -COPY docker/docker-entrypoint.sh /sbin/docker-entrypoint.sh - # copy jbig2enc COPY --from=jbig2enc /usr/src/jbig2enc/src/.libs/libjbig2enc* /usr/local/lib/ COPY --from=jbig2enc /usr/src/jbig2enc/src/jbig2 /usr/local/bin/ COPY --from=jbig2enc /usr/src/jbig2enc/src/*.h /usr/local/include/ +WORKDIR /usr/src/paperless/src/ + +COPY requirements.txt ../ + +# Python dependencies +RUN apt-get update \ + && apt-get -y --no-install-recommends install \ + build-essential \ + libpq-dev \ + libqpdf-dev \ + && python3 -m pip install --upgrade --no-cache-dir supervisor \ + && python3 -m pip install --no-cache-dir -r ../requirements.txt \ + && apt-get -y purge build-essential libqpdf-dev \ + && apt-get -y autoremove --purge \ + && rm -rf /var/lib/apt/lists/* + +# setup docker-specific things +COPY docker/ ./docker/ + +RUN cd docker \ + && cp imagemagick-policy.xml /etc/ImageMagick-6/policy.xml \ + && mkdir /var/log/supervisord /var/run/supervisord \ + && cp supervisord.conf /etc/supervisord.conf \ + && cp docker-entrypoint.sh /sbin/docker-entrypoint.sh \ + && chmod 755 /sbin/docker-entrypoint.sh \ + && chmod +x install_management_commands.sh \ + && ./install_management_commands.sh \ + && cd .. \ + && rm docker -rf + +COPY gunicorn.conf.py ../ # copy app -COPY src/ ./src/ +COPY src/ ./ # add users, setup scripts RUN addgroup --gid 1000 paperless \ && useradd --uid 1000 --gid paperless --home-dir /usr/src/paperless paperless \ - && chown -R paperless:paperless . \ - && chmod 755 /sbin/docker-entrypoint.sh - -WORKDIR /usr/src/paperless/src/ - -RUN sudo -HEu paperless python3 manage.py collectstatic --clear --no-input - -RUN sudo -HEu paperless python3 manage.py compilemessages + && chown -R paperless:paperless ../ \ + && sudo -HEu paperless python3 manage.py collectstatic --clear --no-input \ + && sudo -HEu paperless python3 manage.py compilemessages VOLUME ["/usr/src/paperless/data", "/usr/src/paperless/media", "/usr/src/paperless/consume", "/usr/src/paperless/export"] ENTRYPOINT ["/sbin/docker-entrypoint.sh"] diff --git a/Pipfile b/Pipfile index cbe5c5103..29a91ece3 100644 --- a/Pipfile +++ b/Pipfile @@ -17,19 +17,19 @@ django-filter = "~=2.4.0" django-q = "~=1.3.4" djangorestframework = "~=3.12.2" filelock = "*" -fuzzywuzzy = "*" +fuzzywuzzy = {extras = ["speedup"], version = "*"} gunicorn = "*" imap-tools = "*" langdetect = "*" -pdftotext = "*" +# numpy 1.20.0 drops python 3.6 support +numpy = "~=1.19.5" pathvalidate = "*" # pinned to 8.1.0, since aarch64 wheels might not be available beyond that https://github.com/python-pillow/Pillow/issues/5202 pillow = "==8.1.0" -pikepdf = "~=2.2.5" +pikepdf = "~=2.5.0" python-gnupg = "*" python-dotenv = "*" python-dateutil = "*" -python-Levenshtein = "*" python-magic = "*" psycopg2-binary = "*" redis = "*" @@ -38,10 +38,10 @@ scikit-learn="==0.24.0" # Prevent scipy updates because 1.6 is incompatible with python 3.6 scipy="~=1.5.4" whitenoise = "~=5.2.0" -watchdog = "*" +watchdog = "~=1.0.0" whoosh="~=2.7.4" inotifyrecursive = "~=0.3.4" -ocrmypdf = "~=11.4.5" +ocrmypdf = "~=11.6" tqdm = "*" tika = "*" # TODO: This will sadly also install daphne+dependencies, @@ -49,6 +49,12 @@ tika = "*" channels = "~=3.0" channels-redis = "*" uvicorn = {extras = ["standard"], version = "*"} +concurrent-log-handler = "*" +# uvloop 0.15+ incompatible with python 3.6 +uvloop = "~=0.14.0" +# TODO: keep an eye on piwheel builds and update this once available (https://www.piwheels.org/project/cryptography/) +cryptography = "~=3.3.2" +"pdfminer.six" = "*" [dev-packages] coveralls = "*" diff --git a/Pipfile.lock b/Pipfile.lock index ff9e96717..eefaeeecd 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "d80d2539a4528a8fd9e848875c2e2d5bcdb3e98154f45b612706094b84ecaaea" + "sha256": "71959eb287fc97969263be5e3a1b1f4f369b7a5ace85bd1947a25b9b92e17e8a" }, "pipfile-spec": 6, "requires": {}, @@ -60,11 +60,11 @@ }, "autobahn": { "hashes": [ - "sha256:410a93e0e29882c8b5d5ab05d220b07609b886ef5f23c0b8d39153254ffd6895", - "sha256:52ee4236ff9a1fcbbd9500439dcf3284284b37f8a6b31ecc8a36e00cf9f95049" + "sha256:41a3a3f89cde48643baf4e105d9491c566295f9abee951379e59121784044b8b", + "sha256:7e6b1bf95196b733978bab2d54a7ab8899c16ce11be369dc58422c07b7eea726" ], "markers": "python_version >= '3.6'", - "version": "==20.12.3" + "version": "==21.2.1" }, "automat": { "hashes": [ @@ -90,46 +90,47 @@ }, "cffi": { "hashes": [ - "sha256:00a1ba5e2e95684448de9b89888ccd02c98d512064b4cb987d48f4b40aa0421e", - "sha256:00e28066507bfc3fe865a31f325c8391a1ac2916219340f87dfad602c3e48e5d", - "sha256:045d792900a75e8b1e1b0ab6787dd733a8190ffcf80e8c8ceb2fb10a29ff238a", - "sha256:0638c3ae1a0edfb77c6765d487fee624d2b1ee1bdfeffc1f0b58c64d149e7eec", - "sha256:105abaf8a6075dc96c1fe5ae7aae073f4696f2905fde6aeada4c9d2926752362", - "sha256:155136b51fd733fa94e1c2ea5211dcd4c8879869008fc811648f16541bf99668", - "sha256:1a465cbe98a7fd391d47dce4b8f7e5b921e6cd805ef421d04f5f66ba8f06086c", - "sha256:1d2c4994f515e5b485fd6d3a73d05526aa0fcf248eb135996b088d25dfa1865b", - "sha256:23f318bf74b170c6e9adb390e8bd282457f6de46c19d03b52f3fd042b5e19654", - "sha256:2c24d61263f511551f740d1a065eb0212db1dbbbbd241db758f5244281590c06", - "sha256:51a8b381b16ddd370178a65360ebe15fbc1c71cf6f584613a7ea08bfad946698", - "sha256:594234691ac0e9b770aee9fcdb8fa02c22e43e5c619456efd0d6c2bf276f3eb2", - "sha256:5cf4be6c304ad0b6602f5c4e90e2f59b47653ac1ed9c662ed379fe48a8f26b0c", - "sha256:64081b3f8f6f3c3de6191ec89d7dc6c86a8a43911f7ecb422c60e90c70be41c7", - "sha256:6bc25fc545a6b3d57b5f8618e59fc13d3a3a68431e8ca5fd4c13241cd70d0009", - "sha256:798caa2a2384b1cbe8a2a139d80734c9db54f9cc155c99d7cc92441a23871c03", - "sha256:7c6b1dece89874d9541fc974917b631406233ea0440d0bdfbb8e03bf39a49b3b", - "sha256:840793c68105fe031f34d6a086eaea153a0cd5c491cde82a74b420edd0a2b909", - "sha256:8d6603078baf4e11edc4168a514c5ce5b3ba6e3e9c374298cb88437957960a53", - "sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35", - "sha256:9f7a31251289b2ab6d4012f6e83e58bc3b96bd151f5b5262467f4bb6b34a7c26", - "sha256:9ffb888f19d54a4d4dfd4b3f29bc2c16aa4972f1c2ab9c4ab09b8ab8685b9c2b", - "sha256:a5ed8c05548b54b998b9498753fb9cadbfd92ee88e884641377d8a8b291bcc01", - "sha256:a7711edca4dcef1a75257b50a2fbfe92a65187c47dab5a0f1b9b332c5919a3fb", - "sha256:af5c59122a011049aad5dd87424b8e65a80e4a6477419c0c1015f73fb5ea0293", - "sha256:b18e0a9ef57d2b41f5c68beefa32317d286c3d6ac0484efd10d6e07491bb95dd", - "sha256:b4e248d1087abf9f4c10f3c398896c87ce82a9856494a7155823eb45a892395d", - "sha256:ba4e9e0ae13fc41c6b23299545e5ef73055213e466bd107953e4a013a5ddd7e3", - "sha256:be8661bcee1bc2fc4b033a6ab65bd1f87ce5008492601695d0b9a4e820c3bde5", - "sha256:c6332685306b6417a91b1ff9fae889b3ba65c2292d64bd9245c093b1b284809d", - "sha256:d5ff0621c88ce83a28a10d2ce719b2ee85635e85c515f12bac99a95306da4b2e", - "sha256:d9efd8b7a3ef378dd61a1e77367f1924375befc2eba06168b6ebfa903a5e59ca", - "sha256:df5169c4396adc04f9b0a05f13c074df878b6052430e03f50e68adf3a57aa28d", - "sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775", - "sha256:ec80dc47f54e6e9a78181ce05feb71a0353854cc26999db963695f950b5fb375", - "sha256:f032b34669220030f905152045dfa27741ce1a6db3324a5bc0b96b6c7420c87b", - "sha256:f60567825f791c6f8a592f3c6e3bd93dd2934e3f9dac189308426bd76b00ef3b", - "sha256:f803eaa94c2fcda012c047e62bc7a51b0bdabda1cad7a92a522694ea2d76e49f" + "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813", + "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06", + "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea", + "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee", + "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396", + "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73", + "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315", + "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1", + "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49", + "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892", + "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482", + "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058", + "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5", + "sha256:5560dbf8deedbffb638d8a2da31da91094db361cc07f8a501a339b2daae2cbcc", + "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53", + "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045", + "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3", + "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5", + "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e", + "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c", + "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369", + "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827", + "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053", + "sha256:9338beed13d880320450d95c9e07ccf839faa3ea7b75d788f4ed46d845044a71", + "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa", + "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4", + "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322", + "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132", + "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62", + "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa", + "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0", + "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396", + "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e", + "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991", + "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6", + "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1", + "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406", + "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d", + "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c" ], - "version": "==1.14.4" + "version": "==1.14.5" }, "channels": { "hashes": [ @@ -172,6 +173,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==15.0" }, + "concurrent-log-handler": { + "hashes": [ + "sha256:00d5ca24d463a7013c3479b026f34b76da4b50df8d76194132b8d8403c014379", + "sha256:b12f79abed3f94121c25ce9c24cdb57d889282ec6ff61f5535ab2068dc37d409" + ], + "index": "pypi", + "version": "==0.9.19" + }, "constantly": { "hashes": [ "sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35", @@ -181,24 +190,24 @@ }, "cryptography": { "hashes": [ - "sha256:0003a52a123602e1acee177dc90dd201f9bb1e73f24a070db7d36c588e8f5c7d", - "sha256:0e85aaae861d0485eb5a79d33226dd6248d2a9f133b81532c8f5aae37de10ff7", - "sha256:594a1db4511bc4d960571536abe21b4e5c3003e8750ab8365fafce71c5d86901", - "sha256:69e836c9e5ff4373ce6d3ab311c1a2eed274793083858d3cd4c7d12ce20d5f9c", - "sha256:788a3c9942df5e4371c199d10383f44a105d67d401fb4304178020142f020244", - "sha256:7e177e4bea2de937a584b13645cab32f25e3d96fc0bc4a4cf99c27dc77682be6", - "sha256:83d9d2dfec70364a74f4e7c70ad04d3ca2e6a08b703606993407bf46b97868c5", - "sha256:84ef7a0c10c24a7773163f917f1cb6b4444597efd505a8aed0a22e8c4780f27e", - "sha256:982f661bffc7a24b6d4f8ebe3291f17cf3833a0941c6f4d9d55c790b9aa2cdb3", - "sha256:9e21301f7a1e7c03dbea73e8602905a4ebba641547a462b26dd03451e5769e7c", - "sha256:9f6b0492d111b43de5f70052e24c1f0951cb9e6022188ebcb1cc3a3d301469b0", - "sha256:a69bd3c68b98298f490e84519b954335154917eaab52cf582fa2c5c7efc6e812", - "sha256:b4890d5fb9b7a23e3bf8abf5a8a7da8e228f1e97dc96b30b95685df840b6914a", - "sha256:c366df0401d1ec4e548bebe8f91d55ebcc0ec3137900d214dd7aac8427ef3030", - "sha256:dc42f645f8f3a489c3dd416730a514e7a91a59510ddaadc09d04224c098d3302" + "sha256:0d7b69674b738068fa6ffade5c962ecd14969690585aaca0a1b1fc9058938a72", + "sha256:1bd0ccb0a1ed775cd7e2144fe46df9dc03eefd722bbcf587b3e0616ea4a81eff", + "sha256:3c284fc1e504e88e51c428db9c9274f2da9f73fdf5d7e13a36b8ecb039af6e6c", + "sha256:49570438e60f19243e7e0d504527dd5fe9b4b967b5a1ff21cc12b57602dd85d3", + "sha256:541dd758ad49b45920dda3b5b48c968f8b2533d8981bcdb43002798d8f7a89ed", + "sha256:5a60d3780149e13b7a6ff7ad6526b38846354d11a15e21068e57073e29e19bed", + "sha256:7951a966613c4211b6612b0352f5bf29989955ee592c4a885d8c7d0f830d0433", + "sha256:922f9602d67c15ade470c11d616f2b2364950602e370c76f0c94c94ae672742e", + "sha256:a0f0b96c572fc9f25c3f4ddbf4688b9b38c69836713fb255f4a2715d93cbaf44", + "sha256:a777c096a49d80f9d2979695b835b0f9c9edab73b59e4ceb51f19724dda887ed", + "sha256:a9a4ac9648d39ce71c2f63fe7dc6db144b9fa567ddfc48b9fde1b54483d26042", + "sha256:aa4969f24d536ae2268c902b2c3d62ab464b5a66bcb247630d208a79a8098e9b", + "sha256:c7390f9b2119b2b43160abb34f63277a638504ef8df99f11cb52c1fda66a2e6f", + "sha256:ddd06e71c449a4fe10d0c60846280ee35d69ce49e3e413ce46d5f129e1468083", + "sha256:e18e6ab84dfb0ab997faf8cca25a86ff15dfea4027b986322026cc99e0a892da" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==3.3.1" + "index": "pypi", + "version": "==3.3.2" }, "daphne": { "hashes": [ @@ -218,11 +227,11 @@ }, "django": { "hashes": [ - "sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7", - "sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9" + "sha256:32ce792ee9b6a0cbbec340123e229ac9f765dff8c2a4ae9247a14b2ba3a365a7", + "sha256:baf099db36ad31f970775d0be5587cc58a6256a6771a44eb795b554d45f211b8" ], "index": "pypi", - "version": "==3.1.5" + "version": "==3.1.7" }, "django-cors-headers": { "hashes": [ @@ -234,11 +243,11 @@ }, "django-extensions": { "hashes": [ - "sha256:7cd002495ff0a0e5eb6cdd6be759600905b4e4079232ea27618fc46bdd853651", - "sha256:c7f88625a53f631745d4f2bef9ec4dcb999ed59476393bdbbe99db8596778846" + "sha256:674ad4c3b1587a884881824f40212d51829e662e52f85b012cd83d83fe1271d9", + "sha256:9507f8761ee760748938fd8af766d0608fb2738cf368adfa1b2451f61c15ae35" ], "index": "pypi", - "version": "==3.1.0" + "version": "==3.1.1" }, "django-filter": { "hashes": [ @@ -281,6 +290,9 @@ "version": "==3.0.12" }, "fuzzywuzzy": { + "extras": [ + "speedup" + ], "hashes": [ "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8", "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993" @@ -416,14 +428,6 @@ ], "version": "==0.4.0" }, - "importlib-metadata": { - "hashes": [ - "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771", - "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d" - ], - "markers": "python_version < '3.8'", - "version": "==3.4.0" - }, "incremental": { "hashes": [ "sha256:717e12246dddf231a349175f48d74d93e2897244939173b01974ab6661406b9f", @@ -449,11 +453,11 @@ }, "joblib": { "hashes": [ - "sha256:75ead23f13484a2a414874779d69ade40d4fa1abe62b222a23cd50d4bc822f6f", - "sha256:7ad866067ac1fdec27d51c8678ea760601b70e32ff1881d4dc8e1171f2b64b24" + "sha256:9c17567692206d2f3fb9ecf5e991084254fe631665c450b443761c4186a613f7", + "sha256:feeb1ec69c4d45129954f1b7034954241eedfd6ba39b5e9e4b6883be3332d5e5" ], "markers": "python_version >= '3.6'", - "version": "==1.0.0" + "version": "==1.0.1" }, "langdetect": { "hashes": [ @@ -582,16 +586,16 @@ "sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827", "sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60" ], - "markers": "python_version >= '3.6'", + "index": "pypi", "version": "==1.19.5" }, "ocrmypdf": { "hashes": [ - "sha256:416a9c4321bfc844f250694b8c68ebb538f60609bbc8686bd9f84a13c5127d68", - "sha256:f45fc7e844e6026d6080a623a2936be120fc077d99aaa599df022acf35fb31e6" + "sha256:0f624456a50be0b0bc8c0b59704d159f637616c093a1cabe8bb383706561bcf7", + "sha256:b829ad640a6160423162012e094ee2f7cd074ec99efadd7f7486954ec9182985" ], "index": "pypi", - "version": "==11.4.5" + "version": "==11.6.2" }, "pathvalidate": { "hashes": [ @@ -606,42 +610,37 @@ "sha256:b9aac0ebeafb21c08bf65f2039f4b2c5f78a3449d0a41df711d72445649e952a", "sha256:d78877ba8d8bf957f3bb636c4f73f4f6f30f56c461993877ac22c39c20837509" ], - "markers": "python_version >= '3.4'", - "version": "==20201018" - }, - "pdftotext": { - "hashes": [ - "sha256:98aeb8b07a4127e1a30223bd933ef080bbd29aa88f801717ca6c5618380b8aa6" - ], "index": "pypi", - "version": "==2.1.5" + "version": "==20201018" }, "pikepdf": { "hashes": [ - "sha256:0e67e5beeeed5422b3b8e862e4777fed5a4cd3c72e711e2a449a65d9ee641448", - "sha256:138155ae1f71634cd6eca79f5517f77b2067ef0bd5b627ea9414e308fe868dc5", - "sha256:15cf648dd760a47c55a4106b601b92bb653ae98155b10f04310553629c6695dd", - "sha256:1d6a011ae4c501c78509caf19cbe152c2e3cb5c267f7b47bc3db8cd3436585a7", - "sha256:211f529313953e44ae42eb896c2b688668385e6e8f9d04d21484bddb3c42b34c", - "sha256:22049ad288d603a7fc68e90a0722770d307886788373ddfe71fbf614ced0f5b2", - "sha256:24f7c371f6ecbee8f0ae30030992fc75cd32cd575dcfca8d466a03a8290377ca", - "sha256:26cdf561632866d584fedb6b1c1fce78cefa49b5cae54c65aa6a6ca5fe6de4ac", - "sha256:2c37afcd21a2eb1da1773687e853327fa8ec7d2c5cd90cdcd70180f55f0221e1", - "sha256:65b8ec6403814f51e1b9c7e18a8ff26087fcc7a199b1405583e5ff9eb931db56", - "sha256:66a03103aadb2e2738271cb18c89837ac3980fa0b4687195c4c150228b7e79de", - "sha256:6e8f0124354c53a66f83ec5a18111b760aeff1a64db3a86e7ee5fed8e8624707", - "sha256:70f2836cd468aa25bc8b09a2b9561364bd75d3e6ddb0e50a25d248d7da6cff25", - "sha256:82cebf68952cfb65c86d880eb782a0c558b37531cdae59f2e11fcd0f2bb4669c", - "sha256:84ad3e8fd5f3251fb5b534614da64b04a264ce9348f0fe35b781c0fb378b0f82", - "sha256:af13fbc022efa85d1ae161129d4cde66493479db52b9adb74d525b890a078208", - "sha256:c1d40fb8f8192c75f54f0e74a569ccf45e4e13bed8da78a78a5b488be29979bf", - "sha256:d147ec1ab58512871fdf40a161809f698eaa75720b4a230198e7e028582b20a1", - "sha256:dedad1f68d6b0b54000f7f99386351f1c6e19c8cf70a9700d8dd06b9809c54fb", - "sha256:e72c3f5b624b9c7341fd6a7e657926d4cf12a7ea453681ffd7332cabc3530c62", - "sha256:eb75f22e261b3bc69b6fc9a17b1d6966c95e79d3e792b7737a018a2bf6a2b07f" + "sha256:02815df9499d3a6dfac2e07e4d2fdbe25fcbefae208970e76bff90af4a402d49", + "sha256:06b0c3004cc9e9068ebc62bb59c3c3a54e7af13867f4a326690d79c69a1cf288", + "sha256:1a471c6ca288fbcd0e1b0e128ef12bb14c5e7db745786308ba292fc7cff30bb5", + "sha256:489ed0fd1281beb0343a34fe8b9d94407c440ed0419ab2e6f5ea297a41824a31", + "sha256:5106b27f7085ed449e057b9988f07c80a87292d2bf46c585a8635ac7a3ccf0d5", + "sha256:51acffba6f3d21674eea7a0432ce1adaf0743641d57844a5e3dc92b4a7e81c85", + "sha256:53d694d70dd072a47bd2dd71329dcef0f809dcd8084d1d11c31baf3b64cd345c", + "sha256:6a640fef52dc785abd354d6800a52ecc02656c98dbfc2ecde559323b001bd43f", + "sha256:7006ef95f847412605dea6e772019f637949eaeaf65363d5d6afd9aa96bf5623", + "sha256:81e13b62877dbc089095e7efa03c27834bdf6b49d404d064cb227b0e179ce049", + "sha256:8fc3e97b24dafbd4b1ac057c8f144c18467ce55d342a1a8c42688890ead58ef3", + "sha256:9158dc4d3ef4e2301fb1879d5825530fdb32143ced770d60fa8e5badeee70a35", + "sha256:961337a10b42bd656b59116ce1c574eafd515b45a513221df6ae1f11734bfb6c", + "sha256:994ccac972357a7b9b147217e1beea2f7688697944b862dbb2a3e64aa9a5ff14", + "sha256:a8e9abf7db0351357b55c3f935979e7dc14f3f259a25d15bcc86abce730955a7", + "sha256:b836eda7f70b9dd75ccdeaf4e78b38393118a66821a69a10054b1430f945d1fd", + "sha256:b859a225f6bd953472c50f4df612b4575df646e560189e3720310ad65b6805a0", + "sha256:cae106bd461cfad73c554c09f6db1d5f2c6a28f5b8cb0602b63046840f488226", + "sha256:cdc75b8fa5a650f4fc91214a315358fde7470e09b95a00981b73a7c4ac5ddb97", + "sha256:e2c28da4f37ad9a3efbedbbfc6f1084941bde43903d30dfdbb338d5ba94c9f50", + "sha256:e596bd8fdbf40bfb8dc8068cdbc7e5b72052188d1ad8ca84da9d6b77658a8b31", + "sha256:ee957b9c60b6def20cbdf656d35859ce211eec02dafa3abb9d5ca937d32a3c3b", + "sha256:f9428d4b1f70af4f4560be4dccbbc5ab5308c00c5b62ed2f1c44ce9e2591b3d2" ], "index": "pypi", - "version": "==2.2.5" + "version": "==2.5.2" }, "pillow": { "hashes": [ @@ -690,6 +689,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.13.1" }, + "portalocker": { + "hashes": [ + "sha256:afa66c85041d7bac1532acb74ff26cc2406472dcf7145c90c31ff8ca4f1cc146", + "sha256:e5f6ffb2f360e9aef615a7c284143d2a93bb640c62e8e45a703e6083fc5aa114" + ], + "markers": "python_version >= '3'", + "version": "==2.2.1" + }, "psycopg2-binary": { "hashes": [ "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c", @@ -819,27 +826,26 @@ }, "python-levenshtein": { "hashes": [ - "sha256:108edd3c271f1afda8b21a8d9da81886414dfb6940a085fa7903c592e0f8f54b", - "sha256:1ff19b712c5974080b003fd26ef365cd93dfc1a5e690be621f79f3e63e00a7cc", - "sha256:554e273a88060d177e7b3c1e6ea9158dde11563bfae8f7f661f73f47e5ff0911" + "sha256:212db61934fcb819f5cb2fcb5ad5c0e2e43f3161f524eef4a135f7285dfb308a", + "sha256:d92fe5c3b10c8ad8f2d880499f5e96ed24dbc7cd0414a665c2e2505feaf4ec58", + "sha256:dc2395fbd148a1ab31090dd113c366695934b9e85fe5a4b2a032745efd0346f6" ], - "index": "pypi", - "version": "==0.12.1" + "version": "==0.12.2" }, "python-magic": { "hashes": [ - "sha256:356efa93c8899047d1eb7d3eb91e871ba2f5b1376edbaf4cc305e3c872207355", - "sha256:b757db2a5289ea3f1ced9e60f072965243ea43a2221430048fd8cacab17be0ce" + "sha256:8551e804c09a3398790bd9e392acb26554ae2609f29c72abb0b9dee9a5571eae", + "sha256:ca884349f2c92ce830e3f498c5b7c7051fe2942c3ee4332f65213b8ebff15a62" ], "index": "pypi", - "version": "==0.4.18" + "version": "==0.4.22" }, "pytz": { "hashes": [ - "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4", - "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5" + "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", + "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" ], - "version": "==2020.5" + "version": "==2021.1" }, "pyyaml": { "hashes": [ @@ -1091,11 +1097,11 @@ }, "tqdm": { "hashes": [ - "sha256:4621f6823bab46a9cc33d48105753ccbea671b68bab2c50a9f0be23d4065cb5a", - "sha256:fe3d08dd00a526850568d542ff9de9bbc2a09a791da3c334f3213d8d0bbbca65" + "sha256:65185676e9fdf20d154cffd1c5de8e39ef9696ff7e59fe0156b1b08e468736af", + "sha256:70657337ec104eb4f3fb229285358f23f045433f6aea26846cdd55f0fd68945c" ], "index": "pypi", - "version": "==4.56.0" + "version": "==4.57.0" }, "twisted": { "extras": [ @@ -1133,20 +1139,11 @@ }, "txaio": { "hashes": [ - "sha256:1488d31d564a116538cc1265ac3f7979fb6223bb5a9e9f1479436ee2c17d8549", - "sha256:a8676d6c68aea1f0e2548c4afdb8e6253873af3bc2659bb5bcd9f39dff7ff90f" + "sha256:7d6f89745680233f1c4db9ddb748df5e88d2a7a37962be174c0fd04c8dba1dc8", + "sha256:c16b55f9a67b2419cfdf8846576e2ec9ba94fe6978a83080c352a80db31c93fb" ], "markers": "python_version >= '3.6'", - "version": "==20.12.1" - }, - "typing-extensions": { - "hashes": [ - "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", - "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", - "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" - ], - "markers": "python_version < '3.8'", - "version": "==3.7.4.3" + "version": "==21.2.1" }, "tzlocal": { "hashes": [ @@ -1168,11 +1165,11 @@ "standard" ], "hashes": [ - "sha256:1079c50a06f6338095b4f203e7861dbff318dde5f22f3a324fc6e94c7654164c", - "sha256:ef1e0bb5f7941c6fe324e06443ddac0331e1632a776175f87891c7bd02694355" + "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202", + "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524" ], "index": "pypi", - "version": "==0.13.3" + "version": "==0.13.4" }, "uvloop": { "hashes": [ @@ -1188,6 +1185,7 @@ "sha256:e7514d7a48c063226b7d06617cbb12a14278d4323a065a8d46a7962686ce2e95", "sha256:f07909cd9fc08c52d294b1570bba92186181ca01fe3dc9ffba68955273dd7362" ], + "index": "pypi", "version": "==0.14.0" }, "watchdog": { @@ -1215,11 +1213,10 @@ }, "watchgod": { "hashes": [ - "sha256:59700dab7445aa8e6067a5b94f37bae90fc367554549b1ed2e9d0f4f38a90d2a", - "sha256:5fb60afa9558b79736395db1cb60ad3ed59df5c2f507a3ff729220cf1251ffdc", - "sha256:e9cca0ab9c63f17fc85df9fd8bd18156ff00aff04ebe5976cee473f4968c6858" + "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29", + "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7" ], - "version": "==0.6" + "version": "==0.7" }, "wcwidth": { "hashes": [ @@ -1273,14 +1270,6 @@ "index": "pypi", "version": "==2.7.4" }, - "zipp": { - "hashes": [ - "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108", - "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb" - ], - "markers": "python_version >= '3.6'", - "version": "==3.4.0" - }, "zope.interface": { "hashes": [ "sha256:05a97ba92c1c7c26f25c9f671aa1ef85ffead6cdad13770e5b689cf983adc7e1", @@ -1501,11 +1490,11 @@ }, "faker": { "hashes": [ - "sha256:0783729c61501d52efea2967aff6e6fcb8370f0f6b5a558f2a81233642ae529a", - "sha256:6b2995ffff6c2b02bc5daad96f8c24c021e5bd491d9d53d31bcbd66f348181d4" + "sha256:31a58ec5a8f4672f24da3b5ddea02c82a712de1de3179b432948e5c34d787aca", + "sha256:aadfe0efe11ecbbbc5b3b0b0fab050c2acbd2d8e5201769546d43d236bfff663" ], "markers": "python_version >= '3.6'", - "version": "==5.8.0" + "version": "==6.4.1" }, "filelock": { "hashes": [ @@ -1532,22 +1521,6 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.2.0" }, - "importlib-metadata": { - "hashes": [ - "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771", - "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d" - ], - "markers": "python_version < '3.8'", - "version": "==3.4.0" - }, - "importlib-resources": { - "hashes": [ - "sha256:885b8eae589179f661c909d699a546cf10d83692553e34dca1bf5eb06f7f6217", - "sha256:bfdad047bce441405a49cf8eb48ddce5e56c696e185f59147a8b79e75e9e6380" - ], - "markers": "python_version < '3.7'", - "version": "==5.1.0" - }, "iniconfig": { "hashes": [ "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", @@ -1558,12 +1531,11 @@ }, "jinja2": { "hashes": [ - "sha256:3f172970d5670703bd3812e8ca6459a9a7e069fa8e51b40195f83c81db191ec4", - "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", - "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" + "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", + "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.11.2" + "version": "==2.11.3" }, "markupsafe": { "hashes": [ @@ -1573,8 +1545,12 @@ "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", "sha256:19536834abffb3fa155017053c607cb835b2ecc6a3a2554a88043d991dffb736", + "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", + "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", + "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", "sha256:3d61f15e39611aacd91b7e71d903787da86d9e80896e683c0103fced9add7834", "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", @@ -1584,36 +1560,51 @@ "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", + "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:7952deddf24b85c88dab48f6ec366ac6e39d2761b5280f2f9594911e03fcd064", "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", + "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", + "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", + "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", + "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", + "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.1.1" }, "packaging": { "hashes": [ - "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858", - "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093" + "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", + "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.8" + "version": "==20.9" }, "pluggy": { "hashes": [ @@ -1641,11 +1632,11 @@ }, "pygments": { "hashes": [ - "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435", - "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337" + "sha256:37a13ba168a02ac54cc5891a42b1caec333e59b66addb7fa633ea8a6d73445c0", + "sha256:b21b072d0ccdf29297a82a2363359d99623597b8a265b8081760e4d0f7153c88" ], "markers": "python_version >= '3.5'", - "version": "==2.7.4" + "version": "==2.8.0" }, "pyparsing": { "hashes": [ @@ -1706,11 +1697,11 @@ }, "pytest-xdist": { "hashes": [ - "sha256:1d8edbb1a45e8e1f8e44b1260583107fc23f8bc8da6d18cb331ff61d41258ecf", - "sha256:f127e11e84ad37cc1de1088cb2990f3c354630d428af3f71282de589c5bb779b" + "sha256:2447a1592ab41745955fb870ac7023026f20a5f0bfccf1b52a879bd193d46450", + "sha256:718887296892f92683f6a51f25a3ae584993b06f7076ce1e1fd482e59a8220a2" ], "index": "pypi", - "version": "==2.2.0" + "version": "==2.2.1" }, "python-dateutil": { "hashes": [ @@ -1722,10 +1713,10 @@ }, "pytz": { "hashes": [ - "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4", - "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5" + "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", + "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" ], - "version": "==2020.5" + "version": "==2021.1" }, "requests": { "hashes": [ @@ -1838,20 +1829,11 @@ }, "tox": { "hashes": [ - "sha256:76df3db6eee929bb62bdbacca5bb6bc840669d98e86a015b7a57b7df0a6eaf8b", - "sha256:854e6e4a71c614b488f81cb88df3b92edcb1a9ec43d4102e6289e9669bbf7f18" + "sha256:89afa9c59c04beb55eda789c7a65feb1a70fde117f85f1bd1c27c66758456e60", + "sha256:ed1e650cf6368bcbc4a071eeeba363c480920e0ed8a9ad1793c7caaa5ad33d49" ], "index": "pypi", - "version": "==3.21.3" - }, - "typing-extensions": { - "hashes": [ - "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", - "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", - "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" - ], - "markers": "python_version < '3.8'", - "version": "==3.7.4.3" + "version": "==3.22.0" }, "urllib3": { "hashes": [ @@ -1863,19 +1845,11 @@ }, "virtualenv": { "hashes": [ - "sha256:219ee956e38b08e32d5639289aaa5bd190cfbe7dafcb8fa65407fca08e808f9c", - "sha256:227a8fed626f2f20a6cdb0870054989f82dd27b2560a911935ba905a2a5e0034" + "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d", + "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.4.0" - }, - "zipp": { - "hashes": [ - "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108", - "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb" - ], - "markers": "python_version >= '3.6'", - "version": "==3.4.0" + "version": "==20.4.2" } } } diff --git a/README.md b/README.md index cb866e2ca..b21173ba5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![ci](https://github.com/jonaswinkler/paperless-ng/workflows/ci/badge.svg)](https://github.com/jonaswinkler/paperless-ng/actions) +![Ansible Role](https://github.com/jonaswinkler/paperless-ng/workflows/Ansible%20Role/badge.svg) [![Documentation Status](https://readthedocs.org/projects/paperless-ng/badge/?version=latest)](https://paperless-ng.readthedocs.io/en/latest/?badge=latest) [![Gitter](https://badges.gitter.im/paperless-ng/community.svg)](https://gitter.im/paperless-ng/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/jonaswinkler/paperless-ng.svg)](https://hub.docker.com/r/jonaswinkler/paperless-ng) @@ -6,15 +7,16 @@ # Paperless-ng -[Paperless](https://github.com/the-paperless-project/paperless) is an application by Daniel Quinn and contributors that indexes your scanned documents and allows you to easily search for documents and store metadata alongside your documents. +[Paperless (click me)](https://github.com/the-paperless-project/paperless) is an application by Daniel Quinn and contributors that indexes your scanned documents and allows you to easily search for documents and store metadata alongside your documents. -Paperless-ng is a fork of the original project, adding a new interface and many other changes under the hood. For a detailed list of changes, have a look at the [change log](https://paperless-ng.readthedocs.io/en/latest/changelog.html) in the documentation. +Paperless-ng is a fork of the original project, adding a new interface and many other changes under the hood. These key points should help you decide whether Paperless-ng is something you would prefer over Paperless: -# Survey +* Interface: The new front end is the main interface for paperless-ng, the old interface still exists but most customizations (such as thumbnails for the document list) have been removed. +* Encryption: Paperless-ng does not support GnuPG anymore, since storing your data on encrypted file systems (that you optionally mount on demand) achieves about the same result. +* Resource usage: Paperless-ng does use a bit more resources than Paperless. Running the web server requires about 300MB of RAM or more, depending on the configuration. While adding documents, it requires about 300MB additional RAM, depending on the document. It still runs on Pi (many users do that), but it has been generally geared to better use the resources of more powerful systems. +* API changes: If you rely on the REST API of paperless, some of its functionality has been changed. -If you already used Paperless-ng for a bit, would like to give some anonymous feedback, and help me decide on what to focus on next: I've created a survey, [see here](https://github.com/jonaswinkler/paperless-ng/issues/402). Thank you! - -*See also [Feature Requests](#feature-requests)* +For a detailed list of changes, have a look at the [change log](https://paperless-ng.readthedocs.io/en/latest/changelog.html) in the documentation. # How it Works @@ -37,8 +39,8 @@ Here's what you get: * Performs OCR on your documents, adds selectable text to image only documents and adds tags, correspondents and document types to your documents. * Supports PDF documents, images, plain text files, and Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents). * Office document support is optional and provided by Apache Tika (see [configuration](https://paperless-ng.readthedocs.io/en/latest/configuration.html#tika-settings)) -* Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and can be configured freely. -* Single page application front end. Should be pretty snappy. Will be mobile friendly in the future. +* Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely. +* Single page application front end. * Includes a dashboard that shows basic statistics and has document upload. * Filtering by tags, correspondents, types, and more. * Customizable views can be saved and displayed on the dashboard. @@ -49,14 +51,13 @@ Here's what you get: * Searching for similar documents ("More like this") * Email processing: Paperless adds documents from your email accounts. * Configure multiple accounts and filters for each account. - * When adding documents from mails, paperless can move these mails to a new folder, mark them as read, flag them or delete them. + * When adding documents from mail, paperless can move these mail to a new folder, mark them as read, flag them as important or delete them. * Machine learning powered document matching. * Paperless learns from your documents and will be able to automatically assign tags, correspondents and types to documents once you've stored a few documents in paperless. -* A task processor that processes documents in parallel and also tells you when something goes wrong. On modern multi core systems, consumption is blazing fast. +* Optimized for multi core systems: Paperless-ng consumes multiple documents in parallel. +* The integrated sanity checker makes sure that your document archive is in good health. -If you want to see some screenshots of paperless-ng in action, [some are available in the documentation](https://paperless-ng.readthedocs.io/en/latest/screenshots.html). However, some parts of the UI have changed since I took these. - -For a complete list of changes from paperless, check out the [changelog](https://paperless-ng.readthedocs.io/en/latest/changelog.html) +If you want to see some screenshots of paperless-ng in action, [some are available in the documentation](https://paperless-ng.readthedocs.io/en/latest/screenshots.html). # Getting started @@ -76,9 +77,9 @@ The documentation for Paperless-ng is available on [ReadTheDocs](https://paperle # Translation -Paperless is currently available in English, German, Dutch and French. Translation is coordinated at transifex: https://www.transifex.com/paperless/paperless-ng +Paperless is currently available in English, German, Dutch, French, and Portuguese. -If you want to see paperless in your own language, request that language at transifex and you can start translating after I approve the language. +There's an active translation project at transifex! If you want to help out by translating paperless into your language, please head over to https://github.com/jonaswinkler/paperless-ng/issues/212 for details. # Feature Requests @@ -108,4 +109,4 @@ These projects also exist, but their status and compatibility with paperless-ng # Important Note -Document scanners are typically used to scan sensitive documents. Things like your social insurance number, tax records, invoices, etc. Everything is stored in the clear without encryption by default (it needs to be searchable, so if someone has ideas on how to do that on encrypted data, I'm all ears). This means that Paperless should never be run on an untrusted host. Instead, I recommend that if you do want to use it, run it locally on a server in your own home. +Document scanners are typically used to scan sensitive documents. Things like your social insurance number, tax records, invoices, etc. Everything is stored in the clear without encryption. This means that Paperless should never be run on an untrusted host. Instead, I recommend that if you do want to use it, run it locally on a server in your own home. diff --git a/ansible/README.md b/ansible/README.md index 6cd9e9f9b..0940cf072 100644 --- a/ansible/README.md +++ b/ansible/README.md @@ -107,7 +107,7 @@ Example Playbook paperlessng_db_type: postgresql paperlessng_db_pass: PLEASEPROVIDEASTRONGPASSWORDHERE - paperless_secret_key: AGAINPLEASECHANGETHISNOW + paperlessng_secret_key: AGAINPLEASECHANGETHISNOW paperlessng_ocr_languages: - eng diff --git a/ansible/defaults/main.yml b/ansible/defaults/main.yml index aaeffa507..f1cbb8f88 100644 --- a/ansible/defaults/main.yml +++ b/ansible/defaults/main.yml @@ -23,14 +23,14 @@ paperlessng_filename_format: paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv" # Hosting & Security -paperless_secret_key: PLEASECHANGETHISFORTHELOVEOFGOD -paperless_allowed_hosts: "*" -paperless_cors_allowed_hosts: http://localhost:8000 -paperless_force_script_name: -paperless_static_url: /static/ -paperless_auto_login_username: -paperless_cookie_prefix: "" -paperless_enable_http_remote_user: False +paperlessng_secret_key: PLEASECHANGETHISFORTHELOVEOFGOD +paperlessng_allowed_hosts: "*" +paperlessng_cors_allowed_hosts: http://localhost:8000 +paperlessng_force_script_name: +paperlessng_static_url: /static/ +paperlessng_auto_login_username: +paperlessng_cookie_prefix: "" +paperlessng_enable_http_remote_user: False # OCR settings paperlessng_ocr_languages: diff --git a/ansible/molecule/default/verify.yml b/ansible/molecule/default/verify.yml index 185840783..7646b223e 100644 --- a/ansible/molecule/default/verify.yml +++ b/ansible/molecule/default/verify.yml @@ -38,7 +38,7 @@ - name: verify uploaded document has been accepted uri: - url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/" + url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/paperless/" headers: Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}' return_content: yes @@ -51,7 +51,7 @@ - name: verify uploaded document has been consumed uri: - url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/" + url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/paperless/" headers: Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}' return_content: yes diff --git a/ansible/tasks/main.yml b/ansible/tasks/main.yml index 6857a7c63..bfaf8df17 100644 --- a/ansible/tasks/main.yml +++ b/ansible/tasks/main.yml @@ -15,7 +15,6 @@ - imagemagick - optipng - gnupg - - libpoppler-cpp-dev - libpq-dev - libmagic-dev - mime-support @@ -286,21 +285,21 @@ line: "PAPERLESS_FILENAME_FORMAT={{ paperlessng_filename_format }}" # Hosting & Security - regexp: PAPERLESS_SECRET_KEY - line: "PAPERLESS_SECRET_KEY={{ paperless_secret_key }}" + line: "PAPERLESS_SECRET_KEY={{ paperlessng_secret_key }}" - regexp: PAPERLESS_ALLOWED_HOSTS - line: "PAPERLESS_ALLOWED_HOSTS={{ paperless_allowed_hosts }}" + line: "PAPERLESS_ALLOWED_HOSTS={{ paperlessng_allowed_hosts }}" - regexp: PAPERLESS_CORS_ALLOWED_HOSTS - line: "PAPERLESS_CORS_ALLOWED_HOSTS={{ paperless_cors_allowed_hosts }}" + line: "PAPERLESS_CORS_ALLOWED_HOSTS={{ paperlessng_cors_allowed_hosts }}" - regexp: PAPERLESS_FORCE_SCRIPT_NAME - line: "PAPERLESS_FORCE_SCRIPT_NAME={{ paperless_force_script_name }}" + line: "PAPERLESS_FORCE_SCRIPT_NAME={{ paperlessng_force_script_name }}" - regexp: PAPERLESS_STATIC_URL - line: "PAPERLESS_STATIC_URL={{ paperless_static_url }}" + line: "PAPERLESS_STATIC_URL={{ paperlessng_static_url }}" - regexp: PAPERLESS_AUTO_LOGIN_USERNAME - line: "PAPERLESS_AUTO_LOGIN_USERNAME={{ paperless_auto_login_username }}" + line: "PAPERLESS_AUTO_LOGIN_USERNAME={{ paperlessng_auto_login_username }}" - regexp: PAPERLESS_COOKIE_PREFIX - line: "PAPERLESS_COOKIE_PREFIX={{ paperless_cookie_prefix }}" + line: "PAPERLESS_COOKIE_PREFIX={{ paperlessng_cookie_prefix }}" - regexp: PAPERLESS_ENABLE_HTTP_REMOTE_USER - line: "PAPERLESS_ENABLE_HTTP_REMOTE_USER={{ paperless_enable_http_remote_user }}" + line: "PAPERLESS_ENABLE_HTTP_REMOTE_USER={{ paperlessng_enable_http_remote_user }}" # OCR settings - regexp: PAPERLESS_OCR_LANGUAGE line: "PAPERLESS_OCR_LANGUAGE={{ paperlessng_ocr_languages | join('+') }}" diff --git a/crowdin.yml b/crowdin.yml index f5a7765dd..0e6a46a31 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,5 +1,5 @@ files: - - source: /src/locale/en-us/LC_MESSAGES/django.po - translation: /src/locale/%two_letters_code%/LC_MESSAGES/django.po + - source: /src/locale/en_US/LC_MESSAGES/django.po + translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po - source: /src-ui/messages.xlf - translation: /src-ui/src/locale/messages.%two_letters_code%.xlf + translation: /src-ui/src/locale/messages.%locale_with_underscore%.xlf diff --git a/docker/install_management_commands.sh b/docker/install_management_commands.sh new file mode 100755 index 000000000..17fb8f277 --- /dev/null +++ b/docker/install_management_commands.sh @@ -0,0 +1,6 @@ +for command in document_archiver document_exporter document_importer mail_fetcher document_create_classifier document_index document_renamer document_retagger document_thumbnails document_sanity_checker; +do + echo "installing $command..." + sed "s/management_command/$command/g" management_script.sh > /usr/local/bin/$command + chmod +x /usr/local/bin/$command +done diff --git a/docker/management_script.sh b/docker/management_script.sh new file mode 100644 index 000000000..bffa26e98 --- /dev/null +++ b/docker/management_script.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e + +cd /usr/src/paperless/src/ + +if [[ $(id -u) == 0 ]] ; +then + sudo -HEu paperless python3 manage.py management_command "$@" +elif [[ $(id -un) == "paperless" ]] ; +then + python3 manage.py management_command "$@" +else + echo "Unknown user." +fi diff --git a/docs/administration.rst b/docs/administration.rst index c54323e6e..c91f501bd 100644 --- a/docs/administration.rst +++ b/docs/administration.rst @@ -23,6 +23,12 @@ Options available to any installation of paperless: * The document exporter is also able to update an already existing export. Therefore, incremental backups with ``rsync`` are entirely possible. +.. caution:: + + You cannot import the export generated with one version of paperless in a + different version of paperless. The export contains an exact image of the + database, and migrations may change the database layout. + Options available to docker installations: * Backup the docker volumes. These usually reside within @@ -101,17 +107,17 @@ Then you can start paperless-ng with ``-d`` to have it run in the background. update to newer versions. In order to enable updates as described above, either get the new ``docker-compose.yml`` file from `here `_ or edit the ``docker-compose.yml`` file, find the line that says - + .. code:: image: jonaswinkler/paperless-ng:0.9.x - + and replace the version with ``latest``: .. code:: image: jonaswinkler/paperless-ng:latest - + Bare Metal Route ================ @@ -171,26 +177,63 @@ Most of the update process is automated when using the ansible role. $ ansible-playbook playbook.yml +Downgrading Paperless +##################### + +Downgrades are possible. However, some updates also contain database migrations (these change the layout of the database and may move data). +In order to move back from a version that applied database migrations, you'll have to revert the database migration *before* downgrading, +and then downgrade paperless. + +This table lists the most recent database migrations for each versions: + ++---------+-------------------------+ +| Version | Latest migration number | ++---------+-------------------------+ +| 1.0.0 | 1011 | ++---------+-------------------------+ +| 1.1.0 | 1011 | ++---------+-------------------------+ +| 1.1.1 | 1012 | ++---------+-------------------------+ + +Execute the following management command to migrate your database: + +.. code:: shell-session + + $ python3 manage.py migrate documents + +.. note:: + + Some migrations cannot be undone. The command will issue errors if that happens. + +.. _utilities-management-commands: + Management utilities #################### Paperless comes with some management commands that perform various maintenance -tasks on your paperless instance. You can invoke these commands either by +tasks on your paperless instance. You can invoke these commands in the following way: + +With docker-compose, while paperless is running: .. code:: shell-session $ cd /path/to/paperless - $ docker-compose run --rm webserver + $ docker-compose exec webserver -or +With docker, while paperless is running: + +.. code:: shell-session + + $ docker exec -it + +Bare metal: .. code:: shell-session $ cd /path/to/paperless/src $ python3 manage.py -depending on whether you use docker or not. - All commands have built-in help, which can be accessed by executing them with the argument ``--help``. @@ -210,7 +253,7 @@ backup or migration to another DMS. -c, --compare-checksums -f, --use-filename-format -d, --delete - + ``target`` is a folder to which the data gets written. This includes documents, thumbnails and a ``manifest.json`` file. The manifest contains all metadata from the database (correspondents, tags, etc). @@ -367,6 +410,34 @@ the naming scheme. The command takes no arguments and processes all your documents at once. +.. _utilities-sanity-checker: + +Sanity checker +============== + +Paperless has a built-in sanity checker that inspects your document collection for issues. + +The issues detected by the sanity checker are as follows: + +* Missing original files. +* Missing archive files. +* Inaccessible original files due to improper permissions. +* Inaccessible archive files due to improper permissions. +* Corrupted original documents by comparing their checksum against what is stored in the database. +* Corrupted archive documents by comparing their checksum against what is stored in the database. +* Missing thumbnails. +* Inaccessible thumbnails due to improper permissions. +* Documents without any content (warning). +* Orphaned files in the media directory (warning). These are files that are not referenced by any document im paperless. + + +.. code:: + + document_sanity_checker + +The command takes no arguments. Depending on the size of your document archive, this may take some time. + + Fetching e-mail =============== diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst index 8f6b91b4c..c64df4317 100644 --- a/docs/advanced_usage.rst +++ b/docs/advanced_usage.rst @@ -217,6 +217,7 @@ will create a directory structure as follows: Paperless provides the following placeholders withing filenames: +* ``{asn}``: The archive serial number of the document, or "none". * ``{correspondent}``: The name of the correspondent, or "none". * ``{document_type}``: The name of the document type, or "none". * ``{tag_list}``: A comma separated list of all tags assigned to the document. diff --git a/docs/changelog.rst b/docs/changelog.rst index 1da8621bb..137fa2fd2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,106 @@ Changelog ********* +paperless-ng 1.2.1 +################## + +* `Rodrigo Avelino `_ translated Paperless into Portuguese (Brazil). + +* The date input fields now respect the currently selected date format. + +* Added a fancy icon when adding paperless to the home screen on iOS devices. Thanks to `Joel Nordell `_. + +* When using regular expression matching, the regular expression is now validated before saving the tag/correspondent/type. + +paperless-ng 1.2.0 +################## + +* Changes to the OCRmyPDF integration + + * Added support for deskewing and automatic rotation of incorrectly rotated pages. This is enabled by default, see :ref:`configuration-ocr`. + * Better support for encrypted files. + * Better support for various other PDF files: Paperless will now attempt to force OCR with safe options when OCR fails with the configured options. + * Added an explicit option to skip cleaning with ``unpaper``. + +* Download multiple selected documents as a zip archive. + +* The document list now remembers the current page. + +* Improved responsiveness when switching between saved views and the document list. + +* Increased the default wait time when observing files in the consumption folder + with polling from 1 to 5 seconds. This will decrease the likelihood of paperless + consuming partially written files. + +* Fixed a crash of the document archiver management command when trying to process documents with unknown mime types. + +* Paperless no longer depends on ``libpoppler-cpp-dev``. + +.. note:: + + Some packages that paperless depends on are slowly dropping Python 3.6 + support one after another, including the web server. Supporting Python + 3.6 means that I cannot update these packages anymore. + + At some point, paperless will drop Python 3.6 support. If using a bare + metal installation and you're still on Python 3.6, upgrade to 3.7 or newer. + + If using docker, this does not affect you. + +paperless-ng 1.1.4 +################## + +* Added English (GB) locale. + +* Added ISO-8601 date display option. + +paperless-ng 1.1.3 +################## + +* Added a docker-specific configuration option to adjust the number of + worker processes of the web server. See :ref:`configuration-docker`. + +* Some more memory usage optimizations. + +* Don't show inbox statistics if no inbox tag is defined. + +paperless-ng 1.1.2 +################## + +* Always show top left corner of thumbnails, even for extra wide documents. + +* Added a management command for executing the sanity checker directly. + See :ref:`utilities-sanity-checker`. + +* The weekly sanity check now reports messages in the log files. + +* Fixed an issue with the metadata tab not reporting anything in case of missing files. + +* Reverted a change from 1.1.0 that caused huge memory usage due to redis caching. + +* Some memory usage optimizations. + +paperless-ng 1.1.1 +################## + +This release contains new database migrations. + +* Fixed a bug in the sanity checker that would cause it to display "x not in list" errors instead of actual issues. + +* Fixed a bug with filename generation for archive filenames that would cause the archive files of two documents to overlap. + + * This happened when ``PAPERLESS_FILENAME_FORMAT`` is used and the filenames of two or more documents are the same, except for the file extension. + * Paperless will now store the archive filename in the database as well instead of deriving it from the original filename, and use the + same logic for detecting and avoiding filename clashes that's also used for original filenames. + * The migrations will repair any missing archive files. If you're using tika, ensure that tika is running while performing the migration. Docker-compose will take care of that. + +* Fixed a bug with thumbnail regeneration when TIKA integration was used. + +* Added ASN as a placeholder field to the filename format. + +* The docker image now comes with built-in shortcuts for most management commands. These are now the recommended way to execute management commands, since these + also ensure that they're always executed as the paperless user and you're less likely to run into permission issues. See :ref:`utilities-management-commands`. + paperless-ng 1.1.0 ################## @@ -17,7 +117,7 @@ paperless-ng 1.1.0 or added with one of the mobile apps. * Documents are successfully added to paperless. * Document consumption failed (with error messages) - + * Configuration options to enable/disable individual notifications. * Live updates to document lists and saved views when new documents are added. @@ -37,6 +137,8 @@ paperless-ng 1.1.0 Paperless will continue to work with WSGI, but you will not get any status notifications. + Apache ``mod_wsgi`` users, see :ref:`this note `. + * Paperless now offers suggestions for tags, correspondents and types on the document detail page. * Added an interactive easy install script that automatically downloads, configures and starts paperless with docker. @@ -56,6 +158,13 @@ paperless-ng 1.1.0 * Better info section in the side bar. + * Paperless no longer logs to the database. Instead, logs are written to rotating log files. This solves many "database is locked" + issues on Raspberry Pi, especially when SQLite is used. + + * By default, log files are written to ``PAPERLESS_DATA_DIR/log/``. Logging settings can be adjusted with + ``PAPERLESS_LOGGING_DIR``, ``PAPERLESS_LOGROTATE_MAX_SIZE`` and + ``PAPERLESS_LOGROTATE_MAX_BACKUPS``. + paperless-ng 1.0.0 ################## diff --git a/docs/conf.py b/docs/conf.py index b2442ddc9..71abe2079 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,7 +28,7 @@ master_doc = 'index' # General information about the project. project = u'Paperless-ng' -copyright = u'2015, Daniel Quinn' +copyright = u'2021, Daniel Quinn, Jonas Winkler' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/configuration.rst b/docs/configuration.rst index 36b124350..e26180382 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -72,13 +72,13 @@ PAPERLESS_CONSUMPTION_DIR= container. Change the local consumption directory in the docker-compose.yml file instead. - Defaults to "../consume", relative to the "src" directory. + Defaults to "../consume/", relative to the "src" directory. PAPERLESS_DATA_DIR= This is where paperless stores all its data (search index, SQLite database, classification model, etc). - Defaults to "../data", relative to the "src" directory. + Defaults to "../data/", relative to the "src" directory. PAPERLESS_MEDIA_ROOT= This is where your documents and thumbnails are stored. @@ -86,7 +86,7 @@ PAPERLESS_MEDIA_ROOT= You can set this and PAPERLESS_DATA_DIR to the same folder to have paperless store all its data within the same volume. - Defaults to "../media", relative to the "src" directory. + Defaults to "../media/", relative to the "src" directory. PAPERLESS_STATICDIR= Override the default STATIC_ROOT here. This is where all static files @@ -94,7 +94,7 @@ PAPERLESS_STATICDIR= Unless you're doing something fancy, there is no need to override this. - Defaults to "../static", relative to the "src" directory. + Defaults to "../static/", relative to the "src" directory. PAPERLESS_FILENAME_FORMAT= Changes the filenames paperless uses to store documents in the media directory. @@ -102,6 +102,25 @@ PAPERLESS_FILENAME_FORMAT= Default is none, which disables this feature. +PAPERLESS_LOGGING_DIR= + This is where paperless will store log files. + + Defaults to "``PAPERLESS_DATA_DIR``/log/". + + +Logging +####### + +PAPERLESS_LOGROTATE_MAX_SIZE= + Maximum file size for log files before they are rotated, in bytes. + + Defaults to 1 MiB. + +PAPERLESS_LOGROTATE_MAX_BACKUPS= + Number of rotated log files to keep. + + Defaults to 20. + Hosting & Security ################## @@ -183,7 +202,6 @@ Paperless uses `OCRmyPDF `_ for performing OCR on documents and images. Paperless uses sensible defaults for most settings, but all of them can be configured to your needs. - PAPERLESS_OCR_LANGUAGE= Customize the language that paperless will attempt to use when parsing documents. @@ -226,6 +244,54 @@ PAPERLESS_OCR_MODE= The default is ``skip``, which only performs OCR when necessary and always creates archived documents. + Read more about this in the `OCRmyPDF documentation `_. + +PAPERLESS_OCR_CLEAN= + Tells paperless to use ``unpaper`` to clean any input document before + sending it to tesseract. This uses more resources, but generally results + in better OCR results. The following modes are available: + + * ``clean``: Apply unpaper. + * ``clean-final``: Apply unpaper, and use the cleaned images to build the + output file instead of the original images. + * ``none``: Do not apply unpaper. + + Defaults to ``clean``. + + .. note:: + + ``clean-final`` is incompatible with ocr mode ``redo``. When both + ``clean-final`` and the ocr mode ``redo`` is configured, ``clean`` + is used instead. + +PAPERLESS_OCR_DESKEW= + Tells paperless to correct skewing (slight rotation of input images mainly + due to improper scanning) + + Defaults to ``true``, which enables this feature. + + .. note:: + + Deskewing is incompatible with ocr mode ``redo``. Deskewing will get + disabled automatically if ``redo`` is used as the ocr mode. + +PAPERLESS_OCR_ROTATE_PAGES= + Tells paperless to correct page rotation (90°, 180° and 270° rotation). + + If you notice that paperless is not rotating incorrectly rotated + pages (or vice versa), try adjusting the threshold up or down (see below). + + Defaults to ``true``, which enables this feature. + + +PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD= + Adjust the threshold for automatic page rotation by ``PAPERLESS_OCR_ROTATE_PAGES``. + This is an arbitrary value reported by tesseract. "15" is a very conservative value, + whereas "2" is a very aggressive option and will often result in correctly rotated pages + being rotated as well. + + Defaults to "12". + PAPERLESS_OCR_OUTPUT_TYPE= Specify the the type of PDF documents that paperless should produce. @@ -252,7 +318,6 @@ PAPERLESS_OCR_PAGES= Defaults to 0, which disables this feature and always uses all pages. - PAPERLESS_OCR_IMAGE_DPI= Paperless will OCR any images you put into the system and convert them into PDF documents. This is useful if your scanner produces images. @@ -263,8 +328,8 @@ PAPERLESS_OCR_IMAGE_DPI= Set this to the DPI your scanner produces images at. - Default is none, which causes paperless to fail if no DPI information is - present in an image. + Default is none, which will automatically calculate image DPI so that + the produced PDF documents are A4 sized. PAPERLESS_OCR_USER_ARGS= @@ -273,7 +338,7 @@ PAPERLESS_OCR_USER_ARGS= the API of OCRmyPDF, you have to specify these in a format that can be passed to the API. See `the API reference of OCRmyPDF `_ for valid parameters. All command line options are supported, but they - use underscores instead of dashed. + use underscores instead of dashes. .. caution:: @@ -333,7 +398,7 @@ requires are as follows: PAPERLESS_TIKA_ENABLED: 1 PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000 PAPERLESS_TIKA_ENDPOINT: http://tika:9998 - + # ... gotenberg: @@ -536,3 +601,65 @@ PAPERLESS_GS_BINARY= PAPERLESS_OPTIPNG_BINARY= Defaults to "/usr/bin/optipng". + + +.. _configuration-docker: + +Docker-specific options +####################### + +These options don't have any effect in ``paperless.conf``. These options adjust +the behavior of the docker container. Configure these in `docker-compose.env`. + +PAPERLESS_WEBSERVER_WORKERS= + The number of worker processes the webserver should spawn. More worker processes + usually result in the front end to load data much quicker. However, each worker process + also loads the entire application into memory separately, so increasing this value + will increase RAM usage. + + Consider configuring this to 1 on low power devices with limited amount of RAM. + + Defaults to 2. + +USERMAP_UID= + The ID of the paperless user in the container. Set this to your actual user ID on the + host system, which you can get by executing + + .. code:: shell-session + + $ id -u + + Paperless will change ownership on its folders to this user, so you need to get this right + in order to be able to write to the consumption directory. + + Defaults to 1000. + +USERMAP_GID= + The ID of the paperless Group in the container. Set this to your actual group ID on the + host system, which you can get by executing + + .. code:: shell-session + + $ id -g + + Paperless will change ownership on its folders to this group, so you need to get this right + in order to be able to write to the consumption directory. + + Defaults to 1000. + +PAPERLESS_OCR_LANGUAGES= + Additional OCR languages to install. By default, paperless comes with + English, German, Italian, Spanish and French. If your language is not in this list, install + additional languages with this configuration option: + + .. code:: bash + + PAPERLESS_OCR_LANGUAGES=tur ces + + To actually use these languages, also set the default OCR language of paperless: + + .. code:: bash + + PAPERLESS_OCR_LANGUAGE=tur + + Defaults to none, which does not install any additional languages. diff --git a/docs/extending.rst b/docs/extending.rst index b2ddf3d85..860106258 100644 --- a/docs/extending.rst +++ b/docs/extending.rst @@ -109,6 +109,30 @@ This will build the front end and put it in a location from which the Django ser it as static content. This way, you can verify that authentication is working. +Building the documentation +========================== + +The documentation is built using sphinx. I've configured ReadTheDocs to automatically build +the documentation when changes are pushed. If you want to build the documentation locally, +this is how you do it: + +1. Install python dependencies. + + .. code:: shell-session + + $ cd /path/to/paperless + $ pipenv install --dev + +2. Build the documentation + + .. code:: shell-session + + $ cd /path/to/paperless/docs + $ pipenv run make clean html + +This will build the HTML documentation, and put the resulting files in the ``_build/html`` +directory. + Extending Paperless =================== diff --git a/docs/faq.rst b/docs/faq.rst index e41f9aace..0f4527d62 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -62,9 +62,9 @@ file extensions do not matter. **A:** The short answer is yes. I've tested it on a Raspberry Pi 3 B. The long answer is that certain parts of -Paperless will run very slow, such as the tesseract OCR. On Raspberry Pi, +Paperless will run very slow, such as the OCR. On Raspberry Pi, try to OCR documents before feeding them into paperless so that paperless can -reuse the text. The web interface should be a lot snappier, since it runs +reuse the text. The web interface is a lot snappier, since it runs in your browser and paperless has to do much less work to serve the data. .. note:: @@ -76,7 +76,14 @@ in your browser and paperless has to do much less work to serve the data. **Q:** *How do I install paperless-ng on Raspberry Pi?* **A:** Docker images are available for arm and arm64 hardware, so just follow -the docker-compose instructions, or go the bare metal route. +the docker-compose instructions. Apart from more required disk space compared to +a bare metal installation, docker comes with close to zero overhead, even on +Raspberry Pi. + +If you decide to got with the bare metal route, be aware that some of the +python requirements do not have precompiled packages for ARM / ARM64. Installation +of these will require additional development libraries and compilation will take +a long time. **Q:** *How do I run this on unRaid?* @@ -95,12 +102,21 @@ occasionally build the image on and see if it works. **Q:** *How do I proxy this with NGINX?* -.. code:: +**A:** See :ref:`here `. - location / { - proxy_pass http://localhost:8000/ - } +.. _faq-mod_wsgi: -And that's about it. Paperless serves everything, including static files by itself -when running the docker image. If you want to do anything fancy, you have to -install paperless bare metal. +**Q:** *How do I get WebSocket support with Apache mod_wsgi*? + +**A:** ``mod_wsgi`` by itself does not support ASGI. Paperless will continue +to work with WSGI, but certain features such as status notifications about +document consumption won't be available. + +If you want to continue using ``mod_wsgi``, you will have to run an ASGI-enabled +web server as well that processes WebSocket connections, and configure Apache to +redirect WebSocket connections to this server. Multiple options for ASGI servers +exist: + +* ``gunicorn`` with ``uvicorn`` as the worker implementation (the default of paperless) +* ``daphne`` as a standalone server, which is the reference implementation for ASGI. +* ``uvicorn`` as a standalone server diff --git a/docs/index.rst b/docs/index.rst index a083fb3d1..4035d822e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -70,8 +70,8 @@ Contents configuration api faq - extending troubleshooting + extending contributing scanners screenshots diff --git a/docs/setup.rst b/docs/setup.rst index eda03550e..69cb2bc97 100644 --- a/docs/setup.rst +++ b/docs/setup.rst @@ -81,6 +81,7 @@ Installation You can go multiple routes to setup and run Paperless: +* :ref:`Use the easy install docker script ` * :ref:`Pull the image from Docker Hub ` * :ref:`Build the Docker image yourself ` * :ref:`Install Paperless directly on your system manually (bare metal) ` @@ -101,6 +102,24 @@ it includes the same sensible defaults, and it simultaneously provides the flexi .. _CLI Basics: https://sehn.tech/post/devops-with-docker/ .. _idempotent: https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#Idempotency +.. _setup-docker_script: + +Install Paperless from Docker Hub using the installation script +=============================================================== + +Paperless provides an interactive installation script. This script will ask you +for a couple configuration options, download and create the necessary configuration files, pull the docker image, start paperless and create your user account. This script essentially +performs all the steps described in :ref:`setup-docker_hub` automatically. + +1. Make sure that docker and docker-compose are installed. +2. Download and run the installation script: + + .. code:: shell-session + + $ wget https://raw.githubusercontent.com/jonaswinkler/paperless-ng/master/install-paperless-ng.sh + $ chmod +x install-paperless-ng.sh + $ ./install-paperless-ng.sh + .. _setup-docker_hub: Install Paperless from Docker Hub @@ -157,7 +176,10 @@ Install Paperless from Docker Hub 5. Modify ``docker-compose.env``, following the comments in the file. The most important change is to set ``USERMAP_UID`` and ``USERMAP_GID`` - to the uid and gid of your user on the host system. This ensures that + to the uid and gid of your user on the host system. Use ``id -u`` and + ``id -g`` to get these. + + This ensures that both the docker container and you on the host machine have write access to the consumption directory. If your UID and GID on the host system is 1000 (the default for the first normal user on most systems), it will @@ -176,14 +198,10 @@ Install Paperless from Docker Hub with the default configuration. You will need to use ``PAPERLESS_CONSUMER_POLLING``, which will disable inotify. See :ref:`here `. -6. Now head over to: https://hub.docker.com/r/jonaswinkler/paperless-ng and choose your preferred - image and copy the link. To download this image do a `docker pull` followed by the link. Do this within the directory with the .yml files. - Depending on your network connection and CPU this will take a while. You have time to get a beverage. +6. Run ``docker-compose pull``, followed by ``docker-compose up -d``. + This will pull the image, create and start the necessary containers. -7. Run ``docker-compose up -d``. This will create and start the necessary - containers, but your are not done yet! - -8. To be able to login, you will need a super user. To create it, execute the +7. To be able to login, you will need a super user. To create it, execute the following command: .. code-block:: shell-session @@ -193,8 +211,8 @@ Install Paperless from Docker Hub This will prompt you to set a username, an optional e-mail address and finally a password (at least 8 characters). -9. The default ``docker-compose.yml`` exports the webserver on your local port - 8000. If you haven't adapted this, you should now be able to visit your +8. The default ``docker-compose.yml`` exports the webserver on your local port + 8000. If you did not change this, you should now be able to visit your Paperless instance at ``http://127.0.0.1:8000`` or your servers IP-Address:8000. Use the login credentials you have created with the previous step. @@ -203,7 +221,7 @@ Install Paperless from Docker Hub .. _setup-docker_build: -Build the docker image yourself +Build the Docker image yourself =============================== 1. Clone the entire repository of paperless: @@ -234,14 +252,14 @@ Build the docker image yourself 4. Run the ``compile-frontend.sh`` script. This requires ``node`` and ``npm >= v15``. -5. Follow steps 2 to 7 of :ref:`setup-docker_hub`. When asked to run - ``docker-compose up -d`` to start the containers, do +5. Follow steps 3 to 8 of :ref:`setup-docker_hub`. When asked to run + ``docker-compose pull`` to pull the image, do .. code:: shell-session $ docker-compose build - before that to build the image. + instead to build the image. .. _setup-bare_metal: @@ -262,7 +280,6 @@ writing. Windows is not and will never be supported. * ``imagemagick`` >= 6 for PDF conversion * ``optipng`` for optimizing thumbnails * ``gnupg`` for handling encrypted documents - * ``libpoppler-cpp-dev`` for PDF to text conversion * ``libpq-dev`` for PostgreSQL * ``libmagic-dev`` for mime type detection * ``mime-support`` for mime type detection @@ -291,7 +308,7 @@ writing. Windows is not and will never be supported. 2. Install ``redis`` >= 5.0 and configure it to start automatically. 3. Optional. Install ``postgresql`` and configure a database, user and password for paperless. If you do not wish - to use PostgreSQL, SQLite is avialable as well. + to use PostgreSQL, SQLite is available as well. 4. Get the release archive from ``_. If you clone the git repo as it is, you also have to compile the front end by yourself. @@ -315,8 +332,14 @@ writing. Windows is not and will never be supported. * Set ``PAPERLESS_OCR_LANGUAGE`` to the language most of your documents are written in. * Set ``PAPERLESS_TIME_ZONE`` to your local time zone. -6. Setup permissions. Create a system users under which you wish to run paperless. Ensure that these directories exist - and that the user has write permissions to the following directories +6. Create a system user under which you wish to run paperless. + + .. code:: shell-session + + adduser paperless --system --home /opt/paperless --group + +7. Ensure that these directories exist + and that the paperless user has write permissions to the following directories: * ``/opt/paperless/media`` * ``/opt/paperless/data`` @@ -324,30 +347,32 @@ writing. Windows is not and will never be supported. Adjust as necessary if you configured different folders. -7. Install python requirements from the ``requirements.txt`` file. +8. Install python requirements from the ``requirements.txt`` file. It is up to you if you wish to use a virtual environment or not. .. code:: shell-session - pip3 install -r requirements.txt + sudo -Hu paperless pip3 install -r requirements.txt + This will install all python dependencies in the home directory of + the new paperless user. -8. Go to ``/opt/paperless/src``, and execute the following commands: +9. Go to ``/opt/paperless/src``, and execute the following commands: .. code:: bash # This creates the database schema. - python3 manage.py migrate + sudo -Hu paperless python3 manage.py migrate # This creates your first paperless user - python3 manage.py createsuperuser + sudo -Hu paperless python3 manage.py createsuperuser -9. Optional: Test that paperless is working by executing +10. Optional: Test that paperless is working by executing .. code:: bash # This collects static files from paperless and django. - python3 manage.py runserver + sudo -Hu paperless python3 manage.py runserver and pointing your browser to http://localhost:8000/. @@ -362,7 +387,7 @@ writing. Windows is not and will never be supported. This will not start the consumer. Paperless does this in a separate process. -10. Setup systemd services to run paperless automatically. You may +11. Setup systemd services to run paperless automatically. You may use the service definition files included in the ``scripts`` folder as a starting point. @@ -378,14 +403,7 @@ writing. Windows is not and will never be supported. These services rely on redis and optionally the database server, but don't need to be started in any particular order. The example files depend on redis being started. If you use a database server, you should - add additinal dependencies. - - .. hint:: - - You may optionally set up your preferred web server to serve - paperless as a wsgi application directly instead of running the - ``webserver`` service. The module containing the wsgi application - is named ``paperless.wsgi``. + add additional dependencies. .. caution:: @@ -394,10 +412,13 @@ writing. Windows is not and will never be supported. however, the documentation of GUnicorn states that you should use a proxy server in front of gunicorn instead. -11. Optional: Install a samba server and make the consumption folder + For instructions on how to use nginx for that, + :ref:`see the instructions below `. + +12. Optional: Install a samba server and make the consumption folder available as a network share. -12. Configure ImageMagick to allow processing of PDF documents. Most distributions have +13. Configure ImageMagick to allow processing of PDF documents. Most distributions have this disabled by default, since PDF documents can contain malware. If you don't do this, paperless will fall back to ghostscript for certain steps such as thumbnail generation. @@ -414,7 +435,7 @@ writing. Windows is not and will never be supported. -13. Optional: Install the `jbig2enc `_ +14. Optional: Install the `jbig2enc `_ encoder. This will reduce the size of generated PDF documents. You'll most likely need to compile this by yourself, because this software has been patented until around 2017 and binary packages are not available for most distributions. @@ -423,9 +444,10 @@ writing. Windows is not and will never be supported. Install Paperless using ansible =============================== - .. note:: - This role currently only supports Debian 10 Buster and Ubuntu 20.04 Focal or later as target hosts. +.. note:: + + This role currently only supports Debian 10 Buster and Ubuntu 20.04 Focal or later as target hosts. 1. Install ansible 2.7+ on the management node. This may be the target host paperless-ng is being installed on or any remote host which can access the target host. @@ -462,7 +484,7 @@ Install Paperless using ansible .. code:: sh cd paperless-ng - git checkout ng-0.9.14 + git checkout ng-1.0.0 3. Create an ansible ``playbook.yml`` in the paperless-ng root directory: @@ -512,7 +534,7 @@ Install Paperless using ansible .. code:: yaml - paperless_secret_key: PleaseGenerateAStrongKeyForThis + paperlessng_secret_key: PleaseGenerateAStrongKeyForThis paperlessng_superuser_name: YourUserName paperlessng_superuser_email: name@domain.tld @@ -570,7 +592,7 @@ Migration to paperless-ng is then performed in a few simple steps: paperless. 3. Download the latest release of paperless-ng. You can either go with the - docker-compose files from `here `_ + docker-compose files from `here `__ or clone the repository to build the image yourself (see :ref:`above `). You can either replace your current paperless folder or put paperless-ng in a different location. @@ -740,7 +762,8 @@ configuring some options in paperless can help improve performance immensely: * Stick with SQLite to save some resources. * Consider setting ``PAPERLESS_OCR_PAGES`` to 1, so that paperless will only OCR - the first page of your documents. + the first page of your documents. In most cases, this page contains enough + information to be able to find it. * ``PAPERLESS_TASK_WORKERS`` and ``PAPERLESS_THREADS_PER_WORKER`` are configured to use all cores. The Raspberry Pi models 3 and up have 4 cores, meaning that paperless will use 2 workers and 2 threads per worker. This may result in @@ -751,8 +774,13 @@ configuring some options in paperless can help improve performance immensely: your documents before feeding them into paperless. Some scanners are able to do this! You might want to even specify ``skip_noarchive`` to skip archive file generation for already ocr'ed documents entirely. +* If you want to perform OCR on the the device, consider using ``PAPERLESS_OCR_CLEAN=none``. + This will speed up OCR times and use less memory at the expense of slightly worse + OCR results. * Set ``PAPERLESS_OPTIMIZE_THUMBNAILS`` to 'false' if you want faster consumption times. Thumbnails will be about 20% larger. +* If using docker, consider setting ``PAPERLESS_WEBSERVER_WORKERS`` to + 1. This will save some memory. For details, refer to :ref:`configuration`. @@ -769,3 +797,46 @@ For details, refer to :ref:`configuration`. well as on any other device. .. _redis: https://redis.io/ + + +.. _setup-nginx: + +Using nginx as a reverse proxy +############################## + +If you want to expose paperless to the internet, you should hide it behind a +reverse proxy with SSL enabled. + +In addition to the usual configuration for SSL, +the following configuration is required for paperless to operate: + +.. code:: nginx + + http { + + # Adjust as required. This is the maximum size for file uploads. + # The default value 1M might be a little too small. + client_max_body_size 10M; + + server { + + location / { + + # Adjust host and port as required. + proxy_pass http://localhost:8000/; + + # These configuration options are required for WebSockets to work. + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } + } + +Also read `this `__, towards the end of the section. diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index f55d57af5..d2a76e7b3 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -60,7 +60,8 @@ required so that the user running paperless inside docker has write permissions to these folders. This happens when pointing these directories to NFS shares, for example. -Ensure that `chown` is possible on these directories. +Ensure that ``chown`` is possible on these directories. + Classifier error: No training data available ############################################ @@ -73,6 +74,7 @@ This may have two reasons: with Inbox tags. Verify that there are documents in your archive without inbox tags. The algorithm will only learn from documents not in your inbox. + UserWarning in sklearn on every single document ############################################### @@ -91,6 +93,31 @@ in most cases. This warning will disappear automatically when paperless updates If you want to get rid of the warning or actually experience issues with automatic matching, delete the file ``classification_model.pickle`` in the data directory and let paperless recreate it. + +504 Server Error: Gateway Timeout when adding Office documents +############################################################## + +You may experience these errors when using the optional TIKA integration: + +.. code:: + + requests.exceptions.HTTPError: 504 Server Error: Gateway Timeout for url: http://gotenberg:3000/convert/office + +Gotenberg is a server that converts Office documents into PDF documents and has a default timeout of 10 seconds. +When conversion takes longer, Gotenberg raises this error. + +You can increase the timeout by configuring an environment variable for gotenberg (see also `here `__). +If using docker-compose, this is achieved by the following configuration change in the ``docker-compose.yml`` file: + +.. code:: yaml + + gotenberg: + image: thecodingmachine/gotenberg + restart: unless-stopped + environment: + DISABLE_GOOGLE_CHROME: 1 + DEFAULT_WAIT_TIMEOUT: 30 + Permission denied errors in the consumption directory ##################################################### @@ -106,6 +133,38 @@ different from ``1000``. See :ref:`setup-docker_hub`. Also ensure that you are able to read and write to the consumption directory on the host. + +OSError: [Errno 19] No such device when consuming files +####################################################### + +If you experience errors such as: + +.. code:: shell-session + + File "/usr/local/lib/python3.7/site-packages/whoosh/codec/base.py", line 570, in open_compound_file + return CompoundStorage(dbfile, use_mmap=storage.supports_mmap) + File "/usr/local/lib/python3.7/site-packages/whoosh/filedb/compound.py", line 75, in __init__ + self._source = mmap.mmap(fileno, 0, access=mmap.ACCESS_READ) + OSError: [Errno 19] No such device + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "/usr/local/lib/python3.7/site-packages/django_q/cluster.py", line 436, in worker + res = f(*task["args"], **task["kwargs"]) + File "/usr/src/paperless/src/documents/tasks.py", line 73, in consume_file + override_tag_ids=override_tag_ids) + File "/usr/src/paperless/src/documents/consumer.py", line 271, in try_consume_file + raise ConsumerError(e) + +Paperless uses a search index to provide better and faster full text searching. This search index is stored inside +the ``data`` folder. The search index uses memory-mapped files (mmap). The above error indicates that paperless +was unable to create and open these files. + +This happens when you're trying to store the data directory on certain file systems (mostly network shares) +that don't support memory-mapped files. + + Web-UI stuck at "Loading..." ############################ @@ -148,4 +207,4 @@ This might have multiple reasons. SENDFILE=0 - to your `docker-compose.env` file. \ No newline at end of file + to your `docker-compose.env` file. diff --git a/gunicorn.conf.py b/gunicorn.conf.py index 36bde9cf7..995b4fb9d 100644 --- a/gunicorn.conf.py +++ b/gunicorn.conf.py @@ -1,21 +1,9 @@ +import os + bind = '0.0.0.0:8000' -backlog = 2048 -workers = 3 +workers = int(os.getenv("PAPERLESS_WEBSERVER_WORKERS", 2)) worker_class = 'uvicorn.workers.UvicornWorker' -worker_connections = 1000 -timeout = 20 -keepalive = 2 -spew = False -daemon = False -pidfile = None -umask = 0 -user = None -group = None -tmp_upload_dir = None -loglevel = 'info' -errorlog = '-' -accesslog = '-' -proc_name = None +timeout = 120 def pre_fork(server, worker): pass diff --git a/install-paperless-ng.sh b/install-paperless-ng.sh index ad992fe8c..8fb44e222 100755 --- a/install-paperless-ng.sh +++ b/install-paperless-ng.sh @@ -15,7 +15,7 @@ ask() { fi array=$3 if [[ -z $3 || " ${array[@]} " =~ " ${result} " ]]; then - ask_result=$result + ask_result=$result return else echo "Invalid option: $result" @@ -23,6 +23,47 @@ ask() { done } +ask_docker_folder() { + while true ; do + + read -p "$1 [$2]: " result + + if [[ -z $result ]]; then + ask_result=$2 + return + fi + + if [[ $result == /* || $result == ./* ]]; then + ask_result=$result + return + else + echo "Invalid folder: $result" + fi + + + done +} + +if [[ $(id -u) == "0" ]] ; then + echo "Do not run this script as root." + exit 1 +fi + +if [[ -z $(which wget) ]] ; then + echo "wget executable not found. Is wget installed?" + exit 1 +fi + +if [[ -z $(which docker) ]] ; then + echo "docker executable not found. Is docker installed?" + exit 1 +fi + +if [[ -z $(which docker-compose) ]] ; then + echo "docker-compose executable not found. Is docker-compose installed?" + exit 1 +fi + echo "" echo "############################################" echo "### Paperless-ng docker installation ###" @@ -48,14 +89,13 @@ echo "The consume folder is where paperles will search for new documents." echo "Point this to a folder where your scanner is able to put your scanned" echo "documents." echo "" -echo "HINT: If paperless is unable to pick up any files from this directory after" -echo "installation, you might need to configure PAPERLESS_CONSUMER_POLLING." -echo "See the documentation for details." -echo "" -echo "CAUTION: You must specify an absolute path starting with /" +echo "CAUTION: You must specify an absolute path starting with / or a relative " +echo "path starting with ./ here. Examples:" +echo " /mnt/consume" +echo " ./consume" echo "" -ask "Consume folder" "$TARGET_FOLDER/consume" +ask_docker_folder "Consume folder" "$TARGET_FOLDER/consume" CONSUME_FOLDER=$ask_result echo "" @@ -64,9 +104,10 @@ echo "Leave empty and docker will manage this folder for you." echo "Docker usually stores managed folders in /var/lib/docker/volumes." echo "" echo "CAUTION: If specified, you must specify an absolute path starting with /" +echo "or a relative path starting with ./ here." echo "" -ask "Media folder" "" +ask_docker_folder "Media folder" "" MEDIA_FOLDER=$ask_result echo "" @@ -74,8 +115,11 @@ echo "The data folder is where paperless stores other data, such as your" echo "SQLite database (if used), the search index and other data." echo "As with the media folder, leave empty to have this managed by docker." echo "" +echo "CAUTION: If specified, you must specify an absolute path starting with /" +echo "or a relative path starting with ./ here." +echo "" -ask "Data folder" "" +ask_docker_folder "Data folder" "" DATA_FOLDER=$ask_result echo "" @@ -166,8 +210,36 @@ done ask "Email" "$USERNAME@localhost" EMAIL=$ask_result -echo "Done collecting data. Press any key to install." -read +echo "" +echo "Summary" +echo "=======" +echo "" + +echo "Target folder: $TARGET_FOLDER" +echo "Consume folder: $CONSUME_FOLDER" +if [[ -z $MEDIA_FOLDER ]] ; then + echo "Media folder: Managed by docker" +else + echo "Media folder: $MEDIA_FOLDER" +fi +if [[ -z $DATA_FOLDER ]] ; then + echo "Data folder: Managed by docker" +else + echo "Data folder: $DATA_FOLDER" +fi +echo "" +echo "Port: $PORT" +echo "Database: $DATABASE_BACKEND" +echo "Tika enabled: $TIKA_ENABLED" +echo "OCR language: $OCR_LANGUAGE" +echo "User id: $USERMAP_UID" +echo "Group id: $USERMAP_GID" +echo "" +echo "Paperless username: $USERNAME" +echo "Paperless email: $EMAIL" + +echo "" +read -p "Press any key to install." echo "" echo "Installing paperless..." @@ -209,7 +281,7 @@ sed -i "s/- 8000:8000/- $PORT:8000/g" docker-compose.yml sed -i "s#- \./consume:/usr/src/paperless/consume#- $CONSUME_FOLDER:/usr/src/paperless/consume#g" docker-compose.yml if [[ -n $MEDIA_FOLDER ]] ; then - sed -i "s#- data:/usr/src/paperless/media#- $MEDIA_FOLDER:/usr/src/paperless/media#g" docker-compose.yml + sed -i "s#- media:/usr/src/paperless/media#- $MEDIA_FOLDER:/usr/src/paperless/media#g" docker-compose.yml fi if [[ -n $DATA_FOLDER ]] ; then diff --git a/paperless.conf.example b/paperless.conf.example index 652a741b0..b1b63879d 100644 --- a/paperless.conf.example +++ b/paperless.conf.example @@ -41,6 +41,10 @@ #PAPERLESS_OCR_OUTPUT_TYPE=pdfa #PAPERLESS_OCR_PAGES=1 #PAPERLESS_OCR_IMAGE_DPI=300 +#PAPERLESS_OCR_CLEAN=clean +#PAPERLESS_OCR_DESKEW=true +#PAPERLESS_OCR_ROTATE_PAGES=true +#PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD=12.0 #PAPERLESS_OCR_USER_ARGS={} #PAPERLESS_CONVERT_MEMORY_LIMIT=0 #PAPERLESS_CONVERT_TMPDIR=/var/tmp/paperless diff --git a/requirements.txt b/requirements.txt index c3f32002b..2df1b29ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,29 +12,30 @@ arrow==0.17.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, asgiref==3.3.1; python_version >= '3.5' async-timeout==3.0.1; python_full_version >= '3.5.3' attrs==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -autobahn==20.12.3; python_version >= '3.6' +autobahn==21.2.1; python_version >= '3.6' automat==20.2.0 blessed==1.17.12 certifi==2020.12.5 -cffi==1.14.4 +cffi==1.14.5 channels-redis==3.2.0 channels==3.0.3 chardet==4.0.0; python_version >= '3.1' click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' coloredlogs==15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' +concurrent-log-handler==0.9.19 constantly==15.1.0 -cryptography==3.3.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' +cryptography==3.3.2 daphne==3.0.1; python_version >= '3.6' dateparser==0.7.6 django-cors-headers==3.7.0 -django-extensions==3.1.0 +django-extensions==3.1.1 django-filter==2.4.0 django-picklefield==3.0.1; python_version >= '3' django-q==1.3.4 -django==3.1.5 +django==3.1.7 djangorestframework==3.12.2 filelock==3.0.12 -fuzzywuzzy==0.18.0 +fuzzywuzzy[speedup]==0.18.0 gunicorn==20.0.4 h11==0.12.0; python_version >= '3.6' hiredis==1.1.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' @@ -44,22 +45,21 @@ hyperlink==21.0.0 idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' imap-tools==0.37.0 img2pdf==0.4.0 -importlib-metadata==3.4.0; python_version < '3.8' incremental==17.5.0 inotify-simple==1.3.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' inotifyrecursive==0.3.5 -joblib==1.0.0; python_version >= '3.6' +joblib==1.0.1; python_version >= '3.6' langdetect==1.0.8 lxml==4.6.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' msgpack==1.0.2 -numpy==1.19.5; python_version >= '3.6' -ocrmypdf==11.4.5 +numpy==1.19.5 +ocrmypdf==11.6.2 pathvalidate==2.3.2 -pdfminer.six==20201018; python_version >= '3.4' -pdftotext==2.1.5 -pikepdf==2.2.5 +pdfminer.six==20201018 +pikepdf==2.5.2 pillow==8.1.0 pluggy==0.13.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +portalocker==2.2.1; python_version >= '3' psycopg2-binary==2.8.6 pyasn1-modules==0.2.8 pyasn1==0.4.8 @@ -69,9 +69,9 @@ pyopenssl==20.0.1; python_version >= '2.7' and python_version not in '3.0, 3.1, python-dateutil==2.8.1 python-dotenv==0.15.0 python-gnupg==0.4.6 -python-levenshtein==0.12.1 -python-magic==0.4.18 -pytz==2020.5 +python-levenshtein==0.12.2 +python-magic==0.4.22 +pytz==2021.1 pyyaml==5.4.1 redis==3.5.3 regex==2020.11.13 @@ -85,19 +85,17 @@ sortedcontainers==2.3.0 sqlparse==0.4.1; python_version >= '3.5' threadpoolctl==2.1.0; python_version >= '3.5' tika==1.24 -tqdm==4.56.0 +tqdm==4.57.0 twisted[tls]==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -txaio==20.12.1; python_version >= '3.6' -typing-extensions==3.7.4.3; python_version < '3.8' +txaio==21.2.1; python_version >= '3.6' tzlocal==2.1 urllib3==1.26.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4' -uvicorn[standard]==0.13.3 +uvicorn[standard]==0.13.4 uvloop==0.14.0 watchdog==1.0.2 -watchgod==0.6 +watchgod==0.7 wcwidth==0.2.5 websockets==8.1 whitenoise==5.2.0 whoosh==2.7.4 -zipp==3.4.0; python_version >= '3.6' zope.interface==5.2.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' diff --git a/resources/logo.txt b/resources/logo.txt new file mode 100644 index 000000000..75a1d2c75 --- /dev/null +++ b/resources/logo.txt @@ -0,0 +1,16 @@ + 9w + {@@N + Q@@@@H + G@@@@@@@\ + SilN@@@@@@@ + *Q *@@@@@@@@S /= = = = = = = = = = = = = = = = = =\ + *@ B@@@@@@@@N || || + N R$ A@@@@@@@@@@ || PAPERLESS-NG || +x@@ $U B@@@@@@@@@R || || +N@@N^ @ N@@@@@@@@@* \= = = = = = = = = = = = = = = = = =/ +|@@@u @ E@@@@@@@@l + Q@@@ \ Px@@@@@@P + 1@@S` @@@o' + z$ ; + v + / diff --git a/src-ui/angular.json b/src-ui/angular.json index 829a482af..2877408d2 100644 --- a/src-ui/angular.json +++ b/src-ui/angular.json @@ -18,7 +18,9 @@ "locales": { "de": "src/locale/messages.de.xlf", "nl-NL": "src/locale/messages.nl_NL.xlf", - "fr": "src/locale/messages.fr.xlf" + "fr": "src/locale/messages.fr.xlf", + "en-GB": "src/locale/messages.en_GB.xlf", + "pt-BR": "src/locale/messages.pt_BR.xlf" } }, "architect": { @@ -35,6 +37,7 @@ "aot": true, "assets": [ "src/favicon.ico", + "src/apple-touch-icon.png", "src/assets", "src/manifest.webmanifest", { "glob": "pdf.worker.min.js", @@ -111,6 +114,7 @@ "karmaConfig": "karma.conf.js", "assets": [ "src/favicon.ico", + "src/apple-touch-icon.png", "src/assets", "src/manifest.webmanifest" ], diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 86198ca7f..d04d5a6f8 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -52,17 +52,17 @@ - View "" saved successfully. + View "" saved successfully. src/app/components/document-list/document-list.component.ts - 109 + 115 View "" created successfully. src/app/components/document-list/document-list.component.ts - 130 + 136 @@ -114,8 +114,8 @@ 72 - - Save "" + + Save "" src/app/components/document-list/document-list.component.html 71 @@ -188,35 +188,35 @@ Confirm delete src/app/components/document-detail/document-detail.component.ts - 199 + 203 Do you really want to delete document ""? src/app/components/document-detail/document-detail.component.ts - 200 + 204 The files for this document will be deleted permanently. This operation cannot be undone. src/app/components/document-detail/document-detail.component.ts - 201 + 205 Delete document src/app/components/document-detail/document-detail.component.ts - 203 + 207 Error deleting document: src/app/components/document-detail/document-detail.component.ts - 210 + 214 @@ -513,13 +513,6 @@ 1 - - Filter - - src/app/components/manage/logs/logs.component.html - 7 - - Saved view "" deleted. @@ -538,21 +531,21 @@ Use system language src/app/components/manage/settings/settings.component.ts - 91 + 92 Use date format of display language src/app/components/manage/settings/settings.component.ts - 95 + 98 Error while storing settings on server: src/app/components/manage/settings/settings.component.ts - 111 + 115 @@ -1081,6 +1074,13 @@ 46 + + Title: + + src/app/components/document-list/filter-editor/filter-editor.component.ts + 50 + + Filter tags @@ -1219,21 +1219,21 @@ Error executing bulk operation: src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 73 + 74 "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 112 + 113 "" and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 114 + 115 This is for messages like 'modify "tag1" and "tag2"' @@ -1241,7 +1241,7 @@ , src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 116 + 117 this is used to separate enumerations and should probably be a comma and a whitespace in most languages @@ -1249,7 +1249,7 @@ and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 117 + 118 this is for messages like 'modify "tag1", "tag2" and "tag3"' @@ -1257,112 +1257,112 @@ Confirm tags assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 126 + 127 This operation will add the tag "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 129 + 130 This operation will add the tags to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 131 + 132 This operation will remove the tag "" from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 134 + 135 This operation will remove the tags from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 136 + 137 This operation will add the tags and remove the tags on selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 138 + 139 Confirm correspondent assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 158 + 159 This operation will assign the correspondent "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 160 + 161 This operation will remove the correspondent from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 162 + 163 Confirm document type assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 181 + 182 This operation will assign the document type "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 183 + 184 This operation will remove the document type from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 185 + 186 Delete confirm src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 200 + 201 This operation will permanently delete selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 201 + 202 This operation cannot be undone. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 202 + 203 Delete document(s) src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 204 + 205 @@ -1386,6 +1386,13 @@ 27 + + Download originals + + src/app/components/document-list/bulk-editor/bulk-editor.component.html + 68 + + Suggestions: @@ -1414,20 +1421,20 @@ 1 - - Documents in inbox: - - src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html - 3 - - - - Total documents: + + Total documents: src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html 4 + + Documents in inbox: + + src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html + 3 + + Processing: @@ -1470,6 +1477,13 @@ 126 + + HTTP error: + + src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts + 136 + + Upload new documents @@ -1584,6 +1598,13 @@ 21 + + Invalid date. + + src/app/components/common/input/date/date.component.html + 13 + + Yes @@ -1609,49 +1630,70 @@ English (US) src/app/services/settings.service.ts - 82 + 88 + + + + English (GB) + + src/app/services/settings.service.ts + 89 German src/app/services/settings.service.ts - 83 + 90 Dutch src/app/services/settings.service.ts - 84 + 91 French src/app/services/settings.service.ts - 85 + 92 + + + + Portuguese (Brazil) + + src/app/services/settings.service.ts + 93 + + + + ISO 8601 + + src/app/services/settings.service.ts + 98 Document already exists. src/app/services/consumer-status.service.ts - 14 + 15 File not found. src/app/services/consumer-status.service.ts - 15 + 16 Pre-consume script does not exist. src/app/services/consumer-status.service.ts - 16 + 17 Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation @@ -1659,7 +1701,7 @@ Error while executing pre-consume script. src/app/services/consumer-status.service.ts - 17 + 18 Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation @@ -1667,7 +1709,7 @@ Post-consume script does not exist. src/app/services/consumer-status.service.ts - 18 + 19 Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation @@ -1675,7 +1717,7 @@ Error while executing post-consume script. src/app/services/consumer-status.service.ts - 19 + 20 Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation @@ -1683,49 +1725,49 @@ Received new file. src/app/services/consumer-status.service.ts - 20 + 21 File type not supported. src/app/services/consumer-status.service.ts - 21 + 22 Processing document... src/app/services/consumer-status.service.ts - 22 + 23 Generating thumbnail... src/app/services/consumer-status.service.ts - 23 + 24 Retrieving date from document... src/app/services/consumer-status.service.ts - 24 + 25 Saving document... src/app/services/consumer-status.service.ts - 25 + 26 Finished. src/app/services/consumer-status.service.ts - 26 + 27 diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index c3827f80c..fd6d69538 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -2055,9 +2055,9 @@ } }, "@ng-bootstrap/ng-bootstrap": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.0.tgz", - "integrity": "sha512-v77Gfd8xHH+exq0WqIqVRlxbUEHdA/2+RUJenUP2IDTQN9E1rWl7O461/kosr+0XPuxPArHQJxhh/WsCYckcNg==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.4.tgz", + "integrity": "sha512-EdxTwOPOtlvfnwrglPniulmzdnXdXH3lTGaGAY1HrYRvdtGg6wicRvl+BvwVE/3Qik5NPkOWMVghUHpv3evIYg==", "requires": { "tslib": "^2.0.0" } @@ -5545,6 +5545,11 @@ "schema-utils": "^2.6.5" } }, + "file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", diff --git a/src-ui/package.json b/src-ui/package.json index a7cef2581..896caab30 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -20,9 +20,10 @@ "@angular/platform-browser": "~10.1.5", "@angular/platform-browser-dynamic": "~10.1.5", "@angular/router": "~10.1.5", - "@ng-bootstrap/ng-bootstrap": "^8.0.0", + "@ng-bootstrap/ng-bootstrap": "^8.0.4", "@ng-select/ng-select": "^5.0.9", "bootstrap": "^4.5.0", + "file-saver": "^2.0.5", "ng-bootstrap": "^1.6.3", "ngx-cookie-service": "^10.1.1", "ngx-file-drop": "^10.0.0", diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index d63d34306..e9f52d62e 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbDateAdapter, NgbDateParserFormatter, NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { DocumentListComponent } from './components/document-list/document-list.component'; import { DocumentDetailComponent } from './components/document-detail/document-detail.component'; @@ -39,7 +39,6 @@ import { SelectComponent } from './components/common/input/select/select.compone import { CheckComponent } from './components/common/input/check/check.component'; import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; -import { DateTimeComponent } from './components/common/input/date-time/date-time.component'; import { TagsComponent } from './components/common/input/tags/tags.component'; import { SortableDirective } from './directives/sortable.directive'; import { CookieService } from 'ngx-cookie-service'; @@ -59,14 +58,21 @@ import { NgSelectModule } from '@ng-select/ng-select'; import { NumberComponent } from './components/common/input/number/number.component'; import { SafePipe } from './pipes/safe.pipe'; import { CustomDatePipe } from './pipes/custom-date.pipe'; +import { DateComponent } from './components/common/input/date/date.component'; +import { ISODateTimeAdapter } from './utils/ngb-iso-date-time-adapter'; +import { LocalizedDateParserFormatter } from './utils/ngb-date-parser-formatter'; import localeFr from '@angular/common/locales/fr'; import localeNl from '@angular/common/locales/nl'; import localeDe from '@angular/common/locales/de'; +import localePt from '@angular/common/locales/pt-PT'; +import localeEnGb from '@angular/common/locales/en-GB'; registerLocaleData(localeFr) registerLocaleData(localeNl) registerLocaleData(localeDe) +registerLocaleData(localePt, "pt-BR") +registerLocaleData(localeEnGb) @NgModule({ declarations: [ @@ -101,7 +107,6 @@ registerLocaleData(localeDe) SelectComponent, CheckComponent, SaveViewConfigDialogComponent, - DateTimeComponent, TagsComponent, SortableDirective, SavedViewWidgetComponent, @@ -117,7 +122,8 @@ registerLocaleData(localeDe) SelectDialogComponent, NumberComponent, SafePipe, - CustomDatePipe + CustomDatePipe, + DateComponent ], imports: [ BrowserModule, @@ -138,7 +144,9 @@ registerLocaleData(localeDe) multi: true }, FilterPipe, - DocumentTitlePipe + DocumentTitlePipe, + {provide: NgbDateAdapter, useClass: ISODateTimeAdapter}, + {provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter} ], bootstrap: [AppComponent] }) diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html index 70329d537..34a978fe5 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.html +++ b/src-ui/src/app/components/app-frame/app-frame.component.html @@ -163,13 +163,13 @@ - -
+
diff --git a/src-ui/src/app/components/manage/settings/settings.component.ts b/src-ui/src/app/components/manage/settings/settings.component.ts index 368a01027..62b4b8be8 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -34,7 +34,7 @@ export class SettingsComponent implements OnInit { savedViews: PaperlessSavedView[] get computedDateLocale(): string { - return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage + return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage || this.currentLocale } constructor( @@ -86,11 +86,15 @@ export class SettingsComponent implements OnInit { } get displayLanguageOptions(): LanguageOption[] { - return [{code: "", name: $localize`Use system language`}].concat(this.settings.getLanguageOptions()) + return [ + {code: "", name: $localize`Use system language`} + ].concat(this.settings.getLanguageOptions()) } get dateLocaleOptions(): LanguageOption[] { - return [{code: "", name: $localize`Use date format of display language`}].concat(this.settings.getLanguageOptions()) + return [ + {code: "", name: $localize`Use date format of display language`} + ].concat(this.settings.getDateLocaleOptions()) } get today() { diff --git a/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html index fd4c3b7f0..418a5c281 100644 --- a/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html +++ b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html @@ -20,7 +20,7 @@ - +