diff --git a/app/stiftung/tasks.py b/app/stiftung/tasks.py index 0ef9cba..bcf7a1d 100644 --- a/app/stiftung/tasks.py +++ b/app/stiftung/tasks.py @@ -156,21 +156,27 @@ def _upload_to_paperless(content: bytes, filename: str, destinataer=None, betref @shared_task(bind=True, max_retries=3, default_retry_delay=300, name="stiftung.tasks.poll_destinataer_emails") -def poll_destinataer_emails(self): +def poll_destinataer_emails(self, search_all_recent_days=0): """ - Liest ungelesene E-Mails aus dem IMAP-Postfach und verarbeitet sie. + Liest E-Mails aus dem IMAP-Postfach und verarbeitet sie. Wird durch Celery Beat alle 15 Minuten ausgeführt. + + Args: + search_all_recent_days: Wenn > 0, werden alle E-Mails der letzten N Tage + durchsucht (nicht nur ungelesene). Nützlich für manuellen Abruf. """ from stiftung.models import Destinataer, DestinataerEmailEingang, DokumentLink - # IMAP-Konfiguration aus Settings - imap_host = getattr(settings, "IMAP_HOST", None) - imap_port = int(getattr(settings, "IMAP_PORT", 993)) - imap_user = getattr(settings, "IMAP_USER", None) - imap_password = getattr(settings, "IMAP_PASSWORD", None) - imap_folder = getattr(settings, "IMAP_FOLDER", "INBOX") - imap_use_ssl = getattr(settings, "IMAP_USE_SSL", True) + # IMAP-Konfiguration: DB (AppConfiguration) mit Fallback auf env/settings + from stiftung.utils.config import get_config + + imap_host = get_config("imap_host") + imap_port = int(get_config("imap_port", 993)) + imap_user = get_config("imap_user") + imap_password = get_config("imap_password") + imap_folder = get_config("imap_folder", "INBOX") + imap_use_ssl = get_config("imap_use_ssl", True) if not all([imap_host, imap_user, imap_password]): logger.warning( @@ -199,11 +205,18 @@ def poll_destinataer_emails(self): mail.login(imap_user, imap_password) mail.select(imap_folder) - # Ungelesene Nachrichten suchen - _, message_ids_raw = mail.search(None, "UNSEEN") + # Nachrichten suchen + if search_all_recent_days and search_all_recent_days > 0: + from datetime import timedelta + since_date = (datetime.now(dt_timezone.utc) - timedelta(days=search_all_recent_days)).strftime("%d-%b-%Y") + _, message_ids_raw = mail.search(None, "SINCE", since_date) + search_mode = f"ALL seit {since_date}" + else: + _, message_ids_raw = mail.search(None, "UNSEEN") + search_mode = "UNSEEN" message_ids = message_ids_raw[0].split() - logger.info("Postfach '%s': %d ungelesene Nachricht(en) gefunden.", imap_folder, len(message_ids)) + logger.info("Postfach '%s' (%s): %d Nachricht(en) gefunden.", imap_folder, search_mode, len(message_ids)) for msg_id in message_ids: try: diff --git a/app/stiftung/views/geschichte.py b/app/stiftung/views/geschichte.py index cf6f23d..1f50483 100644 --- a/app/stiftung/views/geschichte.py +++ b/app/stiftung/views/geschichte.py @@ -689,18 +689,22 @@ def email_eingang_detail(request, pk): @login_required def email_eingang_poll_trigger(request): - """Löst den IMAP-Poll-Task manuell aus (für Tests und manuelle Verarbeitung).""" + """Löst den IMAP-Poll manuell aus – sucht alle E-Mails der letzten 30 Tage.""" if request.method == "POST": from stiftung.tasks import poll_destinataer_emails try: - task = poll_destinataer_emails.delay() - messages.success( - request, - f"E-Mail-Abruf wurde gestartet (Task-ID: {task.id}). " - "Bitte Seite in ca. 30 Sekunden neu laden.", - ) + # Synchron ausführen für sofortiges Feedback; sucht auch bereits + # gelesene E-Mails der letzten 30 Tage (Duplikate werden übersprungen). + result = poll_destinataer_emails.apply(kwargs={"search_all_recent_days": 30}).get(timeout=60) + processed = result.get("processed", 0) if isinstance(result, dict) else 0 + if result and result.get("status") == "skipped": + messages.warning(request, "IMAP ist nicht konfiguriert. Bitte Einstellungen unter Administration → E-Mail / IMAP prüfen.") + elif processed > 0: + messages.success(request, f"{processed} neue E-Mail(s) importiert.") + else: + messages.info(request, "Keine neuen E-Mails gefunden.") except Exception as exc: - messages.error(request, f"Fehler beim Starten des Tasks: {exc}") + messages.error(request, f"Fehler beim E-Mail-Abruf: {exc}") return redirect("stiftung:email_eingang_list")