185 lines
8 KiB
Python
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
|