314 lines
11 KiB
Python
314 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'))
|
|
|
|
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)
|