From 369fbb8602211a6c4b96270319e9ae9972d6ac0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= Date: Mon, 18 Jul 2022 16:27:03 +0200 Subject: [PATCH] pricing: import/export pricings (#67196) --- lingo/agendas/models.py | 13 ------ lingo/pricing/forms.py | 1 + lingo/pricing/models.py | 39 +++++++++++----- lingo/pricing/utils.py | 5 ++ lingo/pricing/views.py | 17 ++++++- tests/pricing/manager/test_import_export.py | 3 ++ tests/pricing/test_import_export.py | 51 +++++++++++---------- 7 files changed, 79 insertions(+), 50 deletions(-) diff --git a/lingo/agendas/models.py b/lingo/agendas/models.py index b095f07..5ef9534 100644 --- a/lingo/agendas/models.py +++ b/lingo/agendas/models.py @@ -52,25 +52,16 @@ class Agenda(models.Model): def export_json(self): return { 'slug': self.slug, - 'pricings': [x.export_json() for x in self.old_agendapricings.all()], 'check_type_group': self.check_type_group.slug if self.check_type_group else None, } @classmethod def import_json(cls, data, overwrite=False): - from lingo.pricing.models import AgendaPricing, Pricing - data = copy.deepcopy(data) try: agenda = Agenda.objects.get(slug=data['slug']) except Agenda.DoesNotExist: raise AgendaImportError(_('Missing "%s" agenda') % data['slug']) - pricings = data.pop('pricings', None) or [] - for pricing_data in pricings: - try: - pricing_data['pricing'] = Pricing.objects.get(slug=pricing_data['pricing']) - except Pricing.DoesNotExist: - raise AgendaImportError(_('Missing "%s" pricing model') % pricing_data['pricing']) if data.get('check_type_group'): try: data['check_type_group'] = CheckTypeGroup.objects.get(slug=data['check_type_group']) @@ -80,10 +71,6 @@ class Agenda(models.Model): agenda.check_type_group = data.get('check_type_group') agenda.save() - for pricing_data in pricings: - pricing_data['agenda'] = agenda - AgendaPricing.import_json(pricing_data) - return False, agenda def get_chrono_url(self): diff --git a/lingo/pricing/forms.py b/lingo/pricing/forms.py index c35e5b7..a3520a0 100644 --- a/lingo/pricing/forms.py +++ b/lingo/pricing/forms.py @@ -34,6 +34,7 @@ class ExportForm(forms.Form): label=_('Pricing criteria categories'), required=False, initial=True ) pricing_models = forms.BooleanField(label=_('Pricing models'), required=False, initial=True) + pricings = forms.BooleanField(label=_('Pricings'), required=False, initial=True) class ImportForm(forms.Form): diff --git a/lingo/pricing/models.py b/lingo/pricing/models.py index a4aaa45..c8cc693 100644 --- a/lingo/pricing/models.py +++ b/lingo/pricing/models.py @@ -343,25 +343,42 @@ class AgendaPricing(models.Model): date_end = models.DateField() pricing_data = JSONField(null=True) - @classmethod - def import_json(cls, data): - data = clean_import_data(cls, data) - cls.objects.update_or_create( - agenda=data['agenda'], - pricing=data['pricing'], - date_start=data['date_start'], - date_end=data['date_end'], - defaults=data, - ) - def export_json(self): return { 'pricing': self.pricing.slug, 'date_start': self.date_start.strftime('%Y-%m-%d'), 'date_end': self.date_end.strftime('%Y-%m-%d'), 'pricing_data': self.pricing_data, + 'agendas': [a.slug for a in self.agendas.all()], } + @classmethod + def import_json(cls, data, overwrite=False): + data = copy.deepcopy(data) + agenda_slugs = data.pop('agendas', None) or [] + data = clean_import_data(cls, data) + agendas = [] + for agenda_slug in agenda_slugs: + try: + agendas.append(Agenda.objects.get(slug=agenda_slug)) + except Agenda.DoesNotExist: + raise AgendaImportError(_('Missing "%s" agenda') % agenda_slug) + try: + data['pricing'] = Pricing.objects.get(slug=data['pricing']) + except Pricing.DoesNotExist: + raise AgendaImportError(_('Missing "%s" pricing model') % data['pricing']) + + agenda_pricing, created = cls.objects.update_or_create( + pricing=data['pricing'], + date_start=data['date_start'], + date_end=data['date_end'], + defaults=data, + ) + if overwrite and not created: + agenda_pricing.agendas.clear() + agenda_pricing.agendas.add(*agendas) + return created, agenda_pricing + @staticmethod def get_pricing_data( request, diff --git a/lingo/pricing/utils.py b/lingo/pricing/utils.py index 6288feb..7312a81 100644 --- a/lingo/pricing/utils.py +++ b/lingo/pricing/utils.py @@ -27,9 +27,12 @@ def export_site( check_type_groups=True, pricing_categories=True, pricing_models=True, + pricings=True, ): '''Dump site objects to JSON-dumpable dictionnary''' data = collections.OrderedDict() + if pricings: + data['pricings'] = [x.export_json() for x in AgendaPricing.objects.all()] if pricing_models: data['pricing_models'] = [x.export_json() for x in Pricing.objects.all()] if pricing_categories: @@ -63,6 +66,7 @@ def import_site(data, if_empty=False, clean=False, overwrite=False): 'check_type_groups', 'pricing_categories', 'pricing_models', + 'pricings', ] } @@ -72,6 +76,7 @@ def import_site(data, if_empty=False, clean=False, overwrite=False): (Pricing, 'pricing_models'), (CheckTypeGroup, 'check_type_groups'), (Agenda, 'agendas'), + (AgendaPricing, 'pricings'), ): objs = data.get(key, []) for obj in objs: diff --git a/lingo/pricing/views.py b/lingo/pricing/views.py index 242984f..dfc440d 100644 --- a/lingo/pricing/views.py +++ b/lingo/pricing/views.py @@ -151,6 +151,20 @@ class ConfigImportView(FormView): x, ), }, + 'pricings': { + 'create_noop': _('No pricing created.'), + 'create': lambda x: ungettext( + 'A pricing has been created.', + '%(count)d pricings have been created.', + x, + ), + 'update_noop': _('No pricing updated.'), + 'update': lambda x: ungettext( + 'A pricing has been updated.', + '%(count)d pricings have been updated.', + x, + ), + }, } global_noop = True @@ -192,7 +206,7 @@ class ConfigImportView(FormView): # only one criteria category imported, redirect to criteria page return HttpResponseRedirect(reverse('lingo-manager-pricing-criteria-list')) if (a_count, ct_count, pc_count, pm_count) == (0, 0, 0, 1): - # only one pricing imported, redirect to pricing page + # only one pricing model imported, redirect to pricing model page return HttpResponseRedirect( reverse( 'lingo-manager-pricing-detail', @@ -207,6 +221,7 @@ class ConfigImportView(FormView): messages.info(self.request, results['check_type_groups']['messages']) messages.info(self.request, results['pricing_categories']['messages']) messages.info(self.request, results['pricing_models']['messages']) + messages.info(self.request, results['pricings']['messages']) return super().form_valid(form) diff --git a/tests/pricing/manager/test_import_export.py b/tests/pricing/manager/test_import_export.py index 2a06971..d50ab9b 100644 --- a/tests/pricing/manager/test_import_export.py +++ b/tests/pricing/manager/test_import_export.py @@ -27,6 +27,7 @@ def test_export_site(freezer, app, admin_user): 'check_type_groups': [], 'pricing_categories': [], 'pricing_models': [], + 'pricings': [], } Agenda.objects.create(label='Foo Bar') @@ -41,6 +42,7 @@ def test_export_site(freezer, app, admin_user): resp.form['check_type_groups'] = False resp.form['pricing_categories'] = False resp.form['pricing_models'] = False + resp.form['pricings'] = False resp = resp.form.submit() site_json = json.loads(resp.text) @@ -48,6 +50,7 @@ def test_export_site(freezer, app, admin_user): assert 'check_type_groups' not in site_json assert 'pricing_categories' not in site_json assert 'pricing_models' not in site_json + assert 'pricings' not in site_json @pytest.mark.freeze_time('2021-07-08') diff --git a/tests/pricing/test_import_export.py b/tests/pricing/test_import_export.py index de8700e..6ccfa3c 100644 --- a/tests/pricing/test_import_export.py +++ b/tests/pricing/test_import_export.py @@ -28,10 +28,9 @@ def get_output_of_command(command, *args, **kwargs): def test_import_export(app): - agenda = Agenda.objects.create(label='Foo Bar') + Agenda.objects.create(label='Foo Bar') pricing = Pricing.objects.create(label='Foo') AgendaPricing.objects.create( - agenda=agenda, pricing=pricing, date_start=datetime.date(year=2021, month=9, day=1), date_end=datetime.date(year=2021, month=10, day=1), @@ -40,12 +39,14 @@ def test_import_export(app): output = get_output_of_command('export_pricing_config') assert len(json.loads(output)['agendas']) == 1 + assert len(json.loads(output)['pricings']) == 1 + assert len(json.loads(output)['pricings'][0]['agendas']) == 0 assert len(json.loads(output)['pricing_models']) == 1 assert len(json.loads(output)['pricing_categories']) == 1 import_site(data={}, clean=True) empty_output = get_output_of_command('export_pricing_config') assert len(json.loads(empty_output)['agendas']) == 1 - assert len(json.loads(empty_output)['agendas'][0]['pricings']) == 0 + assert len(json.loads(empty_output)['pricings']) == 0 assert len(json.loads(empty_output)['pricing_models']) == 0 assert len(json.loads(empty_output)['pricing_categories']) == 0 @@ -53,7 +54,6 @@ def test_import_export(app): sys.stdin = StringIO(json.dumps({})) pricing = Pricing.objects.create(label='Foo') AgendaPricing.objects.create( - agenda=agenda, pricing=pricing, date_start=datetime.date(year=2021, month=9, day=1), date_end=datetime.date(year=2021, month=10, day=1), @@ -92,11 +92,10 @@ def test_import_export(app): shutil.rmtree(tempdir) -def test_import_export_agenda_with_pricing(app): +def test_import_export_agenda_pricing(app): pricing = Pricing.objects.create(label='Foo') agenda = Agenda.objects.create(label='Foo Bar') agenda_pricing = AgendaPricing.objects.create( - agenda=agenda, pricing=pricing, date_start=datetime.date(year=2021, month=9, day=1), date_end=datetime.date(year=2021, month=10, day=1), @@ -104,59 +103,61 @@ def test_import_export_agenda_with_pricing(app): 'foo': 'bar', }, ) + agenda_pricing.agendas.set([agenda]) output = get_output_of_command('export_pricing_config') import_site(data={}, clean=True) assert Pricing.objects.count() == 0 data = json.loads(output) - del data['pricing_models'] - - with pytest.raises(AgendaImportError) as excinfo: - import_site(data, overwrite=True) - assert str(excinfo.value) == 'Missing "foo" pricing model' - - Pricing.objects.create(label='foobar') - with pytest.raises(AgendaImportError) as excinfo: - import_site(data, overwrite=True) - assert str(excinfo.value) == 'Missing "foo" pricing model' Agenda.objects.all().delete() - pricing = Pricing.objects.create(label='Foo') with pytest.raises(AgendaImportError) as excinfo: import_site(data, overwrite=True) assert str(excinfo.value) == 'Missing "foo-bar" agenda' + agenda2 = Agenda.objects.create(label='Baz') + with pytest.raises(AgendaImportError) as excinfo: + import_site(data, overwrite=True) + assert str(excinfo.value) == 'Missing "foo-bar" agenda' + + del data['pricing_models'] + Pricing.objects.all().delete() agenda = Agenda.objects.create(label='Foo Bar') + with pytest.raises(AgendaImportError) as excinfo: + import_site(data, overwrite=True) + assert str(excinfo.value) == 'Missing "foo" pricing model' + + pricing = Pricing.objects.create(label='Foo') import_site(data, overwrite=True) - assert agenda.old_agendapricings.count() == 1 - agenda_pricing = agenda.old_agendapricings.get() - assert agenda_pricing.agenda == agenda + agenda_pricing = AgendaPricing.objects.latest('pk') + assert list(agenda_pricing.agendas.all()) == [agenda] assert agenda_pricing.pricing == pricing assert agenda_pricing.date_start == datetime.date(year=2021, month=9, day=1) assert agenda_pricing.date_end == datetime.date(year=2021, month=10, day=1) + assert agenda_pricing.pricing_data == {'foo': 'bar'} # again import_site(data) - assert agenda.old_agendapricings.count() == 1 agenda_pricing = AgendaPricing.objects.get(pk=agenda_pricing.pk) - assert agenda_pricing.agenda == agenda + assert list(agenda_pricing.agendas.all()) == [agenda] assert agenda_pricing.pricing == pricing - data['agendas'][0]['pricings'].append( + data['pricings'].append( { 'pricing': 'foo', + 'agendas': ['foo-bar', 'baz'], 'date_start': '2022-09-01', 'date_end': '2022-10-01', 'pricing_data': {'foo': 'bar'}, } ) import_site(data) - assert agenda.old_agendapricings.count() == 2 agenda_pricing = AgendaPricing.objects.latest('pk') - assert agenda_pricing.agenda == agenda + assert list(agenda_pricing.agendas.all().order_by('slug')) == [agenda2, agenda] assert agenda_pricing.pricing == pricing assert agenda_pricing.date_start == datetime.date(year=2022, month=9, day=1) assert agenda_pricing.date_end == datetime.date(year=2022, month=10, day=1) + assert agenda_pricing.pricing_data == {'foo': 'bar'} def test_import_export_agenda_with_check_types(app):