166 lines
No EOL
6.3 KiB
Python
166 lines
No EOL
6.3 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()
|
|
|
|
|
|
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']) |