add manual report

This commit is contained in:
NABU Jena 2025-07-09 19:59:29 +02:00
parent 67a49aa193
commit 56164b2f47
2 changed files with 224 additions and 0 deletions

View file

@ -0,0 +1,108 @@
# Manuelle "Sofort senden" Funktion für Automatische Reports
## Übersicht
Die neue "Sofort senden" Aktion im Django Admin ermöglicht es Administratoren, automatische Reports manuell und sofort zu versenden, ohne auf den geplanten Zeitpunkt warten zu müssen.
## Verwendung
1. **Admin-Bereich öffnen**: Navigieren Sie zu `/admin/reports/automaticreport/`
2. **Reports auswählen**: Wählen Sie einen oder mehrere automatische Reports aus der Liste aus
3. **Aktion ausführen**: Wählen Sie aus dem Aktions-Dropdown "Ausgewählte Reports sofort senden"
4. **Bestätigen**: Klicken Sie auf "Ausführen"
## Funktionalität
### Automatische Zeitraumberechnung
Die Aktion berechnet automatisch den Zeitraum basierend auf der Frequenz des Reports:
- **Wöchentlich**: Letzte 7 Tage
- **Monatlich**: Letzter Monat (vom 1. des Vormonats bis heute)
- **Vierteljährlich**: Letztes Quartal (3 Monate zurück)
### Validierung und Filterung
Vor dem Versenden wird überprüft:
- ✅ **Aktivierung**: Report muss aktiv sein (`is_active = True`)
- ✅ **E-Mail-Adressen**: Mindestens eine E-Mail-Adresse muss konfiguriert sein
- ✅ **Filter**: Die konfigurierten Filter (Naturschutz-/Jagdbehörde) werden angewendet
### Rückmeldungen
Das System gibt detaillierte Rückmeldungen:
- ✅ **Erfolgreich**: Anzahl gesendeter E-Mails, Zeitraum, Patientenzahl
- ⚠️ **Übersprungen**: Grund für das Überspringen (deaktiviert, keine E-Mail-Adressen)
- ❌ **Fehler**: Detaillierte Fehlermeldungen bei Problemen
### Protokollierung
- Alle versendeten Reports werden in `ReportLog` protokolliert
- Das `last_sent` Feld des AutomaticReport wird aktualisiert
- CSV-Dateien werden gespeichert und können später heruntergeladen werden
## Technische Details
### Implementierung
- **Datei**: `/app/reports/admin.py`
- **Methode**: `send_report_now()`
- **Service**: Verwendet `ReportGenerator` aus `reports.services`
### Sicherheit
- Nur Administratoren mit entsprechenden Berechtigungen können diese Aktion ausführen
- Deaktivierte Reports werden automatisch übersprungen
- Umfassende Fehlerbehandlung verhindert Systemausfälle
## Beispiel-Ausgaben
### Erfolgreicher Versand
```
✅ Report 'Monatlicher Naturschutzbericht' erfolgreich gesendet (letzter Monat, 15 Patienten, 3 Empfänger).
✅ Zusammenfassung: 1 Report(s) erfolgreich gesendet.
```
### Übersprungener Report
```
⚠️ Report 'Deaktivierter Report' ist deaktiviert und wurde übersprungen.
Alle 1 ausgewählten Reports wurden übersprungen (deaktiviert oder keine E-Mail-Adressen).
```
### Fehler beim Versand
```
❌ Fehler beim Senden von 'Fehlerhafter Report': SMTP-Server nicht erreichbar.
❌ Alle 1 Reports konnten nicht gesendet werden.
```
## Troubleshooting
### Häufige Probleme
1. **"Report ist deaktiviert"**
- Lösung: Report in der Detail-Ansicht aktivieren (`is_active = True`)
2. **"Keine E-Mail-Adressen"**
- Lösung: E-Mail-Adressen im Report konfigurieren
3. **"'Emailadress' object has no attribute 'email'"**
- Technischer Fehler: Das Emailadress-Model verwendet `email_address` statt `email`
- Lösung: Wurde in Version 1.1 behoben
4. **"SMTP-Fehler"**
- Lösung: E-Mail-Konfiguration in `settings.py` überprüfen
5. **"Keine Patienten gefunden"**
- Normal: Report wird trotzdem versendet, zeigt 0 Patienten an
### Log-Überprüfung
- Alle Versandaktivitäten werden in `/admin/reports/reportlog/` protokolliert
- Bei Fehlern die Django-Logs überprüfen
## Entwickler-Hinweise
### Anpassungen
- Zeitraumberechnung kann in der `send_report_now()` Methode angepasst werden
- Zusätzliche Validierungen können hinzugefügt werden
- E-Mail-Templates befinden sich in `/templates/reports/email/`
### Erweiterungen
- Mögliche Erweiterung: Benutzerdefinierte Zeiträume
- Mögliche Erweiterung: Vorschau vor dem Versand
- Mögliche Erweiterung: Stapelverarbeitung mit Fortschrittsanzeige

View file

@ -2,7 +2,11 @@ from django.contrib import admin
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils.html import format_html from django.utils.html import format_html
from django.urls import reverse from django.urls import reverse
from django.contrib import messages
from django.http import HttpResponseRedirect
from datetime import date, timedelta
from .models import AutomaticReport, ReportLog from .models import AutomaticReport, ReportLog
from .services import ReportGenerator
@admin.register(AutomaticReport) @admin.register(AutomaticReport)
@ -19,6 +23,7 @@ class AutomaticReportAdmin(admin.ModelAdmin):
list_filter = ['frequency', 'is_active', 'created_at', 'include_naturschutzbehoerde', 'include_jagdbehoerde'] list_filter = ['frequency', 'is_active', 'created_at', 'include_naturschutzbehoerde', 'include_jagdbehoerde']
search_fields = ['name', 'description'] search_fields = ['name', 'description']
readonly_fields = ['created_by', 'created_at', 'updated_at'] readonly_fields = ['created_by', 'created_at', 'updated_at']
actions = ['send_report_now']
fieldsets = ( fieldsets = (
(None, { (None, {
@ -50,6 +55,117 @@ class AutomaticReportAdmin(admin.ModelAdmin):
return f"{count} E-Mail-Adresse(n)" return f"{count} E-Mail-Adresse(n)"
email_count.short_description = _("E-Mail-Adressen") email_count.short_description = _("E-Mail-Adressen")
def send_report_now(self, request, queryset):
"""Send selected reports immediately."""
sent_count = 0
error_count = 0
skipped_count = 0
for report in queryset:
if not report.is_active:
messages.warning(
request,
f"Report '{report.name}' ist deaktiviert und wurde übersprungen."
)
skipped_count += 1
continue
if not report.email_addresses.exists():
messages.warning(
request,
f"Report '{report.name}' hat keine E-Mail-Adressen und wurde übersprungen."
)
skipped_count += 1
continue
# Get email addresses
email_addresses = [addr.email_address for addr in report.email_addresses.all()]
# Calculate date range based on frequency
today = date.today()
if report.frequency == 'weekly':
date_from = today - timedelta(days=7)
range_description = "letzte 7 Tage"
elif report.frequency == 'monthly':
# Go back one month
if today.month == 1:
date_from = today.replace(year=today.year-1, month=12, day=1)
else:
date_from = today.replace(month=today.month-1, day=1)
range_description = "letzter Monat"
elif report.frequency == 'quarterly':
# Go back three months
if today.month <= 3:
date_from = today.replace(year=today.year-1, month=today.month+9, day=1)
else:
date_from = today.replace(month=today.month-3, day=1)
range_description = "letztes Quartal"
else:
date_from = today - timedelta(days=30) # Default to 30 days
range_description = "letzte 30 Tage"
# Generate and send report
try:
generator = ReportGenerator(
date_from=date_from,
date_to=today,
include_naturschutzbehoerde=report.include_naturschutzbehoerde,
include_jagdbehoerde=report.include_jagdbehoerde
)
report_log, success, error_msg = generator.send_email_report(
email_addresses=email_addresses,
automatic_report=report
)
if success:
# Update last_sent timestamp
report.last_sent = today
report.save(update_fields=['last_sent'])
sent_count += 1
# Success message with details
messages.success(
request,
f"Report '{report.name}' erfolgreich gesendet "
f"({range_description}, {report_log.patient_count} Patienten, "
f"{len(email_addresses)} Empfänger)."
)
else:
error_count += 1
messages.error(
request,
f"Fehler beim Senden von '{report.name}': {error_msg}"
)
except Exception as e:
error_count += 1
messages.error(
request,
f"Unerwarteter Fehler bei '{report.name}': {str(e)}"
)
# Show summary message
if sent_count > 0:
messages.success(
request,
f"Zusammenfassung: {sent_count} Report(s) erfolgreich gesendet"
+ (f", {skipped_count} übersprungen" if skipped_count > 0 else "")
+ (f", {error_count} Fehler" if error_count > 0 else "") + "."
)
elif skipped_count > 0 and error_count == 0:
messages.info(
request,
f"Alle {skipped_count} ausgewählten Reports wurden übersprungen (deaktiviert oder keine E-Mail-Adressen)."
)
elif error_count > 0 and sent_count == 0:
messages.error(
request,
f"Alle {error_count} Reports konnten nicht gesendet werden."
)
send_report_now.short_description = _("Ausgewählte Reports sofort senden")
@admin.register(ReportLog) @admin.register(ReportLog)
class ReportLogAdmin(admin.ModelAdmin): class ReportLogAdmin(admin.ModelAdmin):