let the year be changeable
This commit is contained in:
parent
1368217187
commit
e047dade4d
2 changed files with 121 additions and 12 deletions
|
@ -281,6 +281,27 @@
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Jahr-Navigation */
|
||||||
|
.year-navigation .btn {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-navigation .btn:hover:not(:disabled) {
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-navigation .btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -295,11 +316,40 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 1. Übersicht aktuelles Jahr -->
|
<!-- 1. Übersicht ausgewähltes Jahr mit Navigation -->
|
||||||
<div class="section-header">
|
<div class="section-header d-flex justify-content-between align-items-center">
|
||||||
<h2 class="mb-0">
|
<h2 class="mb-0">
|
||||||
<i class="fas fa-calendar-alt"></i> Übersicht {{ current_year }}
|
<i class="fas fa-calendar-alt"></i> Übersicht {{ selected_year }}
|
||||||
</h2>
|
</h2>
|
||||||
|
<div class="year-navigation d-flex align-items-center">
|
||||||
|
{% if can_go_previous %}
|
||||||
|
<a href="?year={{ previous_year }}" class="btn btn-outline-light me-2" title="Vorheriges Jahr ({{ previous_year }})">
|
||||||
|
<i class="fas fa-chevron-left"></i>
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<button class="btn btn-outline-light me-2" disabled title="Keine Daten vor {{ earliest_year }}">
|
||||||
|
<i class="fas fa-chevron-left"></i>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span class="mx-3 fw-bold">{{ selected_year }}</span>
|
||||||
|
|
||||||
|
{% if can_go_next %}
|
||||||
|
<a href="?year={{ next_year }}" class="btn btn-outline-light ms-2" title="Nächstes Jahr ({{ next_year }})">
|
||||||
|
<i class="fas fa-chevron-right"></i>
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<button class="btn btn-outline-light ms-2" disabled title="Aktuelles Jahr erreicht">
|
||||||
|
<i class="fas fa-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if selected_year != current_year %}
|
||||||
|
<a href="?year={{ current_year }}" class="btn btn-light ms-3" title="Zurück zu {{ current_year }}">
|
||||||
|
<i class="fas fa-calendar-day me-1"></i>{{ current_year }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-4 mb-4">
|
<div class="row g-4 mb-4">
|
||||||
|
@ -441,7 +491,7 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="mb-0">{{ current_year }}</h5>
|
<h5 class="mb-0">{{ selected_year }}</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{% if circumstances_this_year %}
|
{% if circumstances_this_year %}
|
||||||
|
@ -456,7 +506,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="text-center text-muted">
|
<div class="text-center text-muted">
|
||||||
<p>Keine Daten für {{ current_year }} verfügbar</p>
|
<p>Keine Daten für {{ selected_year }} verfügbar</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -516,6 +566,37 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Tastatur-Navigation für Jahre
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
// Nur reagieren wenn kein Input-Element fokussiert ist
|
||||||
|
if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === 'ArrowLeft') {
|
||||||
|
// Vorheriges Jahr
|
||||||
|
const prevButton = document.querySelector('a[href*="year={{ previous_year }}"]');
|
||||||
|
if (prevButton) {
|
||||||
|
window.location.href = prevButton.href;
|
||||||
|
}
|
||||||
|
} else if (e.key === 'ArrowRight') {
|
||||||
|
// Nächstes Jahr
|
||||||
|
const nextButton = document.querySelector('a[href*="year={{ next_year }}"]');
|
||||||
|
if (nextButton) {
|
||||||
|
window.location.href = nextButton.href;
|
||||||
|
}
|
||||||
|
} else if (e.key === 'Home') {
|
||||||
|
// Zurück zum aktuellen Jahr
|
||||||
|
const currentYearButton = document.querySelector('a[href*="year={{ current_year }}"]');
|
||||||
|
if (currentYearButton) {
|
||||||
|
window.location.href = currentYearButton.href;
|
||||||
|
} else {
|
||||||
|
// Falls schon im aktuellen Jahr, zur Statistik-Hauptseite
|
||||||
|
window.location.href = window.location.pathname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q, Min
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from bird.models import FallenBird, Bird, BirdStatus, Circumstance
|
from bird.models import FallenBird, Bird, BirdStatus, Circumstance
|
||||||
|
@ -13,9 +13,31 @@ class StatisticView(LoginRequiredMixin, TemplateView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
# Aktuelles Jahr
|
# Jahr aus URL-Parameter oder aktuelles Jahr
|
||||||
|
selected_year = self.request.GET.get('year', timezone.now().year)
|
||||||
|
try:
|
||||||
|
selected_year = int(selected_year)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
selected_year = timezone.now().year
|
||||||
|
|
||||||
current_year = timezone.now().year
|
current_year = timezone.now().year
|
||||||
|
|
||||||
|
# Stelle sicher, dass das ausgewählte Jahr nicht in der Zukunft liegt
|
||||||
|
if selected_year > current_year:
|
||||||
|
selected_year = current_year
|
||||||
|
|
||||||
|
# Finde das früheste Jahr mit Daten
|
||||||
|
earliest_year_with_data = FallenBird.objects.aggregate(
|
||||||
|
earliest=Min('date_found__year')
|
||||||
|
)['earliest']
|
||||||
|
|
||||||
|
if earliest_year_with_data is None:
|
||||||
|
earliest_year_with_data = current_year
|
||||||
|
|
||||||
|
# Stelle sicher, dass das ausgewählte Jahr nicht vor dem frühesten Jahr liegt
|
||||||
|
if selected_year < earliest_year_with_data:
|
||||||
|
selected_year = earliest_year_with_data
|
||||||
|
|
||||||
# Lade aktive Konfiguration
|
# Lade aktive Konfiguration
|
||||||
try:
|
try:
|
||||||
config = StatisticConfiguration.objects.get(is_active=True)
|
config = StatisticConfiguration.objects.get(is_active=True)
|
||||||
|
@ -31,11 +53,17 @@ class StatisticView(LoginRequiredMixin, TemplateView):
|
||||||
|
|
||||||
context['config'] = config
|
context['config'] = config
|
||||||
context['current_year'] = current_year
|
context['current_year'] = current_year
|
||||||
|
context['selected_year'] = selected_year
|
||||||
|
context['earliest_year'] = earliest_year_with_data
|
||||||
|
context['can_go_previous'] = selected_year > earliest_year_with_data
|
||||||
|
context['can_go_next'] = selected_year < current_year
|
||||||
|
context['previous_year'] = selected_year - 1 if context['can_go_previous'] else None
|
||||||
|
context['next_year'] = selected_year + 1 if context['can_go_next'] else None
|
||||||
|
|
||||||
# 1. Jahresstatistik
|
# 1. Jahresstatistik
|
||||||
if config.show_year_total_patients:
|
if config.show_year_total_patients:
|
||||||
patients_this_year = FallenBird.objects.filter(
|
patients_this_year = FallenBird.objects.filter(
|
||||||
date_found__year=current_year
|
date_found__year=selected_year
|
||||||
).count()
|
).count()
|
||||||
context['patients_this_year'] = patients_this_year
|
context['patients_this_year'] = patients_this_year
|
||||||
|
|
||||||
|
@ -46,7 +74,7 @@ class StatisticView(LoginRequiredMixin, TemplateView):
|
||||||
for group in year_groups:
|
for group in year_groups:
|
||||||
status_ids = list(group.status_list.values_list('id', flat=True))
|
status_ids = list(group.status_list.values_list('id', flat=True))
|
||||||
year_count = FallenBird.objects.filter(
|
year_count = FallenBird.objects.filter(
|
||||||
date_found__year=current_year,
|
date_found__year=selected_year,
|
||||||
status__id__in=status_ids
|
status__id__in=status_ids
|
||||||
).count()
|
).count()
|
||||||
|
|
||||||
|
@ -144,10 +172,10 @@ class StatisticView(LoginRequiredMixin, TemplateView):
|
||||||
|
|
||||||
context['bird_stats'] = bird_stats
|
context['bird_stats'] = bird_stats
|
||||||
|
|
||||||
# 4. Fundumstände-Statistiken (unverändert)
|
# 4. Fundumstände-Statistiken
|
||||||
# Fundumstände für aktuelles Jahr
|
# Fundumstände für ausgewähltes Jahr
|
||||||
circumstances_this_year = FallenBird.objects.filter(
|
circumstances_this_year = FallenBird.objects.filter(
|
||||||
date_found__year=current_year,
|
date_found__year=selected_year,
|
||||||
find_circumstances__isnull=False
|
find_circumstances__isnull=False
|
||||||
).values('find_circumstances__name', 'find_circumstances__description').annotate(
|
).values('find_circumstances__name', 'find_circumstances__description').annotate(
|
||||||
count=Count('id')
|
count=Count('id')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue