From 8d7ede728e8f1d29c65a3cc968776d95f132c71b Mon Sep 17 00:00:00 2001 From: Joseph Nuthalapati Date: Fri, 28 Sep 2018 19:45:35 -0700 Subject: [PATCH] backups: List apps that don't require backup too - This serves two purposes. First is to assure user in the interface that backup of the module is not required. Second is to make sure that if an application is installed during backup it is also reinstalled during restore process (this need to be implemented). - Allow backup test to run independently. Initialize the cfg module so that load_modules() works. Reviewed-by: James Valleroy --- plinth/modules/backups/api.py | 6 +++- plinth/modules/backups/forms.py | 29 ++++++++++----- plinth/modules/backups/tests/test_api.py | 45 +++++++++++++++++++----- plinth/modules/backups/views.py | 4 +-- 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/plinth/modules/backups/api.py b/plinth/modules/backups/api.py index e8ff67257..b4b3016f1 100644 --- a/plinth/modules/backups/api.py +++ b/plinth/modules/backups/api.py @@ -202,7 +202,11 @@ def get_all_apps_for_backup(): if not hasattr(module, 'backup'): continue - apps.append((module_name, module)) + apps.append({ + 'name': module_name, + 'app': module, + 'has_data': bool(module.backup) + }) return apps diff --git a/plinth/modules/backups/forms.py b/plinth/modules/backups/forms.py index 9dcfea0b1..088ed2aba 100644 --- a/plinth/modules/backups/forms.py +++ b/plinth/modules/backups/forms.py @@ -23,12 +23,26 @@ import os from django import forms from django.core import validators from django.core.validators import FileExtensionValidator -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext, ugettext_lazy as _ from . import api from . import get_export_locations, get_archive_path, get_location_path +def _get_app_choices(apps): + """Return a list of check box multiple choices from list of apps.""" + choices = [] + for app in apps: + name = app['app'].name + if not app['has_data']: + name = ugettext('{app} (No data to backup)').format( + app=app['app'].name) + + choices.append((app['name'], name)) + + return choices + + class CreateArchiveForm(forms.Form): name = forms.CharField( label=_('Archive name'), strip=True, @@ -37,17 +51,15 @@ class CreateArchiveForm(forms.Form): ]) selected_apps = forms.MultipleChoiceField( - label=_('Included apps'), - help_text=_('Apps to include in the backup'), + label=_('Included apps'), help_text=_('Apps to include in the backup'), widget=forms.CheckboxSelectMultiple) def __init__(self, *args, **kwargs): """Initialize the form with selectable apps.""" super().__init__(*args, **kwargs) apps = api.get_all_apps_for_backup() - self.fields['selected_apps'].choices = [ - (app[0], app[1].name) for app in apps] - self.fields['selected_apps'].initial = [app[0] for app in apps] + self.fields['selected_apps'].choices = _get_app_choices(apps) + self.fields['selected_apps'].initial = [app['name'] for app in apps] class ExportArchiveForm(forms.Form): @@ -72,9 +84,8 @@ class RestoreForm(forms.Form): """Initialize the form with selectable apps.""" apps = kwargs.pop('apps') super().__init__(*args, **kwargs) - self.fields['selected_apps'].choices = [ - (app[0], app[1].name) for app in apps] - self.fields['selected_apps'].initial = [app[0] for app in apps] + self.fields['selected_apps'].choices = _get_app_choices(apps) + self.fields['selected_apps'].initial = [app['name'] for app in apps] class UploadForm(forms.Form): diff --git a/plinth/modules/backups/tests/test_api.py b/plinth/modules/backups/tests/test_api.py index bd92a5834..c9b257482 100644 --- a/plinth/modules/backups/tests/test_api.py +++ b/plinth/modules/backups/tests/test_api.py @@ -23,7 +23,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile import unittest from unittest.mock import MagicMock, call, patch - +from plinth import cfg, module_loader from plinth.errors import PlinthError from plinth.module_loader import load_modules from .. import api, forms, get_export_locations, get_location_path @@ -52,6 +52,12 @@ def _get_test_manifest(name): class TestBackupProcesses(unittest.TestCase): """Test cases for backup processes""" + @classmethod + def setUpClass(cls): + """Setup all the test cases.""" + super().setUpClass() + cfg.read() + @staticmethod def test_packet_process_manifests(): """Test that directories/files are collected from manifests.""" @@ -82,13 +88,34 @@ class TestBackupProcesses(unittest.TestCase): api.restore_apps(restore_handler) restore_handler.assert_called_once() - @staticmethod - def test_get_all_apps_for_backups(): - """Test that apps supporting backup are included in returned list.""" - load_modules() - apps = api.get_all_apps_for_backup() - assert isinstance(apps, list) - # apps may be empty, if no apps supporting backup are installed. + @patch('plinth.module_loader.loaded_modules.items') + def test_get_all_apps_for_backup(self, modules): + """Test listing apps supporting backup and needing backup.""" + apps = [ + ('a', MagicMock(backup=_get_test_manifest('a'))), + ('b', MagicMock(backup=_get_test_manifest('b'))), + ('c', MagicMock(backup=None)), + ('d', MagicMock()), + ] + del apps[3][1].backup + modules.return_value = apps + + module_loader.load_modules() + returned_apps = api.get_all_apps_for_backup() + expected_apps = [{ + 'name': 'a', + 'app': apps[0][1], + 'has_data': True + }, { + 'name': 'b', + 'app': apps[1][1], + 'has_data': True + }, { + 'name': 'c', + 'app': apps[2][1], + 'has_data': False + }] + self.assertEqual(returned_apps, expected_apps) def test_export_locations(self): """Check get_export_locations returns a list of tuples of length 2.""" @@ -99,7 +126,7 @@ class TestBackupProcesses(unittest.TestCase): @staticmethod def test__get_apps_in_order(): """Test that apps are listed in correct dependency order.""" - load_modules() + module_loader.load_modules() app_names = ['config', 'names'] apps = api._get_apps_in_order(app_names) ordered_app_names = [app[0] for app in apps] diff --git a/plinth/modules/backups/views.py b/plinth/modules/backups/views.py index ea1ff11b1..560b4ca5d 100644 --- a/plinth/modules/backups/views.py +++ b/plinth/modules/backups/views.py @@ -60,7 +60,7 @@ class IndexView(TemplateView): context['exports'] = backups.get_export_files() context['subsubmenu'] = subsubmenu apps = api.get_all_apps_for_backup() - context['available_apps'] = [app[0] for app in apps] + context['available_apps'] = [app['name'] for app in apps] return context @@ -199,7 +199,7 @@ class RestoreView(SuccessMessageMixin, FormView): included_apps = self._get_included_apps() installed_apps = api.get_all_apps_for_backup() kwargs['apps'] = [ - app for app in installed_apps if app[0] in included_apps + app for app in installed_apps if app['name'] in included_apps ] return kwargs