import json from django.shortcuts import render, get_object_or_404, redirect from django.contrib import messages from django.contrib.auth.decorators import login_required from django.db.models import Q, F from django.http import JsonResponse, HttpResponseForbidden from django.urls import reverse from django.utils.translation import gettext as _, get_language from django.contrib.gis.geos import GEOSGeometry from common.models import UserThematics from common.utils import check_thematic_access, get_regional_terms from assets.permissions import can_edit_asset from assets.forms import PublicBuildingForm from assets.models.buildings import ( PublicBuilding, BuildingLevel, BuildingRoom, BuildingEquipment, PublicBuildingDocument, USAGE_TYPE_CHOICES, BUILDING_STATUS_CHOICES, DPE_RATING_CHOICES, ERP_CATEGORY_CHOICES ) def publicbuildings_locations_geojson(request): """ Vue GeoJSON pour les emplacements des bâtiments publics (vide car les bâtiments sont gérés directement comme assets). """ return JsonResponse({"type": "FeatureCollection", "features": []}) def publicbuildings_assets_geojson(request): """ Vue GeoJSON renvoyant la collection de bâtiments publics pour la carte unifiée des assets. """ lang = get_language() if get_language() in ('fr', 'nl') else 'fr' if request.method == "POST": try: data = json.loads(request.body) except json.JSONDecodeError: return JsonResponse({"error": "Invalid JSON"}, status=400) statuses = data.get('asset_status', []) categories = data.get('category') or [] if isinstance(categories, str): categories = [categories] filter_geom_raw = data.get("filter_geom") if filter_geom_raw: try: filter_geom = GEOSGeometry(json.dumps(filter_geom_raw['geometry']), srid=4326) filter_geom.transform(3812) except Exception as e: return JsonResponse({"error": f"Invalid geometry: {str(e)}"}, status=400) else: filter_geom = None search_q = (data.get('q') or '').strip() else: statuses = request.GET.getlist('asset_status') categories = request.GET.getlist('category', []) filter_geom = None search_q = request.GET.get('q', '').strip() qs = PublicBuilding.objects.all() if categories: qs = qs.filter(category__id__in=categories) if statuses: qs = qs.filter(building_status__in=statuses) if filter_geom: qs = qs.filter(geom__intersects=filter_geom) if search_q: qs = qs.filter( Q(code__icontains=search_q) | Q(name_fr__icontains=search_q) | Q(name_nl__icontains=search_q) | Q(address__icontains=search_q) | Q(owner__icontains=search_q) ) features = [] from assets.views.core import get_element_type_label for b in qs: geometry = None if b.geom: geom_4326 = b.geom.clone() geom_4326.transform(4326) centroid = geom_4326.centroid geometry = {"type": "Point", "coordinates": [centroid.x, centroid.y]} elif b.lon is not None and b.lat is not None: geometry = {"type": "Point", "coordinates": [b.lon, b.lat]} detail_url = reverse('assets:publicbuilding_detail', kwargs={'pk': b.pk}) features.append({ "type": "Feature", "id": b.id, "geometry": geometry, "properties": { "code": b.code, "name": b.get_name(), "category": b.category.get_name() if b.category else _("Bâtiment public"), "category_id": b.category_id, "status": b.building_status, "building_status": b.building_status, "building_status_display": b.get_building_status_display(), "usage_type": b.usage_type, "usage_type_display": b.get_usage_type_display(), "owner": b.owner or '', "is_erp": b.is_erp, "erp_category": b.erp_category or '', "dpe_rating": b.dpe_rating or '', "lon": b.lon, "lat": b.lat, "color": "#2B579A", "display_color": "#2B579A", "asset_type": "publicbuilding", "detail_url": detail_url, "element_type": get_element_type_label("publicbuilding"), "thematic": "publicbuildings", "is_location": False, } }) return JsonResponse({"type": "FeatureCollection", "features": features}) @login_required def buildings_index(request): """ Vue principale (Index / Liste) des Bâtiments Publics. """ lang = get_language() buildings = PublicBuilding.objects.all() # Search filter search_query = request.GET.get('q', '').strip() if search_query: buildings = buildings.filter( Q(code__icontains=search_query) | Q(name_fr__icontains=search_query) | Q(name_nl__icontains=search_query) | Q(address__icontains=search_query) | Q(owner__icontains=search_query) ) # Usage filter usage_filter = request.GET.get('usage_type') if usage_filter: buildings = buildings.filter(usage_type=usage_filter) # Status filter status_filter = request.GET.get('building_status') if status_filter: buildings = buildings.filter(building_status=status_filter) # ERP filter erp_filter = request.GET.get('is_erp') if erp_filter in ['true', '1']: buildings = buildings.filter(is_erp=True) elif erp_filter in ['false', '0']: buildings = buildings.filter(is_erp=False) # DPE rating filter dpe_filter = request.GET.get('dpe_rating') if dpe_filter: buildings = buildings.filter(dpe_rating=dpe_filter) building_list = [] for b in buildings: b.can_edit = can_edit_asset(request.user, b) building_list.append(b) context = { 'buildings': building_list, 'search_query': search_query, 'usage_type_choices': USAGE_TYPE_CHOICES, 'building_status_choices': BUILDING_STATUS_CHOICES, 'dpe_rating_choices': DPE_RATING_CHOICES, 'selected_usage': usage_filter, 'selected_status': status_filter, 'selected_erp': erp_filter, 'selected_dpe': dpe_filter, 'active_tab': 'buildings', } return render(request, 'assets/buildings/publicbuilding_list.html', context) @login_required def building_detail(request, pk): """ Vue détaillée d'un Bâtiment Public avec ses niveaux, pièces, équipements, documents, interventions et cartographie. """ from django.contrib.contenttypes.models import ContentType from interventions.models import Intervention from interventions.permissions import filter_viewable_interventions_for_user building = get_object_or_404(PublicBuilding, pk=pk) can_edit = can_edit_asset(request.user, building) regional_terms = get_regional_terms(getattr(building, '_regional_standard', 'BE')) levels = building.levels.all().prefetch_related('rooms') rooms = building.rooms.all() equipments = building.equipments.all() documents = building.documents.all() content_type = ContentType.objects.get_for_model(PublicBuilding) # Récupération des interventions associées au bâtiment base_interventions = filter_viewable_interventions_for_user(request.user) interventions = base_interventions.filter( interventionasset__object_id=building.id, interventionasset__content_type=content_type ).distinct().order_by('-creation_time') # GeoJSON pour la carte de localisation building_geojson = None center_lon, center_lat = None, None if building.geom: geom_4326 = building.geom.clone() geom_4326.transform(4326) building_geojson = json.loads(geom_4326.json) centroid = geom_4326.centroid center_lon, center_lat = centroid.x, centroid.y context = { 'building': building, 'can_edit': can_edit, 'regional_terms': regional_terms, 'levels': levels, 'rooms': rooms, 'equipments': equipments, 'documents': documents, 'interventions': interventions, 'content_type': content_type, 'building_geojson_json': json.dumps(building_geojson) if building_geojson else 'null', 'center_lon': center_lon, 'center_lat': center_lat, } return render(request, 'assets/buildings/publicbuilding_detail.html', context) @login_required def publicbuilding_edit(request, pk): """ Vue d'édition d'un Bâtiment Public réservée aux utilisateurs autorisés. """ building = get_object_or_404(PublicBuilding, pk=pk) if not can_edit_asset(request.user, building): return HttpResponseForbidden(_("Vous n'avez pas la permission de modifier ce bâtiment.")) if request.method == 'POST': form = PublicBuildingForm(request.POST, instance=building) if form.is_valid(): saved_building = form.save() messages.success(request, _("Le bâtiment '%s' a été mis à jour avec succès.") % saved_building.code) return redirect('assets:publicbuilding_detail', pk=saved_building.pk) else: form = PublicBuildingForm(instance=building) regional_terms = get_regional_terms(getattr(building, '_regional_standard', 'BE')) center_lon, center_lat = None, None if building.lon is not None and building.lat is not None: center_lon, center_lat = building.lon, building.lat elif building.geom: try: geom_4326 = building.geom.clone() geom_4326.transform(4326) centroid = geom_4326.centroid center_lon, center_lat = centroid.x, centroid.y except Exception: pass context = { 'building': building, 'form': form, 'regional_terms': regional_terms, 'center_lon': center_lon, 'center_lat': center_lat, } return render(request, 'assets/buildings/publicbuilding_form.html', context) @login_required def publicbuildings_assets_detail(request, asset_model, asset_id): """ Vue générique pour le détail d'un équipement/asset d'un bâtiment public. """ from django.contrib.contenttypes.models import ContentType from assets.views.core import get_asset_edit_context from interventions.models import Intervention from interventions.permissions import filter_viewable_interventions_for_user content_type = get_object_or_404(ContentType, app_label='assets', model=asset_model.lower()) model_class = content_type.model_class() asset = get_object_or_404(model_class, pk=asset_id) base_interventions = filter_viewable_interventions_for_user(request.user) interventions = base_interventions.filter( interventionasset__object_id=asset.id, interventionasset__content_type=content_type ).distinct().order_by('-creation_time') context = { 'asset': asset, 'content_type': content_type, 'interventions': interventions, } context.update(get_asset_edit_context(request, asset, content_type)) return render(request, "assets/detail_generic.html", context)