from django.http import HttpResponseForbidden, HttpResponseRedirect, JsonResponse from django.urls import reverse from django.shortcuts import get_object_or_404 from django.db.models import Func, CharField from urllib.parse import urlencode import time import functools import unicodedata from common.models import Thematic, UserConfig, UserThematics def check_thematic_access(thematic_code, edit_permission_required=False): """Decorator to verify user access to a specific thematic (passed as a parameter).""" def decorator(view_func): @functools.wraps(view_func) def _wrapped_view(request, *args, **kwargs): user_config = UserConfig.objects.filter(user=request.user).first() # Get user access configuration thematic = get_object_or_404(Thematic, code=thematic_code) # Get thematic from the given parameter # Check if the user has a configuration if not user_config: return HttpResponseForbidden("Access denied: You do not have access to this thematic.") # Check if the user has access to the thematic if edit_permission_required: if not UserThematics.objects.filter( user_config=user_config, thematic=thematic, can_edit_assets=True ).exists(): return HttpResponseForbidden("Access denied: You do not have edit permissions for this thematic.") else: # Si l'utilisateur a un rôle 'admin' ou 'operator', il peut voir tous les assets if ( (not user_config.roles.filter(name__in=['admin','operator']).exists()) and (not UserThematics.objects.filter( user_config=user_config, thematic=thematic, can_view_assets=True ).exists()) ): return HttpResponseForbidden("Access denied: You do not have view permissions for this thematic.") # Execute the view if everything is OK return view_func(request, *args, **kwargs) return _wrapped_view return decorator def redirect_with_variant(viewname, variant=None, url_kwargs=None, extra_query_params=None): """ Redirige vers une vue avec des paramètres d'URL et le paramètre ?v=... si fourni. :param viewname: nom de la vue (ex: 'controls:controls_detail') :param variant: 'mobile' ou 'desktop' (ajouté comme ?v=mobile dans l'URL) :param url_kwargs: dictionnaire des arguments de reverse (ex: {'control_id': 3}) :param extra_query_params: dictionnaire de paramètres supplémentaires à ajouter dans la query string """ if url_kwargs is None: url_kwargs = {} if extra_query_params is None: extra_query_params = {} if variant: extra_query_params['v'] = variant base_url = reverse(viewname, kwargs=url_kwargs) if extra_query_params: query_string = urlencode(extra_query_params) full_url = f"{base_url}?{query_string}" else: full_url = base_url return HttpResponseRedirect(full_url) def measure_time(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.time() response = func(*args, **kwargs) elapsed = time.time() - start print(f"{func.__name__} exécutée en {elapsed:.4f} secondes") return response return wrapper # Appelle la fonction PostgreSQL unaccent(...) class Unaccent(Func): function = 'public.unaccent_immutable' output_field = CharField() def strip_accents_lower(s: str) -> str: if not s: return '' s = ''.join(c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn') return s.lower() def get_short_name(user): """ Retourne 'Prénom N.' à partir d'un objet User. """ if not user: return "" first = getattr(user, "first_name", "") or "" last = getattr(user, "last_name", "") or "" initial = (last[0].upper() + ".") if last else "" return f"{first} {initial}".strip()