FallenBirdyForm/app/statistic/views.py
2025-07-08 17:46:30 +02:00

185 lines
8 KiB
Python

from django.views.generic import TemplateView
from django.db.models import Count, Q
from django.utils import timezone
from datetime import datetime
from bird.models import FallenBird, Bird, BirdStatus, Circumstance
from .models import StatisticIndividual, StatisticYearGroup, StatisticTotalGroup, StatisticConfiguration
class StatisticView(TemplateView):
template_name = 'statistic/overview.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Aktuelles Jahr
current_year = timezone.now().year
# Lade aktive Konfiguration
try:
config = StatisticConfiguration.objects.get(is_active=True)
except StatisticConfiguration.DoesNotExist:
# Fallback: Erstelle Standard-Konfiguration wenn keine vorhanden
config = StatisticConfiguration.objects.create(
is_active=True,
show_year_total_patients=True,
show_total_patients=True,
show_percentages=True,
show_absolute_numbers=True
)
context['config'] = config
context['current_year'] = current_year
# 1. Jahresstatistik
if config.show_year_total_patients:
patients_this_year = FallenBird.objects.filter(
date_found__year=current_year
).count()
context['patients_this_year'] = patients_this_year
# Lade aktive Jahres-Gruppen und berechne Statistiken
year_groups = StatisticYearGroup.objects.filter(is_active=True).order_by('order')
year_summary = []
for group in year_groups:
status_ids = list(group.status_list.values_list('id', flat=True))
year_count = FallenBird.objects.filter(
date_found__year=current_year,
status__id__in=status_ids
).count()
# Berechne Prozentanteil
year_percentage = (year_count / patients_this_year * 100) if patients_this_year > 0 else 0
year_summary.append({
'name': group.name,
'count': year_count,
'percentage': round(year_percentage, 1),
'color': group.color,
'order': group.order
})
context['year_summary'] = year_summary
# 2. Gesamtstatistik
if config.show_total_patients:
total_patients = FallenBird.objects.count()
context['total_patients'] = total_patients
# Lade aktive Gesamt-Gruppen und berechne Statistiken
total_groups = StatisticTotalGroup.objects.filter(is_active=True).order_by('order')
total_summary = []
for group in total_groups:
status_ids = list(group.status_list.values_list('id', flat=True))
total_count = FallenBird.objects.filter(
status__id__in=status_ids
).count()
# Berechne Prozentanteil
total_percentage = (total_count / total_patients * 100) if total_patients > 0 else 0
total_summary.append({
'name': group.name,
'count': total_count,
'percentage': round(total_percentage, 1),
'color': group.color,
'order': group.order
})
context['total_summary'] = total_summary
# 3. Statistik pro Vogelart (Individuen - dynamisch basierend auf Konfiguration)
individual_groups = StatisticIndividual.objects.filter(is_active=True).order_by('order')
context['statistic_individuals'] = individual_groups
bird_stats = []
for bird in Bird.objects.all():
fallen_birds = FallenBird.objects.filter(bird=bird)
total_count = fallen_birds.count()
if total_count > 0: # Nur Vögel anzeigen, die auch Patienten haben
bird_data = {
'name': bird.name,
'species': bird.species or 'Unbekannt',
'total': total_count,
'groups': []
}
# Berechne Statistiken für jede konfigurierte Individuen-Gruppe
for group in individual_groups:
status_ids = list(group.status_list.values_list('id', flat=True))
group_count = fallen_birds.filter(status__id__in=status_ids).count()
group_percentage = (group_count / total_count) * 100 if total_count > 0 else 0
bird_data['groups'].append({
'name': group.name,
'color': group.color,
'count': group_count,
'percentage': round(group_percentage, 1),
'order': group.order
})
bird_stats.append(bird_data)
# Sortiere nach Gesamtanzahl (absteigend)
bird_stats.sort(key=lambda x: x['total'], reverse=True)
# Berechne Balkenbreiten basierend auf der höchsten Vogelart für bessere Visualisierung
if bird_stats:
max_count = bird_stats[0]['total'] # Höchste Anzahl (wird 100% der Balkenbreite)
for bird in bird_stats:
# Berechne Balkenbreite für Gesamtanzahl (proportional zur häufigsten Art)
total_bar_width = (bird['total'] / max_count) * 100 if max_count > 0 else 0
bird['total_bar_width'] = f"{total_bar_width:.1f}".replace(',', '.')
# Berechne absolute Segmentbreiten (bezogen auf die gesamte verfügbare Container-Breite)
for group_data in bird['groups']:
# Absolute Breite = (Anteil dieser Gruppe / 100) * Gesamtbalkenbreite
absolute_width = (group_data['percentage'] / 100) * total_bar_width
group_data['absolute_width'] = f"{absolute_width:.1f}".replace(',', '.')
context['bird_stats'] = bird_stats
# 4. Fundumstände-Statistiken (unverändert)
# Fundumstände für aktuelles Jahr
circumstances_this_year = FallenBird.objects.filter(
date_found__year=current_year,
find_circumstances__isnull=False
).values('find_circumstances__name', 'find_circumstances__description').annotate(
count=Count('id')
).order_by('-count')
# Fundumstände für alle Jahre
circumstances_all_time = FallenBird.objects.filter(
find_circumstances__isnull=False
).values('find_circumstances__name', 'find_circumstances__description').annotate(
count=Count('id')
).order_by('-count')
# Formatiere Daten für Tortendiagramme
def format_circumstances_data(circumstances_data):
total = sum(item['count'] for item in circumstances_data)
formatted_data = []
colors = [
'#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF',
'#FF9F40', '#FF6384', '#C9CBCF', '#4BC0C0', '#FF6384'
]
for i, item in enumerate(circumstances_data):
name = item['find_circumstances__name'] or item['find_circumstances__description']
percentage = round((item['count'] / total) * 100, 1) if total > 0 else 0
formatted_data.append({
'name': name,
'count': item['count'],
'percentage': percentage,
'color': colors[i % len(colors)]
})
return formatted_data, total
context['circumstances_this_year'], context['circumstances_this_year_total'] = format_circumstances_data(circumstances_this_year)
context['circumstances_all_time'], context['circumstances_all_time_total'] = format_circumstances_data(circumstances_all_time)
return context