implement report feature

This commit is contained in:
Maximilian 2025-06-10 12:46:53 +02:00
parent 4218ee6b7d
commit d6d47f714a
31 changed files with 2472 additions and 8 deletions

187
app/reports/services.py Normal file
View file

@ -0,0 +1,187 @@
import csv
from io import StringIO
from datetime import date
from django.db.models import Q
from django.core.mail import EmailMessage
from django.template.loader import render_to_string
from django.conf import settings
from django.core.files.base import ContentFile
from bird.models import FallenBird
class ReportGenerator:
"""Service class for generating bird reports."""
def __init__(self, date_from, date_to, include_naturschutzbehoerde=True, include_jagdbehoerde=False):
self.date_from = date_from
self.date_to = date_to
self.include_naturschutzbehoerde = include_naturschutzbehoerde
self.include_jagdbehoerde = include_jagdbehoerde
def get_birds_queryset(self):
"""Get queryset of birds based on filters."""
# Date filter
queryset = FallenBird.objects.filter(
date_found__gte=self.date_from,
date_found__lte=self.date_to
)
# Bird type filter based on notification settings
bird_filter = Q()
if self.include_naturschutzbehoerde:
bird_filter |= Q(bird__melden_an_naturschutzbehoerde=True)
if self.include_jagdbehoerde:
bird_filter |= Q(bird__melden_an_jagdbehoerde=True)
if bird_filter:
queryset = queryset.filter(bird_filter)
return queryset.select_related('bird', 'status', 'aviary', 'user').order_by('date_found')
def generate_csv(self):
"""Generate CSV content and return as string with bird count."""
birds = self.get_birds_queryset()
bird_count = birds.count()
# Create CSV in memory
output = StringIO()
writer = csv.writer(output, delimiter=';', quoting=csv.QUOTE_ALL)
# Header row
headers = [
'Vogel',
'Alter',
'Geschlecht',
'Gefunden am',
'Fundort',
'Fundumstände',
'Diagnose bei Fund',
'Status'
]
writer.writerow(headers)
# Data rows
for bird in birds:
row = [
bird.bird.name if bird.bird else '',
bird.get_age_display() if bird.age else '',
bird.get_sex_display() if bird.sex else '',
bird.date_found.strftime('%d.%m.%Y') if bird.date_found else '',
bird.place or '',
bird.find_circumstances or '',
bird.diagnostic_finding or '',
bird.status.description if bird.status else '',
]
writer.writerow(row)
csv_content = output.getvalue()
output.close()
return csv_content, bird_count
def get_filename(self):
"""Generate filename for the report."""
return f"wildvogelhilfe_report_{self.date_from}_{self.date_to}.csv"
def get_summary(self):
"""Get summary statistics for the report."""
birds = self.get_birds_queryset()
summary = {
'total_birds': birds.count(),
'naturschutz_birds': birds.filter(bird__melden_an_naturschutzbehoerde=True).count() if self.include_naturschutzbehoerde else 0,
'jagd_birds': birds.filter(bird__melden_an_jagdbehoerde=True).count() if self.include_jagdbehoerde else 0,
'date_from': self.date_from,
'date_to': self.date_to,
}
return summary
def send_email_report(self, email_addresses, automatic_report=None):
"""Send the report via email to specified addresses."""
from .models import ReportLog
csv_content, bird_count = self.generate_csv()
filename = self.get_filename()
# Prepare email context
context = {
'date_from': self.date_from.strftime('%d.%m.%Y'),
'date_to': self.date_to.strftime('%d.%m.%Y'),
'patient_count': bird_count,
'filter_naturschutzbehörde': self.include_naturschutzbehoerde,
'filter_jagdbehörde': self.include_jagdbehoerde,
'automatic_report': automatic_report,
'created_at': date.today().strftime('%d.%m.%Y'),
}
# Render email templates
subject = render_to_string('reports/email/report_subject.txt', context).strip()
message = render_to_string('reports/email/report_message.txt', context)
# Create email
email = EmailMessage(
subject=subject,
body=message,
from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@wildvogelhilfe-jena.de'),
to=email_addresses,
)
# Attach CSV file
email.attach(filename, csv_content, 'text/csv')
try:
# Send email
email.send()
# Create log entry
report_log = ReportLog.objects.create(
automatic_report=automatic_report,
date_from=self.date_from,
date_to=self.date_to,
patient_count=bird_count,
include_naturschutzbehörde=self.include_naturschutzbehoerde,
include_jagdbehörde=self.include_jagdbehoerde,
email_sent_to=email_addresses,
)
# Save CSV file to the log
report_log.csv_file.save(
filename,
ContentFile(csv_content.encode('utf-8')),
save=True
)
return report_log, True, None
except Exception as e:
return None, False, str(e)
def create_download_log(self, automatic_report=None):
"""Create a log entry for downloaded reports."""
from .models import ReportLog
csv_content, bird_count = self.generate_csv()
filename = self.get_filename()
# Create log entry
report_log = ReportLog.objects.create(
automatic_report=automatic_report,
date_from=self.date_from,
date_to=self.date_to,
patient_count=bird_count,
include_naturschutzbehörde=self.include_naturschutzbehoerde,
include_jagdbehörde=self.include_jagdbehoerde,
email_sent_to=[], # Empty list indicates download
)
# Save CSV file to the log
report_log.csv_file.save(
filename,
ContentFile(csv_content.encode('utf-8')),
save=True
)
return report_log