From fe535683a1d2d840e5612b813597546433f69758 Mon Sep 17 00:00:00 2001 From: kdeterme Date: Mon, 14 Sep 2026 12:15:48 +0200 Subject: [PATCH] feat: implement automated status calculation and unit tests for GIEP inspections --- loko/inspections/models.py | 142 ++++++++ .../components/asset_details/_naturerwiz.html | 15 + .../components/asset_fields/_naturerwiz.html | 311 ++++++++++++++++-- loko/inspections/tests.py | 191 +++++++++++ 4 files changed, 623 insertions(+), 36 deletions(-) diff --git a/loko/inspections/models.py b/loko/inspections/models.py index fbc8fe6..dbf6c62 100644 --- a/loko/inspections/models.py +++ b/loko/inspections/models.py @@ -545,6 +545,148 @@ class GiepInspection(Inspection): verbose_name = _("Inspection GIEP / Eaux pluviales") verbose_name_plural = _("Inspections GIEP / Eaux pluviales") + @classmethod + def compute_status_from_data(cls, data): + """ + Calcule le résultat global ('damaged', 'non_compliant', 'compliant') + sur base des réponses aux questions de chaque ouvrage d'une inspection GIEP. + Priorité : damaged > non_compliant > compliant. + """ + if not data or not isinstance(data, dict): + return 'compliant' + ouvrages = data.get('ouvrages', []) + if not isinstance(ouvrages, list) or not ouvrages: + return 'compliant' + + has_damaged = False + has_non_compliant = False + + for ouv in ouvrages: + if not isinstance(ouv, dict): + continue + device_type = (ouv.get('device_type') or 'evc').lower() + q = ouv.get('questions') or {} + if not isinstance(q, dict): + continue + + def is_val(key, *expected): + v = q.get(key) + if isinstance(v, bool): + v_str = 'true' if v else 'false' + elif v is None: + v_str = 'null' + else: + v_str = str(v).strip().lower() + return v_str in [str(e).strip().lower() for e in expected] + + if device_type == 'evc': + # 1. Point d'entrée + if is_val('evc_pe_ok', 'false', 'non'): + has_non_compliant = True + + # 2. Empierrement + if is_val('evc_empierrement_present', True, 'true'): + if is_val('evc_empierrement_ok', 'false', 'non'): + has_non_compliant = True + if is_val('evc_empierrement_emporte', True, 'true'): + has_damaged = True + + # 3. Végétation + if is_val('evc_ev_veg_etat', 'mauvais'): + has_non_compliant = True + if is_val('evc_ev_plantes_mortes', 'plus_10', '+ de 10%'): + has_damaged = True + + # 4. Chenaux d'érosion + if is_val('evc_ev_chenaux_erosion', 'true', 'oui', True): + has_non_compliant = True + + # 5. Flaques d'eau + if is_val('evc_ev_flaques', 'true', 'oui', True): + has_non_compliant = True + + # 6. Sédiments + if is_val('evc_ev_sediments', 'true', 'oui', True): + has_non_compliant = True + if is_val('evc_ev_croute_sediments', 'true', 'oui', True): + has_damaged = True + + # 7. Espèces envahissantes + if is_val('evc_ev_envahissantes', 'true', 'oui', True): + has_non_compliant = True + + # 8. Surverse + if is_val('evc_surverse_presente', True, 'true'): + if is_val('evc_sv_grille_propre', 'false', 'non'): + has_non_compliant = True + if is_val('evc_sv_decantation_vide', 'false', 'non'): + has_non_compliant = True + if is_val('evc_sv_haute', 'false', 'non'): + has_damaged = True + + elif device_type == 'oed': + # 1. Revêtement général + if is_val('oed_revetement_ok', 'false', 'non'): + has_non_compliant = True + + # 2. Bouche d'injection + if is_val('oed_injection_presente', True, 'true'): + if is_val('oed_bi_niveaux_ok', 'false', 'non'): + has_damaged = True + if is_val('oed_bi_grille_propre', 'false', 'non'): + has_non_compliant = True + if is_val('oed_bi_decantation_vide', 'false', 'non'): + has_damaged = True + if is_val('oed_bi_drain_colmate', 'true', 'oui', True): + has_damaged = True + + # 3. Structure enherbée / minérale + if is_val('oed_rhm_presente', True, 'true'): + if is_val('oed_rhm_stagne', 'true', 'oui', True): + has_non_compliant = True + if is_val('oed_rhm_joints_ok', 'false', 'non'): + has_non_compliant = True + if is_val('oed_rhm_joints_fines', 'true', 'oui', True): + has_damaged = True + if is_val('oed_rhm_joints_remplissage', 'true', 'oui', True): + has_non_compliant = True + + # 4. Modulaire poreux / continu poreux + if is_val('oed_rp_presente', True, 'true'): + if is_val('oed_rp_stagne', 'true', 'oui', True): + has_non_compliant = True + if is_val('oed_rp_depots', 'true', 'oui', True): + has_non_compliant = True + + elif device_type == 'ta': + # 1. Point d'entrée + if is_val('ta_pe_ok', 'false', 'non'): + has_non_compliant = True + if is_val('ta_pe_conduits_ok', 'false', 'non'): + has_damaged = True + if is_val('ta_pe_decantation_vide', 'false', 'non'): + has_non_compliant = True + + # 2. Bassin d'orage + if is_val('ta_bo_vegetation_etat', 'mauvais'): + has_non_compliant = True + if is_val('ta_bo_plantes_mortes', 'plus_10', '+ de 10%'): + has_damaged = True + if is_val('ta_bo_chenaux', 'true', 'oui', True): + has_non_compliant = True + if is_val('ta_sediments', 'true', 'oui', True): + has_non_compliant = True + if is_val('ta_sediments_rempli', 'true', 'oui', True): + has_damaged = True + if is_val('ta_bo_envahissantes', 'true', 'oui', True): + has_non_compliant = True + + if has_damaged: + return 'damaged' + if has_non_compliant: + return 'non_compliant' + return 'compliant' + class SignPanelInspection(Inspection): """ diff --git a/loko/inspections/templates/inspections/components/asset_details/_naturerwiz.html b/loko/inspections/templates/inspections/components/asset_details/_naturerwiz.html index 3e99659..f58c8d0 100644 --- a/loko/inspections/templates/inspections/components/asset_details/_naturerwiz.html +++ b/loko/inspections/templates/inspections/components/asset_details/_naturerwiz.html @@ -97,21 +97,25 @@ {% endwith %} {% with val=q|get_q_val:'evc_pe_feuilles' %} + {% if val != 'null' and val is not None %}
{% translate "Des feuilles ou branches bloquent-elles l'eau ?" %} {% 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 %}
+ {% endif %} {% endwith %} {% with val=q|get_q_val:'evc_pe_detritus' %} + {% if val != 'null' and val is not None %}
{% translate "Un amas de détritus bloque-t-il l'eau ?" %} {% 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 %}
+ {% endif %} {% endwith %} {% with val=q|get_q_val:'evc_empierrement_present' %} @@ -283,6 +287,17 @@ {% endwith %} + + {% with val=q|get_q_val:'ta_pe_decantation_vide' %} + {% if val != 'null' and val is not None %} +
+ {% translate "La décantation est-elle vide ou presque ?" %} + + {% 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 %} + +
+ {% endif %} + {% endwith %} diff --git a/loko/inspections/templates/inspections/components/asset_fields/_naturerwiz.html b/loko/inspections/templates/inspections/components/asset_fields/_naturerwiz.html index 0f39012..18c7afa 100644 --- a/loko/inspections/templates/inspections/components/asset_fields/_naturerwiz.html +++ b/loko/inspections/templates/inspections/components/asset_fields/_naturerwiz.html @@ -15,6 +15,9 @@ + +
+
@@ -172,8 +175,6 @@ questions: { // EVC evc_pe_ok: null, - evc_pe_feuilles: null, - evc_pe_detritus: null, evc_empierrement_present: false, evc_empierrement_ok: null, evc_empierrement_colmate: false, @@ -200,9 +201,6 @@ oed_bi_niveaux_ok: null, oed_bi_grille_propre: null, oed_bi_decantation_vide: null, - oed_bi_filtre_present: null, - oed_bi_filtre_propre: null, - oed_bi_filtre_bon: null, oed_bi_drain_colmate: null, oed_rhm_presente: false, oed_rhm_stagne: null, @@ -224,16 +222,12 @@ // TA ta_pe_ok: null, ta_pe_conduits_ok: null, - ta_pe_grilles: null, - ta_pe_feuilles: null, - ta_pe_detritus: null, + ta_pe_decantation_vide: null, ta_bo_vegetation_etat: null, ta_bo_plantes_mortes: null, ta_bo_chenaux: null, - ta_bo_flaques: null, ta_sediments: null, ta_sediments_rempli: null, - ta_bo_detritus: null, ta_bo_envahissantes: null }, actions: {} @@ -527,8 +521,6 @@ // et "Non" représente un bon fonctionnement (absence de défaut). const GIEP_DEFECT_KEYS = new Set([ // 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_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 ? @@ -548,13 +540,9 @@ 'oed_rna_stagne_plus_2h', // L'eau stagne plus de 2 heures ? 'oed_rna_geotextile_visible', // Le géotextile est-il visible ? // TA - '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_chenaux', // Présence de chenaux d'érosion ? - 'ta_bo_flaques', // Présence de flaques d'eau persistantes ? 'ta_sediments', // Présence de sédiments dans l'ouvrage ? 'ta_sediments_rempli', // L'ouvrage est-il rempli au 5e de sa capacité ? - 'ta_bo_detritus', // Présence de détritus dans l'ouvrage ? 'ta_bo_envahissantes' // Présence d'espèces exotiques envahissantes ? ]); @@ -603,6 +591,265 @@ `; } + // Gestion du statut calculé et surcharge manuelle + let isGiepStatusManuallyOverridden = false; + + function getStatusLabel(status) { + if (status === 'damaged') return '{% filter escapejs %}{% translate "Endommagé" %}{% endfilter %}'; + if (status === 'non_compliant') return '{% filter escapejs %}{% translate "Non conforme" %}{% endfilter %}'; + return '{% filter escapejs %}{% translate "Conforme" %}{% endfilter %}'; + } + + function evaluateGiepInspectionStatus(ouvrages) { + if (!Array.isArray(ouvrages) || ouvrages.length === 0) { + return 'compliant'; + } + + let hasDamaged = false; + let hasNonCompliant = false; + + for (const ouv of ouvrages) { + if (!ouv) continue; + const deviceType = (ouv.device_type || 'evc').toLowerCase(); + const q = ouv.questions || {}; + + const isVal = (key, ...expected) => { + const v = q[key]; + if (v === undefined || v === null) return expected.includes('null'); + const vStr = String(v).trim().toLowerCase(); + return expected.map(e => String(e).trim().toLowerCase()).includes(vStr); + }; + + if (deviceType === 'evc') { + // 1. Point d'entrée + if (isVal('evc_pe_ok', 'false', 'non')) { + hasNonCompliant = true; + } + + // 2. Empierrement + if (isVal('evc_empierrement_present', true, 'true')) { + if (isVal('evc_empierrement_ok', 'false', 'non')) { + hasNonCompliant = true; + if (isVal('evc_empierrement_emporte', true, 'true')) { + hasDamaged = true; + } + } + } + + // 3. Végétation + if (isVal('evc_ev_veg_etat', 'mauvais')) { + hasNonCompliant = true; + if (isVal('evc_ev_plantes_mortes', 'plus_10', '+ de 10%')) { + hasDamaged = true; + } + } + + // 4. Chenaux d'érosion + if (isVal('evc_ev_chenaux_erosion', true, 'true', 'oui')) { + hasNonCompliant = true; + } + + // 5. Flaques + if (isVal('evc_ev_flaques', true, 'true', 'oui')) { + hasNonCompliant = true; + } + + // 6. Sédiments + if (isVal('evc_ev_sediments', true, 'true', 'oui')) { + hasNonCompliant = true; + if (isVal('evc_ev_croute_sediments', true, 'true', 'oui')) { + hasDamaged = true; + } + } + + // 7. Espèces envahissantes + if (isVal('evc_ev_envahissantes', true, 'true', 'oui')) { + hasNonCompliant = true; + } + + // 8. Surverse + if (isVal('evc_surverse_presente', true, 'true')) { + if (isVal('evc_sv_grille_propre', 'false', 'non')) { + hasNonCompliant = true; + } + if (isVal('evc_sv_decantation_vide', 'false', 'non')) { + hasNonCompliant = true; + } + if (isVal('evc_sv_haute', 'false', 'non')) { + hasDamaged = true; + } + } + + } else if (deviceType === 'oed') { + // 1. Revêtement + if (isVal('oed_revetement_ok', 'false', 'non')) { + hasNonCompliant = true; + } + + // 2. Bouche d'injection + if (isVal('oed_injection_presente', true, 'true')) { + if (isVal('oed_bi_niveaux_ok', 'false', 'non')) { + hasDamaged = true; + } + if (isVal('oed_bi_grille_propre', 'false', 'non')) { + hasNonCompliant = true; + } + if (isVal('oed_bi_decantation_vide', 'false', 'non')) { + hasDamaged = true; + } + if (isVal('oed_bi_drain_colmate', true, 'true', 'oui')) { + hasDamaged = true; + } + } + + // 3. Structure enherbée / minérale + if (isVal('oed_rhm_presente', true, 'true')) { + if (isVal('oed_rhm_stagne', true, 'true', 'oui')) { + hasNonCompliant = true; + } + if (isVal('oed_rhm_joints_ok', 'false', 'non')) { + hasNonCompliant = true; + if (isVal('oed_rhm_joints_fines', true, 'true', 'oui')) { + hasDamaged = true; + } + if (isVal('oed_rhm_joints_remplissage', true, 'true', 'oui')) { + hasNonCompliant = true; + } + } + } + + // 4. Modulaire poreux / continu poreux + if (isVal('oed_rp_presente', true, 'true')) { + if (isVal('oed_rp_stagne', true, 'true', 'oui')) { + hasNonCompliant = true; + } + if (isVal('oed_rp_depots', true, 'true', 'oui')) { + hasNonCompliant = true; + } + } + + } else if (deviceType === 'ta') { + // 1. Point d'entrée + if (isVal('ta_pe_ok', 'false', 'non')) { + hasNonCompliant = true; + } + if (isVal('ta_pe_conduits_ok', 'false', 'non')) { + hasDamaged = true; + } + if (isVal('ta_pe_decantation_vide', 'false', 'non')) { + hasNonCompliant = true; + } + + // 2. Bassin d'orage + if (isVal('ta_bo_vegetation_etat', 'mauvais')) { + hasNonCompliant = true; + if (isVal('ta_bo_plantes_mortes', 'plus_10', '+ de 10%')) { + hasDamaged = true; + } + } + if (isVal('ta_bo_chenaux', true, 'true', 'oui')) { + hasNonCompliant = true; + } + if (isVal('ta_sediments', true, 'true', 'oui')) { + hasNonCompliant = true; + if (isVal('ta_sediments_rempli', true, 'true', 'oui')) { + hasDamaged = true; + } + } + if (isVal('ta_bo_envahissantes', true, 'true', 'oui')) { + hasNonCompliant = true; + } + } + } + + if (hasDamaged) return 'damaged'; + if (hasNonCompliant) return 'non_compliant'; + return 'compliant'; + } + + function getAllStatusRadios() { + return document.querySelectorAll('input[type="radio"][name="result_status"]'); + } + + function getActiveStatusRadio(status) { + const editRad = document.getElementById('edit_res_' + status); + if (editRad && editRad.closest('.modal.show, form')) return editRad; + const createRad = document.getElementById('res_' + status); + if (createRad && createRad.closest('.modal.show, form')) return createRad; + return createRad || editRad || null; + } + + function syncGiepCalculatedStatus() { + initStatusRadioListeners(); + const calculated = evaluateGiepInspectionStatus(giepOuvrages); + const banner = document.getElementById('giep_status_auto_banner'); + + if (!isGiepStatusManuallyOverridden) { + const rad = getActiveStatusRadio(calculated); + if (rad && !rad.checked) { + rad.checked = true; + rad.dispatchEvent(new Event('change', { bubbles: true })); + } + } + + if (banner) { + banner.classList.remove('d-none'); + const calcLabel = getStatusLabel(calculated); + let badgeClass = 'bg-success'; + if (calculated === 'damaged') badgeClass = 'bg-danger'; + else if (calculated === 'non_compliant') badgeClass = 'bg-warning text-dark'; + + if (isGiepStatusManuallyOverridden) { + let checkedVal = ''; + getAllStatusRadios().forEach(r => { if (r.checked) checkedVal = r.value; }); + const currentLabel = getStatusLabel(checkedVal); + + banner.className = 'alert alert-warning border border-warning-subtle rounded-3 p-2.5 mb-3 shadow-sm d-flex align-items-center justify-content-between flex-wrap gap-2'; + banner.innerHTML = ` +
+ + {% filter escapejs %}{% translate "Statut modifié manuellement" %}{% endfilter %} : + ${currentLabel} + ({% filter escapejs %}{% translate "Calculé" %}{% endfilter %} : ${calcLabel}) +
+ + `; + document.getElementById('btn_reset_giep_auto_status')?.addEventListener('click', () => { + isGiepStatusManuallyOverridden = false; + syncGiepCalculatedStatus(); + }); + } else { + banner.className = 'alert alert-light border border-info-subtle bg-info-subtle text-info-emphasis rounded-3 p-2.5 mb-3 shadow-sm d-flex align-items-center justify-content-between flex-wrap gap-2'; + banner.innerHTML = ` +
+ + {% filter escapejs %}{% translate "Statut calculé automatiquement" %}{% endfilter %} : + ${calcLabel} +
+ `; + } + } + } + + function initStatusRadioListeners() { + getAllStatusRadios().forEach(radio => { + if (radio.dataset.giepListenerAttached) return; + radio.dataset.giepListenerAttached = 'true'; + + radio.addEventListener('click', () => { + const calculated = evaluateGiepInspectionStatus(giepOuvrages); + if (radio.value !== calculated) { + isGiepStatusManuallyOverridden = true; + } else { + isGiepStatusManuallyOverridden = false; + } + syncGiepCalculatedStatus(); + }); + }); + } + function populateModalAssetsList(filterText = '', filterType = '') { // Obsolete list, filtering operates directly on map layers } @@ -878,11 +1125,6 @@
{% translate "Point d'entrée dans l'ouvrage" %}
${renderSegGroup(idx, 'evc_pe_ok', '{% filter escapejs %}{% translate "L'eau peut-elle facilement entrer dans l'EVC ?" %}{% endfilter %}', q.evc_pe_ok)} -
- ${renderSegGroup(idx, 'evc_pe_feuilles', '{% filter escapejs %}{% translate "Des feuilles ou branches bloquent-elles l'eau ?" %}{% endfilter %}', q.evc_pe_feuilles, true)} - ${renderSegGroup(idx, 'evc_pe_detritus', '{% filter escapejs %}{% translate "Un amas de détritus bloque-t-il l'eau ?" %}{% endfilter %}', q.evc_pe_detritus, true)} -
-
@@ -998,13 +1240,6 @@ ${renderSegGroup(idx, 'oed_bi_niveaux_ok', '{% filter escapejs %}{% translate "Les niveaux du revêtement conduisent bien l'eau vers la bouche ?" %}{% endfilter %}', q.oed_bi_niveaux_ok)} ${renderSegGroup(idx, 'oed_bi_grille_propre', '{% filter escapejs %}{% translate "La grille est propre et dégagée ?" %}{% endfilter %}', q.oed_bi_grille_propre)} ${renderSegGroup(idx, 'oed_bi_decantation_vide', '{% filter escapejs %}{% translate "La décantation est-elle vide ou presque ?" %}{% endfilter %}', q.oed_bi_decantation_vide)} - ${renderSegGroup(idx, 'oed_bi_filtre_present', '{% filter escapejs %}{% translate "La bouche d'injection dispose d'un filtre ?" %}{% endfilter %}', q.oed_bi_filtre_present)} - -
- ${renderSegGroup(idx, 'oed_bi_filtre_propre', '{% filter escapejs %}{% translate "Le filtre est propre ?" %}{% endfilter %}', q.oed_bi_filtre_propre)} - ${renderSegGroup(idx, 'oed_bi_filtre_bon', '{% filter escapejs %}{% translate "Le filtre est en bon état ?" %}{% endfilter %}', q.oed_bi_filtre_bon)} -
- ${renderSegGroup(idx, 'oed_bi_drain_colmate', '{% filter escapejs %}{% translate "Le drain semble colmaté ?" %}{% endfilter %}', q.oed_bi_drain_colmate, true)}
@@ -1091,12 +1326,7 @@
{% translate "Points d'entrée dans l'ouvrage" %}
${renderSegGroup(idx, 'ta_pe_ok', '{% filter escapejs %}{% translate "L'eau peut-elle facilement entrer dans l'ouvrage ?" %}{% endfilter %}', q.ta_pe_ok)} ${renderSegGroup(idx, 'ta_pe_conduits_ok', '{% filter escapejs %}{% translate "Les conduits et dispositifs amenant l'eau sont-ils fonctionnels ?" %}{% endfilter %}', q.ta_pe_conduits_ok)} - -
- ${renderSegGroup(idx, 'ta_pe_grilles', '{% filter escapejs %}{% translate "Les grilles sont-elles propres et dégagées ?" %}{% endfilter %}', q.ta_pe_grilles)} - ${renderSegGroup(idx, 'ta_pe_feuilles', '{% filter escapejs %}{% translate "Des feuilles ou branches bloquent-elles l'eau ?" %}{% endfilter %}', q.ta_pe_feuilles, true)} - ${renderSegGroup(idx, 'ta_pe_detritus', '{% filter escapejs %}{% translate "Un amas de détritus bloque-t-il l'eau ?" %}{% endfilter %}', q.ta_pe_detritus, true)} -
+ ${renderSegGroup(idx, 'ta_pe_decantation_vide', '{% filter escapejs %}{% translate "La décantation est-elle vide ou presque ?" %}{% endfilter %}', q.ta_pe_decantation_vide)}
@@ -1118,14 +1348,12 @@ ${renderSegGroup(idx, 'ta_bo_chenaux', '{% filter escapejs %}{% translate "Présence de chenaux d'érosion ?" %}{% endfilter %}', q.ta_bo_chenaux, true)} - ${renderSegGroup(idx, 'ta_bo_flaques', '{% filter escapejs %}{% translate "Présence de flaques d'eau persistantes ?" %}{% endfilter %}', q.ta_bo_flaques, true)} ${renderSegGroup(idx, 'ta_sediments', '{% filter escapejs %}{% translate "Présence de sédiments dans l'ouvrage ?" %}{% endfilter %}', q.ta_sediments, true)}
${renderSegGroup(idx, 'ta_sediments_rempli', '{% filter escapejs %}{% translate "L'ouvrage est-il rempli au 5e de sa capacité ?" %}{% endfilter %}', q.ta_sediments_rempli, true)}
- ${renderSegGroup(idx, 'ta_bo_detritus', '{% filter escapejs %}{% translate "Présence de détritus dans l'ouvrage ?" %}{% endfilter %}', q.ta_bo_detritus, true)} ${renderSegGroup(idx, 'ta_bo_envahissantes', '{% filter escapejs %}{% translate "Présence d'espèces exotiques envahissantes ?" %}{% endfilter %}', q.ta_bo_envahissantes, true)} `; @@ -1341,6 +1569,7 @@ }); updateSerializedGiepData(); + syncGiepCalculatedStatus(); } if (addBtn) { @@ -1395,10 +1624,20 @@ } else { giepOuvrages = []; } + + // Si le statut existant diffère du statut calculé automatiquement, marquer comme surchargé + const autoStatus = evaluateGiepInspectionStatus(giepOuvrages); + if (data.result_status && data.result_status !== autoStatus) { + isGiepStatusManuallyOverridden = true; + } else { + isGiepStatusManuallyOverridden = false; + } + renderOuvrages(); updateSerializedGiepData(); }; + initStatusRadioListeners(); renderOuvrages(); })(); diff --git a/loko/inspections/tests.py b/loko/inspections/tests.py index 26b8658..124f736 100644 --- a/loko/inspections/tests.py +++ b/loko/inspections/tests.py @@ -1276,3 +1276,194 @@ class ViewerInspectionPermissionsAndGeojsonTestCase(TestCase): # (O(1) queries instead of O(N), unaffected by inspection count) self.assertLessEqual(len(ctx), 28) + +class GiepInspectionStatusCalculationTestCase(TestCase): + def test_empty_or_none_data(self): + self.assertEqual(GiepInspection.compute_status_from_data(None), 'compliant') + self.assertEqual(GiepInspection.compute_status_from_data({}), 'compliant') + self.assertEqual(GiepInspection.compute_status_from_data({'ouvrages': []}), 'compliant') + + def test_evc_compliant(self): + data = { + 'ouvrages': [{ + 'device_type': 'evc', + 'questions': { + 'evc_pe_ok': 'true', + 'evc_empierrement_present': False, + 'evc_ev_veg_etat': 'bon', + 'evc_ev_chenaux_erosion': 'false', + 'evc_ev_flaques': 'false', + 'evc_ev_sediments': 'false', + 'evc_ev_envahissantes': 'false', + 'evc_surverse_presente': False + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data), 'compliant') + + def test_evc_non_compliant(self): + data = { + 'ouvrages': [{ + 'device_type': 'evc', + 'questions': { + 'evc_pe_ok': 'false', + 'evc_empierrement_present': False + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data), 'non_compliant') + + def test_evc_damaged_empierrement(self): + data = { + 'ouvrages': [{ + 'device_type': 'evc', + 'questions': { + 'evc_pe_ok': 'true', + 'evc_empierrement_present': True, + 'evc_empierrement_ok': 'false', + 'evc_empierrement_emporte': True + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data), 'damaged') + + def test_evc_damaged_vegetation_mortes(self): + data = { + 'ouvrages': [{ + 'device_type': 'evc', + 'questions': { + 'evc_ev_veg_etat': 'mauvais', + 'evc_ev_plantes_mortes': 'plus_10' + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data), 'damaged') + + def test_evc_damaged_croute_sediments(self): + data = { + 'ouvrages': [{ + 'device_type': 'evc', + 'questions': { + 'evc_ev_sediments': 'true', + 'evc_ev_croute_sediments': 'true' + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data), 'damaged') + + def test_evc_damaged_surverse_haute(self): + data = { + 'ouvrages': [{ + 'device_type': 'evc', + 'questions': { + 'evc_surverse_presente': True, + 'evc_sv_haute': 'false' + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data), 'damaged') + + def test_oed_non_compliant(self): + data = { + 'ouvrages': [{ + 'device_type': 'oed', + 'questions': { + 'oed_revetement_ok': 'false' + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data), 'non_compliant') + + def test_oed_damaged_drain_colmate(self): + data = { + 'ouvrages': [{ + 'device_type': 'oed', + 'questions': { + 'oed_injection_presente': True, + 'oed_bi_drain_colmate': 'true' + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data), 'damaged') + + def test_oed_damaged_joints_fines(self): + data = { + 'ouvrages': [{ + 'device_type': 'oed', + 'questions': { + 'oed_rhm_presente': True, + 'oed_rhm_joints_ok': 'false', + 'oed_rhm_joints_fines': 'true' + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data), 'damaged') + + def test_ta_non_compliant_and_damaged(self): + # TA non conforme: point d'entrée non ok + data_nc = { + 'ouvrages': [{ + 'device_type': 'ta', + 'questions': { + 'ta_pe_ok': 'false' + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data_nc), 'non_compliant') + + # TA endommagé: conduits non fonctionnels + data_dmg = { + 'ouvrages': [{ + 'device_type': 'ta', + 'questions': { + 'ta_pe_ok': 'true', + 'ta_pe_conduits_ok': 'false' + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data_dmg), 'damaged') + + # TA endommagé: capacité remplie au 5e + data_rempli = { + 'ouvrages': [{ + 'device_type': 'ta', + 'questions': { + 'ta_sediments': 'true', + 'ta_sediments_rempli': 'true' + } + }] + } + self.assertEqual(GiepInspection.compute_status_from_data(data_rempli), 'damaged') + + def test_multi_ouvrages_priority(self): + # Ouvrage 1: compliant, Ouvrage 2: non_compliant -> overall non_compliant + data_mix1 = { + 'ouvrages': [ + { + 'device_type': 'evc', + 'questions': {'evc_pe_ok': 'true'} + }, + { + 'device_type': 'oed', + 'questions': {'oed_revetement_ok': 'false'} + } + ] + } + self.assertEqual(GiepInspection.compute_status_from_data(data_mix1), 'non_compliant') + + # Ouvrage 1: non_compliant, Ouvrage 2: damaged -> overall damaged (priority) + data_mix2 = { + 'ouvrages': [ + { + 'device_type': 'evc', + 'questions': {'evc_pe_ok': 'false'} # non_compliant + }, + { + 'device_type': 'ta', + 'questions': {'ta_pe_conduits_ok': 'false'} # damaged + } + ] + } + self.assertEqual(GiepInspection.compute_status_from_data(data_mix2), 'damaged') + +