Repository: GeotrekCE/Geotrek-admin

Issue:
Do not perform database operation on topologies if user did not edit field



Discussion:

Resolving patch:
diff --git a/geotrek/core/helpers.py b/geotrek/core/helpers.py
--- a/geotrek/core/helpers.py
+++ b/geotrek/core/helpers.py
@@ -65,20 +65,38 @@ def deserialize(cls, serialized):
         if not isinstance(objdict, (list,)):
             lat = objdict.get('lat')
             lng = objdict.get('lng')
+            pk = objdict.get('pk')
             kind = objdict.get('kind')
             # Point topology ?
             if lat and lng:
+                if pk:
+                    try:
+                        return Topology.objects.get(pk=int(pk))
+                    except (Topology.DoesNotExist, ValueError):
+                        pass
+
                 return cls._topologypoint(lng, lat, kind, snap=objdict.get('snap'))
             else:
                 objdict = [objdict]
 
-        # Path aggregation, remove all existing
         if len(objdict) == 0:
             raise ValueError("Invalid serialized topology : empty list found")
+
+        # If pk is still here, the user did not edit it.
+        # Return existing topology instead
+        pk = objdict[0].get('pk')
+        if pk:
+            try:
+                return Topology.objects.get(pk=int(pk))
+            except (Topology.DoesNotExist, ValueError):
+                pass
+
         kind = objdict[0].get('kind')
         offset = objdict[0].get('offset', 0.0)
         topology = TopologyFactory.create(no_path=True, kind=kind, offset=offset)
+        # Remove all existing path aggregation (WTF: created from factory ?)
         PathAggregation.objects.filter(topo_object=topology).delete()
+
         try:
             counter = 0
             for j, subtopology in enumerate(objdict):
@@ -155,7 +173,7 @@ def serialize(cls, topology):
         # Point topology
         if topology.ispoint():
             point = topology.geom.transform(settings.API_SRID, clone=True)
-            objdict = dict(kind=topology.kind, lng=point.x, lat=point.y)
+            objdict = dict(pk=topology.pk, kind=topology.kind, lng=point.x, lat=point.y)
             if topology.offset == 0:
                 objdict['snap'] = topology.aggregations.all()[0].path.pk
         else:
@@ -170,6 +188,7 @@ def serialize(cls, topology):
                 last = i == len(aggregations) - 1
                 intermediary = aggr.start_position == aggr.end_position
 
+                current.setdefault('pk', topology.pk)
                 current.setdefault('kind', topology.kind)
                 current.setdefault('offset', topology.offset)
                 if not intermediary:


Test patch:
diff --git a/geotrek/core/tests/test_topology.py b/geotrek/core/tests/test_topology.py
--- a/geotrek/core/tests/test_topology.py
+++ b/geotrek/core/tests/test_topology.py
@@ -616,9 +616,13 @@ def test_spoon_loop_2(self):
 
         # De/Serializing should work too
         serialized = """
-           [{"kind": "TOPOLOGY","positions":{"0":[0.3,1],"1":[0, 0.4]},"paths":[%(pk1)s,%(pk2)s],"offset": 0.0},
-            {"kind": "TOPOLOGY","positions":{"0":[0.4, 0.8]},"paths":[%(pk2)s],"offset": 0.0},
-            {"kind": "TOPOLOGY","positions":{"0":[0.8,1],"1":[1,0.3]},"paths":[%(pk2)s,%(pk1)s],"offset": 0.0}]""" % {'pk1': p1.pk, 'pk2': p2.pk}
+           [{"kind": "TOPOLOGY","positions":{"0":[0.3,1],"1":[0, 0.4]},"paths":[%(pk1)s,%(pk2)s],"offset": 0.0,"pk": %(topo)s},
+            {"kind": "TOPOLOGY","positions":{"0":[0.4, 0.8]},"paths":[%(pk2)s],"offset": 0.0,"pk": %(topo)s},
+            {"kind": "TOPOLOGY","positions":{"0":[0.8,1],"1":[1,0.3]},"paths":[%(pk2)s,%(pk1)s],"offset": 0.0,"pk": %(topo)s}]""" % {
+            'topo': topo.pk,
+            'pk1': p1.pk,
+            'pk2': p2.pk
+        }
 
         self.assertEqual(json.loads(serialized), json.loads(topo.serialize()))
         topod = Topology.deserialize(serialized)
@@ -654,6 +658,7 @@ def test_serialize_line(self):
         # +|========>+
         topo = TopologyFactory.create(offset=1.0, no_path=True)
         topo.add_path(path)
+        test_objdict['pk'] = topo.pk
         test_objdict['positions']['0'] = [0.0, 1.0]
         objdict = json.loads(topo.serialize())
         self.assertDictEqual(objdict[0], test_objdict)
@@ -661,6 +666,7 @@ def test_serialize_line(self):
         # +<========|+
         topo = TopologyFactory.create(offset=1.0, no_path=True)
         topo.add_path(path, start=1.0, end=0.0)
+        test_objdict['pk'] = topo.pk
         test_objdict['positions']['0'] = [1.0, 0.0]
         objdict = json.loads(topo.serialize())
         self.assertDictEqual(objdict[0], test_objdict)
@@ -670,6 +676,7 @@ def test_serialize_line(self):
         topo = TopologyFactory.create(offset=1.0, no_path=True)
         topo.add_path(path, start=0.0, end=1.0)
         topo.add_path(path2, start=1.0, end=0.0)
+        test_objdict['pk'] = topo.pk
         test_objdict['paths'] = [path.pk, path2.pk]
         test_objdict['positions'] = {'0': [0.0, 1.0], '1': [1.0, 0.0]}
         objdict = json.loads(topo.serialize())
@@ -679,6 +686,7 @@ def test_serialize_line(self):
         topo = TopologyFactory.create(offset=1.0, no_path=True)
         topo.add_path(path, start=1.0, end=0.0)
         topo.add_path(path2, start=0.0, end=1.0)
+        test_objdict['pk'] = topo.pk
         test_objdict['paths'] = [path.pk, path2.pk]
         test_objdict['positions'] = {'0': [1.0, 0.0], '1': [0.0, 1.0]}
         objdict = json.loads(topo.serialize())
@@ -691,15 +699,29 @@ def test_serialize_point(self):
         fieldvalue = topology.serialize()
         # fieldvalue is like '{"lat": -5.983842291017086, "lng": -1.3630770374505987, "kind": "TOPOLOGY"}'
         field = json.loads(fieldvalue)
-        self.assertTrue(almostequal(field['lat'],  -5.983))
-        self.assertTrue(almostequal(field['lng'],  -1.363))
+        self.assertEqual(field['pk'],  topology.pk)
+        self.assertTrue(almostequal(field['lat'], -5.983))
+        self.assertTrue(almostequal(field['lng'], -1.363))
         self.assertEqual(field['kind'],  "TOPOLOGY")
 
+
+class TopologyDerialization(TestCase):
+
     def test_deserialize_foreignkey(self):
         topology = TopologyFactory.create(offset=1, no_path=True)
         deserialized = Topology.deserialize(topology.pk)
         self.assertEqual(topology,  deserialized)
 
+    def test_deserialize_unedited_point_topology(self):
+        topology = TopologyFactory.create(offset=1, no_path=True)
+        deserialized = Topology.deserialize({'pk': topology.pk})
+        self.assertEqual(topology,  deserialized)
+
+    def test_deserialize_unedited_line_topology(self):
+        topology = TopologyFactory.create(offset=1, no_path=True)
+        deserialized = Topology.deserialize([{'pk': topology.pk}, {}])
+        self.assertEqual(topology,  deserialized)
+
     def test_deserialize_line(self):
         path = PathFactory.create()
         topology = Topology.deserialize('[{"paths": [%s], "positions": {"0": [0.0, 1.0]}, "offset": 1}]' % (path.pk))