# 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: ```sql 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:** ```bash 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? ```bash 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: ```bash 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? ```bash 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: ```bash 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? ```bash 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? ```bash 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](03-extraktion.md).