loko/streetup/common/utils.py

164 lines
No EOL
6.2 KiB
Python

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()
REGIONAL_TERMINOLOGY = {
'BE': {
'regional_standard_name': 'Belgique (PEB / Maison Communale / Sécurité PMR)',
'admin_building': 'Hôtel de Ville / Maison Communale',
'energy_diag_short': 'PEB',
'energy_diag_full': 'Performance Énergétique des Bâtiments (PEB)',
'carbon_diag_short': 'Bilan Carbone',
'carbon_diag_full': 'Émissions de Carbone / Bilan Énergétique',
'public_access_short': 'Accès public & PMR',
'public_access_full': 'Accueil & Sécurité du Public (Normes PMR)',
'public_access_category': 'Catégorie d\'accueil',
'public_access_type': 'Type d\'affectation',
},
'FR': {
'regional_standard_name': 'France (DPE / GES / Mairie / ERP)',
'admin_building': 'Mairie / Hôtel de Ville',
'energy_diag_short': 'DPE',
'energy_diag_full': 'Diagnostic de Performance Énergétique (DPE)',
'carbon_diag_short': 'GES',
'carbon_diag_full': 'Émissions de Gaz à Effet de Serre (GES)',
'public_access_short': 'ERP',
'public_access_full': 'Établissement Recevant du Public (ERP)',
'public_access_category': 'Catégorie ERP',
'public_access_type': 'Type ERP',
},
'INT': {
'regional_standard_name': 'International / Générique',
'admin_building': 'Bâtiment Administratif / Hôtel de Ville',
'energy_diag_short': 'PEB / DPE',
'energy_diag_full': 'Performance Énergétique (PEB / DPE / EPC)',
'carbon_diag_short': 'Émissions Carbone',
'carbon_diag_full': 'Bilan Carbone & Émissions',
'public_access_short': 'Accès Public',
'public_access_full': 'Bâtiment d\'Accueil du Public (ERP / PMR)',
'public_access_category': 'Capacité d\'accueil',
'public_access_type': 'Type d\'établissement',
}
}
def get_regional_terms(standard_code='BE'):
"""Returns regional terminology dictionary for a given standard code ('BE', 'FR', 'INT')."""
return REGIONAL_TERMINOLOGY.get(standard_code or 'BE', REGIONAL_TERMINOLOGY['BE'])