684 lines
No EOL
27 KiB
Python
684 lines
No EOL
27 KiB
Python
from django.contrib.gis import admin
|
||
from django.contrib.contenttypes.admin import GenericTabularInline
|
||
from django import forms
|
||
|
||
from documents.models import DocumentFolder, DocumentFolderAttachment
|
||
|
||
from .models import TrafficLightIntersection, TrafficLightIntersectionDocument
|
||
from .models import TrafficLightPoleModel, TrafficLightPole, TrafficLightCableModel, TrafficLightCable, TrafficLightElectricalCabinet, TrafficLightElectricalCabinetModel
|
||
from .models import TrafficLightLanternModel, TrafficLightLantern, TrafficLightControllerModel, TrafficLightController, TrafficLightDetector, TrafficLightDetectorModel
|
||
from .models import TrafficLightContract, TrafficLightRadarModel, TrafficLightRadar
|
||
from .models import (
|
||
TrafficLightRadarPoleModel, TrafficLightRadarPole, TrafficLightElectricalCabinetContentModel,
|
||
TrafficLightElectricalCabinetContent, TrafficLightAccessoryModel, TrafficLightAccessory,
|
||
TrafficLightProgramming, TrafficLightControllerHardware, TrafficLightControllerLogFile,
|
||
TrafficLightNetworkHardware, TrafficLightVoltageHardware
|
||
)
|
||
from .models import SignStreet
|
||
from .models import AssetCategory, AssetHistory
|
||
from .models import GreenSurfacesComment, GreenSurfacesCommentPhoto
|
||
from .models import CleanGlassContainer, CleanGlassContainerModel, CleanLitterBin, CleanLitterBinModel, CleanLocation, CleanLocationContract
|
||
|
||
|
||
|
||
|
||
class AssetAdmin(admin.ModelAdmin):
|
||
def get_search_fields(self, request):
|
||
search_fields = list(super().get_search_fields(request) or [])
|
||
model_fields = {f.name for f in self.model._meta.get_fields()}
|
||
for field in ["code", "name_fr", "name_nl"]:
|
||
if field in model_fields and field not in search_fields:
|
||
search_fields.append(field)
|
||
return search_fields
|
||
|
||
def get_list_filter(self, request):
|
||
list_filter = list(super().get_list_filter(request) or [])
|
||
model_fields = {f.name for f in self.model._meta.get_fields()}
|
||
if "model" in model_fields and "model" not in list_filter:
|
||
list_filter.append("model")
|
||
return list_filter
|
||
|
||
|
||
class AssetGISAdmin(admin.GISModelAdmin):
|
||
gis_widget_kwargs = {
|
||
'attrs': {
|
||
"default_lon" : 4.3517, # Bruxelles
|
||
"default_lat" : 50.8503,
|
||
"default_zoom" : 12
|
||
},
|
||
}
|
||
|
||
def get_search_fields(self, request):
|
||
search_fields = list(super().get_search_fields(request) or [])
|
||
model_fields = {f.name for f in self.model._meta.get_fields()}
|
||
for field in ["code", "name_fr", "name_nl"]:
|
||
if field in model_fields and field not in search_fields:
|
||
search_fields.append(field)
|
||
return search_fields
|
||
|
||
def get_list_filter(self, request):
|
||
list_filter = list(super().get_list_filter(request) or [])
|
||
model_fields = {f.name for f in self.model._meta.get_fields()}
|
||
if "model" in model_fields and "model" not in list_filter:
|
||
list_filter.append("model")
|
||
return list_filter
|
||
|
||
|
||
@admin.register(TrafficLightContract)
|
||
class TrafficLightContractAdmin(AssetAdmin):
|
||
search_fields = ("intersection__code", "contract__contract_number")
|
||
list_display = ("intersection", "contract")
|
||
list_filter = ("contract",)
|
||
|
||
class TrafficLightContractAdminInline(admin.TabularInline):
|
||
model = TrafficLightContract
|
||
extra = 1
|
||
|
||
@admin.register(TrafficLightIntersection)
|
||
class TrafficLightIntersectionAdmin(AssetAdmin):
|
||
search_fields = ("code", "name_fr", "name_nl")
|
||
list_display = ("code", "name_fr", "municipality")
|
||
list_filter = ("municipality",)
|
||
inlines = [TrafficLightContractAdminInline,]
|
||
|
||
|
||
|
||
admin.site.register(TrafficLightIntersectionDocument, AssetAdmin)
|
||
admin.site.register(TrafficLightPoleModel, AssetAdmin)
|
||
admin.site.register(TrafficLightPole, AssetAdmin)
|
||
admin.site.register(TrafficLightCableModel, AssetAdmin)
|
||
admin.site.register(TrafficLightCable, AssetAdmin)
|
||
admin.site.register(TrafficLightLanternModel, AssetAdmin)
|
||
admin.site.register(TrafficLightLantern, AssetAdmin)
|
||
admin.site.register(TrafficLightControllerModel, AssetAdmin)
|
||
admin.site.register(TrafficLightDetector, AssetAdmin)
|
||
admin.site.register(TrafficLightDetectorModel, AssetAdmin)
|
||
admin.site.register(TrafficLightElectricalCabinetModel, AssetAdmin)
|
||
admin.site.register(TrafficLightElectricalCabinet, AssetAdmin)
|
||
admin.site.register(TrafficLightRadarModel, AssetAdmin)
|
||
admin.site.register(TrafficLightRadar, AssetAdmin)
|
||
admin.site.register(TrafficLightRadarPoleModel, AssetAdmin)
|
||
admin.site.register(TrafficLightRadarPole, AssetAdmin)
|
||
admin.site.register(TrafficLightElectricalCabinetContentModel, AssetAdmin)
|
||
admin.site.register(TrafficLightElectricalCabinetContent, AssetAdmin)
|
||
admin.site.register(TrafficLightAccessoryModel, AssetAdmin)
|
||
admin.site.register(TrafficLightAccessory, AssetAdmin)
|
||
admin.site.register(TrafficLightProgramming, AssetAdmin)
|
||
admin.site.register(TrafficLightControllerHardware, AssetAdmin)
|
||
admin.site.register(TrafficLightControllerLogFile, AssetAdmin)
|
||
admin.site.register(TrafficLightNetworkHardware, AssetAdmin)
|
||
admin.site.register(TrafficLightVoltageHardware, AssetAdmin)
|
||
|
||
|
||
@admin.register(TrafficLightController)
|
||
class TrafficLightControllerAdmin(AssetAdmin):
|
||
search_fields = ("intersection__code",)
|
||
list_display = ("intersection", "connected_to_control_center", "controller_maintenance_contract")
|
||
list_filter = ("connected_to_control_center", "controller_maintenance_contract")
|
||
|
||
|
||
|
||
@admin.register(AssetCategory)
|
||
class AssetCategoryAdmin(AssetAdmin):
|
||
search_fields = ("code", "name_fr", "name_nl")
|
||
list_display = ("code", "get_name", "thematic", "parent")
|
||
list_filter = ("thematic", "parent")
|
||
autocomplete_fields = ("parent",)
|
||
filter_horizontal = ("allowed_models",)
|
||
change_list_template = "admin/assets/assetcategory/change_list.html"
|
||
|
||
def get_form(self, request, obj=None, **kwargs):
|
||
form = super().get_form(request, obj, **kwargs)
|
||
if "parent" in form.base_fields:
|
||
# Limiter les choix du parent aux catégories sans parent (1 niveau max)
|
||
form.base_fields["parent"].queryset = AssetCategory.objects.filter(parent__isnull=True)
|
||
return form
|
||
|
||
def get_urls(self):
|
||
from django.urls import path as urlpath
|
||
urls = super().get_urls()
|
||
custom_urls = [
|
||
urlpath(
|
||
"import-excel/",
|
||
self.admin_site.admin_view(self.import_excel_view),
|
||
name="assets_assetcategory_import_excel",
|
||
),
|
||
urlpath(
|
||
"download-template/",
|
||
self.admin_site.admin_view(self.download_template_view),
|
||
name="assets_assetcategory_download_template",
|
||
),
|
||
]
|
||
return custom_urls + urls
|
||
|
||
# ------------------------------------------------------------------
|
||
def import_excel_view(self, request):
|
||
import io
|
||
import tempfile
|
||
import os
|
||
|
||
from django.contrib import messages
|
||
from django.shortcuts import render, redirect
|
||
from django.urls import reverse
|
||
|
||
from assets.management.commands.import_assets_from_excel import Command as ImportCommand
|
||
|
||
from assets.management.commands.import_assets_from_excel import COLUMNS, COLUMN_HINTS
|
||
|
||
column_guide = [
|
||
{"key": key, "desc": COLUMN_HINTS.get(key, "")}
|
||
for key, _label in COLUMNS
|
||
]
|
||
|
||
context = {
|
||
**self.admin_site.each_context(request),
|
||
"title": "Import d'assets depuis Excel",
|
||
"opts": self.model._meta,
|
||
"column_guide": column_guide,
|
||
}
|
||
|
||
if request.method == "POST":
|
||
uploaded = request.FILES.get("excel_file")
|
||
if not uploaded:
|
||
messages.error(request, "Aucun fichier sélectionné.")
|
||
return render(request, "admin/assets/assetcategory/import_excel.html", context)
|
||
|
||
if not uploaded.name.endswith(".xlsx"):
|
||
messages.error(request, "Seuls les fichiers .xlsx sont acceptés.")
|
||
return render(request, "admin/assets/assetcategory/import_excel.html", context)
|
||
|
||
dry_run = "dry_run" in request.POST
|
||
update = "update" in request.POST
|
||
|
||
# Save file to a temp path so the command can open it
|
||
suffix = ".xlsx"
|
||
with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp:
|
||
for chunk in uploaded.chunks():
|
||
tmp.write(chunk)
|
||
tmp_path = tmp.name
|
||
|
||
cmd = ImportCommand()
|
||
|
||
# Capture stdout / stderr via StringIO-compatible wrapper
|
||
class _Writer:
|
||
def __init__(self):
|
||
self.lines = []
|
||
def write(self, msg):
|
||
self.lines.append(str(msg))
|
||
def flush(self):
|
||
pass
|
||
|
||
out = _Writer()
|
||
err = _Writer()
|
||
cmd.stdout = out
|
||
cmd.stderr = err
|
||
|
||
import_error = None
|
||
try:
|
||
cmd._import(tmp_path, dry_run=dry_run, update=update)
|
||
except Exception as e:
|
||
import_error = str(e)
|
||
finally:
|
||
try:
|
||
os.unlink(tmp_path)
|
||
except OSError:
|
||
pass
|
||
|
||
output_lines = out.lines + err.lines
|
||
context["output_lines"] = output_lines
|
||
context["dry_run"] = dry_run
|
||
context["import_error"] = import_error
|
||
|
||
if import_error:
|
||
messages.error(request, f"Erreur lors de l'import : {import_error}")
|
||
elif dry_run:
|
||
messages.warning(request, "Dry-run terminé – aucune donnée modifiée.")
|
||
else:
|
||
messages.success(request, "Import terminé avec succès.")
|
||
|
||
return render(request, "admin/assets/assetcategory/import_excel.html", context)
|
||
|
||
return render(request, "admin/assets/assetcategory/import_excel.html", context)
|
||
|
||
# ------------------------------------------------------------------
|
||
def download_template_view(self, request):
|
||
import io
|
||
from django.http import HttpResponse
|
||
from assets.management.commands.import_assets_from_excel import generate_template
|
||
|
||
buf = io.BytesIO()
|
||
# generate_template expects a file path, so use a temp file
|
||
import tempfile
|
||
import os
|
||
with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as tmp:
|
||
tmp_path = tmp.name
|
||
try:
|
||
generate_template(tmp_path)
|
||
with open(tmp_path, "rb") as f:
|
||
data = f.read()
|
||
finally:
|
||
try:
|
||
os.unlink(tmp_path)
|
||
except OSError:
|
||
pass
|
||
|
||
response = HttpResponse(
|
||
data,
|
||
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||
)
|
||
response["Content-Disposition"] = 'attachment; filename="assets_import_template.xlsx"'
|
||
return response
|
||
|
||
|
||
@admin.register(AssetHistory)
|
||
class AssetHistoryAdmin(AssetAdmin):
|
||
list_display = ("timestamp", "content_type", "object_id", "user", "field_name", "action_type")
|
||
list_filter = ("action_type", "content_type", "timestamp")
|
||
search_fields = ("user__username", "field_name", "object_id")
|
||
readonly_fields = ("content_type", "object_id", "user", "timestamp", "field_name", "old_value", "new_value", "action_type")
|
||
date_hierarchy = "timestamp"
|
||
|
||
def has_add_permission(self, request):
|
||
# Prevent manual creation
|
||
return False
|
||
|
||
def has_change_permission(self, request, obj=None):
|
||
# Read-only
|
||
return False
|
||
|
||
|
||
# ============================================================================
|
||
# Public Lighting
|
||
# ============================================================================
|
||
from .models import (
|
||
PublicLightingStreet, PublicLightingStreetDocument, PublicLightingPoleModel,
|
||
PublicLightingPole, PublicLightingLightFixtureModel, PublicLightingLightFixture,
|
||
PublicLightingElectricalCabinetModel, PublicLightingElectricalCabinet
|
||
)
|
||
|
||
admin.site.register(PublicLightingStreet, AssetAdmin)
|
||
admin.site.register(PublicLightingStreetDocument, AssetAdmin)
|
||
admin.site.register(PublicLightingPoleModel, AssetAdmin)
|
||
admin.site.register(PublicLightingPole, AssetAdmin)
|
||
admin.site.register(PublicLightingLightFixtureModel, AssetAdmin)
|
||
admin.site.register(PublicLightingLightFixture, AssetAdmin)
|
||
admin.site.register(PublicLightingElectricalCabinetModel, AssetAdmin)
|
||
admin.site.register(PublicLightingElectricalCabinet, AssetAdmin)
|
||
|
||
|
||
# ============================================================================
|
||
# Artworks
|
||
# ============================================================================
|
||
from .models import (
|
||
ArtworkStreet, ArtworkMonument, ArtworkFountain,
|
||
ArtworkFountainDocument, ArtworkMonumentDocument
|
||
)
|
||
|
||
admin.site.register(ArtworkStreet, AssetAdmin)
|
||
admin.site.register(ArtworkMonument, AssetAdmin)
|
||
admin.site.register(ArtworkFountain, AssetAdmin)
|
||
admin.site.register(ArtworkFountainDocument, AssetAdmin)
|
||
admin.site.register(ArtworkMonumentDocument, AssetAdmin)
|
||
|
||
|
||
from .models import Structure, StructureLocation, StructureDocument, StructureAssetModel, StructureGeoAsset, StructureContract
|
||
|
||
|
||
def make_folder_attachment_inline(thematic_code):
|
||
"""
|
||
Factory pour créer un inline DocumentFolderAttachment filtré par thématique.
|
||
|
||
Args:
|
||
thematic_code: Le code de la thématique (ex: 'structures', 'control_centers')
|
||
"""
|
||
class FolderAttachmentForm(forms.ModelForm):
|
||
class Meta:
|
||
model = DocumentFolderAttachment
|
||
fields = ['folder', 'is_default']
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
# Filtrer les dossiers par thématique
|
||
self.fields['folder'].queryset = DocumentFolder.objects.filter(
|
||
thematics__code=thematic_code
|
||
).distinct().order_by('name')
|
||
|
||
class DocumentFolderAttachmentInline(GenericTabularInline):
|
||
model = DocumentFolderAttachment
|
||
form = FolderAttachmentForm
|
||
extra = 1
|
||
fields = ('folder', 'is_default')
|
||
verbose_name = "Dossier documentaire"
|
||
verbose_name_plural = "Dossiers documentaires"
|
||
|
||
return DocumentFolderAttachmentInline
|
||
|
||
|
||
# Inlines spécifiques par thématique
|
||
StructureFolderAttachmentInline = make_folder_attachment_inline('structures')
|
||
ControlCenterFolderAttachmentInline = make_folder_attachment_inline('control_centers')
|
||
TrafficLightFolderAttachmentInline = make_folder_attachment_inline('traffic_lights')
|
||
|
||
@admin.register(Structure)
|
||
class StructureAdmin(AssetAdmin):
|
||
search_fields = ("code", "name_fr", "name_nl")
|
||
list_display = ("code", "name_fr", "structure_type")
|
||
list_filter = ("structure_type",)
|
||
inlines = [StructureFolderAttachmentInline]
|
||
|
||
@admin.register(StructureLocation)
|
||
class StructureLocationAdmin(AssetAdmin):
|
||
search_fields = ("code", "name_fr", "name_nl")
|
||
|
||
admin.site.register(StructureDocument, AssetAdmin)
|
||
admin.site.register(StructureAssetModel, AssetAdmin)
|
||
admin.site.register(StructureGeoAsset, AssetAdmin)
|
||
|
||
admin.site.register(StructureContract, AssetAdmin)
|
||
|
||
|
||
|
||
from .models import ControlCenter, ControlCenterLocation, ControlCenterDocument, ControlCenterAssetModel, ControlCenterGeoAsset, ControlCenterContract, ControlCenterGeoAssetContract, ControlCenterAssetDocument
|
||
|
||
@admin.register(ControlCenter)
|
||
class ControlCenterAdmin(AssetAdmin):
|
||
search_fields = ("code", "name_fr", "name_nl")
|
||
inlines = [ControlCenterFolderAttachmentInline]
|
||
|
||
@admin.register(ControlCenterLocation)
|
||
class ControlCenterLocationAdmin(AssetAdmin):
|
||
search_fields = ("code", "name_fr", "name_nl")
|
||
|
||
admin.site.register(ControlCenterDocument, AssetAdmin)
|
||
admin.site.register(ControlCenterAssetModel, AssetAdmin)
|
||
admin.site.register(ControlCenterGeoAsset, AssetAdmin)
|
||
admin.site.register(ControlCenterContract, AssetAdmin)
|
||
admin.site.register(ControlCenterGeoAssetContract, AssetAdmin)
|
||
admin.site.register(ControlCenterAssetDocument, AssetAdmin)
|
||
|
||
|
||
@admin.register(SignStreet)
|
||
class SignStreetAdmin(AssetAdmin):
|
||
search_fields = ("code", "name_fr", "name_nl")
|
||
list_display = ("code", "name_fr", "name_nl", "municipality")
|
||
list_filter = ("municipality",)
|
||
|
||
|
||
# ============================================================================
|
||
# Sign
|
||
# ============================================================================
|
||
from .models import (
|
||
SignStreetDocument, SignPoleModel, SignPole, SignPanelModel,
|
||
SignPanelType, SignPanel, ComplementaryPoliceRegulation, SignParkingSpot
|
||
)
|
||
|
||
admin.site.register(SignStreetDocument, AssetAdmin)
|
||
admin.site.register(SignPoleModel, AssetAdmin)
|
||
admin.site.register(SignPole, AssetAdmin)
|
||
admin.site.register(SignPanelModel, AssetAdmin)
|
||
admin.site.register(SignPanelType, AssetAdmin)
|
||
admin.site.register(SignPanel, AssetAdmin)
|
||
admin.site.register(ComplementaryPoliceRegulation, AssetAdmin)
|
||
admin.site.register(SignParkingSpot, AssetAdmin)
|
||
|
||
|
||
from .models import ITSAssetModel, ITSGeoAsset, ITSGeoAssetContract
|
||
|
||
admin.site.register(ITSAssetModel, AssetAdmin)
|
||
|
||
@admin.register(ITSGeoAsset)
|
||
class ITSGeoAssetAdmin(AssetAdmin):
|
||
search_fields = ("code", "model__name_fr", "location__code")
|
||
list_display = ("code", "model", "location")
|
||
list_filter = ("model", "location")
|
||
|
||
@admin.register(ITSGeoAssetContract)
|
||
class ITSGeoAssetContractAdmin(AssetAdmin):
|
||
search_fields = ("asset__code", "contract__contract_number")
|
||
list_display = ("asset", "contract")
|
||
list_filter = ("contract",)
|
||
|
||
|
||
from .models import ITSLocation, ITSLocationDocument, ITSLocationContract
|
||
|
||
admin.site.register(ITSLocation, AssetAdmin)
|
||
admin.site.register(ITSLocationDocument, AssetAdmin)
|
||
admin.site.register(ITSLocationContract, AssetAdmin)
|
||
|
||
|
||
from .models import NatureAssetModel, NatureRWIAsset, NatureRWIAssetDocument
|
||
from .models import (
|
||
NatureLocation, NatureGreenSurface, NatureGreenSurfaceDocument,
|
||
NatureTree, NatureShrub, NatureBench, NatureRWIZ
|
||
)
|
||
|
||
admin.site.register(NatureAssetModel, AssetAdmin)
|
||
admin.site.register(NatureLocation, AssetAdmin)
|
||
admin.site.register(NatureGreenSurface, AssetAdmin)
|
||
admin.site.register(NatureGreenSurfaceDocument, AssetAdmin)
|
||
admin.site.register(NatureTree, AssetAdmin)
|
||
admin.site.register(NatureShrub, AssetAdmin)
|
||
admin.site.register(NatureBench, AssetAdmin)
|
||
admin.site.register(NatureRWIZ, AssetAdmin)
|
||
|
||
|
||
class CustomNatureGeoAdmin(AssetGISAdmin):
|
||
gis_widget_kwargs = {
|
||
'attrs': {
|
||
"default_lon" : 4.3517, # Bruxelles
|
||
"default_lat" : 50.8503,
|
||
"default_zoom" : 12
|
||
},
|
||
}
|
||
|
||
|
||
@admin.register(NatureRWIAsset)
|
||
class NatureRWIAssetAdmin(CustomNatureGeoAdmin):
|
||
list_display = ("code", "code_name", "structure_type", "geometry_type", "project_manager", "condition")
|
||
list_filter = ("structure_type", "geometry_type", "project_manager", "condition", "construction_status")
|
||
search_fields = ("code", "external_id", "administrator", "comment")
|
||
change_list_template = "admin/assets/naturerwiasset/change_list.html"
|
||
|
||
def get_fields(self, request, obj=None):
|
||
fields = list(super().get_fields(request, obj))
|
||
if obj and obj.geometry_type != 'point':
|
||
if 'has_filter' in fields:
|
||
fields.remove('has_filter')
|
||
return fields
|
||
|
||
def get_urls(self):
|
||
from django.urls import path as urlpath
|
||
urls = super().get_urls()
|
||
custom_urls = [
|
||
urlpath(
|
||
"import-rwi/",
|
||
self.admin_site.admin_view(self.import_rwi_view),
|
||
name="assets_naturerwiasset_import_rwi",
|
||
),
|
||
]
|
||
return custom_urls + urls
|
||
|
||
def import_rwi_view(self, request):
|
||
import io
|
||
import os
|
||
import tempfile
|
||
from django.core.management import call_command
|
||
from django.contrib import messages
|
||
from django.shortcuts import render
|
||
|
||
context = {
|
||
**self.admin_site.each_context(request),
|
||
"title": "Importation du GeoPackage RWI",
|
||
"opts": self.model._meta,
|
||
}
|
||
|
||
if request.method == "POST":
|
||
dry_run = "dry_run" in request.POST
|
||
gpkg_file = request.FILES.get("gpkg_file")
|
||
|
||
if not gpkg_file:
|
||
messages.error(request, "Aucun fichier GeoPackage sélectionné.")
|
||
return render(request, "admin/assets/naturerwiasset/import_rwi.html", context)
|
||
|
||
if not gpkg_file.name.endswith(".gpkg"):
|
||
messages.error(request, "Seuls les fichiers .gpkg sont acceptés.")
|
||
return render(request, "admin/assets/naturerwiasset/import_rwi.html", context)
|
||
|
||
# Save uploaded file to a temporary file
|
||
with tempfile.NamedTemporaryFile(suffix=".gpkg", delete=False) as tmp:
|
||
for chunk in gpkg_file.chunks():
|
||
tmp.write(chunk)
|
||
tmp_path = tmp.name
|
||
|
||
stdout_stream = io.StringIO()
|
||
stderr_stream = io.StringIO()
|
||
|
||
import_error = None
|
||
try:
|
||
if dry_run:
|
||
call_command("import_rwi", tmp_path, "--dry-run", stdout=stdout_stream, stderr=stderr_stream)
|
||
else:
|
||
call_command("import_rwi", tmp_path, stdout=stdout_stream, stderr=stderr_stream)
|
||
except Exception as e:
|
||
import_error = str(e)
|
||
finally:
|
||
try:
|
||
os.unlink(tmp_path)
|
||
except OSError:
|
||
pass
|
||
|
||
output_lines = (stdout_stream.getvalue() + stderr_stream.getvalue()).split("\n")
|
||
context["output_lines"] = output_lines
|
||
context["dry_run"] = dry_run
|
||
context["import_error"] = import_error
|
||
|
||
if import_error:
|
||
messages.error(request, f"Erreur lors de l'importation : {import_error}")
|
||
elif dry_run:
|
||
messages.warning(request, "Simulation d'importation (dry-run) terminée – aucune donnée n'a été modifiée.")
|
||
else:
|
||
messages.success(request, "Importation du GeoPackage RWI terminée avec succès !")
|
||
|
||
return render(request, "admin/assets/naturerwiasset/import_rwi.html", context)
|
||
|
||
return render(request, "admin/assets/naturerwiasset/import_rwi.html", context)
|
||
|
||
|
||
admin.site.register(NatureRWIAssetDocument, AssetAdmin)
|
||
|
||
|
||
# ============================================================================
|
||
# Green Surfaces Comments Admin
|
||
# ============================================================================
|
||
|
||
class GreenSurfacesCommentPhotoInline(admin.TabularInline):
|
||
model = GreenSurfacesCommentPhoto
|
||
extra = 1
|
||
fields = ('photo', 'heading', 'order')
|
||
readonly_fields = ('created_at',)
|
||
|
||
|
||
@admin.register(GreenSurfacesComment)
|
||
class GreenSurfacesCommentAdmin(AssetAdmin):
|
||
list_display = ('id', 'comment_preview', 'created_by', 'created_at', 'address')
|
||
list_filter = ('created_at', 'created_by')
|
||
search_fields = ('comment', 'address', 'created_by__username')
|
||
readonly_fields = ('created_at', 'updated_at', 'geom')
|
||
inlines = [GreenSurfacesCommentPhotoInline]
|
||
|
||
fieldsets = (
|
||
('Commentaire', {
|
||
'fields': ('comment', 'created_by')
|
||
}),
|
||
('Localisation', {
|
||
'fields': ('latitude', 'longitude', 'geom', 'address')
|
||
}),
|
||
('Métadonnées', {
|
||
'fields': ('created_at', 'updated_at')
|
||
}),
|
||
)
|
||
|
||
def comment_preview(self, obj):
|
||
return obj.comment[:50] + '...' if len(obj.comment) > 50 else obj.comment
|
||
comment_preview.short_description = 'Commentaire'
|
||
|
||
|
||
@admin.register(GreenSurfacesCommentPhoto)
|
||
class GreenSurfacesCommentPhotoAdmin(AssetAdmin):
|
||
list_display = ('id', 'comment', 'heading', 'order', 'created_at')
|
||
list_filter = ('created_at',)
|
||
search_fields = ('comment__comment',)
|
||
readonly_fields = ('created_at',)
|
||
|
||
|
||
|
||
# ============================================================================
|
||
# Roads
|
||
# ============================================================================
|
||
from .models import (
|
||
RoadStreet, RoadStreetDocument, RoadwayModel, Roadway,
|
||
RoadSidewalkModel, RoadSidewalk, RoadFurnitureModel, RoadFurniture,
|
||
RoadwayFunction, RoadConcreteBlockSet, RoadDrainModel, RoadDrain
|
||
)
|
||
|
||
admin.site.register(RoadStreet, AssetAdmin)
|
||
admin.site.register(RoadStreetDocument, AssetAdmin)
|
||
admin.site.register(RoadwayModel, AssetAdmin)
|
||
admin.site.register(Roadway, AssetAdmin)
|
||
admin.site.register(RoadSidewalkModel, AssetAdmin)
|
||
admin.site.register(RoadSidewalk, AssetAdmin)
|
||
admin.site.register(RoadFurnitureModel, AssetAdmin)
|
||
admin.site.register(RoadFurniture, AssetAdmin)
|
||
admin.site.register(RoadwayFunction, AssetAdmin)
|
||
admin.site.register(RoadConcreteBlockSet, AssetAdmin)
|
||
admin.site.register(RoadDrainModel, AssetAdmin)
|
||
admin.site.register(RoadDrain, AssetAdmin)
|
||
|
||
|
||
@admin.register(CleanLocation)
|
||
class CleanLocationAdmin(AssetAdmin):
|
||
search_fields = ("code", "name_fr", "name_nl")
|
||
list_display = ("code", "name_fr", "name_nl", "municipality")
|
||
list_filter = ("municipality",)
|
||
|
||
admin.site.register(CleanLitterBinModel, AssetAdmin)
|
||
admin.site.register(CleanLitterBin, AssetAdmin)
|
||
admin.site.register(CleanGlassContainerModel, AssetAdmin)
|
||
admin.site.register(CleanGlassContainer, AssetAdmin)
|
||
admin.site.register(CleanLocationContract, AssetAdmin)
|
||
|
||
from .models import CleanLitterBinPhoto, CleanLitterBinObservation
|
||
from .models import PublicBuilding, BuildingAsset, PublicBuildingDocument
|
||
from .models import OutdoorLocation, OutdoorAsset, OutdoorLocationDocument
|
||
|
||
@admin.register(PublicBuilding)
|
||
class PublicBuildingAdmin(admin.GISModelAdmin):
|
||
list_display = ("code", "name_fr", "location_type", "parent_location", "usage_type", "building_status", "owner", "manager", "is_erp", "dpe_rating")
|
||
list_filter = ("location_type", "usage_type", "building_status", "is_erp", "erp_category", "dpe_rating", "pmr_accessible")
|
||
search_fields = ("code", "name_fr", "name_nl", "address", "owner")
|
||
raw_id_fields = ("parent_location", "manager")
|
||
|
||
@admin.register(BuildingAsset)
|
||
class BuildingAssetAdmin(admin.GISModelAdmin):
|
||
list_display = ("code", "name_fr", "asset_type", "building", "location", "parent_asset", "status", "obsolescence")
|
||
list_filter = ("asset_type", "status", "obsolescence")
|
||
search_fields = ("code", "name_fr", "name_nl", "serial_number")
|
||
raw_id_fields = ("building", "location", "parent_asset")
|
||
|
||
admin.site.register(PublicBuildingDocument, AssetAdmin)
|
||
|
||
@admin.register(OutdoorLocation)
|
||
class OutdoorLocationAdmin(admin.GISModelAdmin):
|
||
list_display = ("code", "name_fr", "site_type", "parent_location", "owner", "manager", "pmr_accessible", "has_water_point", "has_power_supply", "is_fenced")
|
||
list_filter = ("site_type", "pmr_accessible", "has_water_point", "has_power_supply", "is_fenced")
|
||
search_fields = ("code", "name_fr", "name_nl", "address", "owner")
|
||
raw_id_fields = ("parent_location", "manager")
|
||
|
||
@admin.register(OutdoorAsset)
|
||
class OutdoorAssetAdmin(admin.GISModelAdmin):
|
||
list_display = ("code", "name_fr", "asset_type", "site", "location", "norm", "last_annual_inspection", "next_annual_inspection", "status")
|
||
list_filter = ("asset_type", "norm", "status", "obsolescence")
|
||
search_fields = ("code", "name_fr", "name_nl", "serial_number", "certifying_body")
|
||
raw_id_fields = ("site", "location", "parent_asset")
|
||
|
||
admin.site.register(OutdoorLocationDocument, AssetAdmin) |