agendas: add slug form absence reason model (#53147)

This commit is contained in:
Lauréline Guérin 2021-04-15 14:17:07 +02:00
parent b8105d19ff
commit 992189a335
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
8 changed files with 142 additions and 4 deletions

View File

@ -0,0 +1,20 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('agendas', '0082_text_to_jsonb'),
]
operations = [
migrations.AddField(
model_name='absencereason',
name='slug',
field=models.SlugField(max_length=160, null=True, verbose_name='Identifier'),
),
migrations.AlterUniqueTogether(
name='absencereason',
unique_together=['group', 'slug'],
),
]

View File

@ -0,0 +1,40 @@
from django.db import migrations
from django.utils.text import slugify
def generate_slug(instance, **query_filters):
base_slug = slugify(instance.label)
slug = base_slug
i = 1
while True:
queryset = instance._meta.model.objects.filter(slug=slug, **query_filters).exclude(pk=instance.pk)
if not queryset.exists():
break
slug = '%s-%s' % (base_slug, i)
i += 1
return slug
def set_slug_on_absence_reasons(apps, schema_editor):
AbsenceReason = apps.get_model('agendas', 'AbsenceReason')
for reason in AbsenceReason.objects.all().order_by('-pk'):
if (
reason.slug
and not AbsenceReason.objects.filter(slug=reason.slug, group=reason.group)
.exclude(pk=reason.pk)
.exists()
):
continue
reason.slug = generate_slug(reason, group=reason.group)
reason.save(update_fields=['slug'])
class Migration(migrations.Migration):
dependencies = [
('agendas', '0083_reason_slug'),
]
operations = [
migrations.RunPython(set_slug_on_absence_reasons, migrations.RunPython.noop),
]

View File

@ -0,0 +1,16 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('agendas', '0084_reason_slug'),
]
operations = [
migrations.AlterField(
model_name='absencereason',
name='slug',
field=models.SlugField(max_length=160, verbose_name='Identifier'),
),
]

View File

@ -2554,10 +2554,21 @@ class AbsenceReasonGroup(models.Model):
class AbsenceReason(models.Model):
group = models.ForeignKey(AbsenceReasonGroup, on_delete=models.CASCADE, related_name='absence_reasons')
slug = models.SlugField(_('Identifier'), max_length=160)
label = models.CharField(_('Label'), max_length=150)
class Meta:
ordering = ['label']
unique_together = ['group', 'slug']
def __str__(self):
return self.label
def save(self, *args, **kwargs):
if not self.slug:
self.slug = generate_slug(self, group=self.group)
super().save(*args, **kwargs)
@property
def base_slug(self):
return slugify(self.label)

View File

@ -53,6 +53,20 @@ from . import widgets
from .widgets import SplitDateTimeField
class AbsenceReasonForm(forms.ModelForm):
class Meta:
model = AbsenceReason
fields = ['label', 'slug']
def clean_slug(self):
slug = self.cleaned_data['slug']
if self.instance.group.absence_reasons.filter(slug=slug).exclude(pk=self.instance.pk).exists():
raise ValidationError(_('Another absence reason exists with the same identifier.'))
return slug
class AgendaAddForm(forms.ModelForm):
edit_role = forms.ModelChoiceField(
label=_('Edit Role'), required=False, queryset=Group.objects.all().order_by('name')

View File

@ -76,6 +76,7 @@ from chrono.agendas.models import (
)
from .forms import (
AbsenceReasonForm,
AgendaAddForm,
AgendaBookingDelaysForm,
AgendaDuplicateForm,
@ -700,7 +701,7 @@ absence_reason_add = AbsenceReasonAddView.as_view()
class AbsenceReasonEditView(UpdateView):
template_name = 'chrono/manager_absence_reason_form.html'
model = AbsenceReason
fields = ['label']
form_class = AbsenceReasonForm
def dispatch(self, request, *args, **kwargs):
self.group_pk = kwargs.pop('group_pk')

View File

@ -68,17 +68,24 @@ def test_add_group_as_manager(app, manager_user, agenda_with_restrictions):
def test_edit_group(app, admin_user):
group = AbsenceReasonGroup.objects.create(label='Foo bar')
group2 = AbsenceReasonGroup.objects.create(label='baz')
app = login(app)
resp = app.get('/manage/absence-reasons/')
resp = resp.click(href='/manage/absence-reason/group/%s/edit/' % group.pk)
resp.form['label'] = 'Foo bar baz'
resp.form['slug'] = 'baz'
resp.form['slug'] = group2.slug
resp = resp.form.submit()
assert resp.context['form'].errors['slug'] == [
'Absence reason group with this Identifier already exists.'
]
resp.form['slug'] = 'baz2'
resp = resp.form.submit()
assert resp.location.endswith('/manage/absence-reasons/')
group.refresh_from_db()
assert group.label == 'Foo bar baz'
assert group.slug == 'baz'
assert group.slug == 'baz2'
def test_edit_group_as_manager(app, manager_user, agenda_with_restrictions):
@ -121,6 +128,7 @@ def test_add_reason(app, admin_user):
assert resp.location.endswith('/manage/absence-reasons/')
assert reason.label == 'Foo reason'
assert reason.group == group
assert reason.slug == 'foo-reason'
def test_add_reason_as_manager(app, manager_user, agenda_with_restrictions):
@ -133,17 +141,25 @@ def test_add_reason_as_manager(app, manager_user, agenda_with_restrictions):
def test_edit_reason(app, admin_user):
group = AbsenceReasonGroup.objects.create(label='Foo bar')
reason = AbsenceReason.objects.create(label='Foo reason', group=group)
reason2 = AbsenceReason.objects.create(label='Baz', group=group)
group2 = AbsenceReasonGroup.objects.create(label='Foo bar')
reason3 = AbsenceReason.objects.create(label='Foo bar reason', group=group2)
app = login(app)
resp = app.get('/manage/absence-reasons/')
resp = resp.click(href='/manage/absence-reason/group/%s/%s/edit/' % (group.pk, reason.pk))
resp.form['label'] = 'Foo bar reason'
resp.form['slug'] = reason2.slug
resp = resp.form.submit()
assert resp.context['form'].errors['slug'] == ['Another absence reason exists with the same identifier.']
resp.form['slug'] = reason3.slug
resp = resp.form.submit()
assert resp.location.endswith('/manage/absence-reasons/')
reason.refresh_from_db()
assert reason.label == 'Foo bar reason'
assert reason.slug == 'foo-bar-reason'
group2 = AbsenceReasonGroup.objects.create(label='Foo bar baz')
app.get('/manage/absence-reason/group/%s/%s/edit/' % (group2.pk, reason.pk), status=404)

View File

@ -13,6 +13,7 @@ from django.test import override_settings
from django.utils.timezone import localtime, make_aware, now
from chrono.agendas.models import (
AbsenceReasonGroup,
Agenda,
AgendaNotificationsSettings,
AgendaReminderSettings,
@ -176,6 +177,25 @@ def test_category_duplicate_slugs():
assert category.slug == 'foo-baz-2'
def test_absence_reason_group_slug():
group = AbsenceReasonGroup.objects.create(label=u'Foo bar')
assert group.slug == 'foo-bar'
def test_absence_reason_group_existing_slug():
group = AbsenceReasonGroup.objects.create(label=u'Foo bar', slug='bar')
assert group.slug == 'bar'
def test_absence_reason_group_duplicate_slugs():
group = AbsenceReasonGroup.objects.create(label=u'Foo baz')
assert group.slug == 'foo-baz'
group = AbsenceReasonGroup.objects.create(label=u'Foo baz')
assert group.slug == 'foo-baz-1'
group = AbsenceReasonGroup.objects.create(label=u'Foo baz')
assert group.slug == 'foo-baz-2'
@pytest.mark.parametrize('with_prefetch', [True, False])
def test_agenda_is_available_for_simple_management(settings, with_prefetch):
settings.EXCEPTIONS_SOURCES = {