224 lines
6.7 KiB
Python
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()
|