diff --git a/accounts/tests/test_views.py b/accounts/tests/test_views.py index 612d0884a..0f7ad29aa 100644 --- a/accounts/tests/test_views.py +++ b/accounts/tests/test_views.py @@ -46,7 +46,12 @@ def setUp(self): self.sound.moderation_state = "OK" self.sound.processing_state = "OK" self.sound.similarity_state = "OK" - self.sound.geotag = GeoTag.objects.create(user=user, lat=45.8498, lon=-62.6879, zoom=9) + GeoTag.objects.create( + sound=self.sound, + lat=45.8498, + lon=-62.6879, + zoom=9 + ) self.sound.save() SoundOfTheDay.objects.create(sound=self.sound, date_display=datetime.date.today()) self.download = Download.objects.create(user=self.user, sound=self.sound, license=self.sound.license, diff --git a/apiv2/serializers.py b/apiv2/serializers.py index 5dc89baba..32b4ab973 100644 --- a/apiv2/serializers.py +++ b/apiv2/serializers.py @@ -295,7 +295,7 @@ def get_comments(self, obj): geotag = serializers.SerializerMethodField() def get_geotag(self, obj): - if obj.geotag: + if hasattr(obj, 'geotag'): return str(obj.geotag.lat) + " " + str(obj.geotag.lon) else: return None diff --git a/apiv2/tests.py b/apiv2/tests.py index e77144471..fa4b3deb9 100755 --- a/apiv2/tests.py +++ b/apiv2/tests.py @@ -636,7 +636,7 @@ def test_oauth2_authorization_code_grant_flow(self): }, secure=True) self.assertEqual(resp.status_code, 302) resp = self.client.get(resp.request['PATH_INFO'] + '?' + resp.request['QUERY_STRING'], secure=True) - self.assertEquals(resp.url.startswith(client.get_default_redirect_uri()), True) + self.assertEqual(resp.url.startswith(client.get_default_redirect_uri()), True) resp_params = self.get_params_from_url(resp.url) self.check_dict_has_fields(resp_params, ['error']) self.assertEqual(resp_params['error'], 'unauthorized_client') @@ -674,7 +674,7 @@ def test_oauth2_authorization_code_grant_flow(self): }, secure=True) self.assertTrue(resp.url.startswith(client.get_default_redirect_uri())) resp_params = self.get_params_from_url(resp.url) - self.assertEquals(resp_params['state'], 'an_optional_state') # Check state is returned and preserved + self.assertEqual(resp_params['state'], 'an_optional_state') # Check state is returned and preserved self.check_dict_has_fields(resp_params, ['code']) # Check code is there # Return 200 OK when requesting access token setting client_id and client_secret in body params diff --git a/apiv2/views.py b/apiv2/views.py index a682061d0..3f9907e52 100755 --- a/apiv2/views.py +++ b/apiv2/views.py @@ -943,12 +943,11 @@ def post(self, request, *args, **kwargs): if 'geotag' in serializer.data: if serializer.data['geotag']: lat, lon, zoom = serializer.data['geotag'].split(',') - geotag = GeoTag(user=self.user, + geotag = GeoTag.objects.create( + sound=sound, lat=float(lat), lon=float(lon), zoom=int(zoom)) - geotag.save() - sound.geotag = geotag if 'pack' in serializer.data: if serializer.data['pack']: if Pack.objects.filter(name=serializer.data['pack'], user=self.user)\ diff --git a/geotags/migrations/0006_remove_geotag_user.py b/geotags/migrations/0006_remove_geotag_user.py new file mode 100644 index 000000000..d26350fe3 --- /dev/null +++ b/geotags/migrations/0006_remove_geotag_user.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.23 on 2025-01-30 20:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('geotags', '0005_alter_geotag_information'), + ] + + operations = [ + migrations.RemoveField( + model_name='geotag', + name='user', + ), + ] diff --git a/geotags/migrations/0007_geotag_sound2.py b/geotags/migrations/0007_geotag_sound2.py new file mode 100644 index 000000000..3da7facc1 --- /dev/null +++ b/geotags/migrations/0007_geotag_sound2.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.23 on 2025-02-04 10:58 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('sounds', '0052_alter_sound_type'), + ('geotags', '0006_remove_geotag_user'), + ] + + operations = [ + migrations.AddField( + model_name='geotag', + name='sound2', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='geotags2', to='sounds.sound'), + ), + ] diff --git a/geotags/migrations/0008_geotag_migrate_sound.py b/geotags/migrations/0008_geotag_migrate_sound.py new file mode 100644 index 000000000..a5c8aa91d --- /dev/null +++ b/geotags/migrations/0008_geotag_migrate_sound.py @@ -0,0 +1,31 @@ +# This needs to be run in a separate transaction due to indexes + +from django.db import migrations + + +def migrate_geotags(apps, schema_editor): + GeoTag = apps.get_model('geotags', 'GeoTag') + Sound = apps.get_model('sounds', 'Sound') + to_update = [] + to_delete = [] + for geotag in GeoTag.objects.prefetch_related('sound_set').all(): + sounds = geotag.sound_set.all() + if len(sounds) == 1: + geotag.sound2_id = sounds.first().id + to_update.append(geotag) + else: + to_delete.append(geotag) + GeoTag.objects.bulk_update(to_update, ['sound2']) + print(f"Deleting {len(to_delete)} geotags with 0 sounds") + GeoTag.objects.filter(id__in=[gt.id for gt in to_delete]).delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('geotags', '0007_geotag_sound2'), + ] + + operations = [ + migrations.RunPython(migrate_geotags), + ] diff --git a/geotags/migrations/0009_rename_sound2_geotag_sound.py b/geotags/migrations/0009_rename_sound2_geotag_sound.py new file mode 100644 index 000000000..fade293d2 --- /dev/null +++ b/geotags/migrations/0009_rename_sound2_geotag_sound.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.23 on 2025-02-04 13:19 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('sounds', '0052_alter_sound_type'), + ('geotags', '0008_geotag_migrate_sound'), + ] + + operations = [ + migrations.RenameField( + model_name='geotag', + old_name='sound2', + new_name='sound', + ), + migrations.AlterField( + model_name='geotag', + name='sound', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='geotags2', to='sounds.sound'), + ), + ] diff --git a/geotags/migrations/0010_alter_geotag_sound.py b/geotags/migrations/0010_alter_geotag_sound.py new file mode 100644 index 000000000..76ab80d18 --- /dev/null +++ b/geotags/migrations/0010_alter_geotag_sound.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.23 on 2025-02-10 14:21 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('sounds', '0053_remove_sound_geotag'), + ('geotags', '0009_rename_sound2_geotag_sound'), + ] + + operations = [ + migrations.AlterField( + model_name='geotag', + name='sound', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='geotag', to='sounds.sound'), + ), + ] diff --git a/geotags/models.py b/geotags/models.py index a81272bd8..461b2f2b4 100644 --- a/geotags/models.py +++ b/geotags/models.py @@ -30,7 +30,7 @@ class GeoTag(models.Model): - user = models.ForeignKey(User, on_delete=models.CASCADE) + sound = models.OneToOneField("sounds.Sound", on_delete=models.CASCADE, related_name="geotag") lat = models.FloatField(db_index=True) lon = models.FloatField(db_index=True) zoom = models.IntegerField() @@ -40,14 +40,14 @@ class GeoTag(models.Model): created = models.DateTimeField(db_index=True, auto_now_add=True) def __str__(self): - return f"{self.user} ({self.lat:f},{self.lon:f})" + return f"({self.lat:f},{self.lon:f})" def get_absolute_url(self): return reverse('geotag', args=[smart_str(self.id)]) def retrieve_location_information(self): """Use the mapbox API to retrieve information about the latitude and longitude of this geotag. - If no iformation has been retrieved from mapbox and a mapbox access token is available, retrieve and + If no information has been retrieved from mapbox and a mapbox access token is available, retrieve and store that information. Then, pre-process that information to save a place name for display purposes. """ if settings.MAPBOX_ACCESS_TOKEN and (self.information is None or self.should_update_information): @@ -67,11 +67,11 @@ def retrieve_location_information(self): # Try with "place" feature self.location_name = [feature for feature in features if 'place' in feature['place_type']][0]['place_name'] except IndexError: - # If "place" feature is not avialable, use "locality" feature + # If "place" feature is not available, use "locality" feature try: self.location_name = [feature for feature in features if 'locality' in feature['place_type']][0]['place_name'] except IndexError: - # If "place" nor "locality" features are avialable, use "region" + # If "place" nor "locality" features are available, use "region" try: self.location_name = [feature for feature in features if 'region' in feature['place_type']][0]['place_name'] except: diff --git a/geotags/tests.py b/geotags/tests.py index c3eaa009b..f199ae76a 100644 --- a/geotags/tests.py +++ b/geotags/tests.py @@ -67,9 +67,7 @@ def test_browse_geotags_for_user_deleted_user(self): def test_geotags_infowindow(self): sound = Sound.objects.first() - gt = GeoTag.objects.create(user=sound.user, lat=45.8498, lon=-62.6879, zoom=9) - sound.geotag = gt - sound.save() + gt = GeoTag.objects.create(sound=sound, lat=45.8498, lon=-62.6879, zoom=9) resp = self.client.get(reverse('geotags-infowindow', kwargs={'sound_id': sound.id})) self.check_context(resp.context, {'sound': sound}) self.assertContains(resp, f'href="/people/{sound.user.username}/sounds/{sound.id}/"') @@ -82,10 +80,11 @@ def test_browse_geotags_case_insensitive(self): sounds[1].set_tags([tag]) sounds[0].set_tags([tag.upper()]) - gt = GeoTag.objects.create(user=user, lat=45.8498, lon=-62.6879, zoom=9) + lat = 45.8498 + lon = -62.6879 + for sound in sounds: - sound.geotag = gt - sound.save() + GeoTag.objects.create(sound=sound, lat=lat + 0.0001, lon=lon + 0.0001, zoom=9) resp = self.client.get(reverse('geotags-barray', kwargs={'tag': tag})) # Response contains 3 int32 objects per sound: id, lat and lng. Total size = 3 * 4 bytes = 12 bytes diff --git a/sounds/fixtures/sounds.json b/sounds/fixtures/sounds.json index 554ad09c7..803ec5d35 100644 --- a/sounds/fixtures/sounds.json +++ b/sounds/fixtures/sounds.json @@ -131,7 +131,6 @@ "created": "2005-03-09 10:45:28", "processing_log": null, "avg_rating": 7.375, - "geotag": null, "original_filename": "cello-voice.wav", "moderation_date": "2005-03-09 10:45:28", "channels": 1, @@ -167,7 +166,6 @@ "created": "2005-03-09 10:46:12", "processing_log": null, "avg_rating": 7.7000000000000002, - "geotag": null, "original_filename": "voice-boat.wav", "moderation_date": "2005-03-09 10:46:12", "channels": 1, @@ -203,7 +201,6 @@ "created": "2005-03-09 10:46:38", "processing_log": null, "avg_rating": 6.7083333333299997, - "geotag": null, "original_filename": "voice-cat.wav", "moderation_date": "2005-03-09 10:46:38", "channels": 1, @@ -239,7 +236,6 @@ "created": "2005-03-09 10:47:07", "processing_log": null, "avg_rating": 7.6166666666699996, - "geotag": null, "original_filename": "voice-gong.wav", "moderation_date": "2005-03-09 10:47:07", "channels": 1, @@ -275,7 +271,6 @@ "created": "2005-03-09 10:47:33", "processing_log": null, "avg_rating": 6.8181818181800002, - "geotag": null, "original_filename": "voice-plane.wav", "moderation_date": "2005-03-09 10:47:33", "channels": 1, @@ -324,7 +319,6 @@ "created": "2005-03-09 13:41:47", "processing_log": null, "avg_rating": 8.1216216216199992, - "geotag": null, "original_filename": "sweep_log.wav", "moderation_date": "2005-03-09 13:41:47", "channels": 2, @@ -360,7 +354,6 @@ "created": "2005-03-09 17:36:23", "processing_log": null, "avg_rating": 7.0, - "geotag": null, "original_filename": "Fuzzy.wav", "moderation_date": "2005-03-09 17:36:23", "channels": 1, @@ -396,7 +389,6 @@ "created": "2005-03-09 18:37:24", "processing_log": null, "avg_rating": 8.0, - "geotag": null, "original_filename": "digighost.wav", "moderation_date": "2005-03-09 18:37:24", "channels": 2, @@ -432,7 +424,6 @@ "created": "2005-03-09 19:01:34", "processing_log": null, "avg_rating": 2.5, - "geotag": null, "original_filename": "gesprekken stoel kraak lachen bij tim.wav", "moderation_date": "2005-03-09 19:01:34", "channels": 2, @@ -481,7 +472,6 @@ "created": "2005-03-09 20:37:55", "processing_log": null, "avg_rating": 8.8947368421099995, - "geotag": null, "original_filename": "Glass A ff.wav", "moderation_date": "2005-03-09 20:37:55", "channels": 1, @@ -517,7 +507,6 @@ "created": "2005-03-09 20:40:34", "processing_log": null, "avg_rating": 0.0, - "geotag": null, "original_filename": "Glass A mf.wav", "moderation_date": "2005-03-09 20:40:34", "channels": 1, @@ -553,7 +542,6 @@ "created": "2005-03-09 20:40:34", "processing_log": null, "avg_rating": 6.61538461538, - "geotag": null, "original_filename": "Glass A pp.wav", "moderation_date": "2005-03-09 20:40:34", "channels": 1, @@ -589,7 +577,6 @@ "created": "2005-03-09 20:40:34", "processing_log": null, "avg_rating": 9.0, - "geotag": null, "original_filename": "Glass A# ff.wav", "moderation_date": "2005-03-09 20:40:34", "channels": 1, @@ -625,7 +612,6 @@ "created": "2005-03-09 20:40:34", "processing_log": null, "avg_rating": 0.0, - "geotag": null, "original_filename": "Glass A# mf.wav", "moderation_date": "2005-03-09 20:40:34", "channels": 1, diff --git a/sounds/fixtures/sounds_with_tags.json b/sounds/fixtures/sounds_with_tags.json index cc2b5c2e7..dbc65df9b 100644 --- a/sounds/fixtures/sounds_with_tags.json +++ b/sounds/fixtures/sounds_with_tags.json @@ -191,7 +191,6 @@ "created": "2005-03-09T10:45:28", "type": "wav", "avg_rating": 7.45454545454545, - "geotag": null, "crc": "d9e4a435", "is_index_dirty": false, "original_filename": "cello-voice-morph.wav", @@ -234,7 +233,6 @@ "created": "2005-03-09T10:46:12", "type": "wav", "avg_rating": 7.69230769230769, - "geotag": null, "crc": "065a9060", "is_index_dirty": false, "original_filename": "voice-boat-morphing.wav", @@ -277,7 +275,6 @@ "created": "2005-03-09T10:46:38", "type": "wav", "avg_rating": 6.53846153846154, - "geotag": null, "crc": "ea47407d", "is_index_dirty": false, "original_filename": "voice-cat-morph.wav", @@ -320,7 +317,6 @@ "created": "2005-03-09T10:47:07", "type": "wav", "avg_rating": 7.609375, - "geotag": null, "crc": "e212542a", "is_index_dirty": false, "original_filename": "voice-gong.wav", @@ -363,7 +359,6 @@ "created": "2005-03-09T10:47:33", "type": "wav", "avg_rating": 7.14285714285714, - "geotag": null, "crc": "1ba67974", "is_index_dirty": false, "original_filename": "voice-plane-morphing.wav", @@ -406,7 +401,6 @@ "created": "2005-03-09T13:41:47", "type": "wav", "avg_rating": 8.24444444444444, - "geotag": null, "crc": "b9420d17", "is_index_dirty": false, "original_filename": "sweep_log.wav", @@ -449,7 +443,6 @@ "created": "2005-03-09T17:36:23", "type": "wav", "avg_rating": 7.0, - "geotag": null, "crc": "0c74097e", "is_index_dirty": false, "original_filename": "Fuzzy.wav", @@ -492,7 +485,6 @@ "created": "2005-03-09T18:37:24", "type": "wav", "avg_rating": 8.33333333333333, - "geotag": null, "crc": "f5859456", "is_index_dirty": false, "original_filename": "digighost.wav", @@ -535,7 +527,6 @@ "created": "2005-03-09T19:01:34", "type": "wav", "avg_rating": 2.5, - "geotag": null, "crc": "5ed40af9", "is_index_dirty": false, "original_filename": "gesprekken stoel kraak lachen bij tim.wav", @@ -578,7 +569,6 @@ "created": "2005-03-09T20:37:55", "type": "wav", "avg_rating": 9.0, - "geotag": null, "crc": "67abf9de", "is_index_dirty": false, "original_filename": "Glass A ff.wav", @@ -621,7 +611,6 @@ "created": "2005-03-09T20:40:34", "type": "wav", "avg_rating": 8.5, - "geotag": null, "crc": "f2ec6fe8", "is_index_dirty": false, "original_filename": "Glass A# pp.wav", @@ -664,7 +653,6 @@ "created": "2005-03-09T20:40:34", "type": "wav", "avg_rating": 9.28571428571429, - "geotag": null, "crc": "8d543684", "is_index_dirty": false, "original_filename": "Glass A# ff.wav", @@ -707,7 +695,6 @@ "created": "2005-03-09T20:40:34", "type": "wav", "avg_rating": 0.0, - "geotag": null, "crc": "82a66dea", "is_index_dirty": false, "original_filename": "Glass A# mf.wav", @@ -750,7 +737,6 @@ "created": "2005-03-09T20:40:34", "type": "wav", "avg_rating": 7.0, - "geotag": null, "crc": "3a59171e", "is_index_dirty": false, "original_filename": "Glass A pp.wav", @@ -793,7 +779,6 @@ "created": "2005-03-09T20:40:34", "type": "wav", "avg_rating": 8.5, - "geotag": null, "crc": "217e8b4a", "is_index_dirty": false, "original_filename": "Glass A mf.wav", @@ -836,7 +821,6 @@ "created": "2005-03-09T20:40:34", "type": "wav", "avg_rating": 8.45454545454546, - "geotag": null, "crc": "a3c1ff41", "is_index_dirty": false, "original_filename": "Glass C ff.wav", @@ -879,7 +863,6 @@ "created": "2005-03-09T20:40:34", "type": "wav", "avg_rating": 7.31034482758621, - "geotag": null, "crc": "c3890356", "is_index_dirty": false, "original_filename": "Glass B mf.wav", @@ -922,7 +905,6 @@ "created": "2005-03-09T20:40:34", "type": "wav", "avg_rating": 9.16666666666667, - "geotag": null, "crc": "5069f1d4", "is_index_dirty": false, "original_filename": "Glass B ff.wav", @@ -965,7 +947,6 @@ "created": "2005-03-09T20:40:34", "type": "wav", "avg_rating": 8.17241379310345, - "geotag": null, "crc": "7ec8228c", "is_index_dirty": false, "original_filename": "Glass B pp.wav", @@ -1008,7 +989,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 0.0, - "geotag": null, "crc": "6419a82a", "is_index_dirty": false, "original_filename": "Glass E0 ff.wav", @@ -1051,7 +1031,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 0.0, - "geotag": null, "crc": "85b22677", "is_index_dirty": false, "original_filename": "Glass D# pp.wav", @@ -1094,7 +1073,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 0.0, - "geotag": null, "crc": "0556b934", "is_index_dirty": false, "original_filename": "Glass D# mf.wav", @@ -1137,7 +1115,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 0.0, - "geotag": null, "crc": "a3b6e5dc", "is_index_dirty": false, "original_filename": "Glass D pp.wav", @@ -1180,7 +1157,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 0.0, - "geotag": null, "crc": "5ee6b1f9", "is_index_dirty": false, "original_filename": "Glass D mf.wav", @@ -1223,7 +1199,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 10.0, - "geotag": null, "crc": "3debaa41", "is_index_dirty": false, "original_filename": "Glass D ff.wav", @@ -1266,7 +1241,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 5.0, - "geotag": null, "crc": "4973ba76", "is_index_dirty": false, "original_filename": "Glass C# pp.wav", @@ -1309,7 +1283,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 8.0, - "geotag": null, "crc": "0314d4f8", "is_index_dirty": false, "original_filename": "Glass C# mf.wav", @@ -1352,7 +1325,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 9.33333333333333, - "geotag": null, "crc": "16ecd77f", "is_index_dirty": false, "original_filename": "Glass C# ff.wav", @@ -1395,7 +1367,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 8.0, - "geotag": null, "crc": "9b2f8864", "is_index_dirty": false, "original_filename": "Glass C pp.wav", @@ -1438,7 +1409,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 6.0, - "geotag": null, "crc": "284f8adf", "is_index_dirty": false, "original_filename": "Glass C mf.wav", @@ -1481,7 +1451,6 @@ "created": "2005-03-09T20:40:35", "type": "wav", "avg_rating": 10.0, - "geotag": null, "crc": "bfb7e656", "is_index_dirty": false, "original_filename": "Glass D# ff.wav", @@ -1524,7 +1493,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 0.0, - "geotag": null, "crc": "7456f2ed", "is_index_dirty": false, "original_filename": "Glass F# pp.wav", @@ -1567,7 +1535,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 10.0, - "geotag": null, "crc": "7ccd1598", "is_index_dirty": false, "original_filename": "Glass E0 pp.wav", @@ -1610,7 +1577,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 0.0, - "geotag": null, "crc": "33791194", "is_index_dirty": false, "original_filename": "Glass E0 mf.wav", @@ -1653,7 +1619,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 8.66666666666667, - "geotag": null, "crc": "b3312685", "is_index_dirty": false, "original_filename": "Glass E1 ff.wav", @@ -1696,7 +1661,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 8.66666666666667, - "geotag": null, "crc": "07b5a01b", "is_index_dirty": false, "original_filename": "Glass E1 mf.wav", @@ -1739,7 +1703,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 7.0, - "geotag": null, "crc": "9b65d0aa", "is_index_dirty": false, "original_filename": "Glass E1 pp.wav", @@ -1782,7 +1745,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 0.0, - "geotag": null, "crc": "7be55851", "is_index_dirty": false, "original_filename": "Glass F ff.wav", @@ -1825,7 +1787,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 5.0, - "geotag": null, "crc": "b405d865", "is_index_dirty": false, "original_filename": "Glass F mf.wav", @@ -1868,7 +1829,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 10.0, - "geotag": null, "crc": "c78c5974", "is_index_dirty": false, "original_filename": "Glass F pp.wav", @@ -1911,7 +1871,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 10.0, - "geotag": null, "crc": "76111399", "is_index_dirty": false, "original_filename": "Glass F# ff.wav", @@ -1954,7 +1913,6 @@ "created": "2005-03-09T20:40:36", "type": "wav", "avg_rating": 0.0, - "geotag": null, "crc": "a7956afc", "is_index_dirty": false, "original_filename": "Glass F# mf.wav", @@ -1997,7 +1955,6 @@ "created": "2005-03-09T20:40:37", "type": "wav", "avg_rating": 8.0, - "geotag": null, "crc": "4f7b5dc2", "is_index_dirty": false, "original_filename": "Glass G ff.wav", @@ -2040,7 +1997,6 @@ "created": "2005-03-09T20:40:37", "type": "wav", "avg_rating": 9.0, - "geotag": null, "crc": "286c452b", "is_index_dirty": false, "original_filename": "Glass G mf.wav", diff --git a/sounds/migrations/0053_remove_sound_geotag.py b/sounds/migrations/0053_remove_sound_geotag.py new file mode 100644 index 000000000..6c7ddb5b7 --- /dev/null +++ b/sounds/migrations/0053_remove_sound_geotag.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.23 on 2025-02-04 13:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('sounds', '0052_alter_sound_type'), + ('geotags', '0009_rename_sound2_geotag_sound'), + ] + + operations = [ + migrations.RemoveField( + model_name='sound', + name='geotag', + ), + ] diff --git a/sounds/models.py b/sounds/models.py index 8284f1460..e1f1c9686 100644 --- a/sounds/models.py +++ b/sounds/models.py @@ -444,12 +444,12 @@ def bulk_query_solr(self, sound_ids): sound.num_comments, sound.duration, sound.pack_id, - sound.geotag_id, sound.bitrate, sound.bitdepth, sound.samplerate, sounds_pack.name as pack_name, sounds_license.name as license_name, + geotags_geotag.id as geotag_id, geotags_geotag.lat as geotag_lat, geotags_geotag.lon as geotag_lon, geotags_geotag.location_name as geotag_name, @@ -471,7 +471,7 @@ def bulk_query_solr(self, sound_ids): LEFT JOIN auth_user ON auth_user.id = sound.user_id LEFT JOIN sounds_pack ON sound.pack_id = sounds_pack.id LEFT JOIN sounds_license ON sound.license_id = sounds_license.id - LEFT JOIN geotags_geotag ON sound.geotag_id = geotags_geotag.id + LEFT JOIN geotags_geotag ON sound.id = geotags_geotag.sound_id %s """ % (self.get_analyzers_data_select_sql(), ContentType.objects.get_for_model(Sound).id, @@ -512,10 +512,15 @@ def bulk_query(self, where, order_by, limit, args, include_analyzers_output=Fals sound.license_id, sounds_license.name as license_name, sounds_license.deed_url as license_deed_url, - sound.geotag_id, - geotags_geotag.lat as geotag_lat, - geotags_geotag.lon as geotag_lon, - geotags_geotag.location_name as geotag_name, + geotags_geotag.id, + geotags_geotag.sound_id, + geotags_geotag.lat, + geotags_geotag.lon, + geotags_geotag.zoom, + geotags_geotag.information, + geotags_geotag.location_name, + geotags_geotag.should_update_information, + geotags_geotag.created, tickets_ticket.key as ticket_key, sounds_remixgroup_sounds.id as remixgroup_id, accounts_profile.has_avatar as user_has_avatar, @@ -534,7 +539,7 @@ def bulk_query(self, where, order_by, limit, args, include_analyzers_output=Fals LEFT JOIN accounts_profile ON accounts_profile.user_id = sound.user_id LEFT JOIN sounds_pack ON sound.pack_id = sounds_pack.id LEFT JOIN sounds_license ON sound.license_id = sounds_license.id - LEFT JOIN geotags_geotag ON sound.geotag_id = geotags_geotag.id + LEFT JOIN geotags_geotag ON sound.id = geotags_geotag.sound_id LEFT JOIN tickets_ticket ON tickets_ticket.sound_id = sound.id %s LEFT OUTER JOIN sounds_remixgroup_sounds ON sounds_remixgroup_sounds.sound_id = sound.id @@ -623,7 +628,6 @@ class Sound(models.Model): sources = models.ManyToManyField('self', symmetrical=False, related_name='remixes', blank=True) pack = models.ForeignKey('Pack', null=True, blank=True, default=None, on_delete=models.SET_NULL, related_name='sounds') tags = fields.GenericRelation(TaggedItem) - geotag = models.ForeignKey(GeoTag, null=True, blank=True, default=None, on_delete=models.SET_NULL) # fields for specifying if the sound was uploaded via API or via bulk upload process (or none) uploaded_with_apiv2_client = models.ForeignKey( @@ -1522,7 +1526,7 @@ def on_delete_sound(sender, instance, **kwargs): data['pack'] = pack geotag = None - if instance.geotag: + if hasattr(instance, 'geotag'): geotag = list(GeoTag.objects.filter(pk=instance.geotag.pk).values())[0] data['geotag'] = geotag @@ -1547,16 +1551,13 @@ def on_delete_sound(sender, instance, **kwargs): tag['created'] = str(tag['created']) for comment in data['comments']: comment['created'] = str(comment['created']) - if instance.geotag: + if hasattr(instance, 'geotag'): geotag['created'] = str(geotag['created']) ds.data = data ds.save() - try: - if instance.geotag: - instance.geotag.delete() - except: - pass + if hasattr(instance, 'geotag'): + instance.geotag.delete() instance.delete_from_indexes() instance.unlink_moderation_ticket() @@ -1599,6 +1600,7 @@ def bulk_query_id(self, pack_ids, sound_ids_for_pack_id=dict(), exclude_deleted= Prefetch('sounds', queryset=Sound.public.order_by('-created')), Prefetch('sounds__tags__tag'), Prefetch('sounds__license'), + Prefetch('sounds__geotag'), ).select_related('user').select_related('user__profile').filter(id__in=pack_ids) if exclude_deleted: packs = packs.exclude(is_deleted=True) @@ -1615,7 +1617,7 @@ def bulk_query_id(self, pack_ids, sound_ids_for_pack_id=dict(), exclude_deleted= licenses.append((s.license.name, s.license.id)) if s.num_ratings >= settings.MIN_NUMBER_RATINGS: ratings.append(s.avg_rating) - if not has_geotags and s.geotag_id is not None: + if not has_geotags and hasattr(s, 'geotag'): has_geotags = True should_add_sound_to_selected_sounds = False if sound_ids_pre_selected is None: diff --git a/sounds/tests/test_sound.py b/sounds/tests/test_sound.py index 5b6b411fa..b10a0e62f 100644 --- a/sounds/tests/test_sound.py +++ b/sounds/tests/test_sound.py @@ -39,6 +39,7 @@ from accounts.models import EmailPreferenceType from comments.models import Comment from general.templatetags.filter_img import replace_img +from geotags.models import GeoTag from sounds.forms import PackForm from sounds.models import Download, PackDownload, PackDownloadSound, SoundAnalysis, Pack, Sound, License, DeletedSound from utils.cache import get_template_cache_key @@ -232,6 +233,8 @@ def test_deletedsound_creation(self, delete_sounds_from_search_engine): sound = sounds[0] sound.change_processing_state("OK") sound.change_moderation_state("OK") + GeoTag.objects.create(sound=sound, lat=45.8498, lon=-62.6879, zoom=9) + sound_id = sound.id sound.delete() delete_sounds_from_search_engine.assert_called_once_with([sound_id]) @@ -240,14 +243,15 @@ def test_deletedsound_creation(self, delete_sounds_from_search_engine): ds = DeletedSound.objects.get(sound_id=sound_id) # Check this elements are in the json saved on DeletedSound - keys = ['num_ratings', 'duration', 'id', 'geotag_id', 'comments', + keys = ['num_ratings', 'duration', 'id', 'comments', 'base_filename_slug', 'num_downloads', 'md5', 'description', 'original_path', 'pack_id', 'license', 'created', 'original_filename', 'geotag'] json_data = list(ds.data.keys()) for k in keys: - self.assertTrue(k in json_data) + self.assertTrue(k in json_data, f"{k} not in data") + self.assertEqual(ds.data['geotag']['lat'], 45.8498) def test_pack_delete(self): user, packs, sounds = create_user_and_sounds(num_sounds=5, num_packs=1) @@ -877,7 +881,8 @@ def _test_add_remove_geotag(self, cache_keys, text, request_func, user=None): self.client.force_login(self.user) - self.assertIsNone(self.sound.geotag) + with self.assertRaises(GeoTag.DoesNotExist): + assert self.sound.geotag self.assertNotContains(request_func(user) if user is not None else request_func() if user is not None else request_func(), text) self._assertCachePresent(cache_keys) @@ -915,7 +920,8 @@ def _test_add_remove_geotag(self, cache_keys, text, request_func, user=None): # Check geotag icon being absent self.sound.refresh_from_db() - self.assertIsNone(self.sound.geotag) + with self.assertRaises(GeoTag.DoesNotExist): + assert self.sound.geotag self.assertNotContains(request_func(user) if user is not None else request_func(), text) def test_add_remove_geotag_display(self): diff --git a/sounds/views.py b/sounds/views.py index cf5125486..ca0dcfa6f 100644 --- a/sounds/views.py +++ b/sounds/views.py @@ -515,20 +515,24 @@ def update_edited_sound(sound, data): packs_to_process.append(old_pack) if data["remove_geotag"]: - if sound.geotag: + if hasattr(sound, 'geotag'): sound.geotag.delete() sound.geotag = None else: if data["lat"] and data["lon"] and data["zoom"]: - if sound.geotag: + if hasattr(sound, 'geotag'): sound.geotag.lat = data["lat"] sound.geotag.lon = data["lon"] sound.geotag.zoom = data["zoom"] sound.geotag.should_update_information = True sound.geotag.save() else: - sound.geotag = GeoTag.objects.create( - lat=data["lat"], lon=data["lon"], zoom=data["zoom"], user=request.user) + GeoTag.objects.create( + sound=sound, + lat=data["lat"], + lon=data["lon"], + zoom=data["zoom"] + ) sound_sources = data["sources"] if sound_sources != sound.get_sound_sources_as_set(): @@ -621,9 +625,9 @@ def update_edited_sound(sound, data): name=element.original_filename, license=element.license, pack=element.pack.id if element.pack else None, - lat=element.geotag.lat if element.geotag else None, - lon=element.geotag.lon if element.geotag else None, - zoom=element.geotag.zoom if element.geotag else None, + lat=element.geotag.lat if hasattr(element, 'geotag') else None, + lon=element.geotag.lon if hasattr(element, 'geotag') else None, + zoom=element.geotag.zoom if hasattr(element, 'geotag') else None, sources=','.join([str(item) for item in sound_sources_ids])) else: sound_sources_ids = [] diff --git a/templates/sounds/display_sound.html b/templates/sounds/display_sound.html index e8640ed0a..8deb951df 100644 --- a/templates/sounds/display_sound.html +++ b/templates/sounds/display_sound.html @@ -141,7 +141,7 @@
{{ sound.get_geotag_name|truncatechars:20 }} diff --git a/templates/sounds/display_sound_small_icons.html b/templates/sounds/display_sound_small_icons.html index b71547db1..6dd8db4e9 100644 --- a/templates/sounds/display_sound_small_icons.html +++ b/templates/sounds/display_sound_small_icons.html @@ -22,7 +22,7 @@ {% endif %} - {% if sound.geotag_id %} + {% if sound.geotag %}
diff --git a/templates/sounds/sound.html b/templates/sounds/sound.html index c0af6107d..d3e52335d 100644 --- a/templates/sounds/sound.html +++ b/templates/sounds/sound.html @@ -100,7 +100,7 @@

- {% if sound.geotag_id %} + {% if sound.geotag %}
{% bw_icon 'pin' %} {{ sound.get_geotag_name }} diff --git a/utils/sound_upload.py b/utils/sound_upload.py index 63e4b080e..736eaf5c8 100644 --- a/utils/sound_upload.py +++ b/utils/sound_upload.py @@ -226,24 +226,22 @@ def create_sound(user, # Create geotag from lat,lon,zoom text format if sound_fields['geotag']: lat, lon, zoom = sound_fields['geotag'].split(',') - geotag = GeoTag(user=user, - lat=float(lat), - lon=float(lon), - zoom=int(zoom)) - geotag.save() - sound.geotag = geotag + geotag = GeoTag.objects.create( + sound=sound, + lat=float(lat), + lon=float(lon), + zoom=int(zoom)) else: # Create geotag from lat, lon, zoom separated fields (if available) lat = sound_fields.get('lat', None) lon = sound_fields.get('lon', None) zoom = sound_fields.get('zoom', None) if lat is not None and lon is not None and zoom is not None: - geotag = GeoTag(user=user, - lat=float(lat), - lon=float(lon), - zoom=int(zoom)) - geotag.save() - sound.geotag = geotag + geotag = GeoTag.objects.create( + sound=sound, + lat=float(lat), + lon=float(lon), + zoom=int(zoom)) # 6 set description, tags sound.description = remove_control_chars(sound_fields['description']) diff --git a/utils/tests/tests.py b/utils/tests/tests.py index 864e54c6d..2e00ae465 100644 --- a/utils/tests/tests.py +++ b/utils/tests/tests.py @@ -28,6 +28,7 @@ from django.test import TestCase, override_settings from django.urls import reverse +from geotags.models import GeoTag import utils.downloads from donations.models import Donation, DonationsModalSettings from sounds.models import Sound, Pack, License, Download @@ -431,7 +432,9 @@ def test_bulk_describe_from_csv(self): self.assertEqual(sound1.pack.name, 'ambient') # Check sound has pack and name of pack is 'ambient' sound2 = Sound.objects.get(user=user, original_filename='file5.wav') # Get last correct sound sound2_id = sound2.id # This is used in a test below - self.assertIsNone(sound2.geotag) # Check sound has no geotag + with self.assertRaises(GeoTag.DoesNotExist): + # sound has no geotag + assert sound2.geotag self.assertIsNone(sound2.pack) # Check sound has no pack # Run again using 'force_import' and sounds won't be created because sounds already exist and md5 check fails