|
|
|
|
|
|
|
|
|
|
|
from apps.analytics.models import SearchLog, PopularSearch |
|
|
from django.db.models import F |
|
|
|
|
|
class SearchTrackingService: |
|
|
"""Service centralisé pour tracker les recherches""" |
|
|
|
|
|
@staticmethod |
|
|
def log_search( |
|
|
category: str, |
|
|
search_query: str, |
|
|
user=None, |
|
|
filters_applied: dict = None, |
|
|
results_count: int = 0, |
|
|
request=None |
|
|
): |
|
|
""" |
|
|
Enregistre une recherche dans les logs |
|
|
|
|
|
Args: |
|
|
category: Catégorie de recherche (QUESTIONS, MENTORS, etc.) |
|
|
search_query: Terme recherché |
|
|
user: Utilisateur qui effectue la recherche (None si anonyme) |
|
|
filters_applied: Dict des filtres appliqués |
|
|
results_count: Nombre de résultats retournés |
|
|
request: Objet request Django pour extraire métadonnées |
|
|
|
|
|
Returns: |
|
|
SearchLog instance |
|
|
""" |
|
|
|
|
|
session_id = '' |
|
|
ip_address = None |
|
|
user_agent = '' |
|
|
page_url = '' |
|
|
|
|
|
if request: |
|
|
session_id = request.session.session_key or '' |
|
|
ip_address = request.META.get('REMOTE_ADDR') |
|
|
user_agent = request.META.get('HTTP_USER_AGENT', '')[:500] |
|
|
page_url = request.META.get('HTTP_REFERER', '')[:500] |
|
|
|
|
|
|
|
|
search_log = SearchLog.objects.create( |
|
|
user=user, |
|
|
category=category, |
|
|
search_query=search_query.strip()[:500], |
|
|
filters_applied=filters_applied or {}, |
|
|
results_count=results_count, |
|
|
session_id=session_id, |
|
|
ip_address=ip_address, |
|
|
user_agent=user_agent, |
|
|
page_url=page_url |
|
|
) |
|
|
|
|
|
|
|
|
SearchTrackingService._update_popular_search(category, search_query.strip()) |
|
|
|
|
|
return search_log |
|
|
|
|
|
@staticmethod |
|
|
def _update_popular_search(category: str, search_query: str): |
|
|
"""Mise à jour incrémentale des recherches populaires""" |
|
|
if not search_query or len(search_query) < 2: |
|
|
return |
|
|
|
|
|
popular, created = PopularSearch.objects.get_or_create( |
|
|
category=category, |
|
|
search_query=search_query[:500], |
|
|
defaults={'search_count': 1} |
|
|
) |
|
|
|
|
|
if not created: |
|
|
popular.search_count = F('search_count') + 1 |
|
|
popular.save(update_fields=['search_count', 'last_searched']) |
|
|
|
|
|
@staticmethod |
|
|
def log_result_click(search_log_id: int, result_id: str, position: int): |
|
|
""" |
|
|
Enregistre le clic sur un résultat de recherche |
|
|
|
|
|
Args: |
|
|
search_log_id: ID du SearchLog |
|
|
result_id: ID du résultat cliqué |
|
|
position: Position dans la liste (0-indexed) |
|
|
""" |
|
|
try: |
|
|
search_log = SearchLog.objects.get(id=search_log_id) |
|
|
search_log.clicked_result_id = str(result_id) |
|
|
search_log.clicked_result_position = position |
|
|
search_log.save(update_fields=['clicked_result_id', 'clicked_result_position']) |
|
|
except SearchLog.DoesNotExist: |
|
|
pass |
|
|
|
|
|
@staticmethod |
|
|
def get_popular_searches(category: str = None, limit: int = 10): |
|
|
""" |
|
|
Récupère les recherches les plus populaires |
|
|
|
|
|
Args: |
|
|
category: Filtrer par catégorie (None = toutes) |
|
|
limit: Nombre max de résultats |
|
|
|
|
|
Returns: |
|
|
QuerySet de PopularSearch |
|
|
""" |
|
|
qs = PopularSearch.objects.all() |
|
|
|
|
|
if category: |
|
|
qs = qs.filter(category=category) |
|
|
|
|
|
return qs[:limit] |
|
|
|
|
|
@staticmethod |
|
|
def get_trending_searches(category: str = None, days: int = 7, limit: int = 10): |
|
|
""" |
|
|
Récupère les recherches tendances (populaires récemment) |
|
|
|
|
|
Args: |
|
|
category: Filtrer par catégorie |
|
|
days: Nombre de jours à considérer |
|
|
limit: Nombre max de résultats |
|
|
|
|
|
Returns: |
|
|
Liste de dicts avec query et count |
|
|
""" |
|
|
from django.utils import timezone |
|
|
from datetime import timedelta |
|
|
from django.db.models import Count |
|
|
|
|
|
since = timezone.now() - timedelta(days=days) |
|
|
|
|
|
qs = SearchLog.objects.filter(created_at__gte=since) |
|
|
|
|
|
if category: |
|
|
qs = qs.filter(category=category) |
|
|
|
|
|
trending = qs.values('search_query').annotate( |
|
|
count=Count('id') |
|
|
).order_by('-count')[:limit] |
|
|
|
|
|
return list(trending) |
|
|
|