feat: add UI indicators and handling for modified pole and cable geometries in traffic lights import
This commit is contained in:
parent
28d6664fbd
commit
3f402e3d6a
3 changed files with 285 additions and 43 deletions
|
|
@ -597,7 +597,7 @@
|
|||
<th>{% translate "Code Asset" %}</th>
|
||||
<th>{% translate "Nom" %}</th>
|
||||
<th>{% translate "Statut" %}</th>
|
||||
<th>{% translate "Géométrie (DB)" %}</th>
|
||||
<th>{% translate "Géométrie" %}</th>
|
||||
<th>{% translate "Modèle Déduit (Excel)" %}</th>
|
||||
<th>{% translate "Modèle Actuel (DB)" %}</th>
|
||||
<th>{% translate "Décision" %}</th>
|
||||
|
|
@ -628,8 +628,12 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
{% if item.has_db_geom %}
|
||||
<td id="geom_cell_cable_{{ item.code }}">
|
||||
{% if item.geom_modified %}
|
||||
<span class="badge bg-warning text-dark d-inline-flex align-items-center gap-1">
|
||||
<i class="bi bi-arrows-move"></i> {% translate "Tracé modifié" %}
|
||||
</span>
|
||||
{% elif item.has_db_geom %}
|
||||
<span class="badge bg-info text-dark d-inline-flex align-items-center gap-1">
|
||||
<i class="bi bi-bezier2"></i> {{ item.db_geom_length }} m
|
||||
</span>
|
||||
|
|
@ -1235,6 +1239,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
{
|
||||
code: "{{ item.code|escapejs }}",
|
||||
name: "{{ item.name|escapejs }}",
|
||||
status: "{{ item.status|escapejs }}",
|
||||
geom_modified: {{ item.geom_modified|yesno:"true,false" }},
|
||||
connected_poles: {{ item.connected_poles|safe|default:"[]" }}
|
||||
},
|
||||
{% endfor %}
|
||||
|
|
@ -1265,9 +1271,26 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
|
||||
let activePlacementPole = null;
|
||||
let activeEditCable = null;
|
||||
const placedPoles = {};
|
||||
const cableGeometries = {};
|
||||
let cableHandles = [];
|
||||
const cableGeometries = {};
|
||||
|
||||
function markCableAsModified(code) {
|
||||
const selectAction = document.querySelector(`select[name="action_cable_${code}"]`);
|
||||
if (selectAction) {
|
||||
if (selectAction.querySelector('option[value="modify"]')) {
|
||||
selectAction.value = 'modify';
|
||||
} else if (selectAction.querySelector('option[value="add"]')) {
|
||||
selectAction.value = 'add';
|
||||
}
|
||||
}
|
||||
const cb = document.querySelector(`input[name="selected_cable_${code}"]`);
|
||||
if (cb) cb.checked = true;
|
||||
|
||||
const geomCell = document.getElementById(`geom_cell_cable_${code}`);
|
||||
if (geomCell) {
|
||||
geomCell.innerHTML = `<span class="badge bg-warning text-dark d-inline-flex align-items-center gap-1"><i class="bi bi-arrows-move"></i> {% translate "Tracé modifié" %}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
const snapCandidates = initialDxfPoles.map(dp => [dp.lon, dp.lat, dp.x_3812, dp.y_3812]);
|
||||
|
||||
|
|
@ -1530,7 +1553,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
// Automatically place markers for poles that have known coordinates (e.g. from DXF or previous georeferencing)
|
||||
polesList.forEach(p => {
|
||||
if (p.status !== 'db_only' && p.new_lon !== null && p.new_lat !== null) {
|
||||
placePoleMarker(p.code, p.new_lon, p.new_lat, p.new_x_3812, p.new_y_3812, false);
|
||||
placePoleMarker(p.code, p.new_lon, p.new_lat, p.new_x_3812, p.new_y_3812, false, true);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1585,7 +1608,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
document.getElementById('placed-poles-count').textContent = `${placed} / ${total}`;
|
||||
}
|
||||
|
||||
function markPoleAsGeoreferenced(code, lon, lat, x_3812, y_3812) {
|
||||
function markPoleAsGeoreferenced(code, lon, lat, x_3812, y_3812, isInitialLoad = false) {
|
||||
const badge = document.getElementById(`georef_badge_${code}`);
|
||||
if (badge) {
|
||||
badge.classList.remove('bg-danger');
|
||||
|
|
@ -1602,24 +1625,49 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
const inputY = document.getElementById(`y_3812_pole_${code}`);
|
||||
if (inputY) inputY.value = (y_3812 !== undefined && y_3812 !== null) ? y_3812 : "";
|
||||
|
||||
// Dynamically update the table row for this pole if position changed
|
||||
const poleMeta = polesList.find(p => p.code === code);
|
||||
if (poleMeta) {
|
||||
const selectAction = document.querySelector(`select[name="action_pole_${code}"]`);
|
||||
if (selectAction) {
|
||||
if (selectAction.querySelector('option[value="modify"]')) {
|
||||
selectAction.value = 'modify';
|
||||
// If not initial load (i.e. user moved or placed the pole interactively), update decision and geom cell
|
||||
if (!isInitialLoad) {
|
||||
const poleMeta = polesList.find(p => p.code === code);
|
||||
if (poleMeta) {
|
||||
const selectAction = document.querySelector(`select[name="action_pole_${code}"]`);
|
||||
const geomCell = document.getElementById(`geom_cell_pole_${code}`);
|
||||
const hasDbPos = (poleMeta.db_x_3812 !== null && poleMeta.db_y_3812 !== null && x_3812 !== null && y_3812 !== null);
|
||||
let dist = hasDbPos ? Math.sqrt((poleMeta.db_x_3812 - x_3812)**2 + (poleMeta.db_y_3812 - y_3812)**2) : null;
|
||||
const isIdenticalPos = (hasDbPos && dist < 0.05);
|
||||
|
||||
if (isIdenticalPos && poleMeta.status === 'identical') {
|
||||
// Position identical and attributes identical: propose "Ne rien faire"
|
||||
if (selectAction && selectAction.querySelector('option[value="keep"]')) {
|
||||
selectAction.value = 'keep';
|
||||
}
|
||||
if (geomCell) {
|
||||
geomCell.innerHTML = `<span class="badge bg-light text-muted border d-inline-flex align-items-center gap-1"><i class="bi bi-check2"></i> {% translate "Inchangée" %}</span> <button type="button" class="btn btn-xs btn-outline-secondary ms-1 show-pole-on-map-btn" data-code="${code}" title="{% translate 'Voir sur la carte' %}"><i class="bi bi-geo-alt"></i></button>`;
|
||||
}
|
||||
} else {
|
||||
// Position changed or newly placed: propose "Mise à jour"
|
||||
if (selectAction) {
|
||||
if (selectAction.querySelector('option[value="modify"]')) {
|
||||
selectAction.value = 'modify';
|
||||
} else if (selectAction.querySelector('option[value="add"]')) {
|
||||
selectAction.value = 'add';
|
||||
}
|
||||
}
|
||||
const cb = document.querySelector(`input[name="selected_pole_${code}"]`);
|
||||
if (cb) cb.checked = true;
|
||||
|
||||
if (geomCell) {
|
||||
let distText = (dist !== null) ? ` (${dist.toFixed(1)} m)` : "";
|
||||
geomCell.innerHTML = `<span class="badge bg-warning text-dark d-inline-flex align-items-center gap-1"><i class="bi bi-arrows-move"></i> {% translate "Position modifiée" %}${distText}</span> <button type="button" class="btn btn-xs btn-outline-primary ms-1 show-pole-on-map-btn" data-code="${code}" title="{% translate 'Voir et ajuster sur la carte' %}"><i class="bi bi-geo-alt"></i></button>`;
|
||||
}
|
||||
|
||||
// Any cable connected to this pole also has its geometry modified
|
||||
cablesList.forEach(c => {
|
||||
if (c.connected_poles && c.connected_poles.includes(code)) {
|
||||
markCableAsModified(c.code);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
const geomCell = document.getElementById(`geom_cell_pole_${code}`);
|
||||
if (geomCell) {
|
||||
let distText = "";
|
||||
if (poleMeta.db_x_3812 !== null && poleMeta.db_y_3812 !== null && x_3812 && y_3812) {
|
||||
const d = Math.sqrt((poleMeta.db_x_3812 - x_3812)**2 + (poleMeta.db_y_3812 - y_3812)**2);
|
||||
distText = ` (${d.toFixed(1)} m)`;
|
||||
}
|
||||
geomCell.innerHTML = `<span class="badge bg-warning text-dark d-inline-flex align-items-center gap-1"><i class="bi bi-geo-alt-fill"></i> {% translate "Positionné sur carte" %}${distText}</span> <button type="button" class="btn btn-xs btn-outline-primary ms-1 show-pole-on-map-btn" data-code="${code}" title="{% translate 'Voir et ajuster sur la carte' %}"><i class="bi bi-geo-alt"></i></button>`;
|
||||
}
|
||||
}
|
||||
|
||||
redrawCablesOnMap();
|
||||
|
|
@ -1633,10 +1681,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
const originalCoords = [e.lngLat.lng, e.lngLat.lat];
|
||||
const snapped = getSnappedCoords(originalCoords[0], originalCoords[1]);
|
||||
|
||||
placePoleMarker(activePlacementPole, snapped.coords[0], snapped.coords[1], snapped.lambert[0], snapped.lambert[1]);
|
||||
placePoleMarker(activePlacementPole, snapped.coords[0], snapped.coords[1], snapped.lambert[0], snapped.lambert[1], true, false);
|
||||
});
|
||||
|
||||
function placePoleMarker(code, lon, lat, x_3812, y_3812, autoAdvance = true) {
|
||||
function placePoleMarker(code, lon, lat, x_3812, y_3812, autoAdvance = true, isInitialLoad = false) {
|
||||
if (placedPoles[code]) {
|
||||
placedPoles[code].marker.remove();
|
||||
}
|
||||
|
|
@ -1678,10 +1726,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
placedPoles[code].lat = snapped.coords[1];
|
||||
placedPoles[code].x_3812 = snapped.lambert[0];
|
||||
placedPoles[code].y_3812 = snapped.lambert[1];
|
||||
markPoleAsGeoreferenced(code, snapped.coords[0], snapped.coords[1], snapped.lambert[0], snapped.lambert[1]);
|
||||
markPoleAsGeoreferenced(code, snapped.coords[0], snapped.coords[1], snapped.lambert[0], snapped.lambert[1], false);
|
||||
});
|
||||
|
||||
markPoleAsGeoreferenced(code, lon, lat, x_3812, y_3812);
|
||||
markPoleAsGeoreferenced(code, lon, lat, x_3812, y_3812, isInitialLoad);
|
||||
|
||||
const mapPane = document.getElementById('map-poles-pane');
|
||||
mapPane.querySelectorAll('.pole-config-item').forEach(item => item.classList.remove('bg-light'));
|
||||
|
|
@ -1821,6 +1869,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
cableGeometries[code][index] = snapped.coords;
|
||||
redrawCablesOnMap();
|
||||
saveCablesToForm();
|
||||
markCableAsModified(code);
|
||||
});
|
||||
|
||||
el.addEventListener('dblclick', function(e) {
|
||||
|
|
@ -1829,6 +1878,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
drawCableHandles(code);
|
||||
redrawCablesOnMap();
|
||||
saveCablesToForm();
|
||||
markCableAsModified(code);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -1885,6 +1935,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
drawCableHandles(activeEditCable);
|
||||
redrawCablesOnMap();
|
||||
saveCablesToForm();
|
||||
markCableAsModified(activeEditCable);
|
||||
});
|
||||
|
||||
map.on('dblclick', function(e) {
|
||||
|
|
@ -1907,6 +1958,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
drawCableHandles(activeEditCable);
|
||||
redrawCablesOnMap();
|
||||
saveCablesToForm();
|
||||
markCableAsModified(activeEditCable);
|
||||
});
|
||||
|
||||
// Hover mouse styles for active cable line
|
||||
|
|
@ -1929,6 +1981,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
drawCableHandles(activeEditCable);
|
||||
redrawCablesOnMap();
|
||||
saveCablesToForm();
|
||||
markCableAsModified(activeEditCable);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2715,6 +2715,164 @@ class TrafficLightCrossPlanImportTest(TestCase):
|
|||
if os.path.exists(tmp_path):
|
||||
os.remove(tmp_path)
|
||||
|
||||
def test_import_cross_plan_default_decisions_identical_vs_modified(self):
|
||||
"""
|
||||
Verify that:
|
||||
- When pole & cable position and model are identical: status is 'identical',
|
||||
default proposed decision in HTML is 'Ne rien faire (Identique)'.
|
||||
- When pole position changed (DXF shift): status is 'modified',
|
||||
default proposed decision in HTML is 'Mettre à jour l'asset'.
|
||||
- When connected pole moved, the cable status becomes 'modified',
|
||||
default proposed decision in HTML is 'Mettre à jour l'asset'.
|
||||
"""
|
||||
import openpyxl
|
||||
import tempfile
|
||||
from unittest.mock import patch
|
||||
from django.contrib.gis.geos import Point as DjangoPoint, LineString as DjangoLineString
|
||||
from assets.models import (
|
||||
TrafficLightPole, TrafficLightCable,
|
||||
TrafficLightPoleModel, TrafficLightCableModel
|
||||
)
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
||||
pole_model = TrafficLightPoleModel.objects.first()
|
||||
cable_model = TrafficLightCableModel.objects.first()
|
||||
|
||||
# Pole A01: position will be identical (DXF matches DB)
|
||||
pole_a = TrafficLightPole.objects.create(
|
||||
intersection=self.intersection,
|
||||
code="SWB01_A01",
|
||||
name_fr="Poteau A01",
|
||||
geom=DjangoPoint(149200.0, 170500.0, srid=3812),
|
||||
lon=4.350,
|
||||
lat=50.840,
|
||||
model=pole_model,
|
||||
status="active"
|
||||
)
|
||||
# Pole B01: position will change (DXF differs by 2.0m)
|
||||
pole_b = TrafficLightPole.objects.create(
|
||||
intersection=self.intersection,
|
||||
code="SWB01_B01",
|
||||
name_fr="Poteau B01",
|
||||
geom=DjangoPoint(149300.0, 170600.0, srid=3812),
|
||||
lon=4.351,
|
||||
lat=50.841,
|
||||
model=pole_model,
|
||||
status="active"
|
||||
)
|
||||
# Cable 01: connects A01 and B01
|
||||
cable_01 = TrafficLightCable.objects.create(
|
||||
intersection=self.intersection,
|
||||
code="SWB01_CAB01",
|
||||
name_fr="Câble 01",
|
||||
geom=DjangoLineString([(149200.0, 170500.0), (149300.0, 170600.0)], srid=3812),
|
||||
model=cable_model,
|
||||
status="active"
|
||||
)
|
||||
|
||||
# Create mock workbook
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "kruisjesplan"
|
||||
ws.cell(row=1, column=1, value="Sleutel:")
|
||||
ws.cell(row=1, column=2, value="SWB01")
|
||||
ws.cell(row=5, column=1, value=f"Câble 01 {cable_model.name_fr}")
|
||||
ws.cell(row=6, column=3, value="richting")
|
||||
ws.cell(row=6, column=4, value="kringen")
|
||||
ws.cell(row=7, column=5, value="A01")
|
||||
ws.cell(row=7, column=6, value="B01")
|
||||
ws.cell(row=8, column=2, value="1")
|
||||
ws.cell(row=8, column=3, value="A")
|
||||
ws.cell(row=8, column=4, value="V")
|
||||
ws.cell(row=8, column=5, value="X")
|
||||
ws.cell(row=8, column=6, value="X")
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as tmp:
|
||||
tmp_path = tmp.name
|
||||
wb.save(tmp_path)
|
||||
|
||||
# Mock DXF poles: A01 identical (0.0m diff), B01 shifted by 2.0m
|
||||
mock_dxf_poles = [
|
||||
{'label': 'A01', 'x_3812': 149200.0, 'y_3812': 170500.0, 'lon': 4.350, 'lat': 50.840},
|
||||
{'label': 'B01', 'x_3812': 149302.0, 'y_3812': 170600.0, 'lon': 4.35102, 'lat': 50.841},
|
||||
]
|
||||
|
||||
try:
|
||||
self.client.force_login(self.user)
|
||||
with open(tmp_path, "rb") as f:
|
||||
uploaded_file = SimpleUploadedFile("plan_test_decisions.xlsx", f.read(), content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
|
||||
with patch('assets.views.trafficlights.resolve_dxf_file_path', return_value='/dummy/test.dxf'), \
|
||||
patch('assets.views.trafficlights.parse_dxf_poles', return_value=mock_dxf_poles):
|
||||
url_import = reverse("assets:import_cross_plan", args=[self.intersection.id])
|
||||
response = self.client.post(url_import, {
|
||||
"cross_plan_file_upload": uploaded_file,
|
||||
"selected_dxf": "test.dxf"
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Context assertions
|
||||
poles = response.context["poles"]
|
||||
cables = response.context["cables"]
|
||||
|
||||
p_a = next(p for p in poles if p["code"] == "SWB01_A01")
|
||||
p_b = next(p for p in poles if p["code"] == "SWB01_B01")
|
||||
c_1 = next(c for c in cables if c["code"] == "SWB01_CAB01")
|
||||
|
||||
# Pole A: Identical position & model
|
||||
self.assertEqual(p_a["status"], "identical")
|
||||
self.assertFalse(p_a["geom_modified"])
|
||||
|
||||
# Pole B: Position shifted
|
||||
self.assertEqual(p_b["status"], "modified")
|
||||
self.assertTrue(p_b["geom_modified"])
|
||||
self.assertEqual(p_b["distance_diff"], 2.0)
|
||||
|
||||
# Cable 1: Connected pole B01 moved -> Cable position is modified!
|
||||
self.assertEqual(c_1["status"], "modified")
|
||||
self.assertTrue(c_1["geom_modified"])
|
||||
self.assertTrue(any("déplacement de poteaux connectés" in d for d in c_1["details_diff"]))
|
||||
|
||||
# HTML rendered assertions
|
||||
content = response.content.decode("utf-8")
|
||||
# Pole A should have 'keep' (Ne rien faire) selected
|
||||
self.assertIn('name="action_pole_SWB01_A01"', content)
|
||||
self.assertRegex(content, r'<select name="action_pole_SWB01_A01"[^>]*>[\s\S]*?<option value="keep" selected>')
|
||||
|
||||
# Pole B should have 'modify' (Mettre à jour l'asset) selected
|
||||
self.assertIn('name="action_pole_SWB01_B01"', content)
|
||||
self.assertRegex(content, r'<select name="action_pole_SWB01_B01"[^>]*>[\s\S]*?<option value="modify" selected>')
|
||||
|
||||
# Cable 1 should have 'modify' (Mettre à jour l'asset) selected because B01 moved
|
||||
self.assertIn('name="action_cable_SWB01_CAB01"', content)
|
||||
self.assertRegex(content, r'<select name="action_cable_SWB01_CAB01"[^>]*>[\s\S]*?<option value="modify" selected>')
|
||||
|
||||
# Check that cable geometry cell rendered
|
||||
self.assertIn('id="geom_cell_cable_SWB01_CAB01"', content)
|
||||
|
||||
# Test POST integrate with 'keep' on pole A01: verifies position and properties are untouched
|
||||
url_integrate = reverse("assets:integrate_cross_plan", args=[self.intersection.id])
|
||||
response_integrate = self.client.post(url_integrate, {
|
||||
"cross_plan_filename": response.context["cross_plan_filename"],
|
||||
"selected_pole_SWB01_A01": "on",
|
||||
"action_pole_SWB01_A01": "keep",
|
||||
"name_pole_SWB01_A01": "Ignored Custom Name",
|
||||
"x_3812_pole_SWB01_A01": "999999.0",
|
||||
"y_3812_pole_SWB01_A01": "999999.0",
|
||||
})
|
||||
self.assertEqual(response_integrate.status_code, 302)
|
||||
|
||||
pole_a.refresh_from_db()
|
||||
# Must remain untouched because action was "keep" (Ne rien faire)
|
||||
self.assertEqual(pole_a.name_fr, "Poteau A01")
|
||||
self.assertEqual(pole_a.geom.x, 149200.0)
|
||||
self.assertEqual(pole_a.geom.y, 170500.0)
|
||||
|
||||
finally:
|
||||
import os
|
||||
if os.path.exists(tmp_path):
|
||||
os.remove(tmp_path)
|
||||
|
||||
|
||||
class AssetDefaultPositionTest(TestCase):
|
||||
def test_assign_default_position_hierarchy(self):
|
||||
|
|
|
|||
|
|
@ -4287,16 +4287,48 @@ def import_cross_plan(request, intersection_id):
|
|||
# ── 2. CABLES COMPARISON ──
|
||||
existing_cables = {c.code: c for c in TrafficLightCable.objects.filter(intersection=intersection, status='active')}
|
||||
compared_cables = []
|
||||
|
||||
# Track poles that moved or are newly placed to see if connected cable paths changed
|
||||
modified_pole_codes = {
|
||||
p['code'] for p in compared_poles
|
||||
if p.get('geom_modified') or p.get('status') == 'new'
|
||||
}
|
||||
modified_pole_short_codes = {
|
||||
p['short_code'] for p in compared_poles
|
||||
if p.get('geom_modified') or p.get('status') == 'new'
|
||||
}
|
||||
|
||||
for dc in deduced["cables"]:
|
||||
code = dc["code"]
|
||||
model_name = dc["model_name"]
|
||||
db_model = TrafficLightCableModel.objects.filter(name_fr__iexact=model_name).first() or TrafficLightCableModel.objects.first()
|
||||
|
||||
connected = dc.get("connected_poles", [])
|
||||
has_connected_moved = any(
|
||||
p_code in modified_pole_codes or p_code.split('_')[-1] in modified_pole_short_codes
|
||||
for p_code in connected
|
||||
)
|
||||
|
||||
if code in existing_cables:
|
||||
ec = existing_cables[code]
|
||||
is_diff = ec.model_id != db_model.id
|
||||
status = 'modified' if is_diff else 'identical'
|
||||
has_geom = ec.geom is not None
|
||||
is_diff = (ec.model_id != db_model.id) if (ec.model and db_model) else False
|
||||
details_diff = []
|
||||
if is_diff:
|
||||
details_diff.append(_("Modèle : %(db_m)s → %(new_m)s") % {
|
||||
'db_m': ec.model.name_fr if ec.model else 'Aucun',
|
||||
'new_m': model_name
|
||||
})
|
||||
|
||||
geom_modified = False
|
||||
if has_connected_moved:
|
||||
geom_modified = True
|
||||
details_diff.append(_("Position modifiée : déplacement de poteaux connectés"))
|
||||
elif not has_geom and len(connected) >= 2:
|
||||
geom_modified = True
|
||||
details_diff.append(_("Nouveau tracé déduit des poteaux"))
|
||||
|
||||
status = 'modified' if (is_diff or geom_modified) else 'identical'
|
||||
compared_cables.append({
|
||||
'code': code,
|
||||
'name': ec.name_fr or dc['name'],
|
||||
|
|
@ -4304,13 +4336,17 @@ def import_cross_plan(request, intersection_id):
|
|||
'deduced_model_name': model_name,
|
||||
'db_model_name': ec.model.name_fr if ec.model else '',
|
||||
'db_id': ec.id,
|
||||
'details_diff': [f"Modèle : {ec.model.name_fr if ec.model else 'Aucun'} → {model_name}"] if is_diff else [],
|
||||
'connected_poles': dc.get("connected_poles", []),
|
||||
'details_diff': details_diff,
|
||||
'connected_poles': connected,
|
||||
'has_db_geom': has_geom,
|
||||
'geom_modified': geom_modified,
|
||||
'db_geom_length': round(ec.geom.length, 1) if has_geom else None,
|
||||
})
|
||||
existing_cables.pop(code)
|
||||
else:
|
||||
details_diff = []
|
||||
if len(connected) >= 2:
|
||||
details_diff.append(_("Tracé déduit des poteaux"))
|
||||
compared_cables.append({
|
||||
'code': code,
|
||||
'name': dc['name'],
|
||||
|
|
@ -4318,9 +4354,10 @@ def import_cross_plan(request, intersection_id):
|
|||
'deduced_model_name': model_name,
|
||||
'db_model_name': '',
|
||||
'db_id': None,
|
||||
'details_diff': [],
|
||||
'connected_poles': dc.get("connected_poles", []),
|
||||
'details_diff': details_diff,
|
||||
'connected_poles': connected,
|
||||
'has_db_geom': False,
|
||||
'geom_modified': False,
|
||||
'db_geom_length': None,
|
||||
})
|
||||
for code, ec in existing_cables.items():
|
||||
|
|
@ -4334,6 +4371,7 @@ def import_cross_plan(request, intersection_id):
|
|||
'db_id': ec.id,
|
||||
'details_diff': [_("Absent du fichier Excel (présent en DB)")],
|
||||
'has_db_geom': has_geom,
|
||||
'geom_modified': False,
|
||||
'db_geom_length': round(ec.geom.length, 1) if has_geom else None,
|
||||
})
|
||||
|
||||
|
|
@ -5291,12 +5329,8 @@ def integrate_cross_plan(request, intersection_id):
|
|||
except Exception:
|
||||
pass
|
||||
else:
|
||||
# 'keep'
|
||||
# 'keep' (Ne rien faire)
|
||||
if existing:
|
||||
existing.name_fr = custom_name
|
||||
existing.name_nl = custom_name
|
||||
update_pole_geometry(existing, code)
|
||||
existing.save()
|
||||
created_poles[code] = existing
|
||||
|
||||
# Handle poles present in DB only (orphans)
|
||||
|
|
@ -5381,12 +5415,9 @@ def integrate_cross_plan(request, intersection_id):
|
|||
existing.replaced_by = new_cable
|
||||
existing.save()
|
||||
else:
|
||||
# 'keep'
|
||||
# 'keep' (Ne rien faire)
|
||||
if existing:
|
||||
existing.name_fr = custom_name
|
||||
existing.name_nl = custom_name
|
||||
update_cable_geometry(existing, code)
|
||||
existing.save()
|
||||
pass
|
||||
|
||||
for key in request.POST.keys():
|
||||
if key.startswith("action_cable_db_"):
|
||||
|
|
|
|||
Loading…
Reference in a new issue