From dd1f8c866704b097817c9ef9f0eca63477f33d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sun, 2 Apr 2017 14:29:10 +0200 Subject: [PATCH] general: enhance import/export cmdline commands (#15665) This unifies behaviour with other publik components, it adds support for stdin/stdout using '-' as filename, and adds if-empty and clean flags to the import command. --- combo/data/management/commands/export_site.py | 20 ++++++++++------ combo/data/management/commands/import_site.py | 23 ++++++++++++++---- combo/data/utils.py | 34 +++++++++++++++++++++++++++ combo/manager/views.py | 5 ++-- tests/test_pages.py | 6 ++--- 5 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 combo/data/utils.py diff --git a/combo/data/management/commands/export_site.py b/combo/data/management/commands/export_site.py index aff86b2..aa64776 100644 --- a/combo/data/management/commands/export_site.py +++ b/combo/data/management/commands/export_site.py @@ -15,18 +15,24 @@ # along with this program. If not, see . import json +from optparse import make_option import sys from django.core.management.base import BaseCommand -from combo.data.models import Page +from combo.data.utils import export_site class Command(BaseCommand): - args = ['...'] + args = '' + help = 'Export the site' + option_list = BaseCommand.option_list + ( + make_option('--output', metavar='FILE', default=None, + help='name of a file to write output to'), + ) - def handle(self, json_file='-', *args, **kwargs): - if json_file == '-': - output = sys.stdout + def handle(self, *args, **options): + if options['output'] and options['output'] != '-': + output = open(options['output'], 'w') else: - output = open(json_file, 'w') - json.dump(Page.export_all_for_json(), output, indent=2) + output = sys.stdout + json.dump(export_site(), output, indent=4) diff --git a/combo/data/management/commands/import_site.py b/combo/data/management/commands/import_site.py index a97d864..58d5682 100644 --- a/combo/data/management/commands/import_site.py +++ b/combo/data/management/commands/import_site.py @@ -15,13 +15,28 @@ # along with this program. If not, see . import json +from optparse import make_option +import sys from django.core.management.base import BaseCommand -from combo.data.models import Page +from combo.data.utils import import_site class Command(BaseCommand): - args = ['...'] + args = '' + help = 'Import an exported site' + option_list = BaseCommand.option_list + ( + make_option('--clean', action='store_true', default=False, + help='Clean site before importing'), + make_option('--if-empty', action='store_true', default=False, + help='Import only if site is empty'), + ) - def handle(self, json_file, *args, **kwargs): - Page.load_serialized_pages(json.load(open(json_file))) + def handle(self, filename, *args, **options): + if filename == '-': + fd = sys.stdin + else: + fd = open(filename) + import_site(json.load(fd), + if_empty=options['if_empty'], + clean=options['clean']) diff --git a/combo/data/utils.py b/combo/data/utils.py new file mode 100644 index 0000000..000ce90 --- /dev/null +++ b/combo/data/utils.py @@ -0,0 +1,34 @@ +# combo - content management system +# Copyright (C) 2017 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.db import transaction + +from .models import Page + +def export_site(): + '''Dump site objects to JSON-dumpable dictionnary''' + return Page.export_all_for_json() + + +def import_site(data, if_empty=False, clean=False): + if if_empty and Page.objects.count(): + return + + if clean: + Page.objects.all().delete() + + with transaction.atomic(): + Page.load_serialized_pages(data) diff --git a/combo/manager/views.py b/combo/manager/views.py index feb6bbe..cf098eb 100644 --- a/combo/manager/views.py +++ b/combo/manager/views.py @@ -33,6 +33,7 @@ from django.views.generic import (TemplateView, RedirectView, DetailView, from combo.data.models import Page, CellBase, ParentContentCell from combo.data.library import get_cell_class +from combo.data.utils import export_site, import_site from combo import plugins from .forms import (PageEditTitleForm, PageVisibilityForm, SiteImportForm, @@ -58,7 +59,7 @@ class SiteExportView(ListView): def render_to_response(self, context, **response_kwargs): response = HttpResponse(content_type='application/json') - json.dump(Page.export_all_for_json(), response, indent=2) + json.dump(export_site(), response, indent=2) return response site_export = SiteExportView.as_view() @@ -71,7 +72,7 @@ class SiteImportView(FormView): def form_valid(self, form): json_site = json.load(self.request.FILES['site_json']) - Page.load_serialized_pages(json_site) + import_site(json_site) return super(SiteImportView, self).form_valid(form) site_import = SiteImportView.as_view() diff --git a/tests/test_pages.py b/tests/test_pages.py index 9b6c4ad..0d076b3 100644 --- a/tests/test_pages.py +++ b/tests/test_pages.py @@ -224,13 +224,13 @@ def test_import_export_management_commands(): os.unlink(export_filename) cmd = ExportSiteCommand() - cmd.handle(export_filename) + cmd.handle(output=export_filename) assert os.path.exists(export_filename) stdout = sys.stdout try: sys.stdout = StringIO() - cmd.handle('-') + cmd.handle(output='-') assert sys.stdout.getvalue() == open(export_filename).read() finally: sys.stdout = stdout @@ -238,7 +238,7 @@ def test_import_export_management_commands(): Page.objects.all().delete() cmd = ImportSiteCommand() - cmd.handle(export_filename) + cmd.handle(filename=export_filename, if_empty=False, clean=False) new_page_1 = Page.objects.all().order_by('order')[0] new_page_2 = Page.objects.all().order_by('order')[1] -- 2.11.0