6.8 KiB
2. Export aus ecoDMS
Der Export läuft über das Datenexport-Plugin, das zum normalen Lizenzumfang gehört. Eine API-Lizenz wird nicht gebraucht.
Warum nicht die Datenbank
Naheliegend wäre, die ecoDMS-PostgreSQL direkt auszulesen. Das funktioniert nicht: ecoDMS legt die Dateien in einem eigenen Container-Speichersystem ab, weder als BLOB in der Datenbank noch als normale Dateien im Archivverzeichnis.
Und die Klassifizierungsdaten liegen dort ebenfalls nicht — in der
Live-Datenbank gibt es weder eine klassifizierung- noch eine
docs-Tabelle. Diese Struktur erzeugt erst der Exporter.
Der Export ist also der einzige Weg an Inhalte und Metadaten.
Vorab: Bestandsaufnahme
Bevor Zeit in Exporte fließt, sollte man die Größenordnung kennen. Am
einfachsten über einen kleinen Testexport (siehe unten) und die Auswertung
der Tabellen ecosimsversions und docs.
Wichtige Zahlen:
| Frage | Warum |
|---|---|
| Wie viele Dokumente haben mehr als eine Version? | bestimmt den Aufwand des schwierigen Teils |
| Wie viele haben keine Version? | brauchen einen Rückfall auf ecosimsarchive |
| Wie viele sind mehrfach klassifiziert? | bestimmt die Zusammenführungsregel |
| Welche Dateitypen kommen vor? | nicht alle kann Paperless konsumieren |
| Welche Rollen und Rechtearten? | müssen in der Übersetzung abgebildet sein |
ecodms_extract.py beantwortet all das im Prüfbericht — es lohnt sich
deshalb, früh einen kleinen Export zu machen und ihn durch Stufe 1 zu
schicken.
Testexport zuerst
Nicht mit dem Gesamtbestand anfangen. Ein Export von fünf bis zehn Dokumenten, der bewusst alle Sonderfälle enthält, ist die beste Investition:
- ein Office-Dokument mit mehreren Versionen
- ein PDF mit mehreren Versionen
- ein Dokument ganz ohne Versionseinträge
- ein mehrfach klassifiziertes Dokument
- eine E-Mail (
.eml) - eine ZUGFeRD-Rechnung
- ein Dokument mit einer Notiz
- ein Dokument mit besonderen Rechten
Gegen diesen Satz lässt sich die gesamte Migrationslogik entwickeln, ohne je den Produktivbestand anzufassen.
Fallstrick: unerfüllbare Exportabfrage
Bei manueller Mehrfachauswahl kann der Exporter eine Abfrage erzeugen, die niemals zutrifft:
docid = '11072' AND docid = '11094' AND docid = '11143' AND …
Das Ergebnis: ein Export mit vollständigen Stammdaten und null Dokumenten. Ohne Fehlermeldung, ohne Warnung.
Nach jedem Export prüfen:
python3 -c "
import sqlite3, base64
c = sqlite3.connect('file:export.data?mode=ro', uri=True)
print(base64.b64decode(c.execute('SELECT exp_query FROM ecodmsexporter').fetchone()[0]).decode())
"
Steht dort AND zwischen den IDs statt OR, ist der Export leer.
Vermeiden lässt es sich, indem man Exporte über ein Suchkriterium oder einen ID-Bereich definiert statt über einzeln markierte Dokumente.
Tranchen bilden
Bei größeren Beständen den Export in Blöcke teilen. Bewährt haben sich etwa 2.000 Dokumente je Tranche.
Empfohlene Struktur:
export-daten/
├── export1/archive/{export.data, export.xml, *.pdf, …}
├── export2/archive/…
└── …
Nach ID-Bereichen teilen, nicht nach manueller Auswahl. Der Wurzelknoten
der XML trägt startid und endid — der Exporter denkt ohnehin in
Bereichen.
Tranchen dürfen sich überschneiden. Das Journal überspringt bereits importierte Dokumente. Das ist nützlich, wenn Teile des Bestands nur unter einem anderen Benutzerkonto sichtbar sind: einfach eine zusätzliche Tranche unter diesem Konto exportieren.
Prüfung je Tranche
1. Abfrage plausibel?
Siehe oben.
2. Dokumentanzahl?
python3 -c "
import sqlite3
c = sqlite3.connect('file:export.data?mode=ro', uri=True)
for t in ('docs','klassifizierung','ecosimsversions','ecosimsarchive',
'docrollen_hist','econotice'):
print(f'{t:20s}', c.execute(f'SELECT COUNT(*) FROM \"{t}\"').fetchone()[0])
"
Sind docs und klassifizierung auf null, während documentenart und
status gefüllt sind, hat die Abfrage nicht gegriffen.
3. Referenzen vollständig?
Die XML verweist auf Dateien im Archivordner. Jede fehlende Referenz ist ein Dokument, das beim Import scheitert:
python3 -c "
import xml.etree.ElementTree as ET, pathlib
root = ET.parse('export.xml').getroot()
ref = {e.get('filePath') for e in root.iter() if e.get('filePath')}
have = {p.name for p in pathlib.Path('.').iterdir() if p.is_file()}
print('fehlt:', sorted(ref - have)[:20])
print('unreferenziert:', sorted(have - ref - {'export.xml','export.data'})[:20])
"
Erwartete Fehlmeldungen: Bei Nicht-PDF-Originalen referenziert die XML eine PDF/A-Fassung je Version, die nicht exportiert wird. Das ist normal und für Paperless unkritisch.
4. Dateitypen?
python3 -c "
import sqlite3, collections
c = sqlite3.connect('file:export.data?mode=ro', uri=True)
n = collections.Counter()
for (f,) in c.execute('SELECT realname FROM ecosimsversions WHERE realname IS NOT NULL'):
n['.' + f.rsplit('.',1)[-1].lower()] += 1
for k, v in n.most_common(): print(f'{v:6d} {k}')
"
Vergleich mit dem, was die Zielinstanz kann:
docker compose exec webserver python manage.py shell -c \
"from documents.parsers import get_supported_file_extensions as f; print(sorted(f()))"
Typischerweise nicht unterstützt: .html, .htm, .zip, .indd.
ecodms_extract.py sortiert solche Dokumente aus und listet sie zur
Nacharbeit.
5. Rollen und Rechtearten?
python3 -c "
import sqlite3
c = sqlite3.connect('file:export.data?mode=ro', uri=True)
for r in c.execute('SELECT DISTINCT role, doc_right FROM docrollen_hist ORDER BY role'):
print(r)
"
Jede Rolle muss in rollen.yml abgebildet oder ausdrücklich verworfen
werden. Stufe 2 bricht sonst vor dem ersten Upload ab — bewusst, denn ein
stilles Verwerfen würde Dokumente öffnen, nicht schließen.
6. Mehrfachklassifizierung?
python3 -c "
import sqlite3, collections
c = sqlite3.connect('file:export.data?mode=ro', uri=True)
n = collections.Counter(d for (d,) in c.execute('SELECT docid FROM docs'))
h = collections.Counter(n.values())
for k in sorted(h): print(f'{h[k]:6d} Dokumente mit {k} Klassifizierung(en)')
"
Was der Export nicht enthält
| Element | Alternative |
|---|---|
| Dokumentverknüpfungen | nicht ermittelbar, Nacharbeit von Hand |
| Klassifizierungshistorie | steht in der XML, aber Paperless kann sie nicht abbilden |
| Anhänge als eigene Dateien | reisen eingebettet im Original mit |
Der lucene-Ordner enthält den Suchindex des Offline-Clients und ist für
die Migration wertlos.
Speicherbedarf
Der Export enthält je Version Original und teils PDF/A. Rechne mit etwa dem Umfang der ecoDMS-Sicherung. Auf der Zielseite kommt Ähnliches hinzu, weil Paperless Original und Archivdatei getrennt ablegt.
Weiter mit Extraktion und Prüfung.