Projet

Général

Profil

0005-agendas-remove-CheckType-CheckTypeGroup-models-66015.patch

Lauréline Guérin, 18 juin 2022 13:17

Télécharger (60,2 ko)

Voir les différences:

Subject: [PATCH 5/5] agendas: remove CheckType & CheckTypeGroup models
 (#66015)

 .../migrations/0132_remove_check_type.py      |  21 ++
 chrono/agendas/models.py                      | 105 --------
 chrono/api/views.py                           |  15 +-
 chrono/manager/forms.py                       |  26 --
 chrono/manager/static/css/style.scss          |  13 +-
 .../chrono/manager_check_type_form.html       |  36 ---
 .../chrono/manager_check_type_group_form.html |  31 ---
 .../chrono/manager_check_type_list.html       |  59 ----
 .../manager_events_agenda_settings.html       |  29 --
 .../templates/chrono/manager_home.html        |   1 -
 chrono/manager/urls.py                        |  36 ---
 chrono/manager/utils.py                       |   8 -
 chrono/manager/views.py                       | 207 +-------------
 tests/api/test_agenda.py                      |  27 +-
 tests/manager/test_check_type.py              | 254 ------------------
 tests/manager/test_event.py                   |   2 +-
 tests/manager/test_import_export.py           |  82 +-----
 tests/test_agendas.py                         |  20 --
 tests/test_import_export.py                   |  86 ------
 19 files changed, 34 insertions(+), 1024 deletions(-)
 create mode 100644 chrono/agendas/migrations/0132_remove_check_type.py
 delete mode 100644 chrono/manager/templates/chrono/manager_check_type_form.html
 delete mode 100644 chrono/manager/templates/chrono/manager_check_type_group_form.html
 delete mode 100644 chrono/manager/templates/chrono/manager_check_type_list.html
 delete mode 100644 tests/manager/test_check_type.py
chrono/agendas/migrations/0132_remove_check_type.py
1
from django.db import migrations
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('agendas', '0131_remove_check_type'),
8
    ]
9

  
10
    operations = [
11
        migrations.RemoveField(
12
            model_name='agenda',
13
            name='check_type_group',
14
        ),
15
        migrations.DeleteModel(
16
            name='CheckType',
17
        ),
18
        migrations.DeleteModel(
19
            name='CheckTypeGroup',
20
        ),
21
    ]
chrono/agendas/models.py
201 201
    category = models.ForeignKey(
202 202
        'Category', verbose_name=_('Category'), blank=True, null=True, on_delete=models.SET_NULL
203 203
    )
204
    check_type_group = models.ForeignKey(
205
        'CheckTypeGroup',
206
        verbose_name=_('Check type group'),
207
        blank=True,
208
        null=True,
209
        on_delete=models.SET_NULL,
210
    )
211 204
    default_view = models.CharField(_('Default view'), max_length=20, choices=AGENDA_VIEWS)
212 205
    booking_form_url = models.CharField(
213 206
        _('Booking form URL'), max_length=200, blank=True, validators=[django_template_validator]
......
398 391
            agenda['events'] = [x.export_json() for x in self.event_set.filter(primary_event__isnull=True)]
399 392
            if hasattr(self, 'notifications_settings'):
400 393
                agenda['notifications_settings'] = self.notifications_settings.export_json()
401
            agenda['check_type_group'] = self.check_type_group.slug if self.check_type_group else None
402 394
            agenda['exceptions_desk'] = self.desk_set.get().export_json()
403 395
            agenda['minimal_booking_delay_in_working_days'] = self.minimal_booking_delay_in_working_days
404 396
            agenda['booking_user_block_template'] = self.booking_user_block_template
......
445 437
                data['category'] = Category.objects.get(slug=data['category'])
446 438
            except Category.DoesNotExist:
447 439
                del data['category']
448
        if data.get('check_type_group'):
449
            try:
450
                data['check_type_group'] = CheckTypeGroup.objects.get(slug=data['check_type_group'])
451
            except CheckTypeGroup.DoesNotExist:
452
                raise AgendaImportError(_('Missing "%s" check type group') % data['check_type_group'])
453 440
        if data.get('events_type'):
454 441
            try:
455 442
                data['events_type'] = EventsType.objects.get(slug=data['events_type'])
......
3058 3045
        return new_settings
3059 3046

  
3060 3047

  
3061
class CheckTypeGroup(models.Model):
3062
    slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
3063
    label = models.CharField(_('Label'), max_length=150)
3064

  
3065
    class Meta:
3066
        ordering = ['label']
3067

  
3068
    def __str__(self):
3069
        return self.label
3070

  
3071
    def save(self, *args, **kwargs):
3072
        if not self.slug:
3073
            self.slug = generate_slug(self)
3074
        super().save(*args, **kwargs)
3075

  
3076
    @property
3077
    def base_slug(self):
3078
        return slugify(self.label)
3079

  
3080
    @classmethod
3081
    def import_json(cls, data, overwrite=False):
3082
        check_types = data.pop('check_types', [])
3083
        data = clean_import_data(cls, data)
3084
        group, created = cls.objects.update_or_create(slug=data['slug'], defaults=data)
3085

  
3086
        if overwrite:
3087
            CheckType.objects.filter(group=group).delete()
3088

  
3089
        for check_type in check_types:
3090
            check_type['group'] = group
3091
            CheckType.import_json(check_type)
3092

  
3093
        return created, group
3094

  
3095
    def export_json(self):
3096
        return {
3097
            'label': self.label,
3098
            'slug': self.slug,
3099
            'check_types': [a.export_json() for a in self.check_types.all()],
3100
        }
3101

  
3102

  
3103
class CheckTypeManager(models.Manager):
3104
    def absences(self):
3105
        return self.filter(kind='absence', disabled=False)
3106

  
3107
    def presences(self):
3108
        return self.filter(kind='presence', disabled=False)
3109

  
3110

  
3111
class CheckType(models.Model):
3112
    group = models.ForeignKey(CheckTypeGroup, on_delete=models.CASCADE, related_name='check_types')
3113
    slug = models.SlugField(_('Identifier'), max_length=160)
3114
    label = models.CharField(_('Label'), max_length=150)
3115
    kind = models.CharField(
3116
        _('Kind'),
3117
        max_length=8,
3118
        choices=[('absence', _('Absence')), ('presence', _('Presence'))],
3119
        default='absence',
3120
    )
3121
    disabled = models.BooleanField(_('Disabled'), default=False)
3122
    objects = CheckTypeManager()
3123

  
3124
    class Meta:
3125
        ordering = ['label']
3126
        unique_together = ['group', 'slug']
3127

  
3128
    def __str__(self):
3129
        return self.label
3130

  
3131
    def save(self, *args, **kwargs):
3132
        if not self.slug:
3133
            self.slug = generate_slug(self, group=self.group)
3134
        super().save(*args, **kwargs)
3135

  
3136
    @property
3137
    def base_slug(self):
3138
        return slugify(self.label)
3139

  
3140
    @classmethod
3141
    def import_json(cls, data):
3142
        data = clean_import_data(cls, data)
3143
        cls.objects.update_or_create(slug=data['slug'], group=data['group'], defaults=data)
3144

  
3145
    def export_json(self):
3146
        return {
3147
            'label': self.label,
3148
            'slug': self.slug,
3149
            'kind': self.kind,
3150
        }
3151

  
3152

  
3153 3048
class Subscription(models.Model):
3154 3049
    agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE, related_name='subscriptions')
3155 3050
    user_external_id = models.CharField(max_length=250)
chrono/api/views.py
380 380
        }
381 381
        if check_events:
382 382
            agenda_detail['opened_events_available'] = bool(agenda.get_open_events().filter(full=False))
383
        if agenda.check_type_group:
384
            agenda_detail['absence_reasons'] = [
385
                {'id': r.slug, 'slug': r.slug, 'text': r.label, 'label': r.label}
386
                for r in agenda.check_type_group.check_types.all()
387
                if r.kind == 'absence'
388
            ]
389
            agenda_detail['presence_reasons'] = [
390
                {'id': r.slug, 'slug': r.slug, 'text': r.label, 'label': r.label}
391
                for r in agenda.check_type_group.check_types.all()
392
                if r.kind == 'presence'
393
            ]
394 383
    elif agenda.accept_meetings():
395 384
        agenda_detail['api'] = {
396 385
            'meetings_url': request.build_absolute_uri(
......
742 731
    def get(self, request, format=None):
743 732
        agendas_queryset = (
744 733
            Agenda.objects.all()
745
            .select_related('check_type_group', 'category', 'edit_role', 'view_role', 'events_type')
746
            .prefetch_related('resources', 'check_type_group__check_types')
734
            .select_related('category', 'edit_role', 'view_role', 'events_type')
735
            .prefetch_related('resources')
747 736
            .order_by('label')
748 737
        )
749 738

  
chrono/manager/forms.py
44 44
    AgendaNotificationsSettings,
45 45
    AgendaReminderSettings,
46 46
    Booking,
47
    CheckType,
48
    CheckTypeGroup,
49 47
    Desk,
50 48
    Event,
51 49
    EventsType,
......
70 68
from .widgets import SplitDateTimeField, WeekdaysWidget
71 69

  
72 70

  
73
class CheckTypeForm(forms.ModelForm):
74
    class Meta:
75
        model = CheckType
76
        fields = ['label', 'slug', 'disabled']
77

  
78
    def clean_slug(self):
79
        slug = self.cleaned_data['slug']
80

  
81
        if self.instance.group.check_types.filter(slug=slug).exclude(pk=self.instance.pk).exists():
82
            raise ValidationError(_('Another check type exists with the same identifier.'))
83

  
84
        return slug
85

  
86

  
87 71
class AgendaAddForm(forms.ModelForm):
88 72
    edit_role = forms.ModelChoiceField(
89 73
        label=_('Edit Role'), required=False, queryset=Group.objects.all().order_by('name')
......
1308 1292
    class Meta:
1309 1293
        model = Agenda
1310 1294
        fields = [
1311
            'check_type_group',
1312 1295
            'booking_check_filters',
1313 1296
            'mark_event_checked_auto',
1314 1297
            'disable_check_update',
1315 1298
        ]
1316 1299

  
1317
    def __init__(self, *args, **kwargs):
1318
        super().__init__(*args, **kwargs)
1319
        if not CheckTypeGroup.objects.exists():
1320
            del self.fields['check_type_group']
1321
        elif Booking.objects.filter(event__agenda=self.instance, user_check_type_slug__isnull=False).exists():
1322
            # not possible to update check_type_group if bookings with non null check_type exist
1323
            del self.fields['check_type_group']
1324

  
1325 1300

  
1326 1301
class AgendaNotificationsForm(forms.ModelForm):
1327 1302
    class Meta:
......
1420 1395
        label=_('Unavailability calendars'), required=False, initial=True
1421 1396
    )
1422 1397
    categories = forms.BooleanField(label=_('Categories'), required=False, initial=True)
1423
    check_type_groups = forms.BooleanField(label=_('Check type groups'), required=False, initial=True)
1424 1398
    events_types = forms.BooleanField(label=_('Events types'), required=False, initial=True)
1425 1399

  
1426 1400

  
chrono/manager/static/css/style.scss
72 72
	margin-top: 0;
73 73
}
74 74

  
75
.timeperiods .timeperiod a.add::before,
76
.check-type-group h3 a.delete::before,
77
.check-type-group a.add::before {
75
.timeperiods .timeperiod a.add::before {
78 76
	content: "\f055"; /* plus-circle */
79 77
	font-family: FontAwesome;
80 78
	padding-right: 1ex;
......
88 86
	content: "\f0ad"; /* wrench */
89 87
}
90 88

  
91
.check-type-group h3 a.delete {
92
	width: 1em;
93
	overflow: hidden;
94
	&::before {
95
		content: "\f057"; /* remove-sign */
96
		padding-right: 3em;
97
	}
98
}
99

  
100 89
.dayview h2 a,
101 90
.monthview h2 a {
102 91
	padding: 0 1ex;
chrono/manager/templates/chrono/manager_check_type_form.html
1
{% extends "chrono/manager_check_type_list.html" %}
2
{% load i18n %}
3

  
4
{% block breadcrumb %}
5
{{ block.super }}
6
{% if form.instance.pk %}
7
<a href="{% url 'chrono-manager-check-type-edit' form.instance.group_id form.instance.pk %}">{{ form.instance }}</a>
8
{% else %}
9
<a href="{% url 'chrono-manager-check-type-add' form.instance.group_id %}">{% trans "New check type" %}</a>
10
{% endif %}
11
{% endblock %}
12

  
13
{% block appbar %}
14
{% if form.instance.pk %}
15
<h2>{{ form.instance.group }} - {% trans "Edit check type" %}</h2>
16
{% else %}
17
<h2>{{ form.instance.group }} - {% trans "New check type" %}</h2>
18
{% endif %}
19
{% endblock %}
20

  
21
{% block content %}
22

  
23
<form method="post" enctype="multipart/form-data">
24
  {% csrf_token %}
25
  {% if is_used %}
26
  <p>
27
  {% trans "This check type is set on some existing bookings, modify it with caution." %}
28
  </p>
29
  {% endif %}
30
  {{ form.as_p }}
31
  <div class="buttons">
32
    <button class="submit-button">{% trans "Save" %}</button>
33
    <a class="cancel" href="{% url 'chrono-manager-check-type-list' %}">{% trans 'Cancel' %}</a>
34
  </div>
35
</form>
36
{% endblock %}
chrono/manager/templates/chrono/manager_check_type_group_form.html
1
{% extends "chrono/manager_check_type_list.html" %}
2
{% load i18n %}
3

  
4
{% block breadcrumb %}
5
{{ block.super }}
6
{% if object.pk %}
7
<a href="{% url 'chrono-manager-check-type-group-edit' object.pk %}">{{ object }}</a>
8
{% else %}
9
<a href="{% url 'chrono-manager-check-type-group-add' %}">{% trans "New check type group" %}</a>
10
{% endif %}
11
{% endblock %}
12

  
13
{% block appbar %}
14
{% if object.pk %}
15
<h2>{% trans "Edit check type group" %}</h2>
16
{% else %}
17
<h2>{% trans "New check type group" %}</h2>
18
{% endif %}
19
{% endblock %}
20

  
21
{% block content %}
22

  
23
<form method="post" enctype="multipart/form-data">
24
  {% csrf_token %}
25
  {{ form.as_p }}
26
  <div class="buttons">
27
    <button class="submit-button">{% trans "Save" %}</button>
28
    <a class="cancel" href="{% url 'chrono-manager-check-type-list' %}">{% trans 'Cancel' %}</a>
29
  </div>
30
</form>
31
{% endblock %}
chrono/manager/templates/chrono/manager_check_type_list.html
1
{% extends "chrono/manager_base.html" %}
2
{% load i18n %}
3

  
4
{% block breadcrumb %}
5
{{ block.super }}
6
<a href="{% url 'chrono-manager-check-type-list' %}">{% trans "Check types" %}</a>
7
{% endblock %}
8

  
9
{% block appbar %}
10
<h2>{% trans 'Check types' %}</h2>
11
<span class="actions">
12
<a rel="popup" href="{% url 'chrono-manager-check-type-group-add' %}">{% trans 'New group' %}</a>
13
</span>
14
{% endblock %}
15

  
16
{% block content %}
17
<div class="pk-information">
18
<p>{% trans "Define here check types used in events agendas to check bookings." %}</p>
19
</div>
20
{% for object in object_list %}
21
<div class="section check-type-group">
22
    <h3>
23
    <a rel="popup" href="{% url 'chrono-manager-check-type-group-edit' object.pk %}">{{ object }}</a>
24
    <span>
25
    <a class="button" href="{% url 'chrono-manager-check-type-group-export' object.pk %}">{% trans "Export"%}</a>
26
    <a class="button" rel="popup" href="{% url 'chrono-manager-check-type-group-delete' object.pk %}">{% trans "Delete"%}</a>
27
    </span>
28
    </h3>
29
  <div>
30
  <ul class="objects-list single-links">
31
    {% for check_type in object.check_types.all %}
32
    {% if check_type.kind == 'absence' %}
33
    <li>
34
        <a rel="popup" href="{% url 'chrono-manager-check-type-edit' object.pk check_type.pk %}">{% trans "Absence" %} - {{ check_type }}{% if check_type.disabled %}<span class="extra-info"> ({% trans "disabled" %})</span>{% endif %}</a>
35
        <a class="delete" rel="popup" href="{% url 'chrono-manager-check-type-delete' object.pk check_type.pk %}">{% trans "delete"%}</a>
36
    </li>
37
    {% endif %}
38
    {% endfor %}
39
    {% for check_type in object.check_types.all %}
40
    {% if check_type.kind == 'presence' %}
41
    <li>
42
        <a rel="popup" href="{% url 'chrono-manager-check-type-edit' object.pk check_type.pk %}">{% trans "Presence" %} - {{ check_type }}{% if check_type.disabled %}<span class="extra-info"> ({% trans "disabled" %})</span>{% endif %}</a>
43
        <a class="delete" rel="popup" href="{% url 'chrono-manager-check-type-delete' object.pk check_type.pk %}">{% trans "delete"%}</a>
44
    </li>
45
    {% endif %}
46
    {% endfor %}
47
    <li><a class="add" rel="popup" href="{% url 'chrono-manager-check-type-add' object.pk %}">{% trans "Add a check type" %}</a></li>
48
  </ul>
49
  </div>
50
</div>
51
{% empty %}
52
<div class="big-msg-info">
53
  {% blocktrans %}
54
  This site doesn't have any check type group yet. Click on the "New group" button in the top
55
  right of the page to add a first one.
56
  {% endblocktrans %}
57
</div>
58
{% endfor %}
59
{% endblock %}
chrono/manager/templates/chrono/manager_events_agenda_settings.html
98 98

  
99 99
    <div aria-labelledby="tab-booking-check-options" hidden="" id="panel-booking-check-options" role="tabpanel" tabindex="0">
100 100
      <ul>
101
        {% if has_check_types %}
102
        {% if agenda.check_type_group %}
103
        <li>{% trans "Check type group:" %} {{ agenda.check_type_group }}
104
          <ul>
105
            <li>{% trans "Absences:" %}
106
              <ul>
107
                {% for check_type in agenda.check_type_group.check_types.absences %}
108
                <li>{{ check_type }}</li>
109
                {% empty %}
110
                <li>({% trans "No absence check type defined" %})</li>
111
                {% endfor %}
112
              </ul>
113
            </li>
114
            <li>{% trans "Presences:" %}
115
              <ul>
116
                {% for check_type in agenda.check_type_group.check_types.presences %}
117
                <li>{{ check_type }}</li>
118
                {% empty %}
119
                <li>({% trans "No presence check type defined" %})</li>
120
                {% endfor %}
121
              </ul>
122
            </li>
123
          </ul>
124
        </li>
125
        {% else %}
126
        <li>{% trans "No check types configured for this agenda." %}</li>
127
        {% endif %}
128
        {% endif %}
129

  
130 101
        {% with agenda.get_booking_check_filters as check_filters %}
131 102
          {% if check_filters %}
132 103
          <li>{% trans "Filters:" %}
chrono/manager/templates/chrono/manager_home.html
11 11
    <li><a rel="popup" href="{% url 'chrono-manager-agendas-import' %}">{% trans 'Import' %}</a></li>
12 12
    <li><a rel="popup" href="{% url 'chrono-manager-agendas-export' %}" data-autoclose-dialog="true">{% trans 'Export' %}</a></li>
13 13
    <li><a href="{% url 'chrono-manager-events-type-list' %}">{% trans 'Events types' %}</a></li>
14
    <li><a href="{% url 'chrono-manager-check-type-list' %}">{% trans 'Check types' %}</a></li>
15 14
    {% endif %}
16 15
    {% if has_access_to_unavailability_calendars %}
17 16
    <li><a href="{% url 'chrono-manager-unavailability-calendar-list' %}">{% trans 'Unavailability calendars' %}</a></li>
chrono/manager/urls.py
84 84
    url(r'^category/add/$', views.category_add, name='chrono-manager-category-add'),
85 85
    url(r'^category/(?P<pk>\d+)/edit/$', views.category_edit, name='chrono-manager-category-edit'),
86 86
    url(r'^category/(?P<pk>\d+)/delete/$', views.category_delete, name='chrono-manager-category-delete'),
87
    url(r'^check-types/$', views.check_type_list, name='chrono-manager-check-type-list'),
88
    url(
89
        r'^check-type/group/add/$',
90
        views.check_type_group_add,
91
        name='chrono-manager-check-type-group-add',
92
    ),
93
    url(
94
        r'^check-type/group/(?P<pk>\d+)/edit/$',
95
        views.check_type_group_edit,
96
        name='chrono-manager-check-type-group-edit',
97
    ),
98
    url(
99
        r'^check-type/group/(?P<pk>\d+)/delete/$',
100
        views.check_type_group_delete,
101
        name='chrono-manager-check-type-group-delete',
102
    ),
103
    url(
104
        r'^check-type/group/(?P<pk>\d+)/export/$',
105
        views.check_type_group_export,
106
        name='chrono-manager-check-type-group-export',
107
    ),
108
    url(
109
        r'^check-type/group/(?P<group_pk>\d+)/add/$',
110
        views.check_type_add,
111
        name='chrono-manager-check-type-add',
112
    ),
113
    url(
114
        r'^check-type/group/(?P<group_pk>\d+)/(?P<pk>\d+)/edit/$',
115
        views.check_type_edit,
116
        name='chrono-manager-check-type-edit',
117
    ),
118
    url(
119
        r'^check-type/group/(?P<group_pk>\d+)/(?P<pk>\d+)/delete/$',
120
        views.check_type_delete,
121
        name='chrono-manager-check-type-delete',
122
    ),
123 87
    url(r'^events-types/$', views.events_type_list, name='chrono-manager-events-type-list'),
124 88
    url(r'^events-type/add/$', views.events_type_add, name='chrono-manager-events-type-add'),
125 89
    url(r'^events-type/(?P<pk>\d+)/edit/$', views.events_type_edit, name='chrono-manager-events-type-edit'),
chrono/manager/utils.py
25 25
    Agenda,
26 26
    AgendaImportError,
27 27
    Category,
28
    CheckTypeGroup,
29 28
    EventsType,
30 29
    Resource,
31 30
    UnavailabilityCalendar,
......
35 34
def export_site(
36 35
    agendas=True,
37 36
    unavailability_calendars=True,
38
    check_type_groups=True,
39 37
    events_types=True,
40 38
    resources=True,
41 39
    categories=True,
......
48 46
        data['resources'] = [x.export_json() for x in Resource.objects.all()]
49 47
    if events_types:
50 48
        data['events_types'] = [x.export_json() for x in EventsType.objects.all()]
51
    if check_type_groups:
52
        data['check_type_groups'] = [x.export_json() for x in CheckTypeGroup.objects.all()]
53 49
    if unavailability_calendars:
54 50
        data['unavailability_calendars'] = [x.export_json() for x in UnavailabilityCalendar.objects.all()]
55 51
    if agendas:
......
64 60
    if if_empty and (
65 61
        Agenda.objects.exists()
66 62
        or UnavailabilityCalendar.objects.exists()
67
        or CheckTypeGroup.objects.exists()
68 63
        or EventsType.objects.exists()
69 64
        or Resource.objects.exists()
70 65
        or Category.objects.exists()
......
74 69
    if clean:
75 70
        Agenda.objects.all().delete()
76 71
        UnavailabilityCalendar.objects.all().delete()
77
        CheckTypeGroup.objects.all().delete()
78 72
        EventsType.objects.all().delete()
79 73
        Resource.objects.all().delete()
80 74
        Category.objects.all().delete()
......
84 78
        for key in [
85 79
            'agendas',
86 80
            'unavailability_calendars',
87
            'check_type_groups',
88 81
            'events_types',
89 82
            'resources',
90 83
            'categories',
......
108 101
            (Category, 'categories'),
109 102
            (Resource, 'resources'),
110 103
            (EventsType, 'events_types'),
111
            (CheckTypeGroup, 'check_type_groups'),
112 104
            (UnavailabilityCalendar, 'unavailability_calendars'),
113 105
            (Agenda, 'agendas'),
114 106
        ):
chrono/manager/views.py
67 67
    Booking,
68 68
    BookingColor,
69 69
    Category,
70
    CheckType,
71
    CheckTypeGroup,
72 70
    Desk,
73 71
    Event,
74 72
    EventCancellationReport,
......
106 104
    BookingCheckAbsenceForm,
107 105
    BookingCheckFilterSet,
108 106
    BookingCheckPresenceForm,
109
    CheckTypeForm,
110 107
    CustomFieldFormSet,
111 108
    DeskExceptionsImportForm,
112 109
    DeskForm,
......
660 657
category_delete = CategoryDeleteView.as_view()
661 658

  
662 659

  
663
class CheckTypeListView(ListView):
664
    template_name = 'chrono/manager_check_type_list.html'
665
    model = CheckTypeGroup
666

  
667
    def dispatch(self, request, *args, **kwargs):
668
        if not request.user.is_staff:
669
            raise PermissionDenied()
670
        return super().dispatch(request, *args, **kwargs)
671

  
672
    def get_queryset(self):
673
        return CheckTypeGroup.objects.prefetch_related('check_types')
674

  
675

  
676
check_type_list = CheckTypeListView.as_view()
677

  
678

  
679
class CheckTypeGroupAddView(CreateView):
680
    template_name = 'chrono/manager_check_type_group_form.html'
681
    model = CheckTypeGroup
682
    fields = ['label']
683

  
684
    def dispatch(self, request, *args, **kwargs):
685
        if not request.user.is_staff:
686
            raise PermissionDenied()
687
        return super().dispatch(request, *args, **kwargs)
688

  
689
    def get_success_url(self):
690
        return reverse('chrono-manager-check-type-list')
691

  
692

  
693
check_type_group_add = CheckTypeGroupAddView.as_view()
694

  
695

  
696
class CheckTypeGroupEditView(UpdateView):
697
    template_name = 'chrono/manager_check_type_group_form.html'
698
    model = CheckTypeGroup
699
    fields = ['label', 'slug']
700

  
701
    def dispatch(self, request, *args, **kwargs):
702
        if not request.user.is_staff:
703
            raise PermissionDenied()
704
        return super().dispatch(request, *args, **kwargs)
705

  
706
    def get_success_url(self):
707
        return reverse('chrono-manager-check-type-list')
708

  
709

  
710
check_type_group_edit = CheckTypeGroupEditView.as_view()
711

  
712

  
713
class CheckTypeGroupDeleteView(DeleteView):
714
    template_name = 'chrono/manager_confirm_delete.html'
715
    model = CheckTypeGroup
716

  
717
    def dispatch(self, request, *args, **kwargs):
718
        if not request.user.is_staff:
719
            raise PermissionDenied()
720
        return super().dispatch(request, *args, **kwargs)
721

  
722
    def get_success_url(self):
723
        return reverse('chrono-manager-check-type-list')
724

  
725

  
726
check_type_group_delete = CheckTypeGroupDeleteView.as_view()
727

  
728

  
729
class CheckTypeGroupExport(DetailView):
730
    model = CheckTypeGroup
731

  
732
    def dispatch(self, request, *args, **kwargs):
733
        if not request.user.is_staff:
734
            raise PermissionDenied()
735
        return super().dispatch(request, *args, **kwargs)
736

  
737
    def get(self, request, *args, **kwargs):
738
        response = HttpResponse(content_type='application/json')
739
        today = datetime.date.today()
740
        attachment = 'attachment; filename="export_check_type_group_{}_{}.json"'.format(
741
            self.get_object().slug, today.strftime('%Y%m%d')
742
        )
743
        response['Content-Disposition'] = attachment
744
        json.dump({'check_type_groups': [self.get_object().export_json()]}, response, indent=2)
745
        return response
746

  
747

  
748
check_type_group_export = CheckTypeGroupExport.as_view()
749

  
750

  
751
class CheckTypeAddView(CreateView):
752
    template_name = 'chrono/manager_check_type_form.html'
753
    model = CheckType
754
    fields = ['label', 'kind']
755

  
756
    def dispatch(self, request, *args, **kwargs):
757
        self.group_pk = kwargs.pop('group_pk')
758
        if not request.user.is_staff:
759
            raise PermissionDenied()
760
        return super().dispatch(request, *args, **kwargs)
761

  
762
    def get_form_kwargs(self):
763
        kwargs = super().get_form_kwargs()
764
        if not kwargs.get('instance'):
765
            kwargs['instance'] = self.model()
766
        kwargs['instance'].group_id = self.group_pk
767
        return kwargs
768

  
769
    def get_success_url(self):
770
        return reverse('chrono-manager-check-type-list')
771

  
772

  
773
check_type_add = CheckTypeAddView.as_view()
774

  
775

  
776
class CheckTypeEditView(UpdateView):
777
    template_name = 'chrono/manager_check_type_form.html'
778
    model = CheckType
779
    form_class = CheckTypeForm
780

  
781
    def dispatch(self, request, *args, **kwargs):
782
        self.group_pk = kwargs.pop('group_pk')
783
        if not request.user.is_staff:
784
            raise PermissionDenied()
785
        return super().dispatch(request, *args, **kwargs)
786

  
787
    def get_queryset(self):
788
        return CheckType.objects.filter(group=self.group_pk)
789

  
790
    def get_context_data(self, **kwargs):
791
        context = super().get_context_data(**kwargs)
792
        context['is_used'] = Booking.objects.filter(user_check_type_slug=self.get_object().slug).exists()
793
        return context
794

  
795
    def get_success_url(self):
796
        return reverse('chrono-manager-check-type-list')
797

  
798

  
799
check_type_edit = CheckTypeEditView.as_view()
800

  
801

  
802
class CheckTypeDeleteView(DeleteView):
803
    template_name = 'chrono/manager_confirm_delete.html'
804
    model = CheckType
805

  
806
    def dispatch(self, request, *args, **kwargs):
807
        self.group_pk = kwargs.pop('group_pk')
808
        if not request.user.is_staff:
809
            raise PermissionDenied()
810
        return super().dispatch(request, *args, **kwargs)
811

  
812
    def get_queryset(self):
813
        return CheckType.objects.filter(group=self.group_pk)
814

  
815
    def get_context_data(self, **kwargs):
816
        context = super().get_context_data(**kwargs)
817
        context['cannot_delete'] = Booking.objects.filter(
818
            user_check_type_slug=self.get_object().slug
819
        ).exists()
820
        context['cannot_delete_msg'] = _(
821
            'Can not delete this check type: it is set on some existing bookings.'
822
        )
823
        return context
824

  
825
    def delete(self, request, *args, **kwargs):
826
        if Booking.objects.filter(user_check_type_slug=self.get_object().slug).exists():
827
            raise Http404
828
        return super().delete(request, *args, **kwargs)
829

  
830
    def get_success_url(self):
831
        return reverse('chrono-manager-check-type-list')
832

  
833

  
834
check_type_delete = CheckTypeDeleteView.as_view()
835

  
836

  
837 660
class EventsTypeListView(ListView):
838 661
    template_name = 'chrono/manager_events_type_list.html'
839 662
    model = EventsType
......
1007 830
                    x,
1008 831
                ),
1009 832
            },
1010
            'check_type_groups': {
1011
                'create_noop': _('No check type group created.'),
1012
                'create': lambda x: ungettext(
1013
                    'A check type group has been created.',
1014
                    '%(count)d check type groups have been created.',
1015
                    x,
1016
                ),
1017
                'update_noop': _('No check type group updated.'),
1018
                'update': lambda x: ungettext(
1019
                    'A check type group has been updated.',
1020
                    '%(count)d check type groups have been updated.',
1021
                    x,
1022
                ),
1023
            },
1024 833
            'events_types': {
1025 834
                'create_noop': _('No events type created.'),
1026 835
                'create': lambda x: ungettext(
......
1083 892

  
1084 893
                obj_results['messages'] = "%s %s" % (message1, message2)
1085 894

  
1086
        a_count, uc_count, arg_count = (
895
        a_count, uc_count = (
1087 896
            len(results['agendas']['all']),
1088 897
            len(results['unavailability_calendars']['all']),
1089
            len(results['check_type_groups']['all']),
1090 898
        )
1091
        if (a_count, uc_count, arg_count) == (1, 0, 0):
899
        if (a_count, uc_count) == (1, 0):
1092 900
            # only one agenda imported, redirect to settings page
1093 901
            return HttpResponseRedirect(
1094 902
                reverse('chrono-manager-agenda-settings', kwargs={'pk': results['agendas']['all'][0].pk})
1095 903
            )
1096
        if (a_count, uc_count, arg_count) == (0, 1, 0):
904
        if (a_count, uc_count) == (0, 1):
1097 905
            # only one unavailability calendar imported, redirect to settings page
1098 906
            return HttpResponseRedirect(
1099 907
                reverse(
......
1101 909
                    kwargs={'pk': results['unavailability_calendars']['all'][0].pk},
1102 910
                )
1103 911
            )
1104
        if (a_count, uc_count, arg_count) == (0, 0, 1):
1105
            # only one check type group imported, redirect to check type page
1106
            return HttpResponseRedirect(reverse('chrono-manager-check-type-list'))
1107 912

  
1108 913
        if global_noop:
1109 914
            messages.info(self.request, _('No data found.'))
1110 915
        else:
1111 916
            messages.info(self.request, results['agendas']['messages'])
1112 917
            messages.info(self.request, results['unavailability_calendars']['messages'])
1113
            messages.info(self.request, results['check_type_groups']['messages'])
1114 918
            messages.info(self.request, results['events_types']['messages'])
1115 919
            messages.info(self.request, results['resources']['messages'])
1116 920
            messages.info(self.request, results['categories']['messages'])
......
1880 1684

  
1881 1685
    def set_agenda(self, **kwargs):
1882 1686
        self.agenda = get_object_or_404(
1883
            Agenda.objects.select_related('edit_role', 'view_role', 'check_type_group'),
1687
            Agenda.objects.select_related('edit_role', 'view_role'),
1884 1688
            pk=kwargs.get('pk'),
1885 1689
        )
1886 1690

  
......
1907 1711
                else False
1908 1712
            )
1909 1713
        if self.agenda.kind == 'events':
1910
            context['has_check_types'] = CheckTypeGroup.objects.exists()
1911 1714
            context['has_recurring_events'] = self.agenda.event_set.filter(
1912 1715
                recurrence_days__isnull=False
1913 1716
            ).exists()
......
2353 2156

  
2354 2157
    def set_agenda(self, **kwargs):
2355 2158
        self.agenda = get_object_or_404(
2356
            Agenda.objects.prefetch_related('check_type_group__check_types'),
2159
            Agenda,
2357 2160
            pk=kwargs.get('pk'),
2358 2161
            kind='events',
2359 2162
        )
tests/api/test_agenda.py
10 10
    Agenda,
11 11
    Booking,
12 12
    Category,
13
    CheckType,
14
    CheckTypeGroup,
15 13
    Desk,
16 14
    Event,
17 15
    EventsType,
......
27 25
    view_group = Group.objects.create(name='View')
28 26
    category_a = Category.objects.create(label='Category A')
29 27
    category_b = Category.objects.create(label='Category B')
30
    group = CheckTypeGroup.objects.create(label='Foo')
31
    reason = CheckType.objects.create(group=group, label='Foo bar')
32
    reason2 = CheckType.objects.create(group=group, label='Foo bar baz')
33
    reason3 = CheckType.objects.create(group=group, label='Foo bar baz presence', kind='presence')
34 28
    events_type = EventsType.objects.create(label='Type A')
35 29
    events_type2 = EventsType.objects.create(label='Type B')
36 30
    event_agenda = Agenda.objects.create(
37 31
        label='Foo bar',
38 32
        category=category_a,
39
        check_type_group=group,
40 33
        events_type=events_type,
41 34
        edit_role=edit_group,
42 35
    )
43 36
    Desk.objects.create(agenda=event_agenda, slug='_exceptions_holder')
44 37
    event_agenda2 = Agenda.objects.create(label='Foo bar 2', category=category_a, events_type=events_type2)
45 38
    Desk.objects.create(agenda=event_agenda2, slug='_exceptions_holder')
46
    event_agenda3 = Agenda.objects.create(label='Foo bar 3', check_type_group=group)
39
    event_agenda3 = Agenda.objects.create(label='Foo bar 3')
47 40
    Desk.objects.create(agenda=event_agenda3, slug='_exceptions_holder')
48 41
    meetings_agenda1 = Agenda.objects.create(
49 42
        label='Foo bar Meeting', kind='meetings', category=category_b, view_role=view_group
......
78 71
                'category': 'category-a',
79 72
                'category_label': 'Category A',
80 73
                'events_type': 'type-a',
81
                'absence_reasons': [
82
                    {'id': reason.slug, 'slug': reason.slug, 'text': reason.label, 'label': reason.label},
83
                    {'id': reason2.slug, 'slug': reason2.slug, 'text': reason2.label, 'label': reason2.label},
84
                ],
85
                'presence_reasons': [
86
                    {'id': reason3.slug, 'slug': reason3.slug, 'text': reason3.label, 'label': reason3.label},
87
                ],
88 74
                'api': {
89 75
                    'datetimes_url': 'http://testserver/api/agenda/foo-bar/datetimes/',
90 76
                    'fillslots_url': 'http://testserver/api/agenda/foo-bar/fillslots/',
......
123 109
                'category': None,
124 110
                'category_label': None,
125 111
                'events_type': None,
126
                'absence_reasons': [
127
                    {'id': reason.slug, 'slug': reason.slug, 'text': reason.label, 'label': reason.label},
128
                    {'id': reason2.slug, 'slug': reason2.slug, 'text': reason2.label, 'label': reason2.label},
129
                ],
130
                'presence_reasons': [
131
                    {'id': reason3.slug, 'slug': reason3.slug, 'text': reason3.label, 'label': reason3.label},
132
                ],
133 112
                'api': {
134 113
                    'datetimes_url': 'http://testserver/api/agenda/foo-bar-3/datetimes/',
135 114
                    'fillslots_url': 'http://testserver/api/agenda/foo-bar-3/fillslots/',
......
209 188

  
210 189
    with CaptureQueriesContext(connection) as ctx:
211 190
        resp = app.get('/api/agenda/')
212
        assert len(ctx.captured_queries) == 4
191
        assert len(ctx.captured_queries) == 3
213 192
    with CaptureQueriesContext(connection) as ctx:
214 193
        resp = app.get('/api/agenda/', params={'q': 'MEET'})
215 194
        assert len(ctx.captured_queries) == 2
......
328 307

  
329 308
    with CaptureQueriesContext(connection) as ctx:
330 309
        resp = app.get('/api/agenda/', params={'with_open_events': '1'})
331
        assert len(ctx.captured_queries) == 4
310
        assert len(ctx.captured_queries) == 3
332 311

  
333 312

  
334 313
def test_agenda_detail_api(app):
tests/manager/test_check_type.py
1
import pytest
2
from django.utils.timezone import now
3

  
4
from chrono.agendas.models import Agenda, Booking, CheckType, CheckTypeGroup, Desk, Event
5
from tests.utils import login
6

  
7
pytestmark = pytest.mark.django_db
8

  
9

  
10
@pytest.fixture
11
def agenda_with_restrictions(manager_user):
12
    agenda = Agenda(label='Foo Bar')
13
    agenda.view_role = manager_user.groups.all()[0]
14
    agenda.save()
15
    return agenda
16

  
17

  
18
def test_list_types_as_manager(app, manager_user, agenda_with_restrictions):
19
    app = login(app, username='manager', password='manager')
20
    app.get('/manage/check-types/', status=403)
21

  
22
    resp = app.get('/manage/')
23
    assert 'Check types' not in resp.text
24

  
25

  
26
def test_add_group(app, admin_user):
27
    app = login(app)
28
    resp = app.get('/manage/')
29
    resp = resp.click('Check types')
30
    resp = resp.click('New group')
31
    resp.form['label'] = 'Foo bar'
32
    resp = resp.form.submit()
33
    group = CheckTypeGroup.objects.latest('pk')
34
    assert resp.location.endswith('/manage/check-types/')
35
    assert group.label == 'Foo bar'
36
    assert group.slug == 'foo-bar'
37

  
38

  
39
def test_add_group_as_manager(app, manager_user, agenda_with_restrictions):
40
    app = login(app, username='manager', password='manager')
41
    app.get('/manage/check-type/group/add/', status=403)
42

  
43

  
44
def test_edit_group(app, admin_user):
45
    group = CheckTypeGroup.objects.create(label='Foo bar')
46
    group2 = CheckTypeGroup.objects.create(label='baz')
47

  
48
    app = login(app)
49
    resp = app.get('/manage/check-types/')
50
    resp = resp.click(href='/manage/check-type/group/%s/edit/' % group.pk)
51
    resp.form['label'] = 'Foo bar baz'
52
    resp.form['slug'] = group2.slug
53
    resp = resp.form.submit()
54
    assert resp.context['form'].errors['slug'] == ['Check type group with this Identifier already exists.']
55

  
56
    resp.form['slug'] = 'baz2'
57
    resp = resp.form.submit()
58
    assert resp.location.endswith('/manage/check-types/')
59
    group.refresh_from_db()
60
    assert group.label == 'Foo bar baz'
61
    assert group.slug == 'baz2'
62

  
63

  
64
def test_edit_group_as_manager(app, manager_user, agenda_with_restrictions):
65
    group = CheckTypeGroup.objects.create(label='Foo bar')
66

  
67
    app = login(app, username='manager', password='manager')
68
    app.get('/manage/check-type/group/%s/edit/' % group.pk, status=403)
69

  
70

  
71
def test_delete_group(app, admin_user):
72
    group = CheckTypeGroup.objects.create(label='Foo bar')
73
    CheckType.objects.create(label='Foo reason', group=group)
74
    agenda = Agenda.objects.create(label='Foo bar')
75
    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
76
    Booking.objects.create(event=event)
77

  
78
    app = login(app)
79
    resp = app.get('/manage/check-types/')
80
    resp = resp.click(href='/manage/check-type/group/%s/delete/' % group.pk)
81
    resp = resp.form.submit()
82
    assert resp.location.endswith('/manage/check-types/')
83
    assert CheckTypeGroup.objects.exists() is False
84
    assert CheckType.objects.exists() is False
85

  
86

  
87
def test_delete_group_as_manager(app, manager_user, agenda_with_restrictions):
88
    group = CheckTypeGroup.objects.create(label='Foo bar')
89

  
90
    app = login(app, username='manager', password='manager')
91
    app.get('/manage/check-type/group/%s/delete/' % group.pk, status=403)
92

  
93

  
94
def test_add_check_type(app, admin_user):
95
    group = CheckTypeGroup.objects.create(label='Foo bar')
96

  
97
    app = login(app)
98
    resp = app.get('/manage/')
99
    resp = resp.click('Check types')
100
    resp = resp.click('Add a check type')
101
    resp.form['label'] = 'Foo reason'
102
    assert 'slug' not in resp.context['form'].fields
103
    assert 'disabled' not in resp.context['form'].fields
104
    resp = resp.form.submit()
105
    check_type = CheckType.objects.latest('pk')
106
    assert resp.location.endswith('/manage/check-types/')
107
    assert check_type.label == 'Foo reason'
108
    assert check_type.group == group
109
    assert check_type.slug == 'foo-reason'
110
    assert check_type.kind == 'absence'
111

  
112
    resp = app.get('/manage/check-type/group/%s/add/' % group.pk)
113
    resp.form['label'] = 'Foo reason'
114
    resp.form['kind'] = 'presence'
115
    resp = resp.form.submit()
116
    assert resp.location.endswith('/manage/check-types/')
117
    check_type = CheckType.objects.latest('pk')
118
    assert check_type.label == 'Foo reason'
119
    assert check_type.slug == 'foo-reason-1'
120
    assert check_type.kind == 'presence'
121

  
122

  
123
def test_add_check_type_as_manager(app, manager_user, agenda_with_restrictions):
124
    group = CheckTypeGroup.objects.create(label='Foo bar')
125

  
126
    app = login(app, username='manager', password='manager')
127
    app.get('/manage/check-type/group/%s/add/' % group.pk, status=403)
128

  
129

  
130
def test_edit_check_type(app, admin_user):
131
    group = CheckTypeGroup.objects.create(label='Foo bar')
132
    check_type = CheckType.objects.create(label='Foo reason', group=group, kind='presence')
133
    check_type2 = CheckType.objects.create(label='Baz', group=group)
134
    group2 = CheckTypeGroup.objects.create(label='Foo bar')
135
    check_type3 = CheckType.objects.create(label='Foo bar reason', group=group2)
136
    agenda = Agenda.objects.create(label='Foo bar')
137
    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
138
    Booking.objects.create(event=event)
139

  
140
    app = login(app)
141
    resp = app.get('/manage/check-types/')
142
    resp = resp.click(href='/manage/check-type/group/%s/%s/edit/' % (group.pk, check_type.pk))
143
    assert 'This check type is set on some existing bookings, modify it with caution.' not in resp
144
    resp.form['label'] = 'Foo bar reason'
145
    resp.form['slug'] = check_type2.slug
146
    resp.form['disabled'] = True
147
    assert 'kind' not in resp.context['form'].fields
148
    resp = resp.form.submit()
149
    assert resp.context['form'].errors['slug'] == ['Another check type exists with the same identifier.']
150

  
151
    resp.form['slug'] = check_type3.slug
152
    resp = resp.form.submit()
153
    assert resp.location.endswith('/manage/check-types/')
154
    check_type.refresh_from_db()
155
    assert check_type.label == 'Foo bar reason'
156
    assert check_type.slug == 'foo-bar-reason'
157
    assert check_type.kind == 'presence'
158
    assert check_type.disabled is True
159

  
160
    # check_type is used
161
    Booking.objects.update(user_check_type_slug=check_type.slug)
162
    resp = app.get('/manage/check-type/group/%s/%s/edit/' % (group.pk, check_type.pk))
163
    assert 'This check type is set on some existing bookings, modify it with caution.' in resp
164

  
165
    app.get('/manage/check-type/group/%s/%s/edit/' % (group2.pk, check_type.pk), status=404)
166

  
167

  
168
def test_edit_check_type_as_manager(app, manager_user, agenda_with_restrictions):
169
    group = CheckTypeGroup.objects.create(label='Foo bar')
170
    check_type = CheckType.objects.create(label='Foo reason', group=group)
171

  
172
    app = login(app, username='manager', password='manager')
173
    app.get('/manage/check-type/group/%s/%s/edit/' % (group.pk, check_type.pk), status=403)
174

  
175

  
176
def test_delete_check_type(app, admin_user):
177
    group = CheckTypeGroup.objects.create(label='Foo bar')
178
    check_type = CheckType.objects.create(label='Foo reason', group=group)
179
    agenda = Agenda.objects.create(label='Foo bar')
180
    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
181
    Booking.objects.create(event=event)
182

  
183
    app = login(app)
184
    resp = app.get('/manage/check-types/')
185
    resp = resp.click(href='/manage/check-type/group/%s/%s/delete/' % (group.pk, check_type.pk))
186
    resp = resp.form.submit()
187
    assert resp.location.endswith('/manage/check-types/')
188
    assert CheckTypeGroup.objects.exists() is True
189
    assert CheckType.objects.exists() is False
190

  
191
    # check_type is used
192
    check_type = CheckType.objects.create(label='Foo reason', group=group)
193
    Booking.objects.update(user_check_type_slug=check_type.slug)
194
    resp = app.get('/manage/check-type/group/%s/%s/delete/' % (group.pk, check_type.pk))
195
    assert 'Can not delete this check type: it is set on some existing bookings.' in resp
196
    resp.form.submit(status=404)
197

  
198
    group2 = CheckTypeGroup.objects.create(label='Foo bar baz')
199
    app.get('/manage/check-type/group/%s/%s/delete/' % (group2.pk, check_type.pk), status=404)
200

  
201

  
202
def test_delete_check_type_as_manager(app, manager_user, agenda_with_restrictions):
203
    group = CheckTypeGroup.objects.create(label='Foo bar')
204
    check_type = CheckType.objects.create(label='Foo reason', group=group)
205

  
206
    app = login(app, username='manager', password='manager')
207
    app.get('/manage/check-type/group/%s/%s/delete/' % (group.pk, check_type.pk), status=403)
208

  
209

  
210
def test_meetings_agenda_group(app, admin_user):
211
    agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
212
    CheckTypeGroup.objects.create(label='Foo bar')
213

  
214
    app = login(app)
215
    resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
216
    assert 'has_check_types' not in resp.context
217

  
218
    # not for meetings agenda
219
    app.get('/manage/agendas/%s/check-types' % agenda.pk, status=404)
220

  
221

  
222
def test_agenda_group(app, admin_user):
223
    agenda = Agenda.objects.create(label='Foo bar', kind='events')
224
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
225
    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
226
    booking = Booking.objects.create(event=event)
227

  
228
    app = login(app)
229
    resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
230
    assert 'has_check_types' in resp.context
231
    assert resp.context['has_check_types'] is False
232
    assert 'No check types configured for this agenda.' not in resp
233
    assert '/manage/agendas/%s/check-types' % agenda.pk not in resp
234
    resp = resp.click(href='/manage/agendas/%s/check-options' % agenda.pk)
235
    assert 'check_type_group' not in resp.context['form'].fields
236

  
237
    group = CheckTypeGroup.objects.create(label='Foo bar')
238
    check_type = CheckType.objects.create(label='Foo reason', group=group)
239
    resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
240
    assert 'has_check_types' in resp.context
241
    assert resp.context['has_check_types'] is True
242
    assert 'No check types configured for this agenda.' in resp
243
    resp = resp.click(href='/manage/agendas/%s/check-options' % agenda.pk)
244
    resp.form['check_type_group'] = group.pk
245
    resp = resp.form.submit().follow()
246
    agenda.refresh_from_db()
247
    assert agenda.check_type_group == group
248
    assert 'Check type group: Foo bar' in resp
249

  
250
    # cannot change check_type group booking with non null check_type exists
251
    booking.user_check_type_slug = check_type.slug
252
    booking.save()
253
    resp = app.get('/manage/agendas/%s/check-options' % agenda.pk)
254
    assert 'check_type_group' not in resp.context['form'].fields
tests/manager/test_event.py
1811 1811
        resp = app.get(
1812 1812
            '/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), params={'extra-data-foo': 'val1'}
1813 1813
        )
1814
        assert len(ctx.captured_queries) == 11
1814
        assert len(ctx.captured_queries) == 10
1815 1815
    assert 'User none' not in resp
1816 1816
    assert 'User empty' not in resp
1817 1817
    assert 'User foo-val1 bar-none presence' in resp
tests/manager/test_import_export.py
7 7
from django.utils.timezone import now
8 8
from webtest import Upload
9 9

  
10
from chrono.agendas.models import (
11
    Agenda,
12
    Booking,
13
    CheckType,
14
    CheckTypeGroup,
15
    Desk,
16
    Event,
17
    MeetingType,
18
    UnavailabilityCalendar,
19
)
10
from chrono.agendas.models import Agenda, Booking, Desk, Event, MeetingType, UnavailabilityCalendar
20 11
from tests.utils import login
21 12

  
22 13
pytestmark = pytest.mark.django_db
......
36 27
    assert site_json == {
37 28
        'unavailability_calendars': [],
38 29
        'agendas': [],
39
        'check_type_groups': [],
40 30
        'events_types': [],
41 31
        'resources': [],
42 32
        'categories': [],
......
51 41
    site_json = json.loads(resp.text)
52 42
    assert len(site_json['agendas']) == 1
53 43
    assert len(site_json['unavailability_calendars']) == 1
54
    assert len(site_json['check_type_groups']) == 0
55 44
    assert len(site_json['events_types']) == 0
56 45
    assert len(site_json['resources']) == 0
57 46
    assert len(site_json['categories']) == 0
58 47

  
59 48
    resp = app.get('/manage/agendas/export/')
60 49
    resp.form['agendas'] = False
61
    resp.form['check_type_groups'] = False
62 50
    resp.form['events_types'] = False
63 51
    resp.form['resources'] = False
64 52
    resp.form['categories'] = False
......
67 55
    site_json = json.loads(resp.text)
68 56
    assert 'agendas' not in site_json
69 57
    assert 'unavailability_calendars' in site_json
70
    assert 'check_type_groups' not in site_json
71 58
    assert 'events_types' not in site_json
72 59
    assert 'resources' not in site_json
73 60
    assert 'categories' not in site_json
......
291 278
    resp = resp.form.submit()
292 279
    assert 'Missing roles: &quot;gé1&quot;' in resp.text
293 280
    del calendar_export_dict['unavailability_calendars'][0]['permissions']['view']
294

  
295

  
296
@pytest.mark.freeze_time('2021-07-08')
297
def test_import_check_type_group(app, admin_user):
298
    group = CheckTypeGroup.objects.create(label='Foo bar')
299
    CheckType.objects.create(label='Foo reason', group=group)
300
    CheckType.objects.create(label='Baz', group=group)
301

  
302
    app = login(app)
303
    resp = app.get('/manage/check-type/group/%s/export/' % group.id)
304
    assert resp.headers['content-type'] == 'application/json'
305
    assert (
306
        resp.headers['content-disposition']
307
        == 'attachment; filename="export_check_type_group_foo-bar_20210708.json"'
308
    )
309
    group_export = resp.text
310

  
311
    # existing group
312
    resp = app.get('/manage/', status=200)
313
    resp = resp.click('Import')
314
    resp.form['agendas_json'] = Upload('export.json', group_export.encode('utf-8'), 'application/json')
315
    resp = resp.form.submit()
316
    assert resp.location.endswith('/manage/check-types/')
317
    resp = resp.follow()
318
    assert 'No check type group created. A check type group has been updated.' not in resp.text
319
    assert CheckTypeGroup.objects.count() == 1
320
    assert CheckType.objects.count() == 2
321

  
322
    # new group
323
    CheckTypeGroup.objects.all().delete()
324
    resp = app.get('/manage/', status=200)
325
    resp = resp.click('Import')
326
    resp.form['agendas_json'] = Upload('export.json', group_export.encode('utf-8'), 'application/json')
327
    resp = resp.form.submit()
328
    assert resp.location.endswith('/manage/check-types/')
329
    resp = resp.follow()
330
    assert 'A check type group has been created. No check type group updated.' not in resp.text
331
    assert CheckTypeGroup.objects.count() == 1
332
    assert CheckType.objects.count() == 2
333

  
334
    # multiple groups
335
    groups = json.loads(group_export)
336
    groups['check_type_groups'].append(copy.copy(groups['check_type_groups'][0]))
337
    groups['check_type_groups'].append(copy.copy(groups['check_type_groups'][0]))
338
    groups['check_type_groups'][1]['label'] = 'Foo bar 2'
339
    groups['check_type_groups'][1]['slug'] = 'foo-bar-2'
340
    groups['check_type_groups'][2]['label'] = 'Foo bar 3'
341
    groups['check_type_groups'][2]['slug'] = 'foo-bar-3'
342

  
343
    resp = app.get('/manage/', status=200)
344
    resp = resp.click('Import')
345
    resp.form['agendas_json'] = Upload('export.json', json.dumps(groups).encode('utf-8'), 'application/json')
346
    resp = resp.form.submit()
347
    assert resp.location.endswith('/manage/')
348
    resp = resp.follow()
349
    assert '2 check type groups have been created. A check type group has been updated.' in resp.text
350
    assert CheckTypeGroup.objects.count() == 3
351
    assert CheckType.objects.count() == 6
352

  
353
    CheckTypeGroup.objects.all().delete()
354
    resp = app.get('/manage/', status=200)
355
    resp = resp.click('Import')
356
    resp.form['agendas_json'] = Upload('export.json', json.dumps(groups).encode('utf-8'), 'application/json')
357
    resp = resp.form.submit().follow()
358
    assert '3 check type groups have been created. No check type group updated.' in resp.text
359
    assert CheckTypeGroup.objects.count() == 3
360
    assert CheckType.objects.count() == 6
tests/test_agendas.py
18 18
    AgendaReminderSettings,
19 19
    Booking,
20 20
    Category,
21
    CheckTypeGroup,
22 21
    Desk,
23 22
    Event,
24 23
    EventCancellationReport,
......
187 186
    assert category.slug == 'foo-baz-2'
188 187

  
189 188

  
190
def test_check_type_group_slug():
191
    group = CheckTypeGroup.objects.create(label='Foo bar')
192
    assert group.slug == 'foo-bar'
193

  
194

  
195
def test_check_type_group_existing_slug():
196
    group = CheckTypeGroup.objects.create(label='Foo bar', slug='bar')
197
    assert group.slug == 'bar'
198

  
199

  
200
def test_check_type_group_duplicate_slugs():
201
    group = CheckTypeGroup.objects.create(label='Foo baz')
202
    assert group.slug == 'foo-baz'
203
    group = CheckTypeGroup.objects.create(label='Foo baz')
204
    assert group.slug == 'foo-baz-1'
205
    group = CheckTypeGroup.objects.create(label='Foo baz')
206
    assert group.slug == 'foo-baz-2'
207

  
208

  
209 189
def test_agenda_minimal_booking_delay(freezer):
210 190
    freezer.move_to('2021-07-09')
211 191
    agenda = Agenda.objects.create(label='Agenda', minimal_booking_delay=4)
tests/test_import_export.py
21 21
    AgendaNotificationsSettings,
22 22
    AgendaReminderSettings,
23 23
    Category,
24
    CheckType,
25
    CheckTypeGroup,
26 24
    Desk,
27 25
    Event,
28 26
    EventsType,
......
435 433
    assert agenda.category == category
436 434

  
437 435

  
438
def test_import_export_agenda_with_check_types(app):
439
    group = CheckTypeGroup.objects.create(label='foo')
440
    agenda = Agenda.objects.create(label='Foo Bar', kind='events', check_type_group=group)
441
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
442
    output = get_output_of_command('export_site')
443

  
444
    import_site(data={}, clean=True)
445
    assert Agenda.objects.count() == 0
446
    assert CheckTypeGroup.objects.count() == 0
447
    data = json.loads(output)
448
    del data['check_type_groups']
449

  
450
    with pytest.raises(AgendaImportError) as excinfo:
451
        import_site(data, overwrite=True)
452
    assert str(excinfo.value) == 'Missing "foo" check type group'
453

  
454
    CheckTypeGroup.objects.create(label='foobar')
455
    with pytest.raises(AgendaImportError) as excinfo:
456
        import_site(data, overwrite=True)
457
    assert str(excinfo.value) == 'Missing "foo" check type group'
458

  
459
    group = CheckTypeGroup.objects.create(label='foo')
460
    import_site(data, overwrite=True)
461
    agenda = Agenda.objects.get(slug=agenda.slug)
462
    assert agenda.check_type_group == group
463

  
464

  
465 436
def test_import_export_agenda_with_events_type(app):
466 437
    events_type = EventsType.objects.create(label='foo')
467 438
    agenda = Agenda.objects.create(label='Foo Bar', kind='events', events_type=events_type)
......
936 907
    assert calendar.label == 'Calendar Updated'
937 908

  
938 909

  
939
def test_import_export_check_type_group(app):
940
    output = get_output_of_command('export_site')
941
    payload = json.loads(output)
942
    assert len(payload['check_type_groups']) == 0
943

  
944
    group = CheckTypeGroup.objects.create(label='Foo bar')
945
    CheckType.objects.create(label='Foo reason', group=group)
946
    CheckType.objects.create(label='Baz', group=group)
947

  
948
    output = get_output_of_command('export_site')
949
    payload = json.loads(output)
950
    assert len(payload['check_type_groups']) == 1
951

  
952
    group.delete()
953
    assert not CheckTypeGroup.objects.exists()
954
    assert not CheckType.objects.exists()
955

  
956
    import_site(copy.deepcopy(payload))
957
    assert CheckTypeGroup.objects.count() == 1
958
    group = CheckTypeGroup.objects.first()
959
    assert group.label == 'Foo bar'
960
    assert group.slug == 'foo-bar'
961
    assert group.check_types.count() == 2
962
    assert CheckType.objects.get(group=group, label='Foo reason', slug='foo-reason')
963
    assert CheckType.objects.get(group=group, label='Baz', slug='baz')
964

  
965
    # update
966
    update_payload = copy.deepcopy(payload)
967
    update_payload['check_type_groups'][0]['label'] = 'Foo bar Updated'
968
    import_site(update_payload)
969
    group.refresh_from_db()
970
    assert group.label == 'Foo bar Updated'
971

  
972
    # insert another group
973
    group.slug = 'foo-bar-updated'
974
    group.save()
975
    import_site(copy.deepcopy(payload))
976
    assert CheckTypeGroup.objects.count() == 2
977
    group = CheckTypeGroup.objects.latest('pk')
978
    assert group.label == 'Foo bar'
979
    assert group.slug == 'foo-bar'
980
    assert group.check_types.count() == 2
981
    assert CheckType.objects.get(group=group, label='Foo reason', slug='foo-reason')
982
    assert CheckType.objects.get(group=group, label='Baz', slug='baz')
983

  
984
    # with overwrite
985
    CheckType.objects.create(group=group, label='Baz2')
986
    import_site(copy.deepcopy(payload), overwrite=True)
987
    assert CheckTypeGroup.objects.count() == 2
988
    group = CheckTypeGroup.objects.latest('pk')
989
    assert group.label == 'Foo bar'
990
    assert group.slug == 'foo-bar'
991
    assert group.check_types.count() == 2
992
    assert CheckType.objects.get(group=group, label='Foo reason', slug='foo-reason')
993
    assert CheckType.objects.get(group=group, label='Baz', slug='baz')
994

  
995

  
996 910
def test_import_export_category(app):
997 911
    output = get_output_of_command('export_site')
998 912
    payload = json.loads(output)
999
-