loko/streetup/common/utils.py

173 lines
No EOL
6.6 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):
if not getattr(request.user, 'is_authenticated', False):
return HttpResponseForbidden("Access denied: Authentication required.")
if getattr(request.user, 'is_superuser', False):
return view_func(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 ""
res = f"{first} {initial}".strip()
return res if res else getattr(user, "username", "")
from django.utils.translation import gettext_lazy as _
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'])