Add Vorlagen editor, upload portal, onboarding, and participant import command
- Dokument-Vorlagen-Editor: create/edit/reset document templates (admin) - Upload-Portal: public portal for Nachweis uploads via token - Onboarding: invite Destinatäre via email with multi-step wizard - Bestätigungsschreiben: preview and send confirmation letters - Email settings: SMTP configuration UI - Management command: import_veranstaltung_teilnehmer for bulk participant import Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
"""
|
||||
Management command to import participants into a Veranstaltung.
|
||||
|
||||
Usage:
|
||||
python manage.py import_veranstaltung_teilnehmer <veranstaltung_id>
|
||||
"""
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from stiftung.models import Veranstaltung, Veranstaltungsteilnehmer
|
||||
|
||||
|
||||
TEILNEHMER_DATA = [
|
||||
{"anrede": "Herr", "vorname": "Stephan", "nachname": "Bohnekamp", "strasse": "Marienthaler Strasse 44", "plz": "46569", "ort": "Hünxe-Drevenack"},
|
||||
{"anrede": "Frau", "vorname": "Maike", "nachname": "Buchmann-Bender", "strasse": "Am Wehagen 6", "plz": "46485", "ort": "Wesel"},
|
||||
{"anrede": "Herr", "vorname": "Edmund", "nachname": "Eichelberg", "strasse": "Schwarzensteiner Weg 75", "plz": "46569", "ort": "Hünxe-Drevenack"},
|
||||
{"anrede": "Herr", "vorname": "Walter", "nachname": "Buchmann-Bender", "strasse": "Büskesheide 11", "plz": "46499", "ort": "Hamminkeln"},
|
||||
{"anrede": "Herr", "vorname": "Gerold", "nachname": "Hurtienne", "strasse": "Birkenweg 14", "plz": "46569", "ort": "Hünxe-Drevenack"},
|
||||
{"anrede": "Frau", "vorname": "Katrin", "nachname": "Kleinpaß", "strasse": "Raesfelder Strasse 3", "plz": "46499", "ort": "Hamminkeln"},
|
||||
{"anrede": "Frau", "vorname": "Zoe", "nachname": "Kleinpaß", "strasse": "Raesfelder Strasse 3", "plz": "46499", "ort": "Hamminkeln"},
|
||||
{"anrede": "Frau", "vorname": "Nele", "nachname": "Schmäh", "strasse": "Raesfelder Strasse 3", "plz": "46499", "ort": "Hamminkeln"},
|
||||
{"anrede": "Frau", "vorname": "Susanne", "nachname": "Menz", "strasse": "Zum Weissenstein 7 a", "plz": "46499", "ort": "Hamminkeln"},
|
||||
{"anrede": "Herr", "vorname": "Jan Remmer", "nachname": "Siebels", "strasse": "Holthauser Feld 7", "plz": "49716", "ort": "Meppen"},
|
||||
{"anrede": "Frau", "vorname": "Annette", "nachname": "von der Höh", "strasse": "Fehmarnstrasse 53", "plz": "33729", "ort": "Bielefeld"},
|
||||
{"anrede": "Herr", "vorname": "Hartmut", "nachname": "Küppers", "strasse": "Jöhrenstr. 10", "plz": "30559", "ort": "Hannover"},
|
||||
{"anrede": "Frau", "vorname": "Ruth", "nachname": "Höhne", "strasse": "Löwenburgstr. 127", "plz": "53229", "ort": "Bonn-Niederholtorf"},
|
||||
{"anrede": "Herr", "vorname": "Aleph", "nachname": "Freese", "strasse": "Christoph Str. 50", "plz": "40225", "ort": "Düsseldorf"},
|
||||
{"anrede": "Herr", "vorname": "Patrik", "nachname": "Schüngel", "strasse": "Im Sand 11a", "plz": "47608", "ort": "Geldern- Walbeck"},
|
||||
{"anrede": "Frau", "vorname": "Christiane", "nachname": "Siebels", "strasse": "Rudolf Kinau Strasse 10", "plz": "49716", "ort": "Meppen"},
|
||||
]
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Importiert Teilnehmer in eine Veranstaltung"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("veranstaltung_id", type=str, help="UUID der Veranstaltung")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Nur anzeigen, nicht importieren")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
vid = options["veranstaltung_id"]
|
||||
dry_run = options["dry_run"]
|
||||
|
||||
try:
|
||||
veranstaltung = Veranstaltung.objects.get(id=vid)
|
||||
except Veranstaltung.DoesNotExist:
|
||||
self.stderr.write(self.style.ERROR(f"Veranstaltung {vid} nicht gefunden"))
|
||||
return
|
||||
|
||||
self.stdout.write(f"Veranstaltung: {veranstaltung}")
|
||||
self.stdout.write(f"Teilnehmer zu importieren: {len(TEILNEHMER_DATA)}")
|
||||
|
||||
if dry_run:
|
||||
for t in TEILNEHMER_DATA:
|
||||
self.stdout.write(f" [DRY] {t['anrede']} {t['vorname']} {t['nachname']}")
|
||||
return
|
||||
|
||||
created = 0
|
||||
for t in TEILNEHMER_DATA:
|
||||
# Check for duplicates
|
||||
exists = Veranstaltungsteilnehmer.objects.filter(
|
||||
veranstaltung=veranstaltung,
|
||||
vorname=t["vorname"],
|
||||
nachname=t["nachname"],
|
||||
).exists()
|
||||
if exists:
|
||||
self.stdout.write(self.style.WARNING(f" SKIP (exists): {t['vorname']} {t['nachname']}"))
|
||||
continue
|
||||
|
||||
Veranstaltungsteilnehmer.objects.create(
|
||||
veranstaltung=veranstaltung,
|
||||
anrede=t["anrede"],
|
||||
vorname=t["vorname"],
|
||||
nachname=t["nachname"],
|
||||
strasse=t["strasse"],
|
||||
plz=t["plz"],
|
||||
ort=t["ort"],
|
||||
rsvp_status="eingeladen",
|
||||
)
|
||||
created += 1
|
||||
self.stdout.write(self.style.SUCCESS(f" OK: {t['vorname']} {t['nachname']}"))
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(f"\n{created} Teilnehmer importiert."))
|
||||
Reference in New Issue
Block a user