feat: implement GIEP inspection question framework with localized labels and template filters
This commit is contained in:
parent
e4d2ca45c5
commit
41aa78e382
19 changed files with 1405 additions and 62 deletions
Binary file not shown.
29
streetup/assets/migrations/0019_naturerwiasset_location.py
Normal file
29
streetup/assets/migrations/0019_naturerwiasset_location.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Generated by Django 6.0.7 on 2026-08-11 12:13
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
def populate_locations(apps, schema_editor):
|
||||||
|
NatureRWIZ = apps.get_model('assets', 'NatureRWIZ')
|
||||||
|
NatureRWIAsset = apps.get_model('assets', 'NatureRWIAsset')
|
||||||
|
|
||||||
|
zones = NatureRWIZ.objects.filter(geom__isnull=False)
|
||||||
|
for zone in zones:
|
||||||
|
NatureRWIAsset.objects.filter(geom__intersects=zone.geom, location__isnull=True).update(location=zone)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0018_remove_parkingspot_scooter_capacity'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='naturerwiasset',
|
||||||
|
name='location',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assets', to='assets.naturerwiz', verbose_name='Zone GIEP / ZIEP'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(populate_locations, reverse_code=migrations.RunPython.noop),
|
||||||
|
]
|
||||||
|
|
@ -277,14 +277,10 @@ class NatureRWIZ(AbstractLocation):
|
||||||
geom = models.MultiPolygonField(srid=3812, null=True,blank=True)
|
geom = models.MultiPolygonField(srid=3812, null=True,blank=True)
|
||||||
|
|
||||||
def get_active_assets(self):
|
def get_active_assets(self):
|
||||||
if not self.geom:
|
return NatureRWIAsset.objects.filter(location=self, status='active')
|
||||||
return NatureRWIAsset.objects.none()
|
|
||||||
return NatureRWIAsset.objects.filter(geom__intersects=self.geom, status='active')
|
|
||||||
|
|
||||||
def get_all_assets(self):
|
def get_all_assets(self):
|
||||||
if not self.geom:
|
return NatureRWIAsset.objects.filter(location=self)
|
||||||
return NatureRWIAsset.objects.none()
|
|
||||||
return NatureRWIAsset.objects.filter(geom__intersects=self.geom)
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse(
|
return reverse(
|
||||||
|
|
@ -451,6 +447,7 @@ GEOMETRY_TYPE_CHOICES = [
|
||||||
|
|
||||||
|
|
||||||
class NatureRWIAsset(AbstractGeoAsset):
|
class NatureRWIAsset(AbstractGeoAsset):
|
||||||
|
location = models.ForeignKey(NatureRWIZ, on_delete=models.SET_NULL, null=True, blank=True, related_name="assets", verbose_name=_("Zone GIEP / ZIEP"))
|
||||||
geom = models.GeometryField(srid=3812, null=True, blank=True)
|
geom = models.GeometryField(srid=3812, null=True, blank=True)
|
||||||
|
|
||||||
external_id = models.IntegerField(null=True, blank=True, verbose_name=_("External ID"))
|
external_id = models.IntegerField(null=True, blank=True, verbose_name=_("External ID"))
|
||||||
|
|
@ -485,6 +482,16 @@ class NatureRWIAsset(AbstractGeoAsset):
|
||||||
verbose_name = _("RWI Asset")
|
verbose_name = _("RWI Asset")
|
||||||
verbose_name_plural = _("RWI Assets")
|
verbose_name_plural = _("RWI Assets")
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.location_id and self.geom:
|
||||||
|
try:
|
||||||
|
matching_rwiz = NatureRWIZ.objects.filter(geom__intersects=self.geom).first()
|
||||||
|
if matching_rwiz:
|
||||||
|
self.location = matching_rwiz
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('assets:water_assets_detail', kwargs={'asset_model': 'naturerwiasset', 'asset_id': self.id})
|
return reverse('assets:water_assets_detail', kwargs={'asset_model': 'naturerwiasset', 'asset_id': self.id})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,7 @@ def _get_asset_thematic(asset):
|
||||||
# Fallback: deduce thematic from asset class name
|
# Fallback: deduce thematic from asset class name
|
||||||
class_name = asset.__class__.__name__.lower()
|
class_name = asset.__class__.__name__.lower()
|
||||||
thematic_mappings = {
|
thematic_mappings = {
|
||||||
|
'naturerwiz': 'water',
|
||||||
'naturerwiasset': 'water',
|
'naturerwiasset': 'water',
|
||||||
'trafficlight': 'trafficlights',
|
'trafficlight': 'trafficlights',
|
||||||
'publiclighting': 'publiclighting',
|
'publiclighting': 'publiclighting',
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
data-object-id="{{ asset_pk }}"
|
data-object-id="{{ asset_pk }}"
|
||||||
data-target-code="{{ asset.code|default:asset.pk }}"
|
data-target-code="{{ asset.code|default:asset.pk }}"
|
||||||
data-target-name="{{ asset.name_fr|default:asset.name_nl|default:asset }}"
|
data-target-name="{{ asset.name_fr|default:asset.name_nl|default:asset }}"
|
||||||
data-target-type="{{ asset.category.name_fr|default:'Asset' }}"
|
data-target-type="{% if asset.category %}{{ asset.category.name_fr }}{% else %}{% translate 'Zone / Équipement' %}{% endif %}"
|
||||||
data-target-status="{{ asset.get_status_display|default:asset.inspection_status|default:'-' }}">
|
data-target-status="{% if asset.get_status_display %}{{ asset.get_status_display }}{% else %}-{% endif %}">
|
||||||
<i class="bi bi-plus-lg me-1"></i>{% translate "Nouvelle inspection" %}
|
<i class="bi bi-plus-lg me-1"></i>{% translate "Nouvelle inspection" %}
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
<div class="modal-content border-0 shadow">
|
<div class="modal-content border-0 shadow">
|
||||||
<div class="modal-header bg-light py-2.5">
|
<div class="modal-header bg-light py-2.5">
|
||||||
<h5 class="modal-title fs-6 fw-bold" id="assetInspectionDetailModalLabel{{ insp.id }}">
|
<h5 class="modal-title fs-6 fw-bold" id="assetInspectionDetailModalLabel{{ insp.id }}">
|
||||||
<i class="bi bi-patch-check me-2 text-primary"></i>{% translate "Détail du contrôle" %} - {{ insp.inspection_date|date:"d/m/Y H:i" }}
|
<i class="bi bi-patch-check me-2 text-primary"></i>{% translate "Détail de l'inspection" %} - {{ insp.inspection_date|date:"d/m/Y H:i" }}
|
||||||
</h5>
|
</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -181,12 +181,60 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = document.getElementById('quickInspectionForm');
|
const form = document.getElementById('quickInspectionForm');
|
||||||
if (form && !form.dataset.reloadHandlerAttached) {
|
if (form && !form.dataset.ajaxHandlerAttached) {
|
||||||
form.dataset.reloadHandlerAttached = 'true';
|
form.dataset.ajaxHandlerAttached = 'true';
|
||||||
form.addEventListener('submit', function() {
|
form.addEventListener('submit', function(e) {
|
||||||
setTimeout(function() {
|
e.preventDefault();
|
||||||
|
const submitBtn = document.getElementById('submitQuickInspectionBtn');
|
||||||
|
const spinner = document.getElementById('submitQuickInspectionSpinner');
|
||||||
|
const icon = document.getElementById('submitQuickInspectionIcon');
|
||||||
|
const alertBox = document.getElementById('quickInspectionAlert');
|
||||||
|
|
||||||
|
if (alertBox) {
|
||||||
|
alertBox.classList.add('d-none');
|
||||||
|
alertBox.textContent = '';
|
||||||
|
}
|
||||||
|
if (submitBtn) submitBtn.disabled = true;
|
||||||
|
if (spinner) spinner.classList.remove('d-none');
|
||||||
|
if (icon) icon.classList.add('d-none');
|
||||||
|
|
||||||
|
const formData = new FormData(form);
|
||||||
|
|
||||||
|
fetch(form.action, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.redirected) {
|
||||||
|
window.location.href = res.url;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (!data) return;
|
||||||
|
if (data.success) {
|
||||||
|
const modalEl = document.getElementById('quickInspectionModal');
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalEl);
|
||||||
|
if (modal) modal.hide();
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
if (alertBox) {
|
||||||
|
alertBox.textContent = data.error || "{% filter escapejs %}{% translate 'Erreur lors de l\'enregistrement.' %}{% endfilter %}";
|
||||||
|
alertBox.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
if (submitBtn) submitBtn.disabled = false;
|
||||||
|
if (spinner) spinner.classList.add('d-none');
|
||||||
|
if (icon) icon.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error("Erreur lors de l'enregistrement de l'inspection:", err);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}, 600);
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<!-- Modale de Saisie Rapide d'Inspection -->
|
<!-- Modale de Saisie Rapide d'Inspection -->
|
||||||
<div class="modal fade" id="quickInspectionModal" tabindex="-1" aria-labelledby="quickInspectionModalLabel" aria-hidden="true">
|
<div class="modal fade" id="quickInspectionModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="quickInspectionModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||||
<div class="modal-content shadow">
|
<div class="modal-content shadow">
|
||||||
<div class="modal-header bg-light">
|
<div class="modal-header bg-light">
|
||||||
|
|
@ -144,7 +144,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal caméra Canvas (mode continu pour inspection) -->
|
<!-- Modal caméra Canvas (mode continu pour inspection) -->
|
||||||
<div class="modal fade" id="inspCameraModal" tabindex="-1" aria-labelledby="inspCameraModalLabel" aria-hidden="true" style="z-index: 1060;">
|
<div class="modal fade" id="inspCameraModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="inspCameraModalLabel" aria-hidden="true" style="z-index: 1060;">
|
||||||
<div class="modal-dialog modal-fullscreen-sm-down d-flex m-0 h-100 mx-auto">
|
<div class="modal-dialog modal-fullscreen-sm-down d-flex m-0 h-100 mx-auto">
|
||||||
<div class="modal-content bg-dark text-white d-flex flex-column">
|
<div class="modal-content bg-dark text-white d-flex flex-column">
|
||||||
<div class="modal-header border-0 flex-shrink-0 py-2">
|
<div class="modal-header border-0 flex-shrink-0 py-2">
|
||||||
|
|
@ -176,7 +176,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modale de Choix d'Asset (Zone dense / Sélection multiple) -->
|
<!-- Modale de Choix d'Asset (Zone dense / Sélection multiple) -->
|
||||||
<div class="modal fade" id="targetChooserModal" tabindex="-1" aria-labelledby="targetChooserModalLabel" aria-hidden="true">
|
<div class="modal fade" id="targetChooserModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="targetChooserModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content shadow">
|
<div class="modal-content shadow">
|
||||||
<div class="modal-header bg-light">
|
<div class="modal-header bg-light">
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load documents_tags %}
|
{% load documents_tags %}
|
||||||
{% load document_viewer_tags %}
|
{% load document_viewer_tags %}
|
||||||
|
{% load asset_tags %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row mt-2" style="height: calc(100vh - 200px);">
|
<div class="row mt-2" style="height: calc(100vh - 200px);">
|
||||||
|
|
@ -224,6 +225,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Inspections & Contrôles Terrain GIEP -->
|
||||||
|
{% asset_inspections location %}
|
||||||
|
|
||||||
<!-- Interventions -->
|
<!-- Interventions -->
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
|
|
|
||||||
|
|
@ -169,13 +169,13 @@ def asset_location_link(context, asset):
|
||||||
location = None
|
location = None
|
||||||
location_type = None
|
location_type = None
|
||||||
|
|
||||||
if hasattr(asset, 'street'):
|
if hasattr(asset, 'street') and getattr(asset, 'street', None):
|
||||||
location = getattr(asset, 'street', None)
|
location = getattr(asset, 'street', None)
|
||||||
location_type = 'street'
|
location_type = 'street'
|
||||||
elif hasattr(asset, 'location'):
|
elif hasattr(asset, 'location') and getattr(asset, 'location', None):
|
||||||
location = getattr(asset, 'location', None)
|
location = getattr(asset, 'location', None)
|
||||||
location_type = 'location'
|
location_type = 'location'
|
||||||
elif hasattr(asset, 'intersection'):
|
elif hasattr(asset, 'intersection') and getattr(asset, 'intersection', None):
|
||||||
location = getattr(asset, 'intersection', None)
|
location = getattr(asset, 'intersection', None)
|
||||||
location_type = 'intersection'
|
location_type = 'intersection'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -192,15 +192,7 @@ def water_assets_geojson(request):
|
||||||
naturerwiasset_qs = NatureRWIAsset.objects.all()
|
naturerwiasset_qs = NatureRWIAsset.objects.all()
|
||||||
|
|
||||||
if sections_ids:
|
if sections_ids:
|
||||||
# Filter by intersecting NatureRWIZ geometries
|
naturerwiasset_qs = naturerwiasset_qs.filter(location_id__in=sections_ids)
|
||||||
rwiz_geoms = NatureRWIZ.objects.filter(id__in=sections_ids, geom__isnull=False).values_list('geom', flat=True)
|
|
||||||
if rwiz_geoms:
|
|
||||||
query = Q()
|
|
||||||
for g in rwiz_geoms:
|
|
||||||
query |= Q(geom__intersects=g)
|
|
||||||
naturerwiasset_qs = naturerwiasset_qs.filter(query)
|
|
||||||
else:
|
|
||||||
naturerwiasset_qs = naturerwiasset_qs.none()
|
|
||||||
|
|
||||||
if categories:
|
if categories:
|
||||||
naturerwiasset_qs = naturerwiasset_qs.filter(category__id__in=categories)
|
naturerwiasset_qs = naturerwiasset_qs.filter(category__id__in=categories)
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -104,3 +104,304 @@ msgid "%(counter)s inspection trouvée"
|
||||||
msgid_plural "%(counter)s inspections trouvées"
|
msgid_plural "%(counter)s inspections trouvées"
|
||||||
msgstr[0] "%(counter)s inspection trouvée"
|
msgstr[0] "%(counter)s inspection trouvée"
|
||||||
msgstr[1] "%(counter)s inspections trouvées"
|
msgstr[1] "%(counter)s inspections trouvées"
|
||||||
|
|
||||||
|
# --- GIEP Translations ---
|
||||||
|
|
||||||
|
msgid "Formulaire d'inspection GIEP"
|
||||||
|
msgstr "Formulaire d'inspection GIEP"
|
||||||
|
|
||||||
|
# --- GIEP Translations ---
|
||||||
|
|
||||||
|
msgid "Eaux Pluviales"
|
||||||
|
msgstr "Eaux Pluviales"
|
||||||
|
|
||||||
|
msgid "Conditions météorologiques"
|
||||||
|
msgstr "Conditions météorologiques"
|
||||||
|
|
||||||
|
msgid "Date de la dernière pluie"
|
||||||
|
msgstr "Date de la dernière pluie"
|
||||||
|
|
||||||
|
msgid "Orage récent (-1h)"
|
||||||
|
msgstr "Orage récent (-1h)"
|
||||||
|
|
||||||
|
msgid "Ouvrages inspectés"
|
||||||
|
msgstr "Ouvrages inspectés"
|
||||||
|
|
||||||
|
msgid "Ouvrages GIEP inspectés"
|
||||||
|
msgstr "Ouvrages GIEP inspectés"
|
||||||
|
|
||||||
|
msgid "Ajouter un ouvrage"
|
||||||
|
msgstr "Ajouter un ouvrage"
|
||||||
|
|
||||||
|
msgid "Ouvrage"
|
||||||
|
msgstr "Ouvrage"
|
||||||
|
|
||||||
|
msgid "Supprimer"
|
||||||
|
msgstr "Supprimer"
|
||||||
|
|
||||||
|
msgid "Non renseigné"
|
||||||
|
msgstr "Non renseigné"
|
||||||
|
|
||||||
|
msgid "Non vérifié"
|
||||||
|
msgstr "Non vérifié"
|
||||||
|
|
||||||
|
msgid "Oui"
|
||||||
|
msgstr "Oui"
|
||||||
|
|
||||||
|
msgid "Non"
|
||||||
|
msgstr "Non"
|
||||||
|
|
||||||
|
msgid "Aujourd'hui"
|
||||||
|
msgstr "Aujourd'hui"
|
||||||
|
|
||||||
|
msgid "Moins de 24h"
|
||||||
|
msgstr "Moins de 24h"
|
||||||
|
|
||||||
|
msgid "Plus de 24h"
|
||||||
|
msgstr "Plus de 24h"
|
||||||
|
|
||||||
|
msgid "Espace vert creux (EVC)"
|
||||||
|
msgstr "Espace vert creux (EVC)"
|
||||||
|
|
||||||
|
msgid "Massif drainant (OED)"
|
||||||
|
msgstr "Massif drainant (OED)"
|
||||||
|
|
||||||
|
msgid "Technique alternative (TA)"
|
||||||
|
msgstr "Technique alternative (TA)"
|
||||||
|
|
||||||
|
msgid "Technique Alternative / Bassin d'orage"
|
||||||
|
msgstr "Technique Alternative / Bassin d'orage"
|
||||||
|
|
||||||
|
msgid "Zone GIEP principale"
|
||||||
|
msgstr "Zone GIEP principale"
|
||||||
|
|
||||||
|
msgid "Synthèse des contrôles"
|
||||||
|
msgstr "Synthèse des contrôles"
|
||||||
|
|
||||||
|
msgid "Point d'entrée dans l'ouvrage"
|
||||||
|
msgstr "Point d'entrée dans l'ouvrage"
|
||||||
|
|
||||||
|
msgid "L'eau peut-elle facilement entrer dans l'EVC ?"
|
||||||
|
msgstr "L'eau peut-elle facilement entrer dans l'EVC ?"
|
||||||
|
|
||||||
|
msgid "L'eau peut-elle facilement entrer dans l'ouvrage ?"
|
||||||
|
msgstr "L'eau peut-elle facilement entrer dans l'ouvrage ?"
|
||||||
|
|
||||||
|
msgid "Des feuilles ou branches bloquent-elles l'eau ?"
|
||||||
|
msgstr "Des feuilles ou branches bloquent-elles l'eau ?"
|
||||||
|
|
||||||
|
msgid "Un amas de détritus bloque-t-il l'eau ?"
|
||||||
|
msgstr "Un amas de détritus bloque-t-il l'eau ?"
|
||||||
|
|
||||||
|
msgid "L'ouvrage dispose-t-il d'un empierrement ?"
|
||||||
|
msgstr "L'ouvrage dispose-t-il d'un empierrement ?"
|
||||||
|
|
||||||
|
msgid "Activer pour compléter l'état de l'empierrement"
|
||||||
|
msgstr "Activer pour compléter l'état de l'empierrement"
|
||||||
|
|
||||||
|
msgid "L'empierrement est-il en bon état ?"
|
||||||
|
msgstr "L'empierrement est-il en bon état ?"
|
||||||
|
|
||||||
|
msgid "Colmaté par les sédiments"
|
||||||
|
msgstr "Colmaté par les sédiments"
|
||||||
|
|
||||||
|
msgid "Emporté par l'eau"
|
||||||
|
msgstr "Emporté par l'eau"
|
||||||
|
|
||||||
|
msgid "Envahi d'adventices"
|
||||||
|
msgstr "Envahi d'adventices"
|
||||||
|
|
||||||
|
msgid "Espace vert"
|
||||||
|
msgstr "Espace vert"
|
||||||
|
|
||||||
|
msgid "Quel est l'état de la végétation ?"
|
||||||
|
msgstr "Quel est l'état de la végétation ?"
|
||||||
|
|
||||||
|
msgid "Bon"
|
||||||
|
msgstr "Bon"
|
||||||
|
|
||||||
|
msgid "Mauvais"
|
||||||
|
msgstr "Mauvais"
|
||||||
|
|
||||||
|
msgid "Présence de plantes mortes ou absentes :"
|
||||||
|
msgstr "Présence de plantes mortes ou absentes :"
|
||||||
|
|
||||||
|
msgid "+ de 10%"
|
||||||
|
msgstr "+ de 10%"
|
||||||
|
|
||||||
|
msgid "- de 10%"
|
||||||
|
msgstr "- de 10%"
|
||||||
|
|
||||||
|
msgid "Sur + de 10% de la surface"
|
||||||
|
msgstr "Sur + de 10% de la surface"
|
||||||
|
|
||||||
|
msgid "Sur - de 10% de la surface"
|
||||||
|
msgstr "Sur - de 10% de la surface"
|
||||||
|
|
||||||
|
msgid "Présence de chenaux d'érosion ?"
|
||||||
|
msgstr "Présence de chenaux d'érosion ?"
|
||||||
|
|
||||||
|
msgid "Présence de flaques d'eau persistantes ?"
|
||||||
|
msgstr "Présence de flaques d'eau persistantes ?"
|
||||||
|
|
||||||
|
msgid "Présence de sédiments dans l'ouvrage ?"
|
||||||
|
msgstr "Présence de sédiments dans l'ouvrage ?"
|
||||||
|
|
||||||
|
msgid "La croûte de sédimentation est-elle supérieure à 5 cm ?"
|
||||||
|
msgstr "La croûte de sédimentation est-elle supérieure à 5 cm ?"
|
||||||
|
|
||||||
|
msgid "Non (inférieure à 5 cm)"
|
||||||
|
msgstr "Non (inférieure à 5 cm)"
|
||||||
|
|
||||||
|
msgid "Oui (supérieure à 5 cm)"
|
||||||
|
msgstr "Oui (supérieure à 5 cm)"
|
||||||
|
|
||||||
|
msgid "Présence d'espèces exotiques envahissantes ?"
|
||||||
|
msgstr "Présence d'espèces exotiques envahissantes ?"
|
||||||
|
|
||||||
|
msgid "Une surverse est-elle présente ?"
|
||||||
|
msgstr "Une surverse est-elle présente ?"
|
||||||
|
|
||||||
|
msgid "Activer si le système dispose d'une évacuation de trop-plein"
|
||||||
|
msgstr "Activer si le système dispose d'une évacuation de trop-plein"
|
||||||
|
|
||||||
|
msgid "La grille de surverse est propre et dégagée ?"
|
||||||
|
msgstr "La grille de surverse est propre et dégagée ?"
|
||||||
|
|
||||||
|
msgid "La décantation est vide ou peu remplie ?"
|
||||||
|
msgstr "La décantation est vide ou peu remplie ?"
|
||||||
|
|
||||||
|
msgid "La surverse est-elle plus haute que le fond de l'ouvrage ?"
|
||||||
|
msgstr "La surverse est-elle plus haute que le fond de l'ouvrage ?"
|
||||||
|
|
||||||
|
msgid "État général du revêtement / chaussée"
|
||||||
|
msgstr "État général du revêtement / chaussée"
|
||||||
|
|
||||||
|
msgid "La surface au-dessus du massif drainant est en bon état ?"
|
||||||
|
msgstr "La surface au-dessus du massif drainant est en bon état ?"
|
||||||
|
|
||||||
|
msgid "Oui (Bon état)"
|
||||||
|
msgstr "Oui (Bon état)"
|
||||||
|
|
||||||
|
msgid "Non (Défaillances constatées)"
|
||||||
|
msgstr "Non (Défaillances constatées)"
|
||||||
|
|
||||||
|
msgid "Présence d'ornières"
|
||||||
|
msgstr "Présence d'ornières"
|
||||||
|
|
||||||
|
msgid "Présence de fissures"
|
||||||
|
msgstr "Présence de fissures"
|
||||||
|
|
||||||
|
msgid "Présence de trous"
|
||||||
|
msgstr "Présence de trous"
|
||||||
|
|
||||||
|
msgid "Une bouche d'injection est-elle présente ?"
|
||||||
|
msgstr "Une bouche d'injection est-elle présente ?"
|
||||||
|
|
||||||
|
msgid "Activer pour inspecter les conduits d'injection d'eau et de drainage"
|
||||||
|
msgstr "Activer pour inspecter les conduits d'injection d'eau et de drainage"
|
||||||
|
|
||||||
|
msgid "Les niveaux du revêtement conduisent bien l'eau vers la bouche ?"
|
||||||
|
msgstr "Les niveaux du revêtement conduisent bien l'eau vers la bouche ?"
|
||||||
|
|
||||||
|
msgid "La grille est propre et dégagée ?"
|
||||||
|
msgstr "La grille est propre et dégagée ?"
|
||||||
|
|
||||||
|
msgid "La décantation est-elle vide ou presque ?"
|
||||||
|
msgstr "La décantation est-elle vide ou presque ?"
|
||||||
|
|
||||||
|
msgid "La bouche d'injection dispose d'un filtre ?"
|
||||||
|
msgstr "La bouche d'injection dispose d'un filtre ?"
|
||||||
|
|
||||||
|
msgid "Le filtre est propre ?"
|
||||||
|
msgstr "Le filtre est propre ?"
|
||||||
|
|
||||||
|
msgid "Le filtre est en bon état ?"
|
||||||
|
msgstr "Le filtre est en bon état ?"
|
||||||
|
|
||||||
|
msgid "Le drain semble colmaté ?"
|
||||||
|
msgstr "Le drain semble colmaté ?"
|
||||||
|
|
||||||
|
msgid "Oui (colmaté)"
|
||||||
|
msgstr "Oui (colmaté)"
|
||||||
|
|
||||||
|
msgid "Type de structure & Revêtement OED"
|
||||||
|
msgstr "Type de structure & Revêtement OED"
|
||||||
|
|
||||||
|
msgid "Structure enherbée ou remplissage minéral ?"
|
||||||
|
msgstr "Structure enherbée ou remplissage minéral ?"
|
||||||
|
|
||||||
|
msgid "Dalles gazon, pavés à joints larges, dalles alvéolaires végétalisées/gravier, etc."
|
||||||
|
msgstr "Dalles gazon, pavés à joints larges, dalles alvéolaires végétalisées/gravier, etc."
|
||||||
|
|
||||||
|
msgid "De l'eau stagne-t-elle en surface ?"
|
||||||
|
msgstr "De l'eau stagne-t-elle en surface ?"
|
||||||
|
|
||||||
|
msgid "L'eau stagne plus de 2 heures ?"
|
||||||
|
msgstr "L'eau stagne plus de 2 heures ?"
|
||||||
|
|
||||||
|
msgid "Les joints sont-ils en bon état ?"
|
||||||
|
msgstr "Les joints sont-ils en bon état ?"
|
||||||
|
|
||||||
|
msgid "Présence importante de fines dans les joints ?"
|
||||||
|
msgstr "Présence importante de fines dans les joints ?"
|
||||||
|
|
||||||
|
msgid "Niveau de remplissage plus haut que la structure ?"
|
||||||
|
msgstr "Niveau de remplissage plus haut que la structure ?"
|
||||||
|
|
||||||
|
msgid "Modulaire poreux ou continu poreux ?"
|
||||||
|
msgstr "Modulaire poreux ou continu poreux ?"
|
||||||
|
|
||||||
|
msgid "Béton poreux, enrobés bitumineux poreux, graviers liés à la résine, EPDM, etc."
|
||||||
|
msgstr "Béton poreux, enrobés bitumineux poreux, graviers liés à la résine, EPDM, etc."
|
||||||
|
|
||||||
|
msgid "Présence de dépôts visibles (poussières, mousses, etc.) ?"
|
||||||
|
msgstr "Présence de dépôts visibles (poussières, mousses, etc.) ?"
|
||||||
|
|
||||||
|
msgid "Test de perméabilité nécessaire ?"
|
||||||
|
msgstr "Test de perméabilité nécessaire ?"
|
||||||
|
|
||||||
|
msgid "Matériaux non agrégés (graviers/mulch) ?"
|
||||||
|
msgstr "Matériaux non agrégés (graviers/mulch) ?"
|
||||||
|
|
||||||
|
msgid "Mulch végétal, graviers calcaires/non calcaires, terre battue, etc."
|
||||||
|
msgstr "Mulch végétal, graviers calcaires/non calcaires, terre battue, etc."
|
||||||
|
|
||||||
|
msgid "Le volume de matière est-il en suffisance ?"
|
||||||
|
msgstr "Le volume de matière est-il en suffisance ?"
|
||||||
|
|
||||||
|
msgid "Le géotextile est-il visible ?"
|
||||||
|
msgstr "Le géotextile est-il visible ?"
|
||||||
|
|
||||||
|
msgid "Les conduits et dispositifs amenant l'eau sont-ils fonctionnels ?"
|
||||||
|
msgstr "Les conduits et dispositifs amenant l'eau sont-ils fonctionnels ?"
|
||||||
|
|
||||||
|
msgid "Les grilles sont-elles propres et dégagées ?"
|
||||||
|
msgstr "Les grilles sont-elles propres et dégagées ?"
|
||||||
|
|
||||||
|
msgid "Présence de sédiments dans le bassin d'orage ?"
|
||||||
|
msgstr "Présence de sédiments dans le bassin d'orage ?"
|
||||||
|
|
||||||
|
msgid "L'ouvrage est-il rempli au 5e de sa capacité ?"
|
||||||
|
msgstr "L'ouvrage est-il rempli au 5e de sa capacité ?"
|
||||||
|
|
||||||
|
msgid "Présence de détritus dans l'ouvrage ?"
|
||||||
|
msgstr "Présence de détritus dans l'ouvrage ?"
|
||||||
|
|
||||||
|
msgid "Recommandations"
|
||||||
|
msgstr "Recommandations"
|
||||||
|
|
||||||
|
msgid "Aucune"
|
||||||
|
msgstr "Aucune"
|
||||||
|
|
||||||
|
msgid "Interventions préventives"
|
||||||
|
msgstr "Interventions préventives"
|
||||||
|
|
||||||
|
msgid "Entretien curatif / Réparation / Adaptation nécessaire"
|
||||||
|
msgstr "Entretien curatif / Réparation / Adaptation nécessaire"
|
||||||
|
|
||||||
|
msgid "Test de perméabilité"
|
||||||
|
msgstr "Test de perméabilité"
|
||||||
|
|
||||||
|
msgid "Inspection"
|
||||||
|
msgstr "Inspection"
|
||||||
|
|
|
||||||
|
|
@ -158,8 +158,11 @@ class Inspection(models.Model):
|
||||||
modified = True
|
modified = True
|
||||||
|
|
||||||
if hasattr(target_asset, 'inspection_status'):
|
if hasattr(target_asset, 'inspection_status'):
|
||||||
target_asset.inspection_status = self.result_status
|
try:
|
||||||
modified = True
|
setattr(target_asset, 'inspection_status', self.result_status)
|
||||||
|
modified = True
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
if hasattr(target_asset, 'validation_status'):
|
if hasattr(target_asset, 'validation_status'):
|
||||||
target_asset.validation_status = 'validated'
|
target_asset.validation_status = 'validated'
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ def get_inspection_partial_template(model_or_name):
|
||||||
else:
|
else:
|
||||||
model_name = str(model_or_name).lower()
|
model_name = str(model_or_name).lower()
|
||||||
|
|
||||||
if model_name in ('naturerwiz', 'naturerwiasset'):
|
if model_name == 'naturerwiz':
|
||||||
model_name = 'naturerwiz'
|
model_name = 'naturerwiz'
|
||||||
|
|
||||||
template_path = f"inspections/components/asset_fields/_{model_name}.html"
|
template_path = f"inspections/components/asset_fields/_{model_name}.html"
|
||||||
|
|
@ -45,7 +45,7 @@ def get_inspection_detail_partial_template(insp_or_model_or_name):
|
||||||
else:
|
else:
|
||||||
model_name = str(insp_or_model_or_name).lower()
|
model_name = str(insp_or_model_or_name).lower()
|
||||||
|
|
||||||
if model_name in ('naturerwiz', 'naturerwiasset', 'giepinspection'):
|
if model_name in ('naturerwiz', 'giepinspection'):
|
||||||
model_name = 'naturerwiz'
|
model_name = 'naturerwiz'
|
||||||
elif model_name == 'parkingspotinspection':
|
elif model_name == 'parkingspotinspection':
|
||||||
model_name = 'parkingspot'
|
model_name = 'parkingspot'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{% load i18n static %}
|
{% load i18n static inspections_tags %}
|
||||||
|
|
||||||
{% with leaf=insp.detailed_inspection %}
|
{% with leaf=insp.detailed_inspection %}
|
||||||
{% if leaf %}
|
{% if leaf %}
|
||||||
|
|
@ -10,21 +10,19 @@
|
||||||
<i class="bi bi-cloud-sun-fill me-2"></i>{% translate "Conditions météorologiques" %}
|
<i class="bi bi-cloud-sun-fill me-2"></i>{% translate "Conditions météorologiques" %}
|
||||||
</h6>
|
</h6>
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
{% if leaf.date_derniere_pluie and leaf.date_derniere_pluie != 'null' %}
|
|
||||||
<div class="col-12 col-md-6 d-flex align-items-center justify-content-between">
|
<div class="col-12 col-md-6 d-flex align-items-center justify-content-between">
|
||||||
<span class="small text-muted"><i class="bi bi-cloud-rain me-1 text-primary"></i>{% translate "Date de la dernière pluie" %} :</span>
|
<span class="small text-muted"><i class="bi bi-cloud-rain me-1 text-primary"></i>{% translate "Date de la dernière pluie" %} :</span>
|
||||||
<span class="badge bg-info text-white px-2 py-1">{{ leaf.get_date_derniere_pluie_display }}</span>
|
<span class="badge {% if leaf.date_derniere_pluie and leaf.date_derniere_pluie != 'null' %}bg-info text-white{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
</div>
|
{% if leaf.date_derniere_pluie and leaf.date_derniere_pluie != 'null' %}{{ leaf.get_date_derniere_pluie_display }}{% else %}{% translate "Non renseignée" %}{% endif %}
|
||||||
{% endif %}
|
</span>
|
||||||
|
</div>
|
||||||
{% if leaf.orage_moins_une_heure is not None %}
|
|
||||||
<div class="col-12 col-md-6 d-flex align-items-center justify-content-between">
|
<div class="col-12 col-md-6 d-flex align-items-center justify-content-between">
|
||||||
<span class="small text-muted"><i class="bi bi-lightning-charge me-1 text-danger"></i>{% translate "Orage récent (-1h)" %} :</span>
|
<span class="small text-muted"><i class="bi bi-lightning-charge me-1 text-danger"></i>{% translate "Orage récent (-1h)" %} :</span>
|
||||||
<span class="badge {% if leaf.orage_moins_une_heure %}bg-danger{% else %}bg-success{% endif %} text-white px-2 py-1">
|
<span class="badge {% if leaf.orage_moins_une_heure is True %}bg-danger text-white{% elif leaf.orage_moins_une_heure is False %}bg-success text-white{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
{% if leaf.orage_moins_une_heure %}{% translate "Oui" %}{% else %}{% translate "Non" %}{% endif %}
|
{% if leaf.orage_moins_une_heure is True %}{% translate "Oui" %}{% elif leaf.orage_moins_une_heure is False %}{% translate "Non" %}{% else %}{% translate "Non vérifié" %}{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
@ -38,9 +36,11 @@
|
||||||
|
|
||||||
<div class="d-flex flex-column gap-3">
|
<div class="d-flex flex-column gap-3">
|
||||||
{% for ouv in leaf.data.ouvrages %}
|
{% for ouv in leaf.data.ouvrages %}
|
||||||
|
{% with q=ouv.questions %}
|
||||||
<div class="card border shadow-sm rounded-3 giep-ouvrage-card overflow-hidden"
|
<div class="card border shadow-sm rounded-3 giep-ouvrage-card overflow-hidden"
|
||||||
style="{% if ouv.device_type == 'evc' %}border-left: 5px solid #2e7d32;{% elif ouv.device_type == 'oed' %}border-left: 5px solid #0277bd;{% elif ouv.device_type == 'ta' %}border-left: 5px solid #00796b;{% else %}border-left: 5px solid #6c757d;{% endif %}">
|
style="{% if ouv.device_type == 'evc' %}border-left: 5px solid #2e7d32;{% elif ouv.device_type == 'oed' %}border-left: 5px solid #0277bd;{% elif ouv.device_type == 'ta' %}border-left: 5px solid #00796b;{% else %}border-left: 5px solid #6c757d;{% endif %}">
|
||||||
|
|
||||||
|
<!-- En-tête de l'ouvrage -->
|
||||||
<div class="card-header bg-white p-3 d-flex align-items-center justify-content-between flex-wrap gap-2">
|
<div class="card-header bg-white p-3 d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||||
<span class="fw-bold text-dark fs-6">{% translate "Ouvrage" %} #{{ forloop.counter }}</span>
|
<span class="fw-bold text-dark fs-6">{% translate "Ouvrage" %} #{{ forloop.counter }}</span>
|
||||||
|
|
@ -61,7 +61,15 @@
|
||||||
<span class="badge bg-secondary px-2 py-1">{% translate "GÉNÉRIQUE" %}</span>
|
<span class="badge bg-secondary px-2 py-1">{% translate "GÉNÉRIQUE" %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if ouv.name %}
|
{% if ouv.asset_code %}
|
||||||
|
<span class="badge bg-primary-subtle text-primary border border-primary-subtle px-2 py-1">
|
||||||
|
<i class="bi bi-droplet-fill me-1"></i>{{ ouv.asset_code }}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if ouv.asset_name and ouv.asset_name != ouv.asset_code and ouv.asset_name != "Ouvrage #"|add:forloop.counter %}
|
||||||
|
<span class="badge bg-light text-dark border px-2 py-1">{{ ouv.asset_name }}</span>
|
||||||
|
{% elif ouv.name and ouv.name != ouv.asset_code and ouv.name != "Ouvrage #"|add:forloop.counter %}
|
||||||
<span class="badge bg-light text-dark border px-2 py-1">{{ ouv.name }}</span>
|
<span class="badge bg-light text-dark border px-2 py-1">{{ ouv.name }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -69,14 +77,252 @@
|
||||||
|
|
||||||
<div class="card-body p-3 bg-light-subtle">
|
<div class="card-body p-3 bg-light-subtle">
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<!-- Recommandations -->
|
|
||||||
|
<!-- ── 1. EVC (Espace Vert Creux) BLOCS ── -->
|
||||||
|
{% if ouv.device_type == 'evc' %}
|
||||||
|
<!-- Bloc EVC: Point d'entrée -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="border rounded-3 p-3 bg-white border-success-subtle shadow-sm">
|
||||||
|
<h6 class="fw-bold mb-3 small text-success text-uppercase border-bottom pb-1">
|
||||||
|
<i class="bi bi-box-arrow-in-right me-1"></i>{% translate "Point d'entrée dans l'ouvrage" %}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_pe_ok' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "L'eau peut-elle facilement entrer dans l'EVC ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-danger-subtle text-danger border border-danger-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_pe_feuilles' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Des feuilles ou branches bloquent-elles l'eau ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-danger-subtle text-danger border border-danger-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-success-subtle text-success border border-success-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_pe_detritus' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Un amas de détritus bloque-t-il l'eau ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-danger-subtle text-danger border border-danger-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-success-subtle text-success border border-success-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_empierrement_present' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "L'ouvrage dispose-t-il d'un empierrement ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-secondary-subtle text-secondary border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bloc EVC: Espace Vert -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="border rounded-3 p-3 bg-white border-success-subtle shadow-sm">
|
||||||
|
<h6 class="fw-bold mb-3 small text-success text-uppercase border-bottom pb-1">
|
||||||
|
<i class="bi bi-flower1 me-1"></i>{% translate "Espace vert" %}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_ev_veg_etat' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Quel est l'état de la végétation ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'bon' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'mauvais' %}bg-danger-subtle text-danger border border-danger-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{{ val|format_giep_q_val }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_ev_chenaux_erosion' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Présence de chenaux d'érosion ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-danger-subtle text-danger border border-danger-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-success-subtle text-success border border-success-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_ev_flaques' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Présence de flaques d'eau persistantes ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-danger-subtle text-danger border border-danger-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-success-subtle text-success border border-success-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_ev_sediments' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Présence de sédiments dans l'ouvrage ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-danger-subtle text-danger border border-danger-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-success-subtle text-success border border-success-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_ev_envahissantes' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Présence d'espèces exotiques envahissantes ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-danger-subtle text-danger border border-danger-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-success-subtle text-success border border-success-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bloc EVC: Surverse / Trop-plein -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="border rounded-3 p-3 bg-white border-success-subtle shadow-sm">
|
||||||
|
<h6 class="fw-bold mb-3 small text-success text-uppercase border-bottom pb-1">
|
||||||
|
<i class="bi bi-water me-1"></i>{% translate "Surverse / Trop-plein" %}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_surverse_presente' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Une surverse est-elle présente ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-secondary-subtle text-secondary border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'evc_sv_grille_propre' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "La grille de surverse est propre et dégagée ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-danger-subtle text-danger border border-danger-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ── 2. OED (Massif Drainant) BLOCS ── -->
|
||||||
|
{% if ouv.device_type == 'oed' %}
|
||||||
|
<!-- Bloc OED: État du revêtement -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="border rounded-3 p-3 bg-white border-info-subtle shadow-sm">
|
||||||
|
<h6 class="fw-bold mb-3 small text-info text-uppercase border-bottom pb-1">
|
||||||
|
<i class="bi bi-road-perspective me-1"></i>{% translate "État général du revêtement / chaussée" %}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'oed_revetement_ok' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "La surface au-dessus du massif drainant est en bon état ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-danger-subtle text-danger border border-danger-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bloc OED: Bouche d'injection -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="border rounded-3 p-3 bg-white border-info-subtle shadow-sm">
|
||||||
|
<h6 class="fw-bold mb-3 small text-info text-uppercase border-bottom pb-1">
|
||||||
|
<i class="bi bi-funnel me-1"></i>{% translate "Bouche d'injection & Drainage" %}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'oed_injection_presente' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Une bouche d'injection est-elle présente ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-secondary-subtle text-secondary border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'oed_bi_grille_propre' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "La grille est propre et dégagée ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-danger-subtle text-danger border border-danger-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ── 3. TA (Technique Alternative) BLOCS ── -->
|
||||||
|
{% if ouv.device_type == 'ta' %}
|
||||||
|
<!-- Bloc TA: Points d'entrée -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="border rounded-3 p-3 bg-white border-teal-subtle shadow-sm" style="border-color:#b2dfdb;">
|
||||||
|
<h6 class="fw-bold mb-3 small text-teal text-uppercase" style="color:#00796b;">
|
||||||
|
<i class="bi bi-box-arrow-in-right me-1"></i>{% translate "Points d'entrée dans l'ouvrage" %}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'ta_pe_ok' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "L'eau peut-elle facilement entrer dans l'ouvrage ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-danger-subtle text-danger border border-danger-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'ta_pe_conduits_ok' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Les conduits et dispositifs amenant l'eau sont-ils fonctionnels ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-danger-subtle text-danger border border-danger-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bloc TA: Bassin d'orage -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="border rounded-3 p-3 bg-white border-teal-subtle shadow-sm" style="border-color:#b2dfdb;">
|
||||||
|
<h6 class="fw-bold mb-3 small text-teal text-uppercase" style="color:#00796b;">
|
||||||
|
<i class="bi bi-cloud-snow me-1"></i>{% translate "Bassin d'orage" %}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'ta_bo_vegetation_etat' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Quel est l'état de la végétation ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'bon' %}bg-success-subtle text-success border border-success-subtle{% elif val == 'mauvais' %}bg-danger-subtle text-danger border border-danger-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{{ val|format_giep_q_val }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% with val=q|get_q_val:'ta_bo_sediments' %}
|
||||||
|
<div class="d-flex align-items-center justify-content-between p-2 mb-1.5 rounded border {% if val == 'null' %}bg-light opacity-75{% else %}bg-white{% endif %}">
|
||||||
|
<span class="small fw-medium {% if val == 'null' %}text-muted{% else %}text-dark{% endif %} me-2">{% translate "Présence de sédiments dans l'ouvrage ?" %}</span>
|
||||||
|
<span class="badge {% if val == 'true' or val == True or val == 'oui' %}bg-danger-subtle text-danger border border-danger-subtle{% elif val == 'false' or val == False or val == 'non' %}bg-success-subtle text-success border border-success-subtle{% else %}bg-light text-muted border{% endif %} px-2 py-1">
|
||||||
|
{% if val == 'true' or val == True or val == 'oui' %}Oui{% elif val == 'false' or val == False or val == 'non' %}Non{% else %}Non vérifié{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ── Recommandations ── -->
|
||||||
{% if ouv.actions %}
|
{% if ouv.actions %}
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="border rounded p-3 bg-white shadow-sm">
|
<div class="border rounded-3 p-3 bg-white shadow-sm">
|
||||||
<p class="fw-bold mb-2 text-secondary small text-uppercase border-bottom pb-1">
|
<h6 class="fw-bold mb-2 text-secondary small text-uppercase border-bottom pb-1">
|
||||||
<i class="bi bi-bookmark-star me-1"></i>{% translate "Recommandations" %}
|
<i class="bi bi-bookmark-star me-1"></i>{% translate "Recommandations" %}
|
||||||
</p>
|
</h6>
|
||||||
<div class="d-flex flex-wrap gap-1.5">
|
<div class="d-flex flex-wrap gap-1.5 pt-1">
|
||||||
{% if ouv.actions.aucune %}
|
{% if ouv.actions.aucune %}
|
||||||
<span class="badge bg-success-subtle text-success border border-success-subtle p-2"><i class="bi bi-check-circle me-1"></i>{% translate "Aucune" %}</span>
|
<span class="badge bg-success-subtle text-success border border-success-subtle p-2"><i class="bi bi-check-circle me-1"></i>{% translate "Aucune" %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
@ -96,9 +342,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endwith %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,45 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal pour Sélection des Équipements GIEP sur Carte -->
|
||||||
|
<div class="modal fade" id="giepAssetMapModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="giepAssetMapModalLabel" aria-hidden="true" style="z-index: 1060;">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
|
||||||
|
<div class="modal-content rounded-4 shadow-lg border-0">
|
||||||
|
<div class="modal-header bg-primary text-white py-2.5 px-3 rounded-top-4">
|
||||||
|
<h6 class="modal-title fw-bold mb-0" id="giepAssetMapModalLabel">
|
||||||
|
<i class="bi bi-droplet-fill me-2"></i>{% translate "Sélectionner un équipement GIEP sur la carte" %}
|
||||||
|
</h6>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body p-3">
|
||||||
|
<div class="row g-2 mb-3">
|
||||||
|
<div class="col-12 col-md-7">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<span class="input-group-text bg-white"><i class="bi bi-search text-muted"></i></span>
|
||||||
|
<input type="text" id="giep_modal_search_input" class="form-control" placeholder="{% translate 'Filtrer par code (ex: BI-01...) ou par type...' %}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-5">
|
||||||
|
<select id="giep_modal_type_filter" class="form-select form-select-sm">
|
||||||
|
<option value="">{% translate "Tous les types d'équipements" %}</option>
|
||||||
|
<option value="evc">{% translate "Espace vert creux (EVC)" %}</option>
|
||||||
|
<option value="oed">{% translate "Massif drainant (OED)" %}</option>
|
||||||
|
<option value="ta">{% translate "Technique alternative (TA)" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Conteneur Carte Agrandie -->
|
||||||
|
<div id="giep_modal_map_container" class="rounded-3 border overflow-hidden" style="height: 480px; max-height: 68vh; position: relative;">
|
||||||
|
<div id="giepModalMap" style="width: 100%; height: 100%;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer py-2 px-3 bg-light">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm rounded-pill" data-bs-dismiss="modal">{% translate "Fermer" %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.device-select-btn {
|
.device-select-btn {
|
||||||
border: 2px solid #dee2e6;
|
border: 2px solid #dee2e6;
|
||||||
|
|
@ -114,11 +153,14 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
function createEmptyOuvrage(idx, deviceType = "evc") {
|
function createEmptyOuvrage(idx, deviceType = "evc", asset = null) {
|
||||||
return {
|
return {
|
||||||
local_id: String(idx),
|
local_id: String(idx),
|
||||||
device_type: deviceType,
|
device_type: deviceType,
|
||||||
name: `Ouvrage #${idx + 1}`,
|
name: asset ? (asset.code + (asset.structure_type ? ' (' + asset.structure_type + ')' : '')) : `Ouvrage #${idx + 1}`,
|
||||||
|
asset_id: asset ? asset.id : null,
|
||||||
|
asset_code: asset ? asset.code : null,
|
||||||
|
asset_name: asset ? (asset.structure_type || asset.name || '') : null,
|
||||||
questions: {
|
questions: {
|
||||||
// EVC
|
// EVC
|
||||||
evc_pe_ok: null,
|
evc_pe_ok: null,
|
||||||
|
|
@ -190,12 +232,163 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let giepOuvrages = [createEmptyOuvrage(0, "evc")];
|
// Formulaire initialement à VIDE (0 ouvrages)
|
||||||
|
let giepOuvrages = [];
|
||||||
|
let locationGiepAssets = [];
|
||||||
|
let activeOuvrageIdxForMap = null;
|
||||||
|
let modalMapInstance = null;
|
||||||
|
let modalMapMarkers = [];
|
||||||
|
|
||||||
const container = document.getElementById('giep_ouvrages_container');
|
const container = document.getElementById('giep_ouvrages_container');
|
||||||
const serializedInput = document.getElementById('giep_data_serialized');
|
const serializedInput = document.getElementById('giep_data_serialized');
|
||||||
const addBtn = document.getElementById('giep_add_ouvrage_btn');
|
const addBtn = document.getElementById('giep_add_ouvrage_btn');
|
||||||
|
|
||||||
|
function extendBoundsFromGeom(bounds, geom) {
|
||||||
|
if (!bounds || !geom || !geom.coordinates) return;
|
||||||
|
const type = geom.type;
|
||||||
|
if (type === 'Point') {
|
||||||
|
bounds.extend(geom.coordinates);
|
||||||
|
} else if (type === 'LineString' || type === 'MultiPoint') {
|
||||||
|
geom.coordinates.forEach(c => bounds.extend(c));
|
||||||
|
} else if (type === 'Polygon' || type === 'MultiLineString') {
|
||||||
|
geom.coordinates.forEach(ring => ring.forEach(c => bounds.extend(c)));
|
||||||
|
} else if (type === 'MultiPolygon') {
|
||||||
|
geom.coordinates.forEach(poly => poly.forEach(ring => ring.forEach(c => bounds.extend(c))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchLocationAssets() {
|
||||||
|
if (locationGiepAssets.length > 0) return Promise.resolve(locationGiepAssets);
|
||||||
|
let locationId = null;
|
||||||
|
const modalTrigger = document.querySelector('[data-object-id]');
|
||||||
|
if (modalTrigger) {
|
||||||
|
locationId = modalTrigger.getAttribute('data-object-id');
|
||||||
|
} else {
|
||||||
|
const urlMatch = window.location.pathname.match(/\/zirw\/(\d+)\//);
|
||||||
|
if (urlMatch) locationId = urlMatch[1];
|
||||||
|
}
|
||||||
|
if (!locationId) return Promise.resolve([]);
|
||||||
|
|
||||||
|
return fetch("{% url 'assets:water_assets_geojson' %}?location=" + locationId)
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data && data.features) {
|
||||||
|
locationGiepAssets = data.features.map(f => {
|
||||||
|
const props = f.properties || {};
|
||||||
|
let lon = props.lon;
|
||||||
|
let lat = props.lat;
|
||||||
|
if ((lon === null || lon === undefined || lat === null || lat === undefined) && f.geometry) {
|
||||||
|
if (f.geometry.type === 'Point' && Array.isArray(f.geometry.coordinates)) {
|
||||||
|
lon = f.geometry.coordinates[0];
|
||||||
|
lat = f.geometry.coordinates[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: f.id,
|
||||||
|
code: props.code || `Asset #${f.id}`,
|
||||||
|
name: props.name || props.structure_type || props.category || props.code || `Asset #${f.id}`,
|
||||||
|
structure_type: props.structure_type || '',
|
||||||
|
category: props.category || '',
|
||||||
|
lon: (lon !== null && lon !== undefined) ? parseFloat(lon) : null,
|
||||||
|
lat: (lat !== null && lat !== undefined) ? parseFloat(lat) : null,
|
||||||
|
geometry: f.geometry,
|
||||||
|
rawFeature: f
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return locationGiepAssets;
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.warn("Erreur de chargement des équipements GIEP :", err);
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferDeviceType(asset) {
|
||||||
|
if (!asset) return null;
|
||||||
|
const text = ((asset.structure_type || '') + ' ' + (asset.name || '') + ' ' + (asset.category || '')).toLowerCase();
|
||||||
|
|
||||||
|
// Avaloirs, Bouches d'injection, Regards, Grilles : équipements d'accès non associés à l'un des 3 types
|
||||||
|
if (text.includes('avaloir') || text.includes('injection') || text.includes('regard') || text.includes('grille')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EVC (Espace Vert Creux, Noue, Jardin de pluie, Zone végétalisée creuse)
|
||||||
|
if (text.includes('espace vert') || text.includes('creux') || text.includes('evc') || text.includes('noue') || text.includes('gazon') || text.includes('jardin') || text.includes('végétalis')) {
|
||||||
|
return 'evc';
|
||||||
|
}
|
||||||
|
|
||||||
|
// TA (Technique Alternative, Bassin d'orage, Surverse, Trop-plein, Marre)
|
||||||
|
if (text.includes('bassin') || text.includes('orage') || text.includes('technique alternative') || text.includes('ta') || text.includes('surverse') || text.includes('trop plein') || text.includes('marre')) {
|
||||||
|
return 'ta';
|
||||||
|
}
|
||||||
|
|
||||||
|
// OED (Massif drainant, Chaussée réservoir, Dalles, Tranchée drainante, Revêtement poreux)
|
||||||
|
if (text.includes('massif') || text.includes('drainant') || text.includes('oed') || text.includes('chaussée') || text.includes('revêtement') || text.includes('dalle') || text.includes('tranchée')) {
|
||||||
|
return 'oed';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentMapPopup = null;
|
||||||
|
|
||||||
|
function showAssetPopup(asset, lngLat) {
|
||||||
|
if (!modalMapInstance) return;
|
||||||
|
if (currentMapPopup) {
|
||||||
|
currentMapPopup.remove();
|
||||||
|
currentMapPopup = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const devType = inferDeviceType(asset);
|
||||||
|
let badgeHtml = '';
|
||||||
|
if (devType === 'evc') badgeHtml = '<span class="badge bg-success-subtle text-success text-uppercase me-1">EVC</span>';
|
||||||
|
else if (devType === 'oed') badgeHtml = '<span class="badge bg-info-subtle text-info text-uppercase me-1">OED</span>';
|
||||||
|
else if (devType === 'ta') badgeHtml = '<span class="badge bg-teal-subtle text-teal text-uppercase me-1">TA</span>';
|
||||||
|
else badgeHtml = '<span class="badge bg-secondary-subtle text-secondary text-uppercase me-1">Équipement</span>';
|
||||||
|
|
||||||
|
const popupNode = document.createElement('div');
|
||||||
|
popupNode.className = 'p-1';
|
||||||
|
popupNode.innerHTML = `
|
||||||
|
<div class="d-flex align-items-center gap-1.5 mb-1">
|
||||||
|
<i class="bi bi-droplet-fill text-primary"></i>
|
||||||
|
<strong class="fw-bold text-dark fs-6">${asset.code}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="small text-dark fw-semibold mb-1">${asset.name || asset.structure_type || ''}</div>
|
||||||
|
<div class="extra-small text-muted mb-2">
|
||||||
|
${badgeHtml}
|
||||||
|
<span>${asset.structure_type || ''}</span>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm w-100 rounded-pill py-1.5 px-3 fw-semibold select-popup-btn">
|
||||||
|
<i class="bi bi-check-circle me-1"></i>{% filter escapejs %}{% translate "Sélectionner" %}{% endfilter %}
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
popupNode.querySelector('.select-popup-btn').addEventListener('click', () => {
|
||||||
|
selectAssetForOuvrage(activeOuvrageIdxForMap, asset);
|
||||||
|
const modalEl = document.getElementById('giepAssetMapModal');
|
||||||
|
const modalInstance = bootstrap.Modal.getInstance(modalEl);
|
||||||
|
if (modalInstance) modalInstance.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
currentMapPopup = new maplibregl.Popup({ offset: 15, closeButton: true, maxWidth: '250px' })
|
||||||
|
.setLngLat(lngLat)
|
||||||
|
.setDOMContent(popupNode)
|
||||||
|
.addTo(modalMapInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAssetForOuvrage(idx, asset) {
|
||||||
|
if (typeof idx !== 'number' || !giepOuvrages[idx]) return;
|
||||||
|
giepOuvrages[idx].asset_id = asset.id;
|
||||||
|
giepOuvrages[idx].asset_code = asset.code;
|
||||||
|
giepOuvrages[idx].asset_name = asset.structure_type || asset.name || asset.code;
|
||||||
|
const inferred = inferDeviceType(asset);
|
||||||
|
if (inferred) {
|
||||||
|
giepOuvrages[idx].device_type = inferred;
|
||||||
|
}
|
||||||
|
renderOuvrages();
|
||||||
|
}
|
||||||
|
|
||||||
function updateSerializedGiepData() {
|
function updateSerializedGiepData() {
|
||||||
const pluieVal = document.getElementById('giep_date_derniere_pluie')?.value || 'null';
|
const pluieVal = document.getElementById('giep_date_derniere_pluie')?.value || 'null';
|
||||||
const orageVal = document.getElementById('giep_orage_moins_une_heure')?.value || 'null';
|
const orageVal = document.getElementById('giep_orage_moins_une_heure')?.value || 'null';
|
||||||
|
|
@ -234,10 +427,274 @@
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function populateModalAssetsList(filterText = '', filterType = '') {
|
||||||
|
// Obsolete list, filtering operates directly on map layers
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderModalMapFeatures(isInitialFit = false) {
|
||||||
|
if (!modalMapInstance) return;
|
||||||
|
|
||||||
|
if (modalMapMarkers) {
|
||||||
|
modalMapMarkers.forEach(m => m.remove());
|
||||||
|
modalMapMarkers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterText = (document.getElementById('giep_modal_search_input')?.value || '').toLowerCase().trim();
|
||||||
|
const filterType = document.getElementById('giep_modal_type_filter')?.value || '';
|
||||||
|
|
||||||
|
const filteredFeatures = [];
|
||||||
|
const bounds = (typeof maplibregl !== 'undefined') ? new maplibregl.LngLatBounds() : null;
|
||||||
|
let hasCoords = false;
|
||||||
|
|
||||||
|
locationGiepAssets.forEach(asset => {
|
||||||
|
const matchText = !filterText || asset.code.toLowerCase().includes(filterText) || (asset.structure_type && asset.structure_type.toLowerCase().includes(filterText)) || (asset.name && asset.name.toLowerCase().includes(filterText));
|
||||||
|
const devType = inferDeviceType(asset);
|
||||||
|
const matchType = !filterType || devType === filterType;
|
||||||
|
|
||||||
|
if (matchText && matchType && asset.rawFeature && asset.rawFeature.geometry && typeof maplibregl !== 'undefined') {
|
||||||
|
const geom = asset.rawFeature.geometry;
|
||||||
|
let color = '#0277bd'; // Bleu GIEP (naturerwiasset)
|
||||||
|
if (devType === 'evc') color = '#2e7d32'; // Vert EVC
|
||||||
|
if (devType === 'oed') color = '#0277bd'; // Bleu OED
|
||||||
|
if (devType === 'ta') color = '#00796b'; // Turquoise TA
|
||||||
|
|
||||||
|
const featureClone = {
|
||||||
|
type: 'Feature',
|
||||||
|
id: asset.id,
|
||||||
|
geometry: geom,
|
||||||
|
properties: {
|
||||||
|
...(asset.rawFeature.properties || {}),
|
||||||
|
id: asset.id,
|
||||||
|
code: asset.code,
|
||||||
|
name: asset.name,
|
||||||
|
structure_type: asset.structure_type,
|
||||||
|
device_type: devType,
|
||||||
|
color: color
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
filteredFeatures.push(featureClone);
|
||||||
|
if (isInitialFit) {
|
||||||
|
extendBoundsFromGeom(bounds, geom);
|
||||||
|
}
|
||||||
|
hasCoords = true;
|
||||||
|
|
||||||
|
// Marqueurs ronds pour les géométries de type Point
|
||||||
|
if (geom.type === 'Point' && Array.isArray(geom.coordinates)) {
|
||||||
|
const lon = geom.coordinates[0];
|
||||||
|
const lat = geom.coordinates[1];
|
||||||
|
if (!isNaN(lon) && !isNaN(lat)) {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.className = 'giep-map-marker';
|
||||||
|
el.style.width = '28px';
|
||||||
|
el.style.height = '28px';
|
||||||
|
el.style.borderRadius = '50%';
|
||||||
|
el.style.backgroundColor = color;
|
||||||
|
el.style.border = '2.5px solid white';
|
||||||
|
el.style.boxShadow = '0 3px 8px rgba(0,0,0,0.4)';
|
||||||
|
el.style.cursor = 'pointer';
|
||||||
|
el.style.display = 'flex';
|
||||||
|
el.style.alignItems = 'center';
|
||||||
|
el.style.justifyContent = 'center';
|
||||||
|
el.innerHTML = '<i class="bi bi-droplet-fill text-white" style="font-size: 14px;"></i>';
|
||||||
|
el.title = `${asset.code} - ${asset.structure_type || asset.name}`;
|
||||||
|
|
||||||
|
const marker = new maplibregl.Marker({ element: el })
|
||||||
|
.setLngLat([lon, lat])
|
||||||
|
.addTo(modalMapInstance);
|
||||||
|
|
||||||
|
el.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
showAssetPopup(asset, [lon, lat]);
|
||||||
|
});
|
||||||
|
|
||||||
|
modalMapMarkers.push(marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const geojsonPayload = {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: filteredFeatures
|
||||||
|
};
|
||||||
|
|
||||||
|
const source = modalMapInstance.getSource('giep-assets-src');
|
||||||
|
if (source) {
|
||||||
|
source.setData(geojsonPayload);
|
||||||
|
} else {
|
||||||
|
modalMapInstance.addSource('giep-assets-src', {
|
||||||
|
type: 'geojson',
|
||||||
|
data: geojsonPayload
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1. Couche de remplissage (Polygones) — EN-DESSOUS
|
||||||
|
modalMapInstance.addLayer({
|
||||||
|
id: 'giep-assets-fill',
|
||||||
|
type: 'fill',
|
||||||
|
source: 'giep-assets-src',
|
||||||
|
filter: ['match', ['geometry-type'], ['Polygon', 'MultiPolygon'], true, false],
|
||||||
|
paint: {
|
||||||
|
'fill-color': ['get', 'color'],
|
||||||
|
'fill-opacity': 0.45
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Couche de lignes (Lignes et contours) — AU MILIEU
|
||||||
|
modalMapInstance.addLayer({
|
||||||
|
id: 'giep-assets-line',
|
||||||
|
type: 'line',
|
||||||
|
source: 'giep-assets-src',
|
||||||
|
filter: ['match', ['geometry-type'], ['LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'], true, false],
|
||||||
|
paint: {
|
||||||
|
'line-color': ['get', 'color'],
|
||||||
|
'line-width': 3.5
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Couche de cercles (Points) — AU-DESSUS
|
||||||
|
modalMapInstance.addLayer({
|
||||||
|
id: 'giep-assets-circle',
|
||||||
|
type: 'circle',
|
||||||
|
source: 'giep-assets-src',
|
||||||
|
filter: ['match', ['geometry-type'], ['Point', 'MultiPoint'], true, false],
|
||||||
|
paint: {
|
||||||
|
'circle-color': ['get', 'color'],
|
||||||
|
'circle-radius': 8,
|
||||||
|
'circle-stroke-width': 2.5,
|
||||||
|
'circle-stroke-color': '#ffffff'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Un seul handler de clic sur la carte avec priorité stricte de géométrie (Points > Lignes > Polygones)
|
||||||
|
modalMapInstance.on('click', (e) => {
|
||||||
|
const layers = ['giep-assets-circle', 'giep-assets-line', 'giep-assets-fill'];
|
||||||
|
const features = modalMapInstance.queryRenderedFeatures(e.point, { layers: layers });
|
||||||
|
if (!features || features.length === 0) return;
|
||||||
|
|
||||||
|
const priorityOrder = { 'Point': 1, 'MultiPoint': 1, 'LineString': 2, 'MultiLineString': 2, 'Polygon': 3, 'MultiPolygon': 3 };
|
||||||
|
features.sort((a, b) => {
|
||||||
|
const typeA = a.geometry ? a.geometry.type : '';
|
||||||
|
const typeB = b.geometry ? b.geometry.type : '';
|
||||||
|
const pA = priorityOrder[typeA] || 99;
|
||||||
|
const pB = priorityOrder[typeB] || 99;
|
||||||
|
return pA - pB;
|
||||||
|
});
|
||||||
|
|
||||||
|
const topFeature = features[0];
|
||||||
|
const props = topFeature.properties;
|
||||||
|
const targetAsset = locationGiepAssets.find(a => String(a.id) === String(props.id));
|
||||||
|
if (targetAsset) {
|
||||||
|
showAssetPopup(targetAsset, e.lngLat);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
['giep-assets-circle', 'giep-assets-line', 'giep-assets-fill'].forEach(layerId => {
|
||||||
|
modalMapInstance.on('mouseenter', layerId, () => {
|
||||||
|
modalMapInstance.getCanvas().style.cursor = 'pointer';
|
||||||
|
});
|
||||||
|
modalMapInstance.on('mouseleave', layerId, () => {
|
||||||
|
modalMapInstance.getCanvas().style.cursor = '';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seul le chargement initial modifie le zoom et cadrage de la carte
|
||||||
|
if (isInitialFit && hasCoords && bounds && !bounds.isEmpty()) {
|
||||||
|
modalMapInstance.fitBounds(bounds, { padding: 50, maxZoom: 18 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initOrUpdateModalMap() {
|
||||||
|
const containerEl = document.getElementById('giepModalMap');
|
||||||
|
if (!containerEl || typeof maplibregl === 'undefined') return;
|
||||||
|
|
||||||
|
const style = window.MAP_STYLE_LIGHT || 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json';
|
||||||
|
|
||||||
|
if (!modalMapInstance) {
|
||||||
|
modalMapInstance = new maplibregl.Map({
|
||||||
|
container: 'giepModalMap',
|
||||||
|
style: style,
|
||||||
|
center: [4.3517, 50.8503],
|
||||||
|
zoom: 15,
|
||||||
|
antialias: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Contrôles de navigation et de géolocalisation
|
||||||
|
modalMapInstance.addControl(new maplibregl.NavigationControl(), 'top-right');
|
||||||
|
|
||||||
|
const geolocate = new maplibregl.GeolocateControl({
|
||||||
|
positionOptions: { enableHighAccuracy: true },
|
||||||
|
trackUserLocation: true,
|
||||||
|
showUserLocation: true
|
||||||
|
});
|
||||||
|
modalMapInstance.addControl(geolocate, 'top-right');
|
||||||
|
|
||||||
|
modalMapInstance.on('load', () => {
|
||||||
|
renderModalMapFeatures(true);
|
||||||
|
geolocate.trigger();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
modalMapInstance.resize();
|
||||||
|
renderModalMapFeatures(true);
|
||||||
|
}, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tentative de géolocalisation de l'utilisateur si navigateur supporté
|
||||||
|
if (navigator.geolocation) {
|
||||||
|
navigator.geolocation.getCurrentPosition(
|
||||||
|
(pos) => {
|
||||||
|
const uLon = pos.coords.longitude;
|
||||||
|
const uLat = pos.coords.latitude;
|
||||||
|
if (modalMapInstance) {
|
||||||
|
modalMapInstance.flyTo({ center: [uLon, uLat], zoom: 16 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
console.log("Géolocalisation utilisateur non disponible:", err);
|
||||||
|
},
|
||||||
|
{ enableHighAccuracy: true, timeout: 6000 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMapModal(idx) {
|
||||||
|
activeOuvrageIdxForMap = idx;
|
||||||
|
fetchLocationAssets().then(assets => {
|
||||||
|
populateModalAssetsList();
|
||||||
|
const modalEl = document.getElementById('giepAssetMapModal');
|
||||||
|
let modalInstance = bootstrap.Modal.getInstance(modalEl);
|
||||||
|
if (!modalInstance) {
|
||||||
|
modalInstance = new bootstrap.Modal(modalEl);
|
||||||
|
}
|
||||||
|
modalInstance.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function renderOuvrages() {
|
function renderOuvrages() {
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
if (giepOuvrages.length === 0) {
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="text-center p-4 border border-dashed rounded-4 bg-light shadow-sm my-2" id="giep_empty_state">
|
||||||
|
<i class="bi bi-inbox fs-2 text-primary opacity-50 d-block mb-2"></i>
|
||||||
|
<h6 class="fw-bold text-secondary mb-1">{% filter escapejs %}{% translate "Aucun ouvrage dans cette inspection" %}{% endfilter %}</h6>
|
||||||
|
<p class="small text-muted mb-3">{% filter escapejs %}{% translate "Cliquez ci-dessous pour ajouter un ouvrage à inspecter." %}{% endfilter %}</p>
|
||||||
|
<button type="button" class="btn btn-success btn-sm px-3.5 py-2 rounded-pill fw-semibold shadow-sm" id="giep_add_first_ouvrage_btn">
|
||||||
|
<i class="bi bi-plus-circle me-1"></i>{% filter escapejs %}{% translate "Ajouter un ouvrage" %}{% endfilter %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.getElementById('giep_add_first_ouvrage_btn')?.addEventListener('click', () => {
|
||||||
|
giepOuvrages.push(createEmptyOuvrage(0, "evc"));
|
||||||
|
renderOuvrages();
|
||||||
|
});
|
||||||
|
updateSerializedGiepData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
giepOuvrages.forEach((ouv, idx) => {
|
giepOuvrages.forEach((ouv, idx) => {
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
card.className = 'card border p-3 bg-light rounded-4 shadow-sm';
|
card.className = 'card border p-3 bg-light rounded-4 shadow-sm';
|
||||||
|
|
@ -513,10 +970,48 @@
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const titleHeader = ouv.asset_code ? `${ouv.asset_code} — ${ouv.asset_name || 'Ouvrage #' + (idx + 1)}` : `Ouvrage #${idx + 1}`;
|
||||||
|
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<div class="d-flex align-items-center justify-content-between mb-3 border-bottom pb-2">
|
<div class="d-flex align-items-center justify-content-between mb-3 border-bottom pb-2">
|
||||||
<span class="fw-bold text-dark">{% translate "Ouvrage" %} #${idx + 1}</span>
|
<span class="fw-bold text-dark"><i class="bi bi-gear-fill me-1.5 text-primary"></i>${titleHeader}</span>
|
||||||
${idx > 0 ? `<button type="button" class="btn btn-outline-danger btn-sm py-0.5 px-2.5 rounded-pill remove-ouv-btn" data-idx="${idx}"><i class="bi bi-trash me-1"></i>{% translate "Supprimer" %}</button>` : ''}
|
<button type="button" class="btn btn-outline-danger btn-sm py-0.5 px-2.5 rounded-pill remove-ouv-btn" data-idx="${idx}">
|
||||||
|
<i class="bi bi-trash me-1"></i>{% translate "Supprimer" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Liaison Équipement GIEP / Ouvrage -->
|
||||||
|
<div class="border rounded-3 p-3 mb-3 bg-white border-primary-subtle shadow-sm">
|
||||||
|
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||||
|
<span class="extra-small fw-bold text-primary text-uppercase">
|
||||||
|
<i class="bi bi-link-45deg me-1"></i>{% translate "Équipement GIEP associé (Optionnel)" %}
|
||||||
|
</span>
|
||||||
|
${ouv.asset_id ? `<button type="button" class="btn btn-link text-danger p-0 extra-small text-decoration-none clear-asset-btn" data-idx="${idx}"><i class="bi bi-x-circle me-1"></i>{% translate "Délier" %}</button>` : ''}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${ouv.asset_id ? `
|
||||||
|
<div class="p-2.5 rounded-3 bg-primary-subtle bg-opacity-20 border border-primary-subtle d-flex align-items-center justify-content-between">
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<i class="bi bi-geo-alt-fill text-primary fs-5"></i>
|
||||||
|
<div>
|
||||||
|
<strong class="small text-primary d-block">${ouv.asset_code}</strong>
|
||||||
|
<span class="extra-small text-muted">${ouv.asset_name || ''}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-primary text-white rounded-pill extra-small">{% translate "Lié" %}</span>
|
||||||
|
</div>
|
||||||
|
` : `
|
||||||
|
<div class="position-relative">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<span class="input-group-text bg-white"><i class="bi bi-search text-muted"></i></span>
|
||||||
|
<input type="text" class="form-control asset-search-input" data-idx="${idx}" placeholder="{% filter escapejs %}{% translate "Rechercher par code (ex: BI-01...) ou par type..." %}{% endfilter %}" autocomplete="off">
|
||||||
|
<button type="button" class="btn btn-outline-primary open-map-modal-btn" data-idx="${idx}">
|
||||||
|
<i class="bi bi-geo-alt-fill me-1"></i>{% translate "Choisir sur la carte" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="list-group position-absolute w-100 shadow-lg d-none asset-autocomplete-dropdown" data-idx="${idx}" style="z-index: 1050; max-height: 200px; overflow-y: auto; background: white;"></div>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Device Selector Symbology -->
|
<!-- Device Selector Symbology -->
|
||||||
|
|
@ -574,13 +1069,72 @@
|
||||||
container.appendChild(card);
|
container.appendChild(card);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Event listeners pour la carte et l'autocomplétion
|
||||||
|
container.querySelectorAll('.open-map-modal-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', (e) => {
|
||||||
|
const idx = parseInt(e.currentTarget.getAttribute('data-idx'));
|
||||||
|
openMapModal(idx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
container.querySelectorAll('.clear-asset-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', (e) => {
|
||||||
|
const idx = parseInt(e.currentTarget.getAttribute('data-idx'));
|
||||||
|
if (giepOuvrages[idx]) {
|
||||||
|
giepOuvrages[idx].asset_id = null;
|
||||||
|
giepOuvrages[idx].asset_code = null;
|
||||||
|
giepOuvrages[idx].asset_name = null;
|
||||||
|
renderOuvrages();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
container.querySelectorAll('.asset-search-input').forEach(input => {
|
||||||
|
input.addEventListener('input', (e) => {
|
||||||
|
const idx = parseInt(e.target.getAttribute('data-idx'));
|
||||||
|
const val = e.target.value.trim().toLowerCase();
|
||||||
|
const dropdown = container.querySelector(`.asset-autocomplete-dropdown[data-idx="${idx}"]`);
|
||||||
|
if (!dropdown) return;
|
||||||
|
|
||||||
|
if (!val) {
|
||||||
|
dropdown.classList.add('d-none');
|
||||||
|
dropdown.innerHTML = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchLocationAssets().then(assets => {
|
||||||
|
const matches = assets.filter(a => a.code.toLowerCase().includes(val) || (a.structure_type && a.structure_type.toLowerCase().includes(val)) || (a.name && a.name.toLowerCase().includes(val))).slice(0, 8);
|
||||||
|
if (matches.length === 0) {
|
||||||
|
dropdown.classList.add('d-none');
|
||||||
|
dropdown.innerHTML = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dropdown.innerHTML = matches.map(a => `
|
||||||
|
<button type="button" class="list-group-item list-group-item-action py-2 px-3 text-start autocomplete-item" data-id="${a.id}">
|
||||||
|
<div class="fw-bold small text-primary">${a.code}</div>
|
||||||
|
<div class="extra-small text-muted">${a.structure_type || a.name || ''}</div>
|
||||||
|
</button>
|
||||||
|
`).join('');
|
||||||
|
dropdown.classList.remove('d-none');
|
||||||
|
|
||||||
|
dropdown.querySelectorAll('.autocomplete-item').forEach(itemBtn => {
|
||||||
|
itemBtn.addEventListener('click', () => {
|
||||||
|
const assetId = itemBtn.getAttribute('data-id');
|
||||||
|
const targetAsset = assets.find(a => String(a.id) === String(assetId));
|
||||||
|
if (targetAsset) selectAssetForOuvrage(idx, targetAsset);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Event listeners pour le sélecteur de dispositif
|
// Event listeners pour le sélecteur de dispositif
|
||||||
container.querySelectorAll('.device-select-btn').forEach(btn => {
|
container.querySelectorAll('.device-select-btn').forEach(btn => {
|
||||||
btn.addEventListener('click', (e) => {
|
btn.addEventListener('click', (e) => {
|
||||||
const target = e.currentTarget;
|
const target = e.currentTarget;
|
||||||
const idx = parseInt(target.getAttribute('data-idx'));
|
const idx = parseInt(target.getAttribute('data-idx'));
|
||||||
const dev = target.getAttribute('data-device');
|
const dev = target.getAttribute('data-device');
|
||||||
giepOuvrages[idx] = createEmptyOuvrage(idx, dev);
|
giepOuvrages[idx].device_type = dev;
|
||||||
renderOuvrages();
|
renderOuvrages();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -635,6 +1189,28 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modal filter & shown listeners
|
||||||
|
const modalEl = document.getElementById('giepAssetMapModal');
|
||||||
|
if (modalEl) {
|
||||||
|
modalEl.addEventListener('shown.bs.modal', () => {
|
||||||
|
initOrUpdateModalMap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('giep_modal_search_input')?.addEventListener('input', (e) => {
|
||||||
|
const text = e.target.value;
|
||||||
|
const type = document.getElementById('giep_modal_type_filter')?.value || '';
|
||||||
|
populateModalAssetsList(text, type);
|
||||||
|
renderModalMapFeatures(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('giep_modal_type_filter')?.addEventListener('change', (e) => {
|
||||||
|
const type = e.target.value;
|
||||||
|
const text = document.getElementById('giep_modal_search_input')?.value || '';
|
||||||
|
populateModalAssetsList(text, type);
|
||||||
|
renderModalMapFeatures(false);
|
||||||
|
});
|
||||||
|
|
||||||
document.querySelectorAll('.giep-input').forEach(el => {
|
document.querySelectorAll('.giep-input').forEach(el => {
|
||||||
el.addEventListener('change', updateSerializedGiepData);
|
el.addEventListener('change', updateSerializedGiepData);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<!-- Modale de Saisie Rapide d'Inspection -->
|
<!-- Modale de Saisie Rapide d'Inspection -->
|
||||||
<div class="modal fade" id="quickInspectionModal" tabindex="-1" aria-labelledby="quickInspectionModalLabel" aria-hidden="true">
|
<div class="modal fade" id="quickInspectionModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="quickInspectionModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||||
<div class="modal-content shadow">
|
<div class="modal-content shadow">
|
||||||
<div class="modal-header bg-light">
|
<div class="modal-header bg-light">
|
||||||
|
|
@ -118,7 +118,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal caméra Canvas (mode continu pour inspection) -->
|
<!-- Modal caméra Canvas (mode continu pour inspection) -->
|
||||||
<div class="modal fade" id="inspCameraModal" tabindex="-1" aria-labelledby="inspCameraModalLabel" aria-hidden="true" style="z-index: 1060;">
|
<div class="modal fade" id="inspCameraModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="inspCameraModalLabel" aria-hidden="true" style="z-index: 1060;">
|
||||||
<div class="modal-dialog modal-fullscreen-sm-down d-flex m-0 h-100 mx-auto">
|
<div class="modal-dialog modal-fullscreen-sm-down d-flex m-0 h-100 mx-auto">
|
||||||
<div class="modal-content bg-dark text-white d-flex flex-column">
|
<div class="modal-content bg-dark text-white d-flex flex-column">
|
||||||
<div class="modal-header border-0 flex-shrink-0 py-2">
|
<div class="modal-header border-0 flex-shrink-0 py-2">
|
||||||
|
|
@ -150,7 +150,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modale de Choix d'Asset (Zone dense / Sélection multiple) -->
|
<!-- Modale de Choix d'Asset (Zone dense / Sélection multiple) -->
|
||||||
<div class="modal fade" id="targetChooserModal" tabindex="-1" aria-labelledby="targetChooserModalLabel" aria-hidden="true">
|
<div class="modal fade" id="targetChooserModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="targetChooserModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content shadow">
|
<div class="modal-content shadow">
|
||||||
<div class="modal-header bg-light">
|
<div class="modal-header bg-light">
|
||||||
|
|
|
||||||
|
|
@ -25,3 +25,98 @@ def result_btn_class(status_code):
|
||||||
@register.filter
|
@register.filter
|
||||||
def result_icon(status_code):
|
def result_icon(status_code):
|
||||||
return INSPECTION_RESULT_CONFIG.get(status_code, {}).get('icon', 'bi-question-circle')
|
return INSPECTION_RESULT_CONFIG.get(status_code, {}).get('icon', 'bi-question-circle')
|
||||||
|
|
||||||
|
GIEP_QUESTION_LABELS = {
|
||||||
|
'evc_pe_ok': "L'eau peut-elle facilement entrer dans l'EVC ?",
|
||||||
|
'evc_pe_feuilles': "Des feuilles ou branches bloquent-elles l'eau ?",
|
||||||
|
'evc_pe_detritus': "Un amas de détritus bloque-t-il l'eau ?",
|
||||||
|
'evc_empierrement_present': "Un empierrement est-il présent à l'entrée ?",
|
||||||
|
'evc_empierrement_ok': "Quel est l'état de l'empierrement ?",
|
||||||
|
'evc_empierrement_colmate': "Colmaté par les sédiments",
|
||||||
|
'evc_empierrement_emporte': "Emporté par l'eau",
|
||||||
|
'evc_empierrement_envahi': "Envahi d'adventices",
|
||||||
|
'evc_ev_veg_etat': "Quel est l'état de la végétation ?",
|
||||||
|
'evc_ev_plantes_mortes': "Présence de plantes mortes ou absentes :",
|
||||||
|
'evc_ev_chenaux_erosion': "Présence de chenaux d'érosion ?",
|
||||||
|
'evc_ev_flaques': "Présence de flaques d'eau persistantes ?",
|
||||||
|
'evc_ev_sediments': "Présence de sédiments dans l'ouvrage ?",
|
||||||
|
'evc_ev_croute_sediments': "La croûte de sédimentation est-elle supérieure à 5 cm ?",
|
||||||
|
'evc_ev_envahissantes': "Présence d'espèces exotiques envahissantes ?",
|
||||||
|
'evc_surverse_presente': "Une surverse est-elle présente ?",
|
||||||
|
'evc_sv_grille_propre': "La grille de surverse est propre et dégagée ?",
|
||||||
|
'evc_sv_decantation_vide': "La décantation est vide ou peu remplie ?",
|
||||||
|
'evc_sv_haute': "La surverse est-elle plus haute que le fond de l'ouvrage ?",
|
||||||
|
'oed_revetement_ok': "La surface au-dessus du massif drainant est en bon état ?",
|
||||||
|
'oed_revetement_ornieres': "Présence d'ornières",
|
||||||
|
'oed_revetement_fissures': "Présence de fissures",
|
||||||
|
'oed_revetement_trous': "Présence de trous",
|
||||||
|
'oed_injection_presente': "Une bouche d'injection est-elle présente ?",
|
||||||
|
'oed_bi_niveaux_ok': "Les niveaux du revêtement conduisent bien l'eau vers la bouche ?",
|
||||||
|
'oed_bi_grille_propre': "La grille est propre et dégagée ?",
|
||||||
|
'oed_bi_decantation_vide': "La décantation est-elle vide ou presque ?",
|
||||||
|
'oed_bi_filtre_present': "La bouche d'injection dispose d'un filtre ?",
|
||||||
|
'oed_bi_filtre_propre': "Le filtre est propre ?",
|
||||||
|
'oed_bi_filtre_bon': "Le filtre est en bon état ?",
|
||||||
|
'oed_bi_drain_colmate': "Le drain semble colmaté ?",
|
||||||
|
'oed_rhm_presente': "Structure enherbée ou remplissage minéral ?",
|
||||||
|
'oed_rhm_stagne': "De l'eau stagne-t-elle en surface ?",
|
||||||
|
'oed_rhm_stagne_plus_2h': "L'eau stagne plus de 2 heures ?",
|
||||||
|
'oed_rhm_joints_ok': "Les joints sont-ils en bon état ?",
|
||||||
|
'oed_rhm_joints_fines': "Présence importante de fines dans les joints ?",
|
||||||
|
'oed_rhm_joints_remplissage': "Niveau de remplissage plus haut que la structure ?",
|
||||||
|
'oed_rp_presente': "Modulaire poreux ou continu poreux ?",
|
||||||
|
'oed_rp_stagne': "De l'eau stagne-t-elle en surface ?",
|
||||||
|
'oed_rp_stagne_plus_2h': "L'eau stagne plus de 2 heures ?",
|
||||||
|
'oed_rp_depots': "Présence de dépôts visibles (poussières, mousses, etc.) ?",
|
||||||
|
'oed_rp_test_permeabilite': "Test de perméabilité nécessaire ?",
|
||||||
|
'oed_rna_presente': "Matériaux non agrégés (graviers/mulch) ?",
|
||||||
|
'oed_rna_stagne': "De l'eau stagne-t-elle en surface ?",
|
||||||
|
'oed_rna_stagne_plus_2h': "L'eau stagne plus de 2 heures ?",
|
||||||
|
'oed_rna_matiere_ok': "Le volume de matière est-il en suffisance ?",
|
||||||
|
'oed_rna_geotextile_visible': "Le géotextile est-il visible ?",
|
||||||
|
'ta_pe_ok': "L'eau peut-elle facilement entrer dans l'ouvrage ?",
|
||||||
|
'ta_pe_conduits_ok': "Les conduits et dispositifs amenant l'eau sont-ils fonctionnels ?",
|
||||||
|
'ta_pe_grilles': "Les grilles sont-elles propres et dégagées ?",
|
||||||
|
'ta_pe_feuilles': "Des feuilles ou branches bloquent-elles l'eau ?",
|
||||||
|
'ta_pe_detritus': "Un amas de détritus bloque-t-il l'eau ?",
|
||||||
|
'ta_bo_vegetation_etat': "Quel est l'état de la végétation ?",
|
||||||
|
'ta_bo_plantes_mortes': "Présence de plantes mortes ou absentes :",
|
||||||
|
'ta_bo_chenaux': "Présence de chenaux d'érosion ?",
|
||||||
|
'ta_bo_flaques': "Présence de flaques d'eau persistantes ?",
|
||||||
|
'ta_bo_sediments': "Présence de sédiments dans le bassin d'orage ?",
|
||||||
|
'ta_bo_rempli_cinquieme': "L'ouvrage est-il rempli au 5e de sa capacité ?",
|
||||||
|
'ta_bo_detritus': "Présence de détritus dans l'ouvrage ?"
|
||||||
|
}
|
||||||
|
|
||||||
|
GIEP_VALUE_LABELS = {
|
||||||
|
'true': "Oui",
|
||||||
|
'false': "Non",
|
||||||
|
'null': "Non vérifié",
|
||||||
|
'bon': "Bon",
|
||||||
|
'mauvais': "Mauvais",
|
||||||
|
'plus_10': "+ de 10%",
|
||||||
|
'moins_10': "- de 10%",
|
||||||
|
}
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def format_giep_q_label(key):
|
||||||
|
return GIEP_QUESTION_LABELS.get(str(key), str(key).replace('_', ' ').capitalize())
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def format_giep_q_val(val):
|
||||||
|
if val is True:
|
||||||
|
return "Oui"
|
||||||
|
if val is False:
|
||||||
|
return "Non"
|
||||||
|
if val is None or val == 'null':
|
||||||
|
return "Non vérifié"
|
||||||
|
return GIEP_VALUE_LABELS.get(str(val).lower(), str(val))
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def get_q_val(questions_dict, key):
|
||||||
|
if not isinstance(questions_dict, dict):
|
||||||
|
return 'null'
|
||||||
|
val = questions_dict.get(str(key))
|
||||||
|
if val is None or val == '' or val == 'null':
|
||||||
|
return 'null'
|
||||||
|
return val
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,21 @@ class InspectionModelAndViewsTestCase(TestCase):
|
||||||
first_name='Jean',
|
first_name='Jean',
|
||||||
last_name='Valjean'
|
last_name='Valjean'
|
||||||
)
|
)
|
||||||
|
from common.models import UserConfig, Role, UserThematics
|
||||||
|
admin_role, _ = Role.objects.get_or_create(name='admin')
|
||||||
|
config = UserConfig.objects.create(user=self.user)
|
||||||
|
config.roles.add(admin_role)
|
||||||
|
|
||||||
|
self.water_thematic, _ = Thematic.objects.get_or_create(
|
||||||
|
code='water',
|
||||||
|
defaults={'name_fr': 'Gestion de l\'Eau / GIEP'}
|
||||||
|
)
|
||||||
|
UserThematics.objects.create(
|
||||||
|
user_config=config,
|
||||||
|
thematic=self.water_thematic,
|
||||||
|
can_view_assets=True,
|
||||||
|
can_edit_assets=True
|
||||||
|
)
|
||||||
|
|
||||||
self.thematic, _ = Thematic.objects.get_or_create(
|
self.thematic, _ = Thematic.objects.get_or_create(
|
||||||
code='parking',
|
code='parking',
|
||||||
|
|
@ -142,7 +157,7 @@ class InspectionModelAndViewsTestCase(TestCase):
|
||||||
tmpl_rwiz = get_inspection_partial_template('naturerwiz')
|
tmpl_rwiz = get_inspection_partial_template('naturerwiz')
|
||||||
tmpl_asset = get_inspection_partial_template('naturerwiasset')
|
tmpl_asset = get_inspection_partial_template('naturerwiasset')
|
||||||
self.assertEqual(tmpl_rwiz, 'inspections/components/asset_fields/_naturerwiz.html')
|
self.assertEqual(tmpl_rwiz, 'inspections/components/asset_fields/_naturerwiz.html')
|
||||||
self.assertEqual(tmpl_asset, 'inspections/components/asset_fields/_naturerwiz.html')
|
self.assertIsNone(tmpl_asset)
|
||||||
|
|
||||||
def test_registry_detail_templates(self):
|
def test_registry_detail_templates(self):
|
||||||
from .registry import get_inspection_detail_partial_template
|
from .registry import get_inspection_detail_partial_template
|
||||||
|
|
@ -155,3 +170,27 @@ class InspectionModelAndViewsTestCase(TestCase):
|
||||||
self.assertEqual(tmpl_giep, 'inspections/components/asset_details/_naturerwiz.html')
|
self.assertEqual(tmpl_giep, 'inspections/components/asset_details/_naturerwiz.html')
|
||||||
self.assertEqual(tmpl_parking, 'inspections/components/asset_details/_parkingspot.html')
|
self.assertEqual(tmpl_parking, 'inspections/components/asset_details/_parkingspot.html')
|
||||||
|
|
||||||
|
def test_zirw_detail_view_renders_cleanly(self):
|
||||||
|
self.client.login(username='inspector_user', password='password123')
|
||||||
|
url = reverse('assets:zirw_detail', kwargs={'location_id': self.rwiz.id})
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_spatial_location_resolution_for_rwi_asset(self):
|
||||||
|
from assets.templatetags.asset_tags import asset_location_link
|
||||||
|
from django.contrib.gis.geos import Polygon, MultiPolygon, Point
|
||||||
|
poly = Polygon(((0, 0), (0, 10), (10, 10), (10, 0), (0, 0)), srid=3812)
|
||||||
|
rwiz_poly = NatureRWIZ.objects.create(
|
||||||
|
code="RWIZ-TEST",
|
||||||
|
name_fr="Zone Test Polygons",
|
||||||
|
geom=MultiPolygon(poly, srid=3812)
|
||||||
|
)
|
||||||
|
rwi_asset = NatureRWIAsset.objects.create(
|
||||||
|
code="RWI-TEST-01",
|
||||||
|
geom=Point(5, 5, srid=3812)
|
||||||
|
)
|
||||||
|
ctx = asset_location_link({'request': None}, rwi_asset)
|
||||||
|
self.assertEqual(ctx['location'], rwiz_poly)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,7 @@ def create_inspection_api(request):
|
||||||
return JsonResponse({'success': False, 'error': _("Cible introuvable : %s") % str(e)}, status=404)
|
return JsonResponse({'success': False, 'error': _("Cible introuvable : %s") % str(e)}, status=404)
|
||||||
|
|
||||||
is_parking_spot = (content_type.model == 'parkingspot')
|
is_parking_spot = (content_type.model == 'parkingspot')
|
||||||
is_giep = (content_type.model in ('naturerwiz', 'naturerwiasset'))
|
is_giep = (content_type.model == 'naturerwiz')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if is_parking_spot:
|
if is_parking_spot:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue