diff --git a/qgis-app/plugins/management/commands/validate_existing_plugins.py b/qgis-app/plugins/management/commands/validate_existing_plugins.py index 0095da85..5ca4e578 100644 --- a/qgis-app/plugins/management/commands/validate_existing_plugins.py +++ b/qgis-app/plugins/management/commands/validate_existing_plugins.py @@ -8,7 +8,7 @@ from django.contrib.sites.models import Site from django.utils.translation import ugettext_lazy as _ -from plugins.models import PluginVersion +from plugins.models import PluginVersion, PluginInvalid from plugins.validator import validator @@ -48,6 +48,7 @@ def validate_zipfile_version(version): 'plugin': f'{version.plugin.name}', 'created_by': f'{version.plugin.created_by}', 'version': f'{version.version}', + 'version_id': version.id, 'msg': [f'File does not exist. Please re-upload.'], 'url': f'http://{DOMAIN}{version.get_absolute_url()}', 'recipients_email': get_recipients_email(version.plugin) @@ -68,6 +69,7 @@ def validate_zipfile_version(version): 'plugin': f'{version.plugin.name}', 'created_by': f'{version.plugin.created_by}', 'version': f'{version.version}', + 'version_id': version.id, 'msg': e.messages, 'url': f'http://{DOMAIN}{version.get_absolute_url()}', 'recipients_email': get_recipients_email(version.plugin) @@ -82,7 +84,9 @@ class Command(BaseCommand): def handle(self, *args, **options): self.stdout.write('Validating existing plugins...') - versions = PluginVersion.objects.all() + # get the latest version + versions = PluginVersion.approved_objects.\ + order_by('plugin_id', '-created_on').distinct('plugin_id').all()[:3] num_count = 0 for version in versions: error_msg = validate_zipfile_version(version) @@ -102,6 +106,12 @@ def handle(self, *args, **options): ) ) num_count += 1 + plugin_version = PluginVersion.objects\ + .select_related('plugin').get(id=error_msg['version_id']) + PluginInvalid.objects.create( + plugin=plugin_version.plugin, + validated_version=plugin_version.version + ) self.stdout.write( _('Successfully sent email notification for %s invalid plugins') % (num_count) diff --git a/qgis-app/plugins/migrations/0002_plugininvalid.py b/qgis-app/plugins/migrations/0002_plugininvalid.py new file mode 100644 index 00000000..0f777898 --- /dev/null +++ b/qgis-app/plugins/migrations/0002_plugininvalid.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.18 on 2021-06-20 10:00 + +from django.db import migrations, models +import django.db.models.deletion +import plugins.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('plugins', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='PluginInvalid', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('validated_version', plugins.models.VersionField(db_index=True, max_length=32, verbose_name='Version')), + ('validated_at', models.DateTimeField(auto_now_add=True, verbose_name='Validated at')), + ('plugin', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='plugins.Plugin')), + ], + ), + ] diff --git a/qgis-app/plugins/models.py b/qgis-app/plugins/models.py index f0e13322..b6d089ac 100644 --- a/qgis-app/plugins/models.py +++ b/qgis-app/plugins/models.py @@ -628,6 +628,25 @@ def delete_plugin_icon(sender, instance, **kw): pass +class PluginInvalid(models.Model): + """Invalid plugins model. + + There were existing plugins on the server before the validation + mechanism updated. + We run the management command to validate them, and track the invalid one + of the latest version. + """ + + plugin = models.ForeignKey(Plugin, on_delete=models.CASCADE) + # We track the version number, not the version instance. + # So that when the version has been deleted, we keep the plugin + # in the tracking list + validated_version = VersionField( + _('Version'), max_length=32, db_index=True) + validated_at = models.DateTimeField( + _('Validated at'), auto_now_add=True, editable=False) + + models.signals.post_delete.connect( delete_version_package, sender=PluginVersion) models.signals.post_delete.connect(delete_plugin_icon, sender=Plugin) diff --git a/qgis-app/plugins/tests/test_models.py b/qgis-app/plugins/tests/test_models.py new file mode 100644 index 00000000..20dae7c1 --- /dev/null +++ b/qgis-app/plugins/tests/test_models.py @@ -0,0 +1,70 @@ +import os + +from django.contrib.auth.models import User +from django.core.files.uploadedfile import InMemoryUploadedFile +from django.test import TestCase, override_settings + +from plugins.models import PluginVersion, Plugin, PluginInvalid + +TESTFILE_DIR = os.path.abspath( + os.path.join(os.path.dirname(__file__), 'testfiles')) + + +@override_settings(MEDIA_ROOT='plugins/tests/testfiles/') +@override_settings(MEDIA_URL='plugins/tests/testfiles/') +class TestPluginInvalidModel(TestCase): + + def setUp(self) -> None: + self.creator = User.objects.create( + username='usertest_creator', + first_name="first_name", + last_name="last_name", + email="creator@example.com", + password="passwordtest", + is_active=True) + self.author = User.objects.create( + username='usertest_author', + first_name="author", + last_name="last_name", + email="author@example.com", + password="passwordtest", + is_active=True) + + invalid_plugin = os.path.join( + TESTFILE_DIR, "web_not_exist.zip") + self.invalid_plugin = open(invalid_plugin, 'rb') + + uploaded_zipfile = InMemoryUploadedFile( + self.invalid_plugin, + field_name='tempfile', + name='testfile.zip', + content_type='application/zip', + size=39889, + charset='utf8') + + self.plugin = Plugin.objects.create( + created_by=self.creator, + name='test_plugin', + package_name='test_plugin' + ) + + self.version = PluginVersion.objects.create( + plugin=self.plugin, + created_by=self.creator, + version='0.1', + package=uploaded_zipfile, + min_qg_version='3.10', + max_qg_version='3.18' + ) + + def tearDown(self) -> None: + self.invalid_plugin.close() + os.remove(self.version.package.url) + + def test_create_PluginInvalid_instance(self): + invalid_plugin = PluginInvalid.objects.create( + plugin=self.plugin, + validated_version=self.plugin.pluginversion_set.get().version + ) + self.assertEqual(invalid_plugin.validated_version, '0.1') + self.assertIsNotNone(invalid_plugin.validated_at) diff --git a/qgis-app/plugins/tests/test_validate_existing_plugins.py b/qgis-app/plugins/tests/test_validate_existing_plugins.py index 6746e5b1..1dd9c849 100644 --- a/qgis-app/plugins/tests/test_validate_existing_plugins.py +++ b/qgis-app/plugins/tests/test_validate_existing_plugins.py @@ -5,7 +5,7 @@ from django.core.files.uploadedfile import InMemoryUploadedFile from django.test import TestCase, override_settings -from plugins.models import PluginVersion, Plugin +from plugins.models import PluginVersion, Plugin, PluginInvalid from plugins.management.commands.validate_existing_plugins import ( validate_zipfile_version, send_email_notification) @@ -16,6 +16,7 @@ @override_settings(MEDIA_ROOT='plugins/tests/testfiles/') @override_settings(MEDIA_URL='plugins/tests/testfiles/') class TestValidateExistingPlugin(TestCase): + def setUp(self) -> None: self.creator = User.objects.create( username='usertest_creator', @@ -72,6 +73,7 @@ def test_validate_zipfile_version(self): 'plugin': 'test_plugin', 'created_by': 'usertest_creator', 'version': '0.1', + 'version_id': self.version.id, 'msg': ['Please provide valid url link for Repository in metadata. ' 'This website cannot be reached.'], 'url': 'http://plugins.qgis.org/plugins/test_plugin/version/0.1/',