From 5fe1615595662d33eb3f36dd6c6f732ab4a22645 Mon Sep 17 00:00:00 2001 From: LePetitTim Date: Mon, 14 Feb 2022 11:36:41 +0100 Subject: [PATCH 1/2] Add delete properties pictures on deletion features --- CHANGES.md | 1 + terra_geocrud/properties/files.py | 18 +++++++++-- terra_geocrud/signals.py | 7 +++++ terra_geocrud/tests/test_signals.py | 47 ++++++++++++++++++++++++++++- terra_geocrud/tests/test_views.py | 4 ++- 5 files changed, 72 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0d68f86..e861687 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ CHANGELOG 1.0.23+dev (XXXX-XX-XX) --------------------------- +* Add deletion signal delete properties pictures and thumbnails 1.0.23 (2022-02-11) --------------------------- diff --git a/terra_geocrud/properties/files.py b/terra_geocrud/properties/files.py index 78f2a77..2373b0e 100644 --- a/terra_geocrud/properties/files.py +++ b/terra_geocrud/properties/files.py @@ -54,13 +54,25 @@ def delete_old_picture_property(file_prop, old_properties): delete(old_storage_file_path) -def store_feature_files(feature, old_properties=None): - """ Handle base64 encoded files to django storage. Use fake base64 to compatibility with react-json-schema """ - fake_content = 'R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=' +def get_files_properties(feature): files_properties = [ key for key, value in feature.layer.schema['properties'].items() if feature.layer.schema['properties'][key].get('format') == 'data-url' ] + return files_properties + + +def delete_feature_files(feature): + files_properties = get_files_properties(feature) + if files_properties: + for file_prop in files_properties: + delete_old_picture_property(file_prop, feature.properties) + + +def store_feature_files(feature, old_properties=None): + """ Handle base64 encoded files to django storage. Use fake base64 to compatibility with react-json-schema """ + fake_content = 'R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=' + files_properties = get_files_properties(feature) if files_properties: storage = get_storage() for file_prop in files_properties: diff --git a/terra_geocrud/signals.py b/terra_geocrud/signals.py index 13eb7d6..1e365ef 100644 --- a/terra_geocrud/signals.py +++ b/terra_geocrud/signals.py @@ -6,6 +6,7 @@ from geostore.helpers import execute_async_func from geostore.models import Feature, LayerRelation from geostore.signals import save_feature, save_layer_relation +from terra_geocrud.properties.files import delete_feature_files from terra_geocrud.tasks import (feature_update_relations_and_properties, layer_relations_set_destinations, feature_update_relations_origins, feature_update_destination_properties) @@ -42,8 +43,14 @@ def save_layer_relation(sender, instance, **kwargs): execute_async_func(layer_relations_set_destinations, (instance.pk, )) +@receiver(post_delete, sender=Feature, dispatch_uid='delete_files_feature') +def delete_files_feature(sender, instance, **kwargs): + delete_feature_files(instance) + + @receiver(post_delete, sender=Feature, dispatch_uid='delete_feature') def delete_feature(sender, instance, **kwargs): + # save base64 file content to storage if app_settings.GEOSTORE_RELATION_CELERY_ASYNC: kwargs['relation_id'] = None kwargs.pop('signal') diff --git a/terra_geocrud/tests/test_signals.py b/terra_geocrud/tests/test_signals.py index f5465c5..a99a35b 100644 --- a/terra_geocrud/tests/test_signals.py +++ b/terra_geocrud/tests/test_signals.py @@ -13,6 +13,7 @@ from django.contrib.gis.geos import LineString, Polygon from terra_geocrud.models import CrudViewProperty +from terra_geocrud.properties.files import get_storage, store_feature_files from terra_geocrud.tasks import ( ConcurrentPropertyModificationError, feature_update_relations_and_properties, @@ -20,9 +21,13 @@ feature_update_destination_properties, sync_properties_relations_destination, ) +from terra_geocrud.thumbnail_backends import ThumbnailDataFileBackend + from terra_geocrud.tests.factories import CrudViewFactory from ..signals import save_feature +thumbnail_backend = ThumbnailDataFileBackend() + class AsyncSideEffect(object): def add_side_effect_async(self, mocked): @@ -31,6 +36,46 @@ def side_effect_async(async_func, args=()): mocked.side_effect = side_effect_async +@patch('terra_geocrud.tasks.feature_update_relations_and_properties.delay') +@patch('terra_geocrud.signals.execute_async_func') +class DeletionFeatureDeletePictureTest(TestCase): + def setUp(self): + + layer = LayerFactory.create(geom_type=GeometryTypes.LineString, + schema={"type": "object", + "required": ["name", ], + "properties": {"name": {"type": "string", "title": "Name"}} + }) + self.crud_view = CrudViewFactory(layer=layer) + self.prop_name = CrudViewProperty.objects.create( + view=self.crud_view, key="picture", + editable=True, + json_schema={'type': "string", + 'title': "Picture", + 'format': "data-url"} + ) + sync_layer_schema(self.crud_view) + self.feature = Feature.objects.create( + layer=self.crud_view.layer, + properties={'name': 'foo', + 'picture': "data:image/png;name=titre_laromieu-fondblanc.jpg;base64,iVBORw0KGgoAAAANSUhEUgAAAA" + "EAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="}, + geom=LineString((0, 0), (1, 0)) + ) + store_feature_files(self.feature, {}) + self.storage = get_storage() + self.property_value = self.feature.properties.get('picture') + self.storage_file_path = self.property_value.split(';name=')[-1].split(';')[0] + self.thumbnail = thumbnail_backend.get_thumbnail(self.storage_file_path, "500x500", crop='noop', upscale=False) + self.assertTrue(self.storage.exists(self.thumbnail.name)) + self.assertTrue(self.storage.exists(self.storage_file_path)) + + def test_signal_feature_delete_pictures(self, async_mocked, mock_delay): + self.feature.delete() + self.assertFalse(self.storage.exists(self.thumbnail.name)) + self.assertFalse(self.storage.exists(self.storage_file_path)) + + @patch('terra_geocrud.tasks.feature_update_relations_and_properties.delay') @patch('terra_geocrud.signals.execute_async_func') @patch('geostore.settings.GEOSTORE_RELATION_CELERY_ASYNC', new_callable=PropertyMock) @@ -70,7 +115,7 @@ def setUp(self): geom=LineString((0, 0), (10, 10)) ) - def test_signal(self, property_mocked, async_mocked, mock_delay): + def test_signal_property_update(self, property_mocked, async_mocked, mock_delay): property_mocked.return_value = True self.add_side_effect_async(async_mocked) self.feature.refresh_from_db() diff --git a/terra_geocrud/tests/test_views.py b/terra_geocrud/tests/test_views.py index f83d28f..a105ec9 100644 --- a/terra_geocrud/tests/test_views.py +++ b/terra_geocrud/tests/test_views.py @@ -376,7 +376,9 @@ def test_list_endpoint(self): response_list = self.client.get(reverse('feature-list', args=(self.crud_view.layer_id,)), format="json") data = response_list.json() - self.assertEqual(len(data), self.crud_view.layer.features.count()) + features = self.crud_view.layer.features.all() + self.assertEqual(len(data), len(features)) + self.assertEqual(len(data), features.count()) def test_property_detail_display_with_groups(self): response_detail = self.client.get(reverse('feature-detail', From 6d81932099f611bf76376cc6135d2ca6bcb5b3ef Mon Sep 17 00:00:00 2001 From: LePetitTim Date: Tue, 15 Feb 2022 10:18:23 +0100 Subject: [PATCH 2/2] Remove test with only count --- terra_geocrud/tests/test_views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/terra_geocrud/tests/test_views.py b/terra_geocrud/tests/test_views.py index a105ec9..5687dff 100644 --- a/terra_geocrud/tests/test_views.py +++ b/terra_geocrud/tests/test_views.py @@ -378,7 +378,6 @@ def test_list_endpoint(self): data = response_list.json() features = self.crud_view.layer.features.all() self.assertEqual(len(data), len(features)) - self.assertEqual(len(data), features.count()) def test_property_detail_display_with_groups(self): response_detail = self.client.get(reverse('feature-detail',