From af411b21054c90945b1fc95bfb8f1da4102cab3e Mon Sep 17 00:00:00 2001 From: kdeterme Date: Sat, 10 Oct 2026 14:09:15 +0200 Subject: [PATCH] feat(nature): create timeline planning events and display due date on asset interventions - Create InterventionPlanificationTimeLine events (expected_begin and expected_end) during Fulcrum tree interventions import and maintenance dates synchronization - Display planned/completion date instead of creation_time in asset_interventions.html - Add unit test assertions for planning timeline events --- .../commands/import_fulcrum_trees.py | 68 +++++++++++++++++++ .../components/asset_interventions.html | 4 +- loko/assets/tests_import_fulcrum.py | 9 +++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/loko/assets/management/commands/import_fulcrum_trees.py b/loko/assets/management/commands/import_fulcrum_trees.py index e69a3c0..295eae0 100644 --- a/loko/assets/management/commands/import_fulcrum_trees.py +++ b/loko/assets/management/commands/import_fulcrum_trees.py @@ -972,8 +972,73 @@ class Command(BaseCommand): InterventionAsset.objects.bulk_create(chunk, batch_size=BATCH_SIZE) self.log(f" Insertion liens : {min(i + BATCH_SIZE, len(assets_to_create))} / {len(assets_to_create)}") + # Synchronisation des événements de planification timeline + self.sync_intervention_planning_events() + self.log(f"Importation des interventions terminée. Total en base : {Intervention.objects.count()}") + def sync_intervention_planning_events(self): + """ + Crée ou met à jour les événements de la timeline de planification (InterventionPlanificationTimeLine) : + - 'expected_end' : échéance prévisionnelle calculée depuis in_deadline + - 'expected_begin' : date de début / diagnostic initial depuis dia_date + """ + self.log("--- Synchronisation des événements de planification (timeline) ---") + from django.db import connection + + with connection.cursor() as cur: + # 1. Échéances prévisionnelles (expected_end) + cur.execute(""" + INSERT INTO interventions_interventionplanificationtimeline ( + intervention_id, event_time, event_type, event_description + ) + SELECT i.id, i.expected_end_time, 'expected_end', 'Échéance prévisionnelle' + FROM interventions_intervention i + WHERE i.expected_end_time IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM interventions_interventionplanificationtimeline p + WHERE p.intervention_id = i.id AND p.event_type = 'expected_end' + ); + """) + created_end = cur.rowcount + + cur.execute(""" + UPDATE interventions_interventionplanificationtimeline p + SET event_time = i.expected_end_time + FROM interventions_intervention i + WHERE p.intervention_id = i.id + AND p.event_type = 'expected_end' + AND p.event_time != i.expected_end_time; + """) + updated_end = cur.rowcount + self.log(f" Échéance prévisionnelle (expected_end) : {created_end} créés, {updated_end} mis à jour") + + # 2. Diagnostics initiaux (expected_begin) + cur.execute(""" + INSERT INTO interventions_interventionplanificationtimeline ( + intervention_id, event_time, event_type, event_description + ) + SELECT i.id, i.expected_begin_time, 'expected_begin', 'Diagnostic initial' + FROM interventions_intervention i + WHERE i.expected_begin_time IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM interventions_interventionplanificationtimeline p + WHERE p.intervention_id = i.id AND p.event_type = 'expected_begin' + ); + """) + created_begin = cur.rowcount + + cur.execute(""" + UPDATE interventions_interventionplanificationtimeline p + SET event_time = i.expected_begin_time + FROM interventions_intervention i + WHERE p.intervention_id = i.id + AND p.event_type = 'expected_begin' + AND p.event_time != i.expected_begin_time; + """) + updated_begin = cur.rowcount + self.log(f" Diagnostic initial (expected_begin) : {created_begin} créés, {updated_begin} mis à jour") + def sync_tree_maintenance_dates(self, gestion_file=None): """ Synchronise les dates et périodicités de maintenance sur les arbres : @@ -981,10 +1046,13 @@ class Command(BaseCommand): - pruning_frequency_years : périodicité extraite du délai/titre (ex: Dans 1 an -> 1) - last_phytosanitary_date : max(expected_begin_time) des interventions de diagnostic - last_pruning_date : date d'exécution de la dernière taille (depuis gestion.csv ou interventions passées) + - timeline de planification : synchronisation des événements expected_begin / expected_end """ self.log("--- Synchronisation des dates de maintenance sur les arbres ---") from django.db import connection + self.sync_intervention_planning_events() + content_type_tree = ContentType.objects.get_for_model(NatureTree) with connection.cursor() as cur: diff --git a/loko/assets/templates/assets/components/asset_interventions.html b/loko/assets/templates/assets/components/asset_interventions.html index be90c92..1c40ae9 100644 --- a/loko/assets/templates/assets/components/asset_interventions.html +++ b/loko/assets/templates/assets/components/asset_interventions.html @@ -85,7 +85,9 @@ {% endif %} - {{ intervention.creation_time|date:"d/m/Y" }} + + {{ intervention.end_time|default:intervention.expected_end_time|default:intervention.expected_begin_time|default:intervention.creation_time|date:"d/m/Y" }} + {% endfor %} diff --git a/loko/assets/tests_import_fulcrum.py b/loko/assets/tests_import_fulcrum.py index 9bb8603..3ad21ab 100644 --- a/loko/assets/tests_import_fulcrum.py +++ b/loko/assets/tests_import_fulcrum.py @@ -158,6 +158,15 @@ class ImportFulcrumTreesCommandTest(TestCase): self.assertEqual(tree.next_inspection_date, date(2025, 5, 10)) self.assertEqual(tree.pruning_frequency_years, 1) + # Vérifications événements de planification (timeline) + self.assertEqual(itv.planned_events.count(), 2) + begin_event = itv.planned_events.filter(event_type='expected_begin').first() + self.assertIsNotNone(begin_event) + self.assertEqual(begin_event.event_time.date(), date(2024, 5, 10)) + end_event = itv.planned_events.filter(event_type='expected_end').first() + self.assertIsNotNone(end_event) + self.assertEqual(end_event.event_time.date(), date(2025, 5, 10)) + # Vérification liaison InterventionAsset links = InterventionAsset.objects.filter(intervention=itv) self.assertEqual(links.count(), 1)