general: make event slugs optional (#37987)

This commit is contained in:
Frédéric Péters 2019-11-27 09:27:44 +01:00
parent a7da68f6a6
commit b9c5fe3774
9 changed files with 31 additions and 40 deletions

View File

@ -5,21 +5,6 @@ from django.db import migrations, models
from django.utils.text import slugify
def set_slug_on_events(apps, schema_editor):
Event = apps.get_model('agendas', 'Event')
for event in Event.objects.filter(slug=''):
base_slug = slugify(event.label)
slug = base_slug
i = 1
while True:
if not Event.objects.filter(slug=slug).exists():
break
slug = '%s-%s' % (base_slug, i)
i += 1
event.slug = slug
event.save(update_fields=['slug'])
class Migration(migrations.Migration):
dependencies = [
@ -30,8 +15,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='event',
name='slug',
field=models.SlugField(default='', max_length=160, verbose_name='Identifier'),
field=models.SlugField(default=None, null=True, blank=True, max_length=160, verbose_name='Identifier'),
preserve_default=False,
),
migrations.RunPython(set_slug_on_events, lambda x, y: None),
]

View File

@ -15,6 +15,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='event',
name='slug',
field=models.SlugField(max_length=160, unique=True, verbose_name='Identifier'),
field=models.SlugField(default=None, null=True, blank=True, max_length=160, verbose_name='Identifier'),
),
]

View File

@ -288,7 +288,7 @@ class Event(models.Model):
_('Places in waiting list'), default=0)
label = models.CharField(_('Label'), max_length=150, null=True, blank=True,
help_text=_('Optional label to identify this date.'))
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
slug = models.SlugField(_('Identifier'), max_length=160, null=True, blank=True, default=None)
description = models.TextField(_('Description'), null=True, blank=True,
help_text=_('Optional event description.'))
full = models.BooleanField(default=False)
@ -297,6 +297,7 @@ class Event(models.Model):
class Meta:
ordering = ['agenda', 'start_datetime', 'label']
unique_together = ('agenda', 'slug')
def __str__(self):
if self.label:
@ -305,8 +306,6 @@ class Event(models.Model):
def save(self, *args, **kwargs):
self.check_full()
if not self.slug:
self.slug = generate_slug(self)
return super(Event, self).save(*args, **kwargs)
def check_full(self):
@ -347,7 +346,7 @@ class Event(models.Model):
def import_json(cls, data):
data['start_datetime'] = make_aware(datetime.datetime.strptime(
data['start_datetime'], '%Y-%m-%d %H:%M:%S'))
if 'slug' in data:
if data.get('slug'):
event, created = cls.objects.get_or_create(slug=data['slug'], defaults=data)
if not created:
for k, v in data.items():

View File

@ -174,6 +174,10 @@ class ImportEventsForm(forms.Form):
'as columns.'))
events = None
def __init__(self, agenda_pk, **kwargs):
self.agenda_pk = agenda_pk
super(ImportEventsForm, self).__init__(**kwargs)
def clean_events_csv_file(self):
content = self.cleaned_data['events_csv_file'].read()
if b'\0' in content:
@ -209,6 +213,7 @@ class ImportEventsForm(forms.Form):
if i == 0 and csvline[0].strip('#') in ('date', 'Date', _('date'), _('Date')):
continue
event = Event()
event.agenda_id = self.agenda_pk
for datetime_fmt in ('%Y-%m-%d %H:%M', '%d/%m/%Y %H:%M',
'%d/%m/%Y %Hh%M', '%Y-%m-%d %H:%M:%S', '%d/%m/%Y %H:%M:%S'):
try:
@ -231,7 +236,7 @@ class ImportEventsForm(forms.Form):
raise ValidationError(_('Invalid file format. (number of places in waiting list, line %d)') % (i+1))
if len(csvline) >= 5:
event.label = force_text(csvline[4])
exclude = ['agenda', 'desk', 'meeting_type']
exclude = ['desk', 'meeting_type']
if len(csvline) >= 6:
event.slug = ' '.join([force_text(x) for x in csvline[5:]])
else:

View File

@ -50,7 +50,7 @@
data-total="{{event.waiting_list_places}}" data-booked="{{event.waiting_list}}"
{% endif %}
><a rel="popup" href="{% if user_can_manage %}{% url 'chrono-manager-event-edit' pk=event.id %}{% else %}#{% endif %}">
{% if event.label %}{{event.label}} {% endif %}[{% trans "identifier:" %} {{ event.slug }}] /
{% if event.label %}{{event.label}} / {% endif %}
{{ event.start_datetime }}
{% if event.full %}/ <span class="full">{% trans "full" %}</span>{% endif %}
(

View File

@ -591,10 +591,19 @@ class AgendaImportEventsView(ManagedAgendaMixin, FormView):
template_name = 'chrono/manager_import_events.html'
agenda = None
def get_form_kwargs(self):
kwargs = super(AgendaImportEventsView, self).get_form_kwargs()
kwargs['agenda_pk'] = self.kwargs['pk']
return kwargs
def form_valid(self, form):
if form.events:
for event in form.events:
event.agenda_id = self.kwargs['pk']
if event.slug and Event.objects.filter(
agenda_id=event.agenda_id,
slug=event.slug).exists():
raise ValidationError(_('Duplicated event identifier'))
event.save()
messages.info(self.request, _('%d events have been imported.') % len(form.events))
return super(AgendaImportEventsView, self).form_valid(form)

View File

@ -123,18 +123,12 @@ def test_slug():
agenda.save()
assert agenda.slug == 'foo-bar'
event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar')
assert event.slug == 'foo-bar'
def test_existing_slug():
agenda = Agenda(label=u'Foo bar', slug='bar')
agenda.save()
assert agenda.slug == 'bar'
event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar', slug='bar')
assert event.slug == 'bar'
def test_duplicate_slugs():
agenda = Agenda(label=u'Foo baz')
@ -147,13 +141,6 @@ def test_duplicate_slugs():
agenda.save()
assert agenda.slug == 'foo-baz-2'
event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar')
assert event.slug == 'foo-bar'
event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar')
assert event.slug == 'foo-bar-1'
event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar')
assert event.slug == 'foo-bar-2'
def test_event_manager():
agenda = Agenda(label=u'Foo baz')

View File

@ -351,6 +351,8 @@ def test_booking_api(app, some_data, user):
assert Booking.objects.count() == 1
# access by slug
event.slug = 'bar'
event.save()
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.slug))
assert Booking.objects.count() == 2
assert Booking.objects.filter(event__agenda=agenda).count() == 2
@ -474,6 +476,9 @@ def test_booking_ics(app, some_data, meetings_agenda, user):
def test_booking_api_fillslots(app, some_data, user):
agenda = Agenda.objects.filter(label=u'Foo bar')[0]
events_ids = [x.id for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()]
for i, event in enumerate([x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()]):
event.slug = 'event-%s' % i
event.save()
events_slugs = [x.slug for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()]
assert len(events_ids) == 3
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0] # first event
@ -992,6 +997,8 @@ def test_status(app, some_data, user):
assert resp.json['places']['waiting_list_reserved'] == 1
# access by slug
event.slug = 'bar'
event.save()
resp = app.get('/api/agenda/%s/status/%s/' % (agenda_id, event.slug))
# not found event
resp = app.get('/api/agenda/%s/status/%s/' % (agenda_id, 'unknown'), status=404)
@ -1453,8 +1460,7 @@ def test_agenda_meeting_api_multiple_desk(app, meetings_agenda, user):
booking_url = event_data['api']['fillslot_url']
with CaptureQueriesContext(connection) as ctx:
app.post(booking_url)
# 2 + idx: because of slug unicity
assert len(ctx.captured_queries) == queries_count_fillslot1 + 2 + idx
assert len(ctx.captured_queries) == queries_count_fillslot1
with CaptureQueriesContext(connection) as ctx:
app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)

View File

@ -328,6 +328,7 @@ def test_add_event(app, admin_user):
resp = resp.form.submit()
resp = resp.follow()
event = Event.objects.get(places=10)
assert event.slug is None
assert not "This agenda doesn't have any event yet." in resp.text
assert '/manage/events/%s/' % event.id in resp.text
assert ('Feb. 15, %s, 5 p.m.' % year) in resp.text
@ -651,7 +652,7 @@ def test_import_events(app, admin_user):
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5,label,slug', 'text/csv')
resp = resp.form.submit(status=200)
assert 'Invalid file format. (slug: Event with this Identifier already exists.' in resp.text
assert 'Invalid file format. (__all__: Event with this Agenda and Identifier already exists.' in resp.text
def test_add_meetings_agenda(app, admin_user):