loko/streetup/assets/views/buildings.py

300 lines
11 KiB
Python

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'))
context = {
'building': building,
'form': form,
'regional_terms': regional_terms,
}
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)