loko/streetup/analytics/views.py
2026-07-22 14:48:40 +02:00

224 lines
6.7 KiB
Python

"""
Vues pour le tableau de bord d'analytics.
"""
from django.shortcuts import render
from django.contrib.auth.decorators import user_passes_test
from django.utils import timezone
from django.db.models import Count, Avg, Q, F
from django.db.models.functions import TruncDate, TruncHour
from datetime import timedelta
import json
from .models import PageView, UserSession
from django.contrib.auth.models import User
def superuser_required(view_func):
"""Décorateur qui vérifie que l'utilisateur est un superadmin."""
decorated_view = user_passes_test(
lambda u: u.is_active and u.is_superuser,
login_url='admin:login'
)(view_func)
return decorated_view
@superuser_required
def dashboard(request):
"""
Vue principale du tableau de bord d'analytics.
Affiche les statistiques d'utilisation de l'application.
"""
# Période à analyser (par défaut: 30 derniers jours)
days = int(request.GET.get('days', 30))
start_date = timezone.now() - timedelta(days=days)
# Statistiques générales
stats = {
'total_users': User.objects.filter(is_active=True).count(),
'active_users': _get_active_users_count(start_date),
'total_sessions': UserSession.objects.filter(login_time__gte=start_date).count(),
'active_sessions': UserSession.objects.filter(is_active=True).count(),
'total_page_views': PageView.objects.filter(timestamp__gte=start_date).count(),
'avg_response_time': PageView.objects.filter(
timestamp__gte=start_date,
response_time_ms__isnull=False
).aggregate(avg=Avg('response_time_ms'))['avg'] or 0,
}
# Calculer les sessions par utilisateur (éviter la division par zéro)
stats['sessions_per_user'] = (
round(stats['total_sessions'] / stats['active_users'], 2)
if stats['active_users'] > 0
else 0
)
# Pages les plus visitées
top_pages = PageView.objects.filter(
timestamp__gte=start_date
).values('path').annotate(
count=Count('id')
).order_by('-count')[:10]
# Utilisateurs les plus actifs
top_users = PageView.objects.filter(
timestamp__gte=start_date,
user__isnull=False
).values('user__username', 'user__id').annotate(
views=Count('id'),
sessions=Count('session_key', distinct=True)
).order_by('-views')[:10]
# Activité par jour
daily_activity = PageView.objects.filter(
timestamp__gte=start_date
).annotate(
date=TruncDate('timestamp')
).values('date').annotate(
views=Count('id'),
users=Count('user', distinct=True),
sessions=Count('session_key', distinct=True)
).order_by('date')
# Préparer les données pour les graphiques
chart_data = {
'labels': [item['date'].strftime('%d/%m') for item in daily_activity],
'views': [item['views'] for item in daily_activity],
'users': [item['users'] for item in daily_activity],
'sessions': [item['sessions'] for item in daily_activity],
}
# Activité par heure (dernières 24h)
yesterday = timezone.now() - timedelta(days=1)
hourly_activity = PageView.objects.filter(
timestamp__gte=yesterday
).annotate(
hour=TruncHour('timestamp')
).values('hour').annotate(
count=Count('id')
).order_by('hour')
hourly_chart_data = {
'labels': [item['hour'].strftime('%H:%M') for item in hourly_activity],
'values': [item['count'] for item in hourly_activity],
}
context = {
'stats': stats,
'top_pages': top_pages,
'top_users': top_users,
'chart_data': json.dumps(chart_data),
'hourly_chart_data': json.dumps(hourly_chart_data),
'days': days,
}
return render(request, 'analytics/dashboard.html', context)
@superuser_required
def sessions_list(request):
"""
Liste détaillée des sessions utilisateurs.
"""
# Paramètres de filtrage
days = int(request.GET.get('days', 30))
user_id = request.GET.get('user')
active_only = request.GET.get('active_only', False)
start_date = timezone.now() - timedelta(days=days)
# Requête de base
sessions = UserSession.objects.filter(
login_time__gte=start_date
).select_related('user')
# Filtres
if user_id:
sessions = sessions.filter(user_id=user_id)
if active_only:
sessions = sessions.filter(is_active=True)
# Ordre
sessions = sessions.order_by('-login_time')
# Pagination (simple, 100 par page)
sessions = sessions[:100]
# Liste des utilisateurs pour le filtre
users = User.objects.filter(
analytics_sessions__login_time__gte=start_date
).distinct().order_by('username')
context = {
'sessions': sessions,
'users': users,
'selected_user_id': user_id,
'active_only': active_only,
'days': days,
}
return render(request, 'analytics/sessions_list.html', context)
@superuser_required
def page_views_list(request):
"""
Liste détaillée des vues de pages.
"""
# Paramètres de filtrage
days = int(request.GET.get('days', 7)) # Par défaut 7 jours car il peut y avoir beaucoup de données
user_id = request.GET.get('user')
path = request.GET.get('path')
start_date = timezone.now() - timedelta(days=days)
# Requête de base
page_views = PageView.objects.filter(
timestamp__gte=start_date
).select_related('user')
# Filtres
if user_id:
page_views = page_views.filter(user_id=user_id)
if path:
page_views = page_views.filter(path__icontains=path)
# Ordre
page_views = page_views.order_by('-timestamp')
# Pagination (simple, 100 par page)
page_views = page_views[:100]
# Liste des utilisateurs et chemins pour les filtres
users = User.objects.filter(
page_views__timestamp__gte=start_date
).distinct().order_by('username')
popular_paths = PageView.objects.filter(
timestamp__gte=start_date
).values('path').annotate(
count=Count('id')
).order_by('-count')[:20]
context = {
'page_views': page_views,
'users': users,
'popular_paths': popular_paths,
'selected_user_id': user_id,
'selected_path': path,
'days': days,
}
return render(request, 'analytics/page_views_list.html', context)
def _get_active_users_count(start_date):
"""
Compte le nombre d'utilisateurs actifs depuis start_date.
Un utilisateur est considéré actif s'il a au moins une page view.
"""
return User.objects.filter(
page_views__timestamp__gte=start_date
).distinct().count()