- Neues Modell DokumentDatei mit PostgreSQL FTS (SearchVectorField, GinIndex) - Upload-Pfad: dokumente/YYYY/MM/<uuid>/dateiname - 7 DMS-Views: list, detail, download, upload (HTMX Drag&Drop), delete, edit, search_api - Templates: list, detail, edit, upload mit Drag&Drop-Zone, Partials - URLs: /dms/ komplett verdrahtet - Sidebar: DMS als Primäreintrag, Paperless als Legacy - Migrationsskript: manage.py migrate_paperless_dokumente (DokumentLink → DokumentDatei) - compose.yml: paperless-Dienst deaktiviert (Legacy-Kommentarblock) - Migration 0048 angewendet Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
125 lines
4.3 KiB
Python
125 lines
4.3 KiB
Python
# management/commands/migrate_paperless_dokumente.py
|
||
# Phase 3: Migriert DokumentLink-Einträge zu DokumentDatei (falls Paperless-Dateien lokal verfügbar)
|
||
#
|
||
# Verwendung:
|
||
# python manage.py migrate_paperless_dokumente [--dry-run] [--limit N]
|
||
#
|
||
# Was dieser Befehl tut:
|
||
# 1. Alle DokumentLink-Objekte abrufen (Paperless-Verweise)
|
||
# 2. Für jeden Link: DokumentDatei erstellen, falls noch keine existiert (paperless_dokument_id)
|
||
# 3. Suchvektor aktualisieren
|
||
# 4. paperless_dokument_id setzen, damit künftige Läufe Duplikate überspringen
|
||
|
||
import os
|
||
|
||
from django.core.management.base import BaseCommand, CommandError
|
||
from django.db import transaction
|
||
|
||
from stiftung.models import DokumentDatei, DokumentLink
|
||
|
||
|
||
class Command(BaseCommand):
|
||
help = "Migriert Paperless-DokumentLink-Einträge zu DokumentDatei (Metadaten only)"
|
||
|
||
def add_arguments(self, parser):
|
||
parser.add_argument(
|
||
"--dry-run",
|
||
action="store_true",
|
||
help="Zeigt an, was migriert würde, ohne Änderungen vorzunehmen.",
|
||
)
|
||
parser.add_argument(
|
||
"--limit",
|
||
type=int,
|
||
default=0,
|
||
help="Maximale Anzahl Einträge (0 = alle).",
|
||
)
|
||
|
||
def handle(self, *args, **options):
|
||
dry_run = options["dry_run"]
|
||
limit = options["limit"]
|
||
|
||
links = DokumentLink.objects.select_related(
|
||
"destinataer", "land", "paechter", "verpachtung"
|
||
).order_by("pk")
|
||
|
||
if limit > 0:
|
||
links = links[:limit]
|
||
|
||
total = links.count()
|
||
self.stdout.write(f"Gefundene DokumentLinks: {total}")
|
||
|
||
if dry_run:
|
||
self.stdout.write(self.style.WARNING("DRY-RUN – keine Datenbankänderungen."))
|
||
|
||
created = 0
|
||
skipped = 0
|
||
|
||
for link in links:
|
||
# Bereits migriert?
|
||
if DokumentDatei.objects.filter(
|
||
paperless_dokument_id=link.paperless_document_id
|
||
).exists():
|
||
skipped += 1
|
||
continue
|
||
|
||
titel = link.titel or f"Paperless #{link.paperless_document_id}"
|
||
kontext = link.kontext or _guess_kontext(titel)
|
||
|
||
if dry_run:
|
||
self.stdout.write(
|
||
f" [DRY] Würde anlegen: {titel!r} (kontext={kontext}, "
|
||
f"paperless_id={link.paperless_document_id})"
|
||
)
|
||
created += 1
|
||
continue
|
||
|
||
with transaction.atomic():
|
||
dok = DokumentDatei(
|
||
titel=titel,
|
||
beschreibung=link.beschreibung or "",
|
||
kontext=kontext,
|
||
paperless_dokument_id=link.paperless_document_id,
|
||
)
|
||
# Assign FKs by ID (DokumentLink stores raw UUIDs, not FK relations)
|
||
if link.destinataer_id:
|
||
dok.destinataer_id = link.destinataer_id
|
||
if link.land_id:
|
||
dok.land_id = link.land_id
|
||
if link.paechter_id:
|
||
dok.paechter_id = link.paechter_id
|
||
if link.land_verpachtung_id:
|
||
dok.verpachtung_id = link.land_verpachtung_id
|
||
dok.save()
|
||
dok.update_suchvektor()
|
||
created += 1
|
||
|
||
self.stdout.write(
|
||
self.style.SUCCESS(
|
||
f"Fertig: {created} angelegt, {skipped} übersprungen (bereits migriert)."
|
||
)
|
||
)
|
||
|
||
|
||
def _guess_kontext(title_lower: str) -> str:
|
||
"""Leitet den Kontext-Code aus dem Titel ab."""
|
||
t = title_lower.lower()
|
||
if any(kw in t for kw in ["pachtvertrag", "pachtvertr"]):
|
||
return "pachtvertrag"
|
||
if any(kw in t for kw in ["antrag", "förderantrag"]):
|
||
return "antrag"
|
||
if any(kw in t for kw in ["nachweis", "verwendungsnachweis"]):
|
||
return "verwendungsnachweis"
|
||
if any(kw in t for kw in ["rechnung"]):
|
||
return "rechnung"
|
||
if any(kw in t for kw in ["bericht", "jahresbericht"]):
|
||
return "bericht"
|
||
if any(kw in t for kw in ["karte", "landkarte", "flurkarte"]):
|
||
return "landkarte"
|
||
if any(kw in t for kw in ["bescheid"]):
|
||
return "bescheid"
|
||
if any(kw in t for kw in ["korrespondenz", "brief"]):
|
||
return "korrespondenz"
|
||
if any(kw in t for kw in ["studium", "immatrikulation", "zeugnis"]):
|
||
return "studiennachweis"
|
||
return "anderes"
|