297 lines
8.0 KiB
Markdown
297 lines
8.0 KiB
Markdown
# 4. Import
|
|
|
|
Stufe 2 spielt ein Manifest über die REST-API in Paperless ein und führt
|
|
dabei ein eigenes SQLite-Journal.
|
|
|
|
---
|
|
|
|
## Das Journal
|
|
|
|
`migration.sqlite` ist der Kern. Es hält je Dokument fest:
|
|
|
|
| Feld | Bedeutung |
|
|
|---|---|
|
|
| `ecodms_docid` | Quell-ID |
|
|
| `paperless_id` | Ziel-ID |
|
|
| `versions_total` / `versions_done` | Fortschritt der Kette |
|
|
| `status` | `pending`, `root_created`, `done`, `failed`, `uebersprungen` |
|
|
| `last_error` | Fehlertext im Klartext |
|
|
|
|
Daraus folgen drei Eigenschaften:
|
|
|
|
- **Wiederaufsetzbar.** Ein abgebrochener Lauf setzt fort, ohne Duplikate.
|
|
- **Tranchen dürfen sich überschneiden.** Bekannte Dokumente werden
|
|
übersprungen.
|
|
- **Tranchenübergreifend auswertbar.** Nötig für Zeitstempel und
|
|
Verknüpfungen am Ende.
|
|
|
|
Das Journal **nicht** löschen, solange die Migration läuft. Und nicht
|
|
zusammen mit dem Paperless-Bestand zurücksetzen, ohne beides gemeinsam zu
|
|
tun — sonst entstehen Doppelanlagen.
|
|
|
|
---
|
|
|
|
## Rollenübersetzung
|
|
|
|
`rollen.yml.example` nach `rollen.yml` kopieren und anpassen.
|
|
|
|
```yaml
|
|
default_owner: benutzer_a
|
|
|
|
roles:
|
|
r_benutzer_a:
|
|
type: user
|
|
paperless: benutzer_a
|
|
owner: true # wird Eigentümer, wenn beteiligt
|
|
FFN:
|
|
type: group
|
|
paperless: Gruppe_A
|
|
ecoSIMSUSER:
|
|
type: null # globales Importrecht, entfällt bewusst
|
|
|
|
rights:
|
|
R: [view]
|
|
W: [view, change]
|
|
|
|
no_rights_policy: owner_only
|
|
```
|
|
|
|
### Warum `ecoSIMSUSER` verworfen wird
|
|
|
|
Das ist das globale Importrecht — jeder hat Zugriff. Es wird beim Import
|
|
gesetzt und bei der ersten Klassifizierung ersetzt. Wer es übernimmt,
|
|
öffnet jedes Dokument für alle.
|
|
|
|
### Warum `W` beide Rechte bekommt
|
|
|
|
In Paperless implizieren `change` und `view` einander nicht. Ein `change`
|
|
ohne `view` ergibt keinen sinnvollen Zustand.
|
|
|
|
### Warum unbekannte Rollen zum Abbruch führen
|
|
|
|
**In Paperless bedeutet „keine Rechte gesetzt" nicht gesperrt, sondern
|
|
unbeschränkt.** Ein Tippfehler im Rollennamen würde Dokumente öffnen, nicht
|
|
schließen. Deshalb: entweder abbilden oder ausdrücklich als `type: null`
|
|
eintragen.
|
|
|
|
---
|
|
|
|
## Zusatzfelder vorab anlegen
|
|
|
|
```yaml
|
|
custom_fields:
|
|
Belegnummer: string
|
|
Zeitraum: string
|
|
Eingangsdatum: date
|
|
Zahlungsdatum: date
|
|
Wiedervorlage: date
|
|
ecoDMS-ID: string
|
|
ecoDMS-Verknuepfung: documentlink
|
|
```
|
|
|
|
**Nicht von Hand in der Oberfläche anlegen.** Weicht der Name auch nur
|
|
minimal ab, erzeugt das Skript ein zweites gleichnamiges Feld, und die
|
|
Werte verteilen sich auf beide.
|
|
|
|
---
|
|
|
|
## Ablauf
|
|
|
|
### Trockenlauf
|
|
|
|
```bash
|
|
export PT=dein_api_token
|
|
python3 paperless_import.py export1.json --dry-run
|
|
```
|
|
|
|
Schreibt nichts, prüft aber die Rollen gegen `rollen.yml` und meldet
|
|
Unbekanntes.
|
|
|
|
### Stammdaten
|
|
|
|
```bash
|
|
python3 paperless_import.py export1.json --setup
|
|
```
|
|
|
|
Legt Tags, Dokumenttypen und Zusatzfelder an. Vorhandene werden erkannt.
|
|
|
|
Danach in der Oberfläche ansehen: Passen die Tags? Bei tiefen
|
|
Ordnerbäumen entstehen schnell mehrere hundert. Das ist der Moment, es zu
|
|
ändern — später kostet es eine Umstellung des gesamten Bestands.
|
|
|
|
### Probelauf
|
|
|
|
```bash
|
|
python3 paperless_import.py export1.json --limit 2 --stop-on-error
|
|
```
|
|
|
|
Zwei Dokumente, Abbruch beim ersten Fehler. Danach in der Oberfläche
|
|
prüfen:
|
|
|
|
- Tags einzeln statt als Pfad?
|
|
- Archiv-Seriennummer gleich der ecoDMS-ID?
|
|
- Zusatzfelder gefüllt?
|
|
- Eigentümer gesetzt?
|
|
- Bei einem mehrversionigen Dokument: Kette vollständig?
|
|
|
|
### Volle Tranche
|
|
|
|
```bash
|
|
python3 paperless_import.py export1.json
|
|
```
|
|
|
|
Ohne `--stop-on-error`, damit einzelne Fehlschläge den Lauf nicht
|
|
anhalten. Sie landen als `failed` im Journal.
|
|
|
|
### Prüfen
|
|
|
|
```bash
|
|
python3 paperless_import.py --verify
|
|
```
|
|
|
|
Vergleicht Journal und Instanz. Meldet abweichende Versionszahlen,
|
|
fehlende Eigentümer, abweichende Seriennummern und **Dokumente ohne
|
|
Journaleintrag** — das sind mögliche Doppelanlagen.
|
|
|
|
---
|
|
|
|
## Tag-Modus
|
|
|
|
Standard ist `segments`: Aus `Beruf/Fortbildung/Steuerberater` werden drei
|
|
Tags. Das erlaubt Filtern nach einzelnen Ebenen, kostet aber den
|
|
Hierarchiekontext — `Steuerberater` allein ist mehrdeutig.
|
|
|
|
Alternative:
|
|
|
|
```bash
|
|
python3 paperless_import.py export1.json --tag-mode path
|
|
```
|
|
|
|
Ein Tag je vollständigem Pfad. Übersichtlicher, aber nicht nach Ebenen
|
|
filterbar.
|
|
|
|
**Die Entscheidung vor dem ersten `--setup` treffen.** Ein Wechsel später
|
|
bedeutet, alle Tags neu zu vergeben.
|
|
|
|
---
|
|
|
|
## Was Stufe 2 je Dokument tut
|
|
|
|
```
|
|
POST /api/documents/post_document/ → Version 1, mit Titel, created,
|
|
Dokumenttyp, Tags
|
|
Task pollen → paperless_id ins Journal
|
|
POST /api/documents/{id}/update_version/ → Version 2…n, mit version_label
|
|
Task pollen, streng seriell
|
|
PATCH /api/documents/{id}/ → Eigentümer, Rechte,
|
|
Zusatzfelder, Seriennummer
|
|
```
|
|
|
|
### Warum seriell
|
|
|
|
`update_version` hängt an den aktuellen Head an. Innerhalb einer
|
|
Versionskette muss jede Version fertig konsumiert sein, bevor die nächste
|
|
kommt. Parallelisiert wird über Dokumente hinweg, nicht innerhalb.
|
|
|
|
### Rechte nur auf die Wurzel
|
|
|
|
Versionen sind eigene Dokumentdatensätze, erben die Rechte aber über
|
|
`root_document`. Ein Zugriffstest mit einem nicht berechtigten Benutzer
|
|
liefert bei allen Versionen 403. Es genügt also, die Rechte einmal auf das
|
|
Wurzeldokument zu setzen.
|
|
|
|
### Was die API nicht kann
|
|
|
|
`update_version` nimmt ausschließlich `document` und `version_label`
|
|
entgegen — **keinen Zeitstempel**. Das `added`-Feld je Version muss
|
|
nachträglich gesetzt werden, siehe [Nacharbeit](05-nacharbeit.md).
|
|
|
|
---
|
|
|
|
## Fehler behandeln
|
|
|
|
```bash
|
|
sqlite3 -header -column migration.sqlite \
|
|
"SELECT tranche, status, COUNT(*) FROM mapping GROUP BY tranche, status;"
|
|
|
|
sqlite3 -header migration.sqlite \
|
|
"SELECT ecodms_docid, last_error FROM mapping WHERE status='failed' LIMIT 20;"
|
|
```
|
|
|
|
Nach einer Korrektur die Fehlschläge erneut anstoßen:
|
|
|
|
```bash
|
|
sqlite3 migration.sqlite \
|
|
"UPDATE mapping SET status='pending', last_error=NULL WHERE status='failed';"
|
|
python3 paperless_import.py export1.json
|
|
```
|
|
|
|
Dauerhaft nicht importierbare Dokumente auf einen eigenen Status setzen,
|
|
damit sie nicht bei jedem Lauf erneut versucht werden:
|
|
|
|
```bash
|
|
sqlite3 migration.sqlite \
|
|
"UPDATE mapping SET status='uebersprungen' WHERE ecodms_docid IN (…);"
|
|
```
|
|
|
|
### Häufige Fehlerbilder
|
|
|
|
| Meldung | Ursache |
|
|
|---|---|
|
|
| `File type … not supported` | Dateityp, oder Inhalt passt nicht zur Endung |
|
|
| `HTTP 406` bei Office/`.eml` | Tika-Version zu neu |
|
|
| `Error while queuing document` | oft eine falsche `DEFAULT_PERMISSIONS_OWNER`-ID |
|
|
| `Task … nach 600s ohne Ergebnis` | großer Scan mit OCR, `TASK_TIMEOUT` erhöhen |
|
|
| `HTTP 400` beim Anlegen eines Tags | Namenskollision nach Normalisierung |
|
|
|
|
### Zwischenzustand nach Abbruch
|
|
|
|
Wird ein Lauf zwischen Upload und Journaleintrag unterbrochen, existiert
|
|
das Dokument in Paperless ohne Journaleintrag. `--verify` findet das.
|
|
|
|
Behandlung: Zeigt das Journal `failed` und das Dokument existiert, ist der
|
|
Eintrag nachzutragen statt neu hochzuladen:
|
|
|
|
```bash
|
|
sqlite3 migration.sqlite \
|
|
"UPDATE mapping SET paperless_id=<id>, versions_done=1,
|
|
status='root_created', last_error=NULL WHERE ecodms_docid=<docid>;"
|
|
python3 paperless_import.py exportN.json
|
|
```
|
|
|
|
Der Lauf ergänzt dann Rechte, Zusatzfelder und Seriennummer.
|
|
|
|
---
|
|
|
|
## Snapshots
|
|
|
|
Vor jeder Tranche einen Snapshot des Datenverzeichnisses:
|
|
|
|
```bash
|
|
docker compose down
|
|
btrfs subvolume snapshot -r data ../snapshots/paperless-vor-tranche2
|
|
docker compose up -d
|
|
```
|
|
|
|
Container vorher stoppen, sonst ist der Datenbankstand nicht konsistent zu
|
|
den Dateien. Beim ersten Durchlauf wird man das brauchen.
|
|
|
|
---
|
|
|
|
## Laufzeit
|
|
|
|
Bei PDFs mit vorhandenem Textlayer und `OCR_MODE=auto` liegt ein
|
|
Konsumvorgang unter einer Sekunde. Für 12.000 Versionen bedeutet das
|
|
wenige Stunden.
|
|
|
|
Der Engpass ist nicht die CPU, sondern die serielle Arbeitsweise plus das
|
|
Abfrageintervall. Mehr Worker beschleunigen nichts, solange immer nur eine
|
|
Aufgabe gleichzeitig läuft.
|
|
|
|
Deutlich länger dauern Office-Dokumente und E-Mails, weil sie über
|
|
Gotenberg und Tika laufen.
|
|
|
|
---
|
|
|
|
Weiter mit [Nacharbeit](05-nacharbeit.md).
|