Projet

Général

Profil

0003-pricing-import-export-check-types-65459.patch

Lauréline Guérin, 23 mai 2022 17:03

Télécharger (25 ko)

Voir les différences:

Subject: [PATCH 3/3] pricing: import/export check types (#65459)

 .../migrations/0003_check_type_group.py       | 23 +++++
 lingo/agendas/models.py                       | 16 ++++
 lingo/pricing/forms.py                        |  1 +
 .../lingo/pricing/manager_agenda_detail.html  | 36 ++++++++
 .../lingo/pricing/manager_agenda_form.html    | 22 +++++
 lingo/pricing/urls.py                         |  5 ++
 lingo/pricing/utils.py                        | 13 ++-
 lingo/pricing/views.py                        | 53 +++++++++---
 tests/pricing/manager/test_agenda.py          | 17 +++-
 tests/pricing/manager/test_import_export.py   | 72 +++++++++++++++-
 tests/pricing/test_import_export.py           | 86 ++++++++++++++++++-
 11 files changed, 325 insertions(+), 19 deletions(-)
 create mode 100644 lingo/agendas/migrations/0003_check_type_group.py
 create mode 100644 lingo/pricing/templates/lingo/pricing/manager_agenda_form.html
lingo/agendas/migrations/0003_check_type_group.py
1
import django.db.models.deletion
2
from django.db import migrations, models
3

  
4

  
5
class Migration(migrations.Migration):
6

  
7
    dependencies = [
8
        ('agendas', '0002_agenda'),
9
    ]
10

  
11
    operations = [
12
        migrations.AddField(
13
            model_name='agenda',
14
            name='check_type_group',
15
            field=models.ForeignKey(
16
                blank=True,
17
                null=True,
18
                on_delete=django.db.models.deletion.SET_NULL,
19
                to='agendas.CheckTypeGroup',
20
                verbose_name='Check type group',
21
            ),
22
        ),
23
    ]
lingo/agendas/models.py
28 28
    slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
29 29
    category_label = models.CharField(_('Category label'), max_length=150, null=True)
30 30
    category_slug = models.SlugField(_('Category identifier'), max_length=160, null=True)
31
    check_type_group = models.ForeignKey(
32
        'CheckTypeGroup',
33
        verbose_name=_('Check type group'),
34
        blank=True,
35
        null=True,
36
        on_delete=models.SET_NULL,
37
    )
31 38

  
32 39
    def __str__(self):
33 40
        return self.label
......
45 52
        return {
46 53
            'slug': self.slug,
47 54
            'pricings': [x.export_json() for x in self.agendapricing_set.all()],
55
            'check_type_group': self.check_type_group.slug if self.check_type_group else None,
48 56
        }
49 57

  
50 58
    @classmethod
......
62 70
                pricing_data['pricing'] = Pricing.objects.get(slug=pricing_data['pricing'])
63 71
            except Pricing.DoesNotExist:
64 72
                raise AgendaImportError(_('Missing "%s" pricing model') % pricing_data['pricing'])
73
        if data.get('check_type_group'):
74
            try:
75
                data['check_type_group'] = CheckTypeGroup.objects.get(slug=data['check_type_group'])
76
            except CheckTypeGroup.DoesNotExist:
77
                raise AgendaImportError(_('Missing "%s" check type group') % data['check_type_group'])
78

  
79
        agenda.check_type_group = data.get('check_type_group')
80
        agenda.save()
65 81

  
66 82
        for pricing_data in pricings:
67 83
            pricing_data['agenda'] = agenda
lingo/pricing/forms.py
25 25

  
26 26
class ExportForm(forms.Form):
27 27
    agendas = forms.BooleanField(label=_('Agendas'), required=False, initial=True)
28
    check_type_groups = forms.BooleanField(label=_('Check type groups'), required=False, initial=True)
28 29
    pricing_categories = forms.BooleanField(
29 30
        label=_('Pricing criteria categories'), required=False, initial=True
30 31
    )
lingo/pricing/templates/lingo/pricing/manager_agenda_detail.html
36 36
  {% endif %}
37 37
</div>
38 38
</div>
39

  
40
<div class="section">
41
<h3>{% trans "Booking check options" %}
42
    <a rel="popup" class="button" href="{% url 'lingo-manager-agenda-booking-check-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
43
</h3>
44
<div>
45
    <ul>
46
    {% if agenda.check_type_group %}
47
    <li>{% trans "Check type group:" %} {{ agenda.check_type_group }}
48
        <ul>
49
            <li>{% trans "Absences:" %}
50
                <ul>
51
                  {% for check_type in agenda.check_type_group.check_types.absences %}
52
                  <li>{{ check_type }}</li>
53
                  {% empty %}
54
                  <li>({% trans "No absence check type defined" %})</li>
55
                  {% endfor %}
56
                </ul>
57
            </li>
58
            <li>{% trans "Presences:" %}
59
                <ul>
60
                  {% for check_type in agenda.check_type_group.check_types.presences %}
61
                  <li>{{ check_type }}</li>
62
                  {% empty %}
63
                  <li>({% trans "No presence check type defined" %})</li>
64
                  {% endfor %}
65
                </ul>
66
            </li>
67
        </ul>
68
    </li>
69
    {% else %}
70
    <li>{% trans "No check types configured for this agenda." %}</li>
71
    {% endif %}
72
    </ul>
73
</div>
74
</div>
39 75
{% endblock %}
lingo/pricing/templates/lingo/pricing/manager_agenda_form.html
1
{% extends "lingo/pricing/manager_agenda_detail.html" %}
2
{% load i18n %}
3

  
4
{% block breadcrumb %}
5
{{ block.super }}
6
<a href="{{ form_url }}">{{ title }}</a>
7
{% endblock %}
8

  
9
{% block appbar %}
10
<h2>{{ title }}</h2>
11
{% endblock %}
12

  
13
{% block content %}
14
<form method="post" enctype="multipart/form-data">
15
  {% csrf_token %}
16
  {{ form.as_p }}
17
  <div class="buttons">
18
    <button class="submit-button">{% trans "Save" %}</button>
19
    <a class="cancel" href="{% url 'lingo-manager-agenda-detail' agenda.pk %}">{% trans 'Cancel' %}</a>
20
  </div>
21
</form>
22
{% endblock %}
lingo/pricing/urls.py
143 143
        staff_member_required(views.agenda_export),
144 144
        name='lingo-manager-agenda-export',
145 145
    ),
146
    url(
147
        r'^agenda/(?P<pk>\d+)/check-options/$',
148
        staff_member_required(views.agenda_booking_check_settings),
149
        name='lingo-manager-agenda-booking-check-settings',
150
    ),
146 151
    url(
147 152
        r'^agenda/(?P<pk>\d+)/pricing/add/$',
148 153
        staff_member_required(views.agenda_pricing_add),
lingo/pricing/utils.py
18 18

  
19 19
from django.db import transaction
20 20

  
21
from lingo.agendas.models import Agenda
21
from lingo.agendas.models import Agenda, CheckTypeGroup
22 22
from lingo.pricing.models import AgendaPricing, CriteriaCategory, Pricing
23 23

  
24 24

  
25 25
def export_site(
26 26
    agendas=True,
27
    check_type_groups=True,
27 28
    pricing_categories=True,
28 29
    pricing_models=True,
29 30
):
......
33 34
        data['pricing_models'] = [x.export_json() for x in Pricing.objects.all()]
34 35
    if pricing_categories:
35 36
        data['pricing_categories'] = [x.export_json() for x in CriteriaCategory.objects.all()]
37
    if check_type_groups:
38
        data['check_type_groups'] = [x.export_json() for x in CheckTypeGroup.objects.all()]
36 39
    if agendas:
37 40
        data['agendas'] = [x.export_json() for x in Agenda.objects.all()]
38 41
    return data
......
40 43

  
41 44
def import_site(data, if_empty=False, clean=False, overwrite=False):
42 45
    if if_empty and (
43
        AgendaPricing.objects.exists() or CriteriaCategory.objects.exists() or Pricing.objects.exists()
46
        AgendaPricing.objects.exists()
47
        or CheckTypeGroup.objects.exists()
48
        or CriteriaCategory.objects.exists()
49
        or Pricing.objects.exists()
44 50
    ):
45 51
        return
46 52

  
......
48 54
        AgendaPricing.objects.all().delete()
49 55
        CriteriaCategory.objects.all().delete()
50 56
        Pricing.objects.all().delete()
57
        CheckTypeGroup.objects.all().delete()
51 58

  
52 59
    results = {
53 60
        key: collections.defaultdict(list)
54 61
        for key in [
55 62
            'agendas',
63
            'check_type_groups',
56 64
            'pricing_categories',
57 65
            'pricing_models',
58 66
        ]
......
62 70
        for cls, key in (
63 71
            (CriteriaCategory, 'pricing_categories'),
64 72
            (Pricing, 'pricing_models'),
73
            (CheckTypeGroup, 'check_type_groups'),
65 74
            (Agenda, 'agendas'),
66 75
        ):
67 76
            objs = data.get(key, [])
lingo/pricing/views.py
108 108
                    x,
109 109
                ),
110 110
            },
111
            'check_type_groups': {
112
                'create_noop': _('No check type group created.'),
113
                'create': lambda x: ungettext(
114
                    'A check type group has been created.',
115
                    '%(count)d check type groups have been created.',
116
                    x,
117
                ),
118
                'update_noop': _('No check type group updated.'),
119
                'update': lambda x: ungettext(
120
                    'A check type group has been updated.',
121
                    '%(count)d check type groups have been updated.',
122
                    x,
123
                ),
124
            },
111 125
            'pricing_categories': {
112 126
                'create_noop': _('No pricing criteria category created.'),
113 127
                'create': lambda x: ungettext(
......
156 170

  
157 171
                obj_results['messages'] = "%s %s" % (message1, message2)
158 172

  
159
        a_count, pc_count, pm_count = (
173
        a_count, ct_count, pc_count, pm_count = (
160 174
            len(results['agendas']['all']),
175
            len(results['check_type_groups']['all']),
161 176
            len(results['pricing_categories']['all']),
162 177
            len(results['pricing_models']['all']),
163 178
        )
164
        if (a_count, pc_count, pm_count) == (1, 0, 0):
179
        if (a_count, ct_count, pc_count, pm_count) == (1, 0, 0, 0):
165 180
            # only one agenda imported, redirect to agenda page
166 181
            return HttpResponseRedirect(
167 182
                reverse(
......
169 184
                    kwargs={'pk': results['agendas']['all'][0].pk},
170 185
                )
171 186
            )
172
        if (a_count, pc_count, pm_count) == (0, 1, 0):
187
        if (a_count, ct_count, pc_count, pm_count) == (0, 1, 0, 0):
188
            # only one check type group imported, redirect to check type page
189
            return HttpResponseRedirect(reverse('lingo-manager-check-type-list'))
190
        if (a_count, ct_count, pc_count, pm_count) == (0, 0, 1, 0):
173 191
            # only one criteria category imported, redirect to criteria page
174 192
            return HttpResponseRedirect(reverse('lingo-manager-pricing-criteria-list'))
175
        if (a_count, pc_count, pm_count) == (0, 0, 1):
193
        if (a_count, ct_count, pc_count, pm_count) == (0, 0, 0, 1):
176 194
            # only one pricing imported, redirect to pricing page
177 195
            return HttpResponseRedirect(
178 196
                reverse(
......
185 203
            messages.info(self.request, _('No data found.'))
186 204
        else:
187 205
            messages.info(self.request, results['agendas']['messages'])
206
            messages.info(self.request, results['check_type_groups']['messages'])
188 207
            messages.info(self.request, results['pricing_categories']['messages'])
189 208
            messages.info(self.request, results['pricing_models']['messages'])
190 209

  
......
646 665
agenda_export = AgendaExport.as_view()
647 666

  
648 667

  
668
class AgendaBookingCheckSettingsView(AgendaMixin, UpdateView):
669
    template_name = 'lingo/pricing/manager_agenda_form.html'
670
    model = Agenda
671
    fields = ['check_type_group']
672

  
673
    def get_context_data(self, **kwargs):
674
        context = super().get_context_data(**kwargs)
675
        context['form_url'] = reverse('lingo-manager-agenda-booking-check-settings', args=[self.agenda.pk])
676
        context['title'] = _("Configure booking check options")
677
        return context
678

  
679

  
680
agenda_booking_check_settings = AgendaBookingCheckSettingsView.as_view()
681

  
682

  
649 683
class AgendaPricingAddView(AgendaMixin, CreateView):
650 684
    template_name = 'lingo/pricing/manager_agenda_pricing_form.html'
651 685
    model = AgendaPricing
......
663 697
    pk_url_kwarg = 'pricing_pk'
664 698
    template_name = 'lingo/pricing/manager_agenda_pricing_detail.html'
665 699

  
666
    def set_agenda(self, **kwargs):
667
        self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'))
668

  
669 700
    def get_queryset(self):
670 701
        return AgendaPricing.objects.filter(agenda=self.agenda).prefetch_related(
671 702
            'pricing__criterias__category'
......
681 712
    pk_url_kwarg = 'pricing_pk'
682 713
    form_class = AgendaPricingForm
683 714

  
684
    def set_agenda(self, **kwargs):
685
        self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'))
686

  
687 715
    def get_queryset(self):
688 716
        return AgendaPricing.objects.filter(agenda=self.agenda)
689 717

  
......
699 727
    model = AgendaPricing
700 728
    pk_url_kwarg = 'pricing_pk'
701 729

  
702
    def set_agenda(self, **kwargs):
703
        self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'))
704

  
705 730
    def get_queryset(self):
706 731
        return AgendaPricing.objects.filter(agenda=self.agenda)
707 732

  
......
713 738
    template_name = 'lingo/pricing/manager_agenda_pricing_matrix_form.html'
714 739

  
715 740
    def set_agenda(self, **kwargs):
716
        self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'))
741
        super().set_agenda(**kwargs)
717 742
        self.object = get_object_or_404(
718 743
            AgendaPricing.objects.filter(agenda=self.agenda), pk=kwargs['pricing_pk']
719 744
        )
tests/pricing/manager/test_agenda.py
3 3

  
4 4
import pytest
5 5

  
6
from lingo.agendas.models import Agenda
6
from lingo.agendas.models import Agenda, CheckTypeGroup
7 7
from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory, Pricing
8 8
from tests.utils import login
9 9

  
......
698 698

  
699 699
    app = login(app)
700 700
    app.get('/manage/pricing/agenda/%s/pricing/%s/matrix/edit/' % (agenda.pk, agenda_pricing.pk), status=404)
701

  
702

  
703
def test_edit_agenda_check_type_group(app, admin_user):
704
    agenda = Agenda.objects.create(label='Foo bar')
705
    group = CheckTypeGroup.objects.create(label='Foo bar')
706

  
707
    app = login(app)
708
    resp = app.get('/manage/pricing/agenda/%s/' % agenda.pk)
709
    assert 'No check types configured for this agenda.' in resp
710
    resp = resp.click(href='/manage/pricing/agenda/%s/check-options' % agenda.pk)
711
    resp.form['check_type_group'] = group.pk
712
    resp = resp.form.submit().follow()
713
    agenda.refresh_from_db()
714
    assert agenda.check_type_group == group
715
    assert 'Check type group: Foo bar' in resp
tests/pricing/manager/test_import_export.py
4 4
import pytest
5 5
from webtest import Upload
6 6

  
7
from lingo.agendas.models import Agenda
7
from lingo.agendas.models import Agenda, CheckType, CheckTypeGroup
8 8
from lingo.pricing.models import Criteria, CriteriaCategory, Pricing
9 9
from tests.utils import login
10 10

  
......
24 24
    site_json = json.loads(resp.text)
25 25
    assert site_json == {
26 26
        'agendas': [],
27
        'check_type_groups': [],
27 28
        'pricing_categories': [],
28 29
        'pricing_models': [],
29 30
    }
......
37 38

  
38 39
    resp = app.get('/manage/pricing/export/')
39 40
    resp.form['agendas'] = False
41
    resp.form['check_type_groups'] = False
40 42
    resp.form['pricing_categories'] = False
41 43
    resp.form['pricing_models'] = False
42 44
    resp = resp.form.submit()
43 45

  
44 46
    site_json = json.loads(resp.text)
45 47
    assert 'agendas' not in site_json
48
    assert 'check_type_groups' not in site_json
46 49
    assert 'pricing_categories' not in site_json
47 50
    assert 'pricing_models' not in site_json
48 51

  
......
233 236
    resp = resp.follow()
234 237
    assert '2 agendas have been updated.' in resp.text
235 238
    assert Agenda.objects.count() == 2
239

  
240

  
241
@pytest.mark.freeze_time('2021-07-08')
242
def test_import_check_type_group(app, admin_user):
243
    group = CheckTypeGroup.objects.create(label='Foo bar')
244
    CheckType.objects.create(label='Foo reason', group=group)
245
    CheckType.objects.create(label='Baz', group=group)
246

  
247
    app = login(app)
248
    resp = app.get('/manage/pricing/check-type/group/%s/export/' % group.id)
249
    assert resp.headers['content-type'] == 'application/json'
250
    assert (
251
        resp.headers['content-disposition']
252
        == 'attachment; filename="export_check_type_group_foo-bar_20210708.json"'
253
    )
254
    group_export = resp.text
255

  
256
    # existing group
257
    resp = app.get('/manage/pricing/', status=200)
258
    resp = resp.click('Import')
259
    resp.form['config_json'] = Upload('export.json', group_export.encode('utf-8'), 'application/json')
260
    resp = resp.form.submit()
261
    assert resp.location.endswith('/manage/pricing/check-types/')
262
    resp = resp.follow()
263
    assert 'No check type group created. A check type group has been updated.' not in resp.text
264
    assert CheckTypeGroup.objects.count() == 1
265
    assert CheckType.objects.count() == 2
266

  
267
    # new group
268
    CheckTypeGroup.objects.all().delete()
269
    resp = app.get('/manage/pricing/', status=200)
270
    resp = resp.click('Import')
271
    resp.form['config_json'] = Upload('export.json', group_export.encode('utf-8'), 'application/json')
272
    resp = resp.form.submit()
273
    assert resp.location.endswith('/manage/pricing/check-types/')
274
    resp = resp.follow()
275
    assert 'A check type group has been created. No check type group updated.' not in resp.text
276
    assert CheckTypeGroup.objects.count() == 1
277
    assert CheckType.objects.count() == 2
278

  
279
    # multiple groups
280
    groups = json.loads(group_export)
281
    groups['check_type_groups'].append(copy.copy(groups['check_type_groups'][0]))
282
    groups['check_type_groups'].append(copy.copy(groups['check_type_groups'][0]))
283
    groups['check_type_groups'][1]['label'] = 'Foo bar 2'
284
    groups['check_type_groups'][1]['slug'] = 'foo-bar-2'
285
    groups['check_type_groups'][2]['label'] = 'Foo bar 3'
286
    groups['check_type_groups'][2]['slug'] = 'foo-bar-3'
287

  
288
    resp = app.get('/manage/pricing/', status=200)
289
    resp = resp.click('Import')
290
    resp.form['config_json'] = Upload('export.json', json.dumps(groups).encode('utf-8'), 'application/json')
291
    resp = resp.form.submit()
292
    assert resp.location.endswith('/manage/pricing/')
293
    resp = resp.follow()
294
    assert '2 check type groups have been created. A check type group has been updated.' in resp.text
295
    assert CheckTypeGroup.objects.count() == 3
296
    assert CheckType.objects.count() == 6
297

  
298
    CheckTypeGroup.objects.all().delete()
299
    resp = app.get('/manage/pricing/', status=200)
300
    resp = resp.click('Import')
301
    resp.form['config_json'] = Upload('export.json', json.dumps(groups).encode('utf-8'), 'application/json')
302
    resp = resp.form.submit().follow()
303
    assert '3 check type groups have been created. No check type group updated.' in resp.text
304
    assert CheckTypeGroup.objects.count() == 3
305
    assert CheckType.objects.count() == 6
tests/pricing/test_import_export.py
11 11
from django.core.management import call_command
12 12
from django.utils.encoding import force_bytes
13 13

  
14
from lingo.agendas.models import Agenda
14
from lingo.agendas.models import Agenda, CheckType, CheckTypeGroup
15 15
from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory, Pricing, PricingCriteriaCategory
16 16
from lingo.pricing.utils import import_site
17 17
from lingo.utils.misc import AgendaImportError
......
159 159
    assert agenda_pricing.date_end == datetime.date(year=2022, month=10, day=1)
160 160

  
161 161

  
162
def test_import_export_agenda_with_check_types(app):
163
    group = CheckTypeGroup.objects.create(label='foo')
164
    agenda = Agenda.objects.create(label='Foo Bar', check_type_group=group)
165
    output = get_output_of_command('export_site')
166

  
167
    import_site(data={}, clean=True)
168
    assert CheckTypeGroup.objects.count() == 0
169
    data = json.loads(output)
170
    del data['check_type_groups']
171
    agenda.check_type_group = None
172
    agenda.save()
173

  
174
    with pytest.raises(AgendaImportError) as excinfo:
175
        import_site(data, overwrite=True)
176
    assert str(excinfo.value) == 'Missing "foo" check type group'
177

  
178
    CheckTypeGroup.objects.create(label='foobar')
179
    with pytest.raises(AgendaImportError) as excinfo:
180
        import_site(data, overwrite=True)
181
    assert str(excinfo.value) == 'Missing "foo" check type group'
182

  
183
    group = CheckTypeGroup.objects.create(label='foo')
184
    import_site(data, overwrite=True)
185
    agenda.refresh_from_db()
186
    assert agenda.check_type_group == group
187

  
188

  
162 189
def test_import_export_pricing_criteria_category(app):
163 190
    output = get_output_of_command('export_site')
164 191
    payload = json.loads(output)
......
372 399
        35,
373 400
    ]
374 401
    assert set(pricing.criterias.all()) == {criteria1, criteria3}
402

  
403

  
404
def test_import_export_check_type_group(app):
405
    output = get_output_of_command('export_site')
406
    payload = json.loads(output)
407
    assert len(payload['check_type_groups']) == 0
408

  
409
    group = CheckTypeGroup.objects.create(label='Foo bar')
410
    CheckType.objects.create(label='Foo reason', group=group)
411
    CheckType.objects.create(label='Baz', group=group)
412

  
413
    output = get_output_of_command('export_site')
414
    payload = json.loads(output)
415
    assert len(payload['check_type_groups']) == 1
416

  
417
    group.delete()
418
    assert not CheckTypeGroup.objects.exists()
419
    assert not CheckType.objects.exists()
420

  
421
    import_site(copy.deepcopy(payload))
422
    assert CheckTypeGroup.objects.count() == 1
423
    group = CheckTypeGroup.objects.first()
424
    assert group.label == 'Foo bar'
425
    assert group.slug == 'foo-bar'
426
    assert group.check_types.count() == 2
427
    assert CheckType.objects.get(group=group, label='Foo reason', slug='foo-reason')
428
    assert CheckType.objects.get(group=group, label='Baz', slug='baz')
429

  
430
    # update
431
    update_payload = copy.deepcopy(payload)
432
    update_payload['check_type_groups'][0]['label'] = 'Foo bar Updated'
433
    import_site(update_payload)
434
    group.refresh_from_db()
435
    assert group.label == 'Foo bar Updated'
436

  
437
    # insert another group
438
    group.slug = 'foo-bar-updated'
439
    group.save()
440
    import_site(copy.deepcopy(payload))
441
    assert CheckTypeGroup.objects.count() == 2
442
    group = CheckTypeGroup.objects.latest('pk')
443
    assert group.label == 'Foo bar'
444
    assert group.slug == 'foo-bar'
445
    assert group.check_types.count() == 2
446
    assert CheckType.objects.get(group=group, label='Foo reason', slug='foo-reason')
447
    assert CheckType.objects.get(group=group, label='Baz', slug='baz')
448

  
449
    # with overwrite
450
    CheckType.objects.create(group=group, label='Baz2')
451
    import_site(copy.deepcopy(payload), overwrite=True)
452
    assert CheckTypeGroup.objects.count() == 2
453
    group = CheckTypeGroup.objects.latest('pk')
454
    assert group.label == 'Foo bar'
455
    assert group.slug == 'foo-bar'
456
    assert group.check_types.count() == 2
457
    assert CheckType.objects.get(group=group, label='Foo reason', slug='foo-reason')
458
    assert CheckType.objects.get(group=group, label='Baz', slug='baz')
375
-