Paperless-ngx (v.1.7.0) auf Ubuntu 22.04lts Server installieren

Ich bin stark daran interessiert den ganzen sich ansammelnden Papierwust etwas professioneller zu handhaben. Nachdem ich mir dazu auf YT einige echt gute Beiträge von Christian Zengel angesehen habe, will ich mir mal eine VitualBox bauen um mir selbst eine Meinung zu dem Thema bilden zu können.

Ich habe mir für solche Zwecke eine standard Ubuntu22.04lts Server aus Ausgangspunkt aufgesetzt und den Klone ich dann je nachdem, was ich testen/probieren möchte.

Die Requirements auf GIT zu Paperless-NGX sind schon sehr umfangreich, also zerlegen wir das mal in handliche Portionen.

Paperless-ngx framework

Zum Zeitpunkt der Erstellung dieses Beitrags hat sich das Paperless-ngx Framework wie im Bild für mich dargestellt. Mag sein, dass ich das noch nicht ganz durchblickt habe – aber genau dafür ist ja das Meinung bilden da. Na dann starten wir mal mit der Postgre-SQL Datenbank.

1. Postgre-SQL Datenbank installieren

In diesem Abschnitt geht es um die Installation und Konfiguration einer lokalen PostgreSQL-Datenbank.

sudo apt update
sudo apt install -y postgresql

Sobald PostgreSQL installiert ist, starten wir den Dienst und aktivieren Sie ihn für die Ausführung beim Booten:

sudo systemctl start postgresql
sudo systemctl enable postgresql

Ich konnte zwar Paperless-ngx kein Requirement zur Version der Postgre-SQL Datenbank finden, aber ich vermute „latest and greatest“ kann nicht schaden. Sehen wir uns mal an, welche Version wir von Postgre-SQL erwischt haben.

psql -V

user@2204lts:~$ psql -V
psql (PostgreSQL) 14.2 (Ubuntu 14.2-1ubuntu1)

Zum Zeitpunkt der Beitragserstellung war 14.2 die letzte Version der Postgre-SQL Datenbank.

Erstellung der Portgre-SQL Datenbank für Paperless-ngx

Wir müssen zumindest eine Datenbank für Paperless-ngx erstellen und ihr einen Benutzernamen und ein Passwort für die Authentifizierung zuweisen. Rufen wir zunächst die PostgreSQL-Shell als Postgres-Systembenutzer auf.

sudo -u postgres psql

could not change directory to "/home/user": Permission denied
psql (14.2 (Ubuntu 14.2-1ubuntu1))
Type "help" for help.

postgres=# 

Geben wir in der Shell die folgenden Befehle ein, um die Datenbank und den Benutzer (die Rolle) anzulegen, und ersetzen Sie das Passwort durch Ihren eigenen Wert:

CREATE DATABASE paperlessngx;
CREATE USER paperlessngx WITH PASSWORD 'd862ca8ad319429e42476b3494183b6570e56';
GRANT ALL PRIVILEGES ON DATABASE paperlessngx TO paperlessngx;

Verwende ein sicheres Passwort!

Verwende nicht das Passwort aus dem Beispiel. Wählen ein sicheres, zufälliges Passwort, um eine sichere Datenbankauthentifizierung für deine Paperless-ngx Installation zu gewährleisten.

Wenn du fertig bist, gib \q ein, um die PostgreSQL-Shell zu beenden.

Überprüfen des Postgre-SQL Datenbankstatus

Wir können überprüfen, ob die Authentifizierung funktioniert, indem Sie den Befehl psql ausführen und den konfigurierten Benutzernamen und das Passwort übergeben. (Ersetzen Sie localhost durch Ihren Datenbankserver, wenn Sie eine Remote-Datenbank verwenden).

psql --username paperlessngx --password --host localhost paperlessngx

user@2204lts:~$ psql --username paperlessngx --password --host localhost paperlessngx
Password:
psql (14.2 (Ubuntu 14.2-1ubuntu1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.

paperlessngx=> \conninfo
You are connected to database "paperlessngx" as user "paperlessngx" on host "localhost" (address "127.0.0.1") at port "5432".
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)

paperlessngx=> \q

Wenn die Verbindung erfolgreich war, wird eine Paperlessngx-Eingabeaufforderung angezeigt. Geben Sie \conninfo ein, um Ihre Verbindung zu bestätigen, oder geben Sie \q ein, um die Verbindung zu beenden.

2. Redis Installation

Redis ist ein In-Memory Key-Value Speicher, der von paperless-ngx vermutlich für das Caching und die Warteschlangenbildung verwendet wird. In diesem Abschnitt geht es um die Installation und Konfiguration einer lokalen Redis-Instanz. Wenn du bereits einen Redis-Dienst eingerichtet haben, kannst du dem nächsten Abschnitt fortfahren. Im Gegensatz zur Postgre-SQL gibt es hier ein Requirement. Die Redis Instanz soll >= Version 5.0 sein.

sudo apt install -y redis-server

Bevor wir fortfahren, vergewissern wir uns, dass die installierte Version von Redis mindestens v5.0 ist:

redis-server -v
user@2204lts:~$ redis-server -v
Redis server v=6.0.16 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=a3fdef44459b3ad6

Das sieht gut aus, im verwendeten Ubuntu 22.04lts ist zur Zeit Redis Version 6.0.16 am Start.

Sobald Redis installiert ist, starten wir den Dienst und aktivieren Sie ihn für die Ausführung beim Boot.

sudo systemctl start redis-server
sudo systemctl enable redis-server

Du kannst die Redis-Konfiguration in /etc/redis.conf oder /etc/redis/redis.conf ändern, in den meisten Fällen ist die Standardkonfiguration jedoch ausreichend.

Überprüfen des Redis-Service Status

Sehen wir mal nach, ob der Serviceläuft:

user@2204lts:~$ sudo systemctl status redis-server
● redis-server.service - Advanced key-value store
     Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2022-05-07 16:50:01 UTC; 4min 20s ago
       Docs: http://redis.io/documentation,
             man:redis-server(1)
   Main PID: 4259 (redis-server)
     Status: "Ready to accept connections"
      Tasks: 5 (limit: 9410)
     Memory: 2.6M
        CPU: 535ms
     CGroup: /system.slice/redis-server.service
             └─4259 "/usr/bin/redis-server 127.0.0.1:6379" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ">

Mai 07 16:50:01 2204lts systemd[1]: Starting Advanced key-value store...
Mai 07 16:50:01 2204lts systemd[1]: Started Advanced key-value store.
user@2204lts:~$

Das sieht soweit gut aus und nun prüfen wir, ob der Service auch Requests anwortet durch Verwenden des Dienstprogrammes redis-cli, um sicherzustellen, dass der Redis-Dienst funktionsfähig ist:

user@2204lts:~$ redis-cli ping
PONG

Wenn wir ein PONG von Redis erhalten, ist alles in Butter.

3. Tesseract Installation

Die hereinkommenden Dokumente, Zeichnungen, Grafiken werde bei paperless-nhx durch die OCR Software Tesseract analysiert. Hierfür benötigt Paperless-ngx die Version >= 4.0.0 von Tesseract. Meine Dokumente sind aus den Deutsch- und Englisch-Raum, weshalb ich gleich die passenden OCR Module mit installiere.

sudo apt install tesseract-ocr tesseract-ocr-deu tesseract-ocr-frk tesseract-ocr-eng

Bevor wir fortfahren, vergewissern wir uns, dass die installierte Version von Tesseract mindestens v4.0.0 ist:

tesseract --version
user@2204lts:~$ tesseract --version
tesseract 4.1.1
 leptonica-1.82.0
  libgif 5.1.9 : libjpeg 8d (libjpeg-turbo 2.1.1) : libpng 1.6.37 : libtiff 4.3.0 : zlib 1.2.11 : libwebp 1.2.2 : libopenjp2 2.4.0
 Found AVX2
 Found AVX
 Found SSE
 Found libarchive 3.6.0 zlib/1.2.11 liblzma/5.2.5 bz2lib/1.0.8 liblz4/1.9.3 libzstd/1.4.8

Das sieht gut aus, im verwendeten Ubuntu 22.04lts ist zur Zeit Tesseract Version 4.1.1 am Start.

4. Paperless-ngx Installation

Kommen wir zum Kern des Beitrags, wir wollen uns ja Paperless-ngx genauer ansehen, fangen wir mit der Installation an. Laut Anleitung sind hier folgende Pakete zu installieren:

sudo apt install -y python3 python3-pip python3-dev imagemagick fonts-liberation optipng gnupg libpq-dev libmagic-dev mime-support libzbar0 poppler-utils unpaper ghostscript icc-profiles-free qpdf liblept5 libxml2 pngquant zlib1g unzip

Nach der Installation fragt Ubuntu welche Services durchgestartet werden sollen. Ich bin eher ein Freund davon, dem gesamten System einen Schubs zu geben. Ist ja noch nicht produktiv..

sudo reboot

Na dann werden wir uns mal die aktuelle Version von Paperless-ngx vom Git holen, zum Zeitpunkt der Beitragserstellung war das die Version 1.7.0. Ich entpacke das immer gerne nach /opt.

sudo wget https://github.com/paperless-ngx/paperless-ngx/releases/download/ngx-1.7.0/paperless-ngx-1.7.0.tar.xz
sudo tar -xvf paperless-ngx-1.7.0.tar.xz -C /opt

Leider hängt keine Versionsnummer am Verzeichnis und das macht ggf. das spätere handling beim Update schwierig. Ich verschiebe das mal in ein passendes Verzeichnis /opt/paperless-ngx_v1.7.0 und mache mir einen Softlink. Hintergrund ist dabei ein Paperless-ngx in einem Verzeichnis so zu installieren, das nach seiner Versionsnummer benannt ist. Also die gerade aktuelle Version 1.7.0 würde beispielsweise in /opt/paperless-ngx_v1.7.0 installiert werden, und ein Symlink von /opt/paperless-ngx/ würde auf dieses Verzeichnis verweisen. (Du kannst diese Konfiguration mit dem Befehl ls -l /opt | grep paperless-ngx überprüfen.) Auf diese Weise können zukünftige Versionen parallel installiert werden, ohne die aktuelle Installation zu unterbrechen. Beim Wechsel zu einer neuen Version muss nur der Symlink aktualisiert werden.

sudo mv /opt/paperless-ngx /opt/paperless-ngx_v1.7.0
sudo ln -s /opt/paperless-ngx_v1.7.0/ /opt/paperless-ngx

5. Paperless-ngx Konfiguration

Im Verzeichnis /opt/paperless-ngx befindet sich eine paperless.conf. Von der mache ich vor dem ersten editieren ein Backup.

sudo cp paperless.conf paperless.conf.org

Nun tragen wir mal unsere Konfigdaten in die paperless.conf.

Fangen wir mit Redis und Postgre-SQL an.

# Required services
PAPERLESS_REDIS=redis://localhost:6379
PAPERLESS_DBHOST=localhost
PAPERLESS_DBPORT=5432
PAPERLESS_DBNAME=paperlessngx
PAPERLESS_DBUSER=paperlessngx
PAPERLESS_DBPASS=d862ca8ad319429e42476b3494183b6570e56
PAPERLESS_DBSSLMODE=prefer

Als nächstes möchte paperless-ngx in der Konfig die Pfade zu seinen Folders haben. Legen wir in /opt die passenden Verzeichnisse an und tragen sie in der paperless.conf ein.

sudo mkdir /opt/paperless
sudo mkdir /opt/paperless/consumption
sudo mkdir /opt/paperless/data
sudo mkdir /opt/paperless/media-root
sudo mkdir /opt/paperless/trash

# Paths and folders
PAPERLESS_CONSUMPTION_DIR=/opt/paperless/consumption
PAPERLESS_DATA_DIR=/opt/paperless/data
PAPERLESS_TRASH_DIR=/opt/paperless/trash
PAPERLESS_MEDIA_ROOT=/opt/paperless/media-root
PAPERLESS_STATICDIR=/opt/paperless-ngx/static
#PAPERLESS_FILENAME_FORMAT=

Als nächstes kümmern wir uns um die Einstellungen für unsere OCR, den Konfigabschnitt für Tesseract. Soweit ich mich mit den Einstellungen vertraut gemacht habe sollte folgendes Setup für mich funktionieren. Wenn ich mit den Ergebnissen unzufrieden bin, werde ich später nochmal Hand anlegen.

# OCR settings
PAPERLESS_OCR_LANGUAGE=deu+eng
PAPERLESS_OCR_MODE=skip
PAPERLESS_OCR_OUTPUT_TYPE=pdfa
PAPERLESS_OCR_PAGES=0
#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=512000000
PAPERLESS_CONVERT_TMPDIR=/var/tmp

Der nächste Schritt sind die Software Tweaks, für mich sollte folgendes funktionieren. Ich hab die VBox mit vier CPUS aufgesetzt..

# Software tweaks
PAPERLESS_TASK_WORKERS=2
PAPERLESS_THREADS_PER_WORKER=2
PAPERLESS_TIME_ZONE=Europe/Berlin
#PAPERLESS_CONSUMER_POLLING=10
PAPERLESS_CONSUMER_DELETE_DUPLICATES=false
PAPERLESS_CONSUMER_RECURSIVE=false
PAPERLESS_CONSUMER_IGNORE_PATTERNS=[".DS_STORE/*", "._*", ".stfolder/*", ".stversions/*", ".localized/*", "desktop.ini"]
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=false
PAPERLESS_CONSUMER_ENABLE_BARCODES=false
PAPERLESS_CONSUMER_ENABLE_BARCODES=PATCHT
PAPERLESS_OPTIMIZE_THUMBNAILS=true
#PAPERLESS_PRE_CONSUME_SCRIPT=/path/to/an/arbitrary/script.sh
#PAPERLESS_POST_CONSUME_SCRIPT=/path/to/an/arbitrary/script.sh
PAPERLESS_FILENAME_DATE_ORDER=YMD
#PAPERLESS_FILENAME_PARSE_TRANSFORMS=[]
#PAPERLESS_THUMBNAIL_FONT_NAME=
#PAPERLESS_IGNORE_DATES=
#PAPERLESS_ENABLE_UPDATE_CHECK=

Die Pfade zu die notwendigen binaries möchte er auch gesetzt bekommen.

# Binaries
PAPERLESS_CONVERT_BINARY=/usr/bin/convert
PAPERLESS_GS_BINARY=/usr/bin/gs
PAPERLESS_OPTIPNG_BINARY=/usr/bin/optipng

Es fehlt noch ein paperless-ngx secret key. Den ziehen wir uns am besten aus /dev/random und tragen ihn in der paperless.conf ein.

user@2204lts:/opt/paperless-ngx$ tr -dc A-Za-z0-9 </dev/urandom | head -c 64 ; echo ''

loOS9WEpHQfDUhTVPOstTkILfUk1S9KtDKuHBCitevmzdQFTIDIm986PwFTF6ChI

# Security and hosting
PAPERLESS_SECRET_KEY=loOS9WEpHQfDUhTVPOstTkILfUk1S9KtDKuHBCitevmzdQFTIDIm986PwFTF6ChI
#PAPERLESS_URL=https://example.com
#PAPERLESS_CSRF_TRUSTED_ORIGINS=https://example.com # can be set using PAPERLESS_URL
#PAPERLESS_ALLOWED_HOSTS=example.com,www.example.com # can be set using PAPERLESS_URL
#PAPERLESS_CORS_ALLOWED_HOSTS=https://localhost:8080,https://example.com # can be set using PAPERLESS_URL
#PAPERLESS_FORCE_SCRIPT_NAME=
#PAPERLESS_STATIC_URL=/static/
#PAPERLESS_AUTO_LOGIN_USERNAME=
#PAPERLESS_COOKIE_PREFIX=
#PAPERLESS_ENABLE_HTTP_REMOTE_USER=false

Der Tika Server sagt mir erst einmal nichts und bleibt uninstalliert und unbenutzt. Vielleicht ergibt sich mir die Funktion im folgenden Test.

# Tika settings
PAPERLESS_TIKA_ENABLED=false
#PAPERLESS_TIKA_ENDPOINT=http://localhost:9998
#PAPERLESS_TIKA_GOTENBERG_ENDPOINT=http://localhost:3000

Wir brauche noch einen User für paperless-ngx und der braucht lesende/schreibende Zugriffsrechte auf die vorher angegebenen Verzeichnisse aus:

PAPERLESS_CONSUMPTION_DIR=/opt/paperless/consumption
PAPERLESS_DATA_DIR=/opt/paperless/data
PAPERLESS_TRASH_DIR=/opt/paperless/trash
PAPERLESS_MEDIA_ROOT=/opt/paperless/media-root

Na dann legen wir den User mal an und drehen an den Zugriffsrechten.

sudo adduser paperless --system --home /opt/paperless --group

sudo chown -Rv paperless:paperless /opt/paperless
changed ownership of '/opt/paperless/trash' from root:root to paperless:paperless
changed ownership of '/opt/paperless/consumption' from root:root to paperless:paperless
changed ownership of '/opt/paperless/media-root' from root:root to paperless:paperless
changed ownership of '/opt/paperless/data' from root:root to paperless:paperless
changed ownership of '/opt/paperless' from root:root to paperless:paperless

Scheinbar war es das in der paperless.conf.

6. Installieren der Requirments

Wenden wir uns den weiteren Python Requirements von paperless zu. Diese stehen im File /opt/paperless-ngx/requirments.txt. Zu erst einmal updaten wir pip und dann installieren wir die Python Module nach.

sudo -Hu paperless pip3 install --upgrade pip

user@2204lts:/opt/paperless-ngx$ sudo -Hu paperless pip3 install --upgrade pip
Defaulting to user installation because normal site-packages is not writeable
Requirement already satisfied: pip in /usr/lib/python3/dist-packages (22.0.2)
Collecting pip
  Downloading pip-22.0.4-py3-none-any.whl (2.1 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1/2.1 MB 15.2 MB/s eta 0:00:00
Installing collected packages: pip
  WARNING: The scripts pip, pip3 and pip3.10 are installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed pip-22.0.4
user@2204lts:/opt/paperless-ngx$

Okay pip3 ist jetzt auf der aktuellen Version 22.0.4. Und jetzt die restlichen Requirements installieren.

sudo -Hu paperless pip3 install -r requirements.txt

user@2204lts:/opt/paperless-ngx$ sudo -Hu paperless pip3 install -r requirements.txt
Defaulting to user installation because normal site-packages is not writeable
Looking in indexes: https://pypi.python.org/simple/, https://www.piwheels.org/simple/
Ignoring backports.zoneinfo: markers 'python_version < "3.9"' don't match your environment
Ignoring importlib-resources: markers 'python_version < "3.9"' don't match your environment
Ignoring zipp: markers 'python_version < "3.9"' don't match your environment
Collecting aioredis==1.3.1
  Downloading https://www.piwheels.org/simple/aioredis/aioredis-1.3.1-py3-none-any.whl (65 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 65.3/65.3 KB 1.7 MB/s eta 0:00:00
Collecting anyio==3.5.0
  Downloading https://www.piwheels.org/simple/anyio/anyio-3.5.0-py3-none-any.whl (79 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 79.2/79.2 KB 2.5 MB/s eta 0:00:00
Collecting arrow==1.2.2
  Downloading https://www.piwheels.org/simple/arrow/arrow-1.2.2-py3-none-any.whl (64 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 64.0/64.0 KB 1.8 MB/s eta 0:00:00
Collecting asgiref==3.5.0
  Downloading https://www.piwheels.org/simple/asgiref/asgiref-3.5.0-py3-none-any.whl (22 kB)
Collecting async-timeout==4.0.2
  Downloading https://www.piwheels.org/simple/async-timeout/async_timeout-4.0.2-py3-none-any.whl (5.8 kB)
Collecting attrs==21.4.0
  Downloading https://www.piwheels.org/simple/attrs/attrs-21.4.0-py2.py3-none-any.whl (60 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 60.5/60.5 KB 13.0 MB/s eta 0:00:00
Collecting autobahn==22.3.2
  Downloading autobahn-22.3.2.tar.gz (376 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 376.0/376.0 KB 4.8 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
Requirement already satisfied: automat==20.2.0 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 17)) (20.2.0)
Collecting blessed==1.19.1
  Downloading https://www.piwheels.org/simple/blessed/blessed-1.19.1-py2.py3-none-any.whl (62 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.1/62.1 KB 1.3 MB/s eta 0:00:00
Collecting certifi==2021.10.8
  Downloading https://www.piwheels.org/simple/certifi/certifi-2021.10.8-py2.py3-none-any.whl (151 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 151.0/151.0 KB 4.6 MB/s eta 0:00:00
Collecting cffi==1.15.0
  Downloading cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (446 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 446.3/446.3 KB 22.2 MB/s eta 0:00:00
Collecting channels-redis==3.4.0
  Downloading https://www.piwheels.org/simple/channels-redis/channels_redis-3.4.0-py3-none-any.whl (20 kB)
Collecting channels==3.0.4
  Downloading https://www.piwheels.org/simple/channels/channels-3.0.4-py3-none-any.whl (38 kB)
Requirement already satisfied: chardet==4.0.0 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 24)) (4.0.0)
Collecting charset-normalizer==2.0.12
  Downloading https://www.piwheels.org/simple/charset-normalizer/charset_normalizer-2.0.12-py3-none-any.whl (44 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 44.2/44.2 KB 9.4 MB/s eta 0:00:00
Collecting click==8.1.2
  Downloading https://www.piwheels.org/simple/click/click-8.1.2-py3-none-any.whl (96 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 96.6/96.6 KB 2.9 MB/s eta 0:00:00
Collecting coloredlogs==15.0.1
  Downloading https://www.piwheels.org/simple/coloredlogs/coloredlogs-15.0.1-py2.py3-none-any.whl (46 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 46.0/46.0 KB 7.6 MB/s eta 0:00:00
Collecting concurrent-log-handler==0.9.20
  Downloading https://www.piwheels.org/simple/concurrent-log-handler/concurrent_log_handler-0.9.20-py2.py3-none-any.whl (25 kB)
Requirement already satisfied: constantly==15.1.0 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 29)) (15.1.0)
Collecting cryptography==36.0.2
  Downloading cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl (3.6 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.6/3.6 MB 43.0 MB/s eta 0:00:00
Collecting daphne==3.0.2
  Downloading https://www.piwheels.org/simple/daphne/daphne-3.0.2-py3-none-any.whl (26 kB)
Collecting dateparser==1.1.1
  Downloading https://www.piwheels.org/simple/dateparser/dateparser-1.1.1-py2.py3-none-any.whl (296 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 296.8/296.8 KB 3.3 MB/s eta 0:00:00
Collecting django-cors-headers==3.11.0
  Downloading https://www.piwheels.org/simple/django-cors-headers/django_cors_headers-3.11.0-py3-none-any.whl (12 kB)
Collecting django-extensions==3.1.5
  Downloading https://www.piwheels.org/simple/django-extensions/django_extensions-3.1.5-py3-none-any.whl (224 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 224.2/224.2 KB 5.9 MB/s eta 0:00:00
Collecting django-filter==21.1
  Downloading https://www.piwheels.org/simple/django-filter/django_filter-21.1-py3-none-any.whl (81 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 81.4/81.4 KB 15.3 MB/s eta 0:00:00
Collecting django-picklefield==3.0.1
  Downloading https://www.piwheels.org/simple/django-picklefield/django_picklefield-3.0.1-py3-none-any.whl (9.4 kB)
Collecting django-q==1.3.9
  Downloading https://www.piwheels.org/simple/django-q/django_q-1.3.9-py3-none-any.whl (89 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 90.0/90.0 KB 22.2 MB/s eta 0:00:00
Collecting django==4.0.4
  Downloading https://www.piwheels.org/simple/django/Django-4.0.4-py3-none-any.whl (8.0 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.0/8.0 MB 8.5 MB/s eta 0:00:00
Collecting djangorestframework==3.13.1
  Downloading https://www.piwheels.org/simple/djangorestframework/djangorestframework-3.13.1-py3-none-any.whl (958 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 958.3/958.3 KB 13.0 MB/s eta 0:00:00
Collecting filelock==3.6.0
  Downloading https://www.piwheels.org/simple/filelock/filelock-3.6.0-py3-none-any.whl (10.0 kB)
Collecting fuzzywuzzy[speedup]==0.18.0
  Downloading https://www.piwheels.org/simple/fuzzywuzzy/fuzzywuzzy-0.18.0-py2.py3-none-any.whl (18 kB)
Collecting gunicorn==20.1.0
  Downloading https://www.piwheels.org/simple/gunicorn/gunicorn-20.1.0-py3-none-any.whl (81 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 81.0/81.0 KB 18.0 MB/s eta 0:00:00
Collecting h11==0.13.0
  Downloading https://www.piwheels.org/simple/h11/h11-0.13.0-py3-none-any.whl (58 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 58.2/58.2 KB 16.3 MB/s eta 0:00:00
Collecting hiredis==2.0.0
  Downloading hiredis-2.0.0.tar.gz (75 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 75.8/75.8 KB 17.1 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
Collecting httptools==0.4.0
  Downloading httptools-0.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (434 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 434.5/434.5 KB 70.7 MB/s eta 0:00:00
Collecting humanfriendly==10.0
  Downloading https://www.piwheels.org/simple/humanfriendly/humanfriendly-10.0-py2.py3-none-any.whl (89 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.9/89.9 KB 2.9 MB/s eta 0:00:00
Requirement already satisfied: hyperlink==21.0.0 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 47)) (21.0.0)
Requirement already satisfied: idna==3.3 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 48)) (3.3)
Collecting imap-tools==0.53.0
  Downloading https://www.piwheels.org/simple/imap-tools/imap_tools-0.53.0-py3-none-any.whl (33 kB)
Collecting img2pdf==0.4.4
  Downloading https://www.piwheels.org/simple/img2pdf/img2pdf-0.4.4-py3-none-any.whl (47 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 47.3/47.3 KB 1.3 MB/s eta 0:00:00
Requirement already satisfied: incremental==21.3.0 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 52)) (21.3.0)
Collecting inotify-simple==1.3.5
  Downloading https://www.piwheels.org/simple/inotify-simple/inotify_simple-1.3.5-py3-none-any.whl (9.4 kB)
Collecting inotifyrecursive==0.3.5
  Downloading https://www.piwheels.org/simple/inotifyrecursive/inotifyrecursive-0.3.5-py3-none-any.whl (8.0 kB)
Collecting joblib==1.1.0
  Downloading https://www.piwheels.org/simple/joblib/joblib-1.1.0-py2.py3-none-any.whl (306 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 307.0/307.0 KB 3.8 MB/s eta 0:00:00
Collecting langdetect==1.0.9
  Downloading https://www.piwheels.org/simple/langdetect/langdetect-1.0.9-py3-none-any.whl (994 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 994.5/994.5 KB 14.4 MB/s eta 0:00:00
Collecting lxml==4.8.0
  Downloading lxml-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (7.0 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.0/7.0 MB 60.2 MB/s eta 0:00:00
Collecting msgpack==1.0.3
  Downloading msgpack-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (323 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 323.7/323.7 KB 65.4 MB/s eta 0:00:00
Collecting numpy==1.22.3
  Downloading numpy-1.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (16.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16.8/16.8 MB 53.4 MB/s eta 0:00:00
Collecting ocrmypdf==13.4.2
  Downloading https://www.piwheels.org/simple/ocrmypdf/ocrmypdf-13.4.2-py37-none-any.whl (123 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 123.2/123.2 KB 1.9 MB/s eta 0:00:00
Collecting packaging==21.3
  Downloading https://www.piwheels.org/simple/packaging/packaging-21.3-py3-none-any.whl (40 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 40.8/40.8 KB 12.4 MB/s eta 0:00:00
Collecting pathvalidate==2.5.0
  Downloading https://www.piwheels.org/simple/pathvalidate/pathvalidate-2.5.0-py3-none-any.whl (19 kB)
Collecting pdf2image==1.16.0
  Downloading https://www.piwheels.org/simple/pdf2image/pdf2image-1.16.0-py3-none-any.whl (10 kB)
Collecting pdfminer.six==20220319
  Downloading https://www.piwheels.org/simple/pdfminer-six/pdfminer.six-20220319-py3-none-any.whl (5.6 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.6/5.6 MB 15.4 MB/s eta 0:00:00
Collecting pikepdf==5.1.1
  Downloading pikepdf-5.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.3 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.3/2.3 MB 61.0 MB/s eta 0:00:00
Collecting pillow==9.1.0
  Downloading Pillow-9.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.3 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.3/4.3 MB 56.5 MB/s eta 0:00:00
Collecting pluggy==1.0.0
  Downloading https://www.piwheels.org/simple/pluggy/pluggy-1.0.0-py2.py3-none-any.whl (13 kB)
Collecting portalocker==2.4.0
  Downloading https://www.piwheels.org/simple/portalocker/portalocker-2.4.0-py2.py3-none-any.whl (16 kB)
Collecting psycopg2==2.9.3
  Downloading psycopg2-2.9.3.tar.gz (380 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 380.6/380.6 KB 69.1 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
Collecting pyasn1-modules==0.2.8
  Downloading https://www.piwheels.org/simple/pyasn1-modules/pyasn1_modules-0.2.8-py2.py3-none-any.whl (155 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 155.3/155.3 KB 2.5 MB/s eta 0:00:00
Requirement already satisfied: pyasn1==0.4.8 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 71)) (0.4.8)
Collecting pycparser==2.21
  Downloading https://www.piwheels.org/simple/pycparser/pycparser-2.21-py2.py3-none-any.whl (119 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 119.7/119.7 KB 25.0 MB/s eta 0:00:00
Collecting pyopenssl==22.0.0
  Downloading https://www.piwheels.org/simple/pyopenssl/pyOpenSSL-22.0.0-py2.py3-none-any.whl (55 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 55.8/55.8 KB 13.6 MB/s eta 0:00:00
Collecting pyparsing==3.0.8
  Downloading https://www.piwheels.org/simple/pyparsing/pyparsing-3.0.8-py3-none-any.whl (98 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 98.5/98.5 KB 16.9 MB/s eta 0:00:00
Collecting python-dateutil==2.8.2
  Downloading https://www.piwheels.org/simple/python-dateutil/python_dateutil-2.8.2-py2.py3-none-any.whl (247 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 247.7/247.7 KB 2.7 MB/s eta 0:00:00
Collecting python-dotenv==0.20.0
  Downloading https://www.piwheels.org/simple/python-dotenv/python_dotenv-0.20.0-py3-none-any.whl (17 kB)
Collecting python-gnupg==0.4.8
  Downloading https://www.piwheels.org/simple/python-gnupg/python_gnupg-0.4.8-py2.py3-none-any.whl (18 kB)
Collecting python-levenshtein==0.12.2
  Downloading python-Levenshtein-0.12.2.tar.gz (50 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 50.5/50.5 KB 8.6 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
Collecting python-magic==0.4.25
  Downloading https://www.piwheels.org/simple/python-magic/python_magic-0.4.25-py2.py3-none-any.whl (15 kB)
Collecting pytz-deprecation-shim==0.1.0.post0
  Downloading https://www.piwheels.org/simple/pytz-deprecation-shim/pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl (15 kB)
Requirement already satisfied: pytz==2022.1 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 81)) (2022.1)
Collecting pyyaml==6.0
  Downloading PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (682 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 682.2/682.2 KB 68.0 MB/s eta 0:00:00
Collecting pyzbar==0.1.9
  Downloading pyzbar-0.1.9-py2.py3-none-any.whl (32 kB)
Collecting redis==3.5.3
  Downloading https://www.piwheels.org/simple/redis/redis-3.5.3-py2.py3-none-any.whl (84 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 84.2/84.2 KB 2.1 MB/s eta 0:00:00
Collecting regex==2022.3.2
  Downloading regex-2022.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (764 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 764.2/764.2 KB 65.1 MB/s eta 0:00:00
Collecting reportlab==3.6.9
  Downloading reportlab-3.6.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.8/2.8 MB 53.5 MB/s eta 0:00:00
Collecting requests==2.27.1
  Downloading https://www.piwheels.org/simple/requests/requests-2.27.1-py2.py3-none-any.whl (63 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 63.1/63.1 KB 1.4 MB/s eta 0:00:00
Collecting scikit-learn==1.0.2
  Downloading scikit_learn-1.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (26.5 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 26.5/26.5 MB 39.6 MB/s eta 0:00:00
Collecting scipy==1.8.0
  Downloading scipy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (42.3 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 42.3/42.3 MB 27.9 MB/s eta 0:00:00
Collecting service-identity==21.1.0
  Downloading https://www.piwheels.org/simple/service-identity/service_identity-21.1.0-py2.py3-none-any.whl (12 kB)
Collecting setuptools==62.1.0
  Downloading https://www.piwheels.org/simple/setuptools/setuptools-62.1.0-py3-none-any.whl (1.1 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.1/1.1 MB 4.3 MB/s eta 0:00:00
Requirement already satisfied: six==1.16.0 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 92)) (1.16.0)
Collecting sniffio==1.2.0
  Downloading https://www.piwheels.org/simple/sniffio/sniffio-1.2.0-py3-none-any.whl (10 kB)
Collecting sqlparse==0.4.2
  Downloading https://www.piwheels.org/simple/sqlparse/sqlparse-0.4.2-py3-none-any.whl (40 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 41.0/41.0 KB 8.5 MB/s eta 0:00:00
Collecting threadpoolctl==3.1.0
  Downloading https://www.piwheels.org/simple/threadpoolctl/threadpoolctl-3.1.0-py3-none-any.whl (14 kB)
Collecting tika==1.24
  Downloading https://www.piwheels.org/simple/tika/tika-1.24-py3-none-any.whl (34 kB)
Collecting tqdm==4.64.0
  Downloading https://www.piwheels.org/simple/tqdm/tqdm-4.64.0-py2.py3-none-any.whl (78 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 78.4/78.4 KB 11.8 MB/s eta 0:00:00
Collecting twisted[tls]==22.4.0
  Downloading https://www.piwheels.org/simple/twisted/Twisted-22.4.0-py3-none-any.whl (3.1 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.1/3.1 MB 6.8 MB/s eta 0:00:00
Collecting txaio==22.2.1
  Downloading https://www.piwheels.org/simple/txaio/txaio-22.2.1-py2.py3-none-any.whl (30 kB)
Collecting typing-extensions==4.1.1
  Downloading https://www.piwheels.org/simple/typing-extensions/typing_extensions-4.1.1-py3-none-any.whl (26 kB)
Collecting tzdata==2022.1
  Downloading https://www.piwheels.org/simple/tzdata/tzdata-2022.1-py2.py3-none-any.whl (339 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 339.4/339.4 KB 7.4 MB/s eta 0:00:00
Collecting tzlocal==4.2
  Downloading https://www.piwheels.org/simple/tzlocal/tzlocal-4.2-py3-none-any.whl (19 kB)
Collecting urllib3==1.26.9
  Downloading https://www.piwheels.org/simple/urllib3/urllib3-1.26.9-py2.py3-none-any.whl (155 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 155.9/155.9 KB 5.1 MB/s eta 0:00:00
Collecting uvicorn[standard]==0.17.6
  Downloading https://www.piwheels.org/simple/uvicorn/uvicorn-0.17.6-py3-none-any.whl (53 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 53.6/53.6 KB 10.7 MB/s eta 0:00:00
Collecting uvloop==0.16.0
  Downloading uvloop-0.16.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (4.3 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.3/4.3 MB 55.7 MB/s eta 0:00:00
Collecting watchdog==2.1.7
  Downloading watchdog-2.1.7-py3-none-manylinux2014_x86_64.whl (76 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 76.8/76.8 KB 16.4 MB/s eta 0:00:00
Collecting watchgod==0.8.2
  Downloading https://www.piwheels.org/simple/watchgod/watchgod-0.8.2-py3-none-any.whl (12 kB)
Collecting wcwidth==0.2.5
  Downloading https://www.piwheels.org/simple/wcwidth/wcwidth-0.2.5-py2.py3-none-any.whl (30 kB)
Collecting websockets==10.2
  Downloading websockets-10.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (110 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 110.8/110.8 KB 26.8 MB/s eta 0:00:00
Collecting whitenoise==6.0.0
  Downloading https://www.piwheels.org/simple/whitenoise/whitenoise-6.0.0-py3-none-any.whl (19 kB)
Collecting whoosh==2.7.4
  Downloading https://www.piwheels.org/simple/whoosh/Whoosh-2.7.4-py2.py3-none-any.whl (468 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 468.8/468.8 KB 3.8 MB/s eta 0:00:00
Requirement already satisfied: zope.interface==5.4.0 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 113)) (5.4.0)
Building wheels for collected packages: autobahn, hiredis, psycopg2, python-levenshtein
  Building wheel for autobahn (setup.py) ... done
  Created wheel for autobahn: filename=autobahn-22.3.2-py2.py3-none-any.whl size=521524 sha256=f40324f42d2aeddc9fd4ed852181ec4ba9f98d18bc5eabab4ca8743590ef3ac9
  Stored in directory: /opt/paperless/.cache/pip/wheels/76/ae/b7/386cb96496b3f06625c23f137cd7a4af72f6a0fc3f9bcba624
  Building wheel for hiredis (setup.py) ... done
  Created wheel for hiredis: filename=hiredis-2.0.0-cp310-cp310-linux_x86_64.whl size=77885 sha256=a63614459c4aa390ae598c5e2b557f0117dca97aa548a33b62465ec03b29328b
  Stored in directory: /opt/paperless/.cache/pip/wheels/a9/b0/b0/a58f2bc314a1c78d077171184d746540f45437234bc67e4b4d
  Building wheel for psycopg2 (setup.py) ... done
  Created wheel for psycopg2: filename=psycopg2-2.9.3-cp310-cp310-linux_x86_64.whl size=498314 sha256=ce4f42edaeed61b7e7c8c5c5fa2fc351e68b7b908a581a06e75902c9f75a5d1d
  Stored in directory: /opt/paperless/.cache/pip/wheels/81/b6/3d/091aad3e8919ea76c84c2674b02ce3ab52de882e091c39249e
  Building wheel for python-levenshtein (setup.py) ... done
  Created wheel for python-levenshtein: filename=python_Levenshtein-0.12.2-cp310-cp310-linux_x86_64.whl size=160215 sha256=98731aad45b17b60df28c54ca120c81cb5f360b28d7134c517de245cde0b9028
  Stored in directory: /opt/paperless/.cache/pip/wheels/7b/c3/05/60b4747cf52e0f6b6ee52022088a4de07d755016493e86373d
Successfully built autobahn hiredis psycopg2 python-levenshtein
Installing collected packages: whoosh, wcwidth, pyzbar, python-gnupg, msgpack, imap-tools, fuzzywuzzy, certifi, whitenoise, websockets, watchdog, uvloop, urllib3, tzdata, typing-extensions, txaio, tqdm, threadpoolctl, sqlparse, sniffio, setuptools, regex, redis, pyyaml, python-magic, python-dotenv, python-dateutil, pyparsing, pycparser, pyasn1-modules, psycopg2, portalocker, pluggy, pillow, pathvalidate, numpy, lxml, langdetect, joblib, inotify-simple, humanfriendly, httptools, hiredis, h11, filelock, click, charset-normalizer, blessed, attrs, async-timeout, asgiref, uvicorn, twisted, scipy, requests, reportlab, pytz-deprecation-shim, python-levenshtein, pdf2image, packaging, inotifyrecursive, gunicorn, django, concurrent-log-handler, coloredlogs, cffi, arrow, anyio, aioredis, watchgod, tzlocal, tika, scikit-learn, pikepdf, djangorestframework, django-picklefield, django-filter, django-extensions, django-cors-headers, cryptography, service-identity, pyopenssl, pdfminer.six, img2pdf, django-q, dateparser, autobahn, ocrmypdf, daphne, channels, channels-redis
  WARNING: The script read_zbar is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script watchmedo is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script tqdm is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script sqlformat is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script dotenv is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The scripts f2py, f2py3 and f2py3.10 are installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script humanfriendly is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script normalizer is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script uvicorn is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The scripts cftp, ckeygen, conch, mailmail, pyhtmlizer, tkconch, trial, twist and twistd are installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script gunicorn is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script django-admin is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script coloredlogs is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script watchgod is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script tika-python is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script img2pdf is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script dateparser-download is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The scripts wamp, xbrnetwork and xbrnetwork-ui are installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script ocrmypdf is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The script daphne is installed in '/opt/paperless/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed aioredis-1.3.1 anyio-3.5.0 arrow-1.2.2 asgiref-3.5.0 async-timeout-4.0.2 attrs-21.4.0 autobahn-22.3.2 blessed-1.19.1 certifi-2021.10.8 cffi-1.15.0 channels-3.0.4 channels-redis-3.4.0 charset-normalizer-2.0.12 click-8.1.2 coloredlogs-15.0.1 concurrent-log-handler-0.9.20 cryptography-36.0.2 daphne-3.0.2 dateparser-1.1.1 django-4.0.4 django-cors-headers-3.11.0 django-extensions-3.1.5 django-filter-21.1 django-picklefield-3.0.1 django-q-1.3.9 djangorestframework-3.13.1 filelock-3.6.0 fuzzywuzzy-0.18.0 gunicorn-20.1.0 h11-0.13.0 hiredis-2.0.0 httptools-0.4.0 humanfriendly-10.0 imap-tools-0.53.0 img2pdf-0.4.4 inotify-simple-1.3.5 inotifyrecursive-0.3.5 joblib-1.1.0 langdetect-1.0.9 lxml-4.8.0 msgpack-1.0.3 numpy-1.22.3 ocrmypdf-13.4.2 packaging-21.3 pathvalidate-2.5.0 pdf2image-1.16.0 pdfminer.six-20220319 pikepdf-5.1.1 pillow-9.1.0 pluggy-1.0.0 portalocker-2.4.0 psycopg2-2.9.3 pyasn1-modules-0.2.8 pycparser-2.21 pyopenssl-22.0.0 pyparsing-3.0.8 python-dateutil-2.8.2 python-dotenv-0.20.0 python-gnupg-0.4.8 python-levenshtein-0.12.2 python-magic-0.4.25 pytz-deprecation-shim-0.1.0.post0 pyyaml-6.0 pyzbar-0.1.9 redis-3.5.3 regex-2022.3.2 reportlab-3.6.9 requests-2.27.1 scikit-learn-1.0.2 scipy-1.8.0 service-identity-21.1.0 setuptools-62.1.0 sniffio-1.2.0 sqlparse-0.4.2 threadpoolctl-3.1.0 tika-1.24 tqdm-4.64.0 twisted-22.4.0 txaio-22.2.1 typing-extensions-4.1.1 tzdata-2022.1 tzlocal-4.2 urllib3-1.26.9 uvicorn-0.17.6 uvloop-0.16.0 watchdog-2.1.7 watchgod-0.8.2 wcwidth-0.2.5 websockets-10.2 whitenoise-6.0.0 whoosh-2.7.4
user@2204lts:/opt/paperless-ngx$ 

Klein und kuschelig ist anders sag ich da nur.

7. Datenbank Initialisierung von Paperless-ngx

Die Datenbank muss durch paperless-ngx initialisiert werden und ein superuser für den ersten login ist zwingend notwendig. Also hinein in den Folder /opt/paperless-ngx/src und die notwendigen Befehle ausgeführt.

cd /opt/paperless-ngx/src
sudo -Hu paperless python3 manage.py migrate

An dieser Stelle geht die Initialisierung von Postgre-SQL auf die Bretter und mault mit „authentication failed“ herum..

django.db.utils.OperationalError: connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  password authentication failed for user "paperlessngx"
connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  password authentication failed for user "paperlessngx"

Ich hab nochmal geprüft, dass beim Kopieren kein Fehler aufgetreten ist und natürlich ist da keiner. Okay.. es scheint so zu sein, dass Paperless-ngx noch nicht so richtig mit Postgre-SQL arbeitet. Na dann kommentiere ich diese Stelle mal in der Config aus und schau mir an, was er macht wenn ich eine SQLite nehme.

Mir wird zumindest klar, warum in der Anleitung folgender Passus steht:

Warning

This is a development server which should not be used in production. It is not audited for security and performance is inferior to production ready web servers.

Na seies drum, ich möchte mir ja erstmal einen Eindruck verschaffen. Mit SQLite läuft das Initialisieren der Datenbank durch. Als nächtes kommt das Anlegen des Superusers. Dann machen wir das doch mal.

user@2204lts:/opt/paperless-ngx/src$ sudo -Hu paperless python3 manage.py createsuperuser
System check identified some issues:

WARNINGS:
?: DEBUG mode is enabled. Disable Debug mode. This is a serious security issue, since it puts security overides in place which are meant to be only used during development. This also means that paperless will tell anyone various debugging information when something goes wrong.
Username (leave blank to use 'paperless'): superuser
Error: That username is already taken.
Username (leave blank to use 'paperless'):
Email address:
Password:
Password (again):
The password is too similar to the username.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
user@2204lts:/opt/paperless-ngx/src$

Läuft auch sauber durch.

8. Teststart von Paperless-ngx

Als nächtes wird ein Test des Servers empfohlen. Mal sehen ob er startet.

user@2204lts:/opt/paperless-ngx/src$ sudo -Hu paperless python3 manage.py runserver
Watching for file changes with StatReloader
[2022-05-15 14:24:12,449] [INFO] [django.utils.autoreload] Watching for file changes with StatReloader
Performing system checks...

System check identified some issues:

WARNINGS:
?: DEBUG mode is enabled. Disable Debug mode. This is a serious security issue, since it puts security overides in place which are meant to be only used during development. This also means that paperless will tell anyone various debugging information when something goes wrong.

System check identified 1 issue (0 silenced).
May 15, 2022 - 14:24:12
Django version 4.0.4, using settings 'paperless.settings'
Starting ASGI/Channels version 3.0.4 development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Er sagt er läuft auf 127.0.0.1:8000. Mal gucken ob er auch antwortet.

Im Webbrowser antwortet er jedenfalls nicht auf anfragen von Außen, aber er sagte ja auch, er hört nur auf die Loopback Adresse. Dann halt von der bash aus mit wget mal die Homepage abholen..

user@2204lts:~$ wget 127.0.0.1:8000/home
--2022-05-15 12:26:39--  http://127.0.0.1:8000/home
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... 302 Found
Location: /accounts/login/?next=/home [following]
--2022-05-15 12:26:39--  http://127.0.0.1:8000/accounts/login/?next=/home
Reusing existing connection to 127.0.0.1:8000.
HTTP request sent, awaiting response... 200 OK
Length: 8284 (8,1K) [text/html]
Saving to: ‘home’

home                100%[===================>]   8,09K  --.-KB/s    in 0s

2022-05-15 12:26:39 (138 MB/s) - ‘home’ saved [8284/8284]

user@2204lts:~$ 

Da ist jemand zuhause und antwortet auch. Ich hab mir die home mal in einen Browser hier herüber auf den anderen Rechner geholt. Und ja, das sieht nach einem gültigen Versuch aus.

Beim Abrufen der homepage zeigt er auf der Console auch ordentlich was passiert.

user@2204lts:/opt/paperless-ngx/src$ sudo -Hu paperless python3 manage.py runserver
Watching for file changes with StatReloader
[2022-05-15 14:24:12,449] [INFO] [django.utils.autoreload] Watching for file changes with StatReloader
Performing system checks...

System check identified some issues:

WARNINGS:
?: DEBUG mode is enabled. Disable Debug mode. This is a serious security issue, since it puts security overides in place which are meant to be only used during development. This also means that paperless will tell anyone various debugging information when something goes wrong.

System check identified 1 issue (0 silenced).
May 15, 2022 - 14:24:12
Django version 4.0.4, using settings 'paperless.settings'
Starting ASGI/Channels version 3.0.4 development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
HTTP GET /home 302 [0.03, 127.0.0.1:55752]
[2022-05-15 14:26:39,858] [INFO] [django.channels.server] HTTP GET /home 302 [0.03, 127.0.0.1:55752]
HTTP GET /accounts/login/?next=/home 200 [0.03, 127.0.0.1:55752]
[2022-05-15 14:26:39,893] [INFO] [django.channels.server] HTTP GET /accounts/login/?next=/home 200 [0.03, 127.0.0.1:55752]

Außer dem Lapsus mit der Postgre-SQL, die nicht sauber angezogen wird ist bisher alles nach Doku gelaufen. Na dann machen wir mal über das grüne Einhorn einen Server daraus.

9. Service für Paperless-ngx mit GUnicorn anlegen.

Im Verzeichnis /scripts liegt eine systemctl unit Datei, welche ich für meine Installation etwas modifiziert habe. Da ich ja die Installation nicht nach /opt/paperless sondern nach /opt/paperless-ngx (über einen Softlink) durchgeführt habe paßt sonst beim Ausführen des GUnicorns der Pfad zur Konfig nicht. Für unsere Installation sieht die zu nutzende Unit Datei in /etc/systemd/system/paperless-webserver.service dann wie folgt aus.

[Unit]
Description=Paperless webserver
After=network.target
Wants=network.target
Requires=redis.service
#Requires=paperless-webserver.socket

[Service]
User=paperless
Group=paperless
WorkingDirectory=/opt/paperless-ngx/src
ExecStart=/opt/paperless/.local/bin/gunicorn -c /opt/paperless-ngx/gunicorn.conf.py paperless.asgi:application

[Install]
WantedBy=multi-user.target

10. Paperless-ngx als reboot festen Service einrichten

Okay die Unit Datei muss natürlich nach von /opt/psudo systemctl start paperless-webserver.serviceaperless-ngx/scripts von root nach /etc/systemd/system/ kopiert werden.

sudo cp /opt/paperless-ngx/scripts/paperless-webserver.service /etc/systemd/system/

Dann ist es Zeit ihn in der bash zu starten und reboot fest zu machen.

sudo systemctl start paperless-webserver.service
sudo systemctl enable paperless-webserver.service
Created symlink /etc/systemd/system/multi-user.target.wants/paperless-webserver.service → /etc/systemd/system/paperless-webserver.service.

sudo systemctl status paperless-webserver.service
● paperless-webserver.service - Paperless webserver
     Loaded: loaded (/etc/systemd/system/paperless-webserver.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-05-15 12:51:20 UTC; 10min ago
   Main PID: 2029 (gunicorn)
      Tasks: 9 (limit: 9410)
     Memory: 170.9M
        CPU: 5.370s
     CGroup: /system.slice/paperless-webserver.service
             ├─2029 /usr/bin/python3 /opt/paperless/.local/bin/gunicorn -c /opt/paperless-ngx/gunicorn.conf.py paperless.asgi>
             ├─2031 /usr/bin/python3 /opt/paperless/.local/bin/gunicorn -c /opt/paperless-ngx/gunicorn.conf.py paperless.asgi>
             └─2032 /usr/bin/python3 /opt/paperless/.local/bin/gunicorn -c /opt/paperless-ngx/gunicorn.conf.py paperless.asgi>

Mai 15 12:51:20 2204lts systemd[1]: Started Paperless webserver.
Mai 15 12:51:21 2204lts gunicorn[2029]: [2022-05-15 14:51:21 +0200] [2029] [INFO] Starting gunicorn 20.1.0
Mai 15 12:51:21 2204lts gunicorn[2029]: [2022-05-15 14:51:21 +0200] [2029] [INFO] Listening at: http://0.0.0.0:8000 (2029)
Mai 15 12:51:21 2204lts gunicorn[2029]: [2022-05-15 14:51:21 +0200] [2029] [INFO] Using worker: paperless.workers.Configurabl>
Mai 15 12:51:21 2204lts gunicorn[2029]: [2022-05-15 14:51:21 +0200] [2029] [INFO] Server is ready. Spawning workers


Sieht soweit ganz gut. Die mitgelieferte Unit Datei funktioniert. Na dann rebooten wir mal uns sehen uns an, ob er auch dann wieder aus der Hocke kommt, der gute Paperless-ngx.


user@2204lts:/opt/paperless-ngx/scripts$ sudo reboot

11. Paperless-ngx läuft

Den Reboot hat er überstanden und kommt auch von alleine wieder hoch.

Er sieht optisch ganz ansprechend aus im Dark-Mode. Ich hab mal ein paar PDFs hinein geworfen. Da scheint ihm noch etwas zu fehlen, denn er bleibt in diesem Status hängen uns verarbeitet die übergebene PDF Datei nicht.

Sehen wir uns doch mal die restlichen Unit Dateien unter Scripts an.

Da die letzten Tage das Internet bei mir zu Hause in den Gurten baumelte, hab ich hier im Fluss eine Unterbrechung. Wo war ich stehen geblieben. Genau – an der Stelle, dass die PDFs für den Import kleben geblieben sind. Das lät sich durch die Nutzung der Paperless-Scheduler Unit im Systemctl beheben. Im Verzeichnis /opt/paperless-ngx/scripts/ gibt es eine Scheduler Unit. Diese muss noch für unsere Bedarf angepasst werden, und zwar wie folgt:

[Unit]
Description=Paperless scheduler
Requires=redis.service

[Service]
User=paperless
Group=paperless
WorkingDirectory=/opt/paperless-ngx/src
ExecStart=python3 manage.py qcluster

[Install]
WantedBy=multi-user.target

Danach muss die Systemctl Unit in das passende Verzeichnis /etc/systemd/system/ kopiert werden und aktiviert und enebaled werden.

sudo cp paperless-scheduler.service /etc/systemd/system/
sudo systemctl start paperless-scheduler.service
sudo systemctl enable paperless-scheduler.service
sudo systemctl status paperless-scheduler.service

Und schon läuft der Import ebenso sauber durch. Zum Testen habe ich einfach mal ein paar Dokus vom Open ZFS DevSummit auf der Website hochgeladen.

Die hochgeladenen PDFs werden auch schön m,it Dokumenten Thumbnails im Dokumentenbereich angezeigt.

Auch das Suchen in den Dokumenten läuft scheinbar recht flüssig. Ich habe Probweise nach Überschrift eines Slides gesucht, welches ich beim schnell mal hineinsehen gefunden habe: „Failure: Where“. Auch das läuft so, wie ich es erhoft hatte, dass Paperless-ngx nur diese PDF dann als Suchergebnis anzeigt.

Beim Clicken auf das Auge wird das gefundene PDF auch geöffnet, jedoch wird der Sucherbgriff nicht sauber übergeben und das erneute Anstoßen des Suchprozesses in der PDF, mittls <STRG>+F unde dem Suchbegriff „Failure: Where“ führt zum Ziel.

Bevor ich mich dem Batch-Import zuwende, möchte ich jetzt noch mal ganz genau checken, welche Dokumentenformate unterstützt werden. Denn am liebsten will ich ja schlussendlich da alles einfach hineinwerfen und dann Paperless-ngx für mich suchen lassen.

Stellt sich heraus, es ist wie in der Dokumenation angeben, es werden primär PDF, Bilder und plain Text unterstützt..

Q: What file types does paperless-ngx support?

A: Currently, the following files are supported:

  • PDF documents, PNG images, JPEG images, TIFF images and GIF images are processed with OCR and converted into PDF documents.
  • Plain text documents are supported as well and are added verbatim to paperless.

Ich habe mir mal einen schlecht lesebaren Screendumo von einem Mathematik Video gezogen, in welchem der Begriff Bremsweg zu sehen ist. Den Screendump habe ich dann Paperless-ngx als .bmp/.jpg/.png/.tif im Webupload zum Fraß vor geworfen. Hat er anstandslos genommen und auch die Suche nach dem Begriff Bremsweg zeigt, dass die OCR sauber arbeitet.

Bei Plaintext hab ich zwei IETF RFC als Textfile über den Webupload hochgeladen. Es sind die beiden RFCs 1918 und 4193, welche sich um die Privaten-Adressen oder Unique Local IPv6 Unicast Adressen drehen. Eine Textsuche nach 10.0.0.0/8 oder fc00::/7 führe, wie erwartet, zur Anzeige des jeweiligen RFC Dokumentes als Thumbnail und mit dem Click auf das Auge wird das Dokument geöffnet. Hier ist es wie bei den PDF Dateien ebenso, dass der gesuchte Parameter nicht übergeben wird und eine erneute Suche notwendig ist. Evuentuell ist mein Anspruch hier etwas zu hoch, dass ich den Jump zum ersten Auftauchen des Suchbegriffs erwarte.

Die Dokumente werden entsprechend der oben durchgeführten Definition als Orginal in folgendem Pfad gespeichert: /opt/paperless/media-root/documents/originals/

Die geOCRten Dokumente liegen dann alle im Archiv Verzeichnis: /opt/paperless/media-root/documents/archive

Lediglich die Plaintextfiles werden nicht zum PDF per OCR konvertiert. Hier wird scheinbar immer auf das orginale File zugegriffen. Die Thumbnails für den Browser landen in folgendem Verzeichnis: /opt/paperless/media-root/documents/thumbnails

Nun würde ich natürlich auch gerne noch meinen ganzen Zoo an verschieden formatierten Office-Files (Microsoft, Openoffice, Libreoffice, etc..) auf das Paperless-ngx bringen. Ich denke mal auf Tika herum..

12. Fazit

Das Projekt Paperless-ngx leistet sehr gute Arbeit, lediglich die scheinbar fehlende Postgre-SQL Unterstützung könnte man negativ ankreiden. Da das Projekt jedoch klar von einem Produktiveneinsatz abrät, wird das wohl noch einge Entwicklerzeit dauern, bis sich dieses von mir als produktionsrelevantes Feature einstellt. Die Tests sind alle positiv Gelaufen. Auch bei einem Balkimport von 400 PDF Files über das Webupload-Tool machte Paperless-ngx nicht schlapp. Die CPU Load der VBox beim Import war entsprechend auf „4 CPUs“ verteilt bei einer gemittelten RAM Nutzung von 5GByte der 8GByte. Die Lastverteilung klappt sehr gut, wenn man sich an die in der Doku stehenden Threadzahlen hält.

Meine langfristige Zielstellung alle Dokumente in einem Tool zu agregieren und hier durchsuchbar zu machen rückt in greifbare Nähe.

Nachtrag vom 05.Juni 2022

Ich hab in der Zwischenzeit erfolglos versucht das Apache Tika mit dem Papless-NGX zum Fliegen zu bekommen. Mir erscheint das Projekt Paperless-NGX auf einem guten Weg zu sein, jedoch wird meine Erwartungshaltung an ein papierloses Büro hier noch nicht weit genug erfüllt. Folgerichtig sehe ich mir als nächstes das Projekt Docspell an. Auch hier werde ich berichten, wie es zu installieren ist und ob es alltagstauglich ist.

Das könnte dich auch interessieren …