0001-api-add-a-field-slug-to-Event-15726.patch
chrono/agendas/migrations/0028_event_slug.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 |
from django.utils.text import slugify |
|
6 | ||
7 | ||
8 |
def set_slug_on_events(apps, schema_editor): |
|
9 |
Event = apps.get_model('agendas', 'Event') |
|
10 |
for event in Event.objects.filter(slug=''): |
|
11 |
base_slug = slugify(event.label) |
|
12 |
slug = base_slug |
|
13 |
i = 1 |
|
14 |
while True: |
|
15 |
if not Event.objects.filter(slug=slug).exists(): |
|
16 |
break |
|
17 |
slug = '%s-%s' % (base_slug, i) |
|
18 |
i += 1 |
|
19 |
event.slug = slug |
|
20 |
event.save(update_fields=['slug']) |
|
21 | ||
22 | ||
23 |
class Migration(migrations.Migration): |
|
24 | ||
25 |
dependencies = [ |
|
26 |
('agendas', '0027_event_description'), |
|
27 |
] |
|
28 | ||
29 |
operations = [ |
|
30 |
migrations.AddField( |
|
31 |
model_name='event', |
|
32 |
name='slug', |
|
33 |
field=models.SlugField(default='', max_length=160, verbose_name='Identifier'), |
|
34 |
preserve_default=False, |
|
35 |
), |
|
36 |
migrations.RunPython(set_slug_on_events, lambda x, y: None), |
|
37 |
] |
chrono/agendas/migrations/0029_auto_20191106_1320.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.18 on 2019-11-06 12:20 |
|
3 |
from __future__ import unicode_literals |
|
4 | ||
5 |
from django.db import migrations, models |
|
6 | ||
7 | ||
8 |
class Migration(migrations.Migration): |
|
9 | ||
10 |
dependencies = [ |
|
11 |
('agendas', '0028_event_slug'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.AlterField( |
|
16 |
model_name='event', |
|
17 |
name='slug', |
|
18 |
field=models.SlugField(max_length=160, unique=True, verbose_name='Identifier'), |
|
19 |
), |
|
20 |
] |
chrono/agendas/models.py | ||
---|---|---|
289 | 289 |
_('Places in waiting list'), default=0) |
290 | 290 |
label = models.CharField(_('Label'), max_length=150, null=True, blank=True, |
291 | 291 |
help_text=_('Optional label to identify this date.')) |
292 |
slug = models.SlugField(_('Identifier'), max_length=160, unique=True) |
|
292 | 293 |
description = models.TextField(_('Description'), null=True, blank=True, |
293 | 294 |
help_text=_('Optional event description.')) |
294 | 295 |
full = models.BooleanField(default=False) |
... | ... | |
305 | 306 | |
306 | 307 |
def save(self, *args, **kwargs): |
307 | 308 |
self.check_full() |
309 |
if not self.slug: |
|
310 |
base_slug = slugify(self.label) |
|
311 |
slug = base_slug |
|
312 |
i = 1 |
|
313 |
while True: |
|
314 |
if not Event.objects.filter(slug=slug).exists(): |
|
315 |
break |
|
316 |
slug = '%s-%s' % (base_slug, i) |
|
317 |
i += 1 |
|
318 |
self.slug = slug |
|
308 | 319 |
return super(Event, self).save(*args, **kwargs) |
309 | 320 | |
310 | 321 |
def check_full(self): |
... | ... | |
345 | 356 |
def import_json(cls, data): |
346 | 357 |
data['start_datetime'] = make_aware(datetime.datetime.strptime( |
347 | 358 |
data['start_datetime'], '%Y-%m-%d %H:%M:%S')) |
359 |
if 'slug' in data: |
|
360 |
event, created = cls.objects.get_or_create(slug=data['slug'], defaults=data) |
|
361 |
if not created: |
|
362 |
for k, v in data.items(): |
|
363 |
setattr(event, k, v) |
|
364 |
return event |
|
348 | 365 |
return cls(**data) |
349 | 366 | |
350 | 367 |
def export_json(self): |
... | ... | |
353 | 370 |
'places': self.places, |
354 | 371 |
'waiting_list_places': self.waiting_list_places, |
355 | 372 |
'label': self.label, |
373 |
'slug': self.slug, |
|
356 | 374 |
'description': self.description, |
357 | 375 |
} |
358 | 376 |
chrono/manager/forms.py | ||
---|---|---|
63 | 63 |
fields = ['label', 'slug', 'edit_role', 'view_role', 'minimal_booking_delay', 'maximal_booking_delay'] |
64 | 64 | |
65 | 65 | |
66 |
class NewEventForm(forms.ModelForm): |
|
67 |
class Meta: |
|
68 |
model = Event |
|
69 |
widgets = { |
|
70 |
'agenda': forms.HiddenInput(), |
|
71 |
'start_datetime': DateTimeWidget(), |
|
72 |
} |
|
73 |
exclude = ['full', 'meeting_type', 'desk', 'slug'] |
|
74 | ||
75 | ||
66 | 76 |
class EventForm(forms.ModelForm): |
67 | 77 |
class Meta: |
68 | 78 |
model = Event |
... | ... | |
220 | 230 |
except ValueError: |
221 | 231 |
raise ValidationError(_('Invalid file format. (number of places in waiting list, line %d)') % (i+1)) |
222 | 232 |
if len(csvline) >= 5: |
223 |
event.label = ' '.join([force_text(x) for x in csvline[4:]]) |
|
233 |
event.label = force_text(csvline[4]) |
|
234 |
exclude = ['agenda', 'desk', 'meeting_type'] |
|
235 |
if len(csvline) >= 6: |
|
236 |
event.slug = ' '.join([force_text(x) for x in csvline[5:]]) |
|
237 |
else: |
|
238 |
exclude += ['slug'] |
|
224 | 239 |
try: |
225 |
event.full_clean(exclude=['agenda', 'desk', 'meeting_type'])
|
|
240 |
event.full_clean(exclude=exclude)
|
|
226 | 241 |
except ValidationError as e: |
227 | 242 |
errors = [ |
228 | 243 |
_('Invalid file format. (%(label)s: %(errors)s, line %(line)d)') % { |
chrono/manager/templates/chrono/manager_agenda_settings.html | ||
---|---|---|
46 | 46 |
data-total="{{event.waiting_list_places}}" data-booked="{{event.waiting_list}}" |
47 | 47 |
{% endif %} |
48 | 48 |
><a rel="popup" href="{% if user_can_manage %}{% url 'chrono-manager-event-edit' pk=event.id %}{% else %}#{% endif %}"> |
49 |
{% if event.label %}{{event.label}} / {% endif %}
|
|
49 |
{% if event.label %}{{event.label}} {% endif %}[{% trans "identifier:" %} {{ event.slug }}] /
|
|
50 | 50 |
{{ event.start_datetime }} |
51 | 51 |
{% if event.full %}/ <span class="full">{% trans "full" %}</span>{% endif %} |
52 | 52 |
( |
chrono/manager/templates/chrono/manager_sample_events.txt | ||
---|---|---|
1 |
{% load i18n %}{% trans 'date' %},{% trans 'time' %},{% trans 'number of places' %},{% trans 'number of places in waiting list' %},{% trans 'label' %} |
|
2 |
{{ some_future_date|date:"Y-m-d" }},{{ some_future_date|date:"H:i" }},15,0,{% trans "example event" %} |
|
1 |
{% load i18n %}{% trans 'date' %},{% trans 'time' %},{% trans 'number of places' %},{% trans 'number of places in waiting list' %},{% trans 'label' %},{% trans 'identifier' %} |
|
2 |
{{ some_future_date|date:"Y-m-d" }},{{ some_future_date|date:"H:i" }},15,0,{% trans "example event" as label %}{{ label }},{{ label|slugify }} |
chrono/manager/views.py | ||
---|---|---|
36 | 36 |
Booking, Desk, TimePeriodException, |
37 | 37 |
ICSError, AgendaImportError) |
38 | 38 | |
39 |
from .forms import (AgendaAddForm, AgendaEditForm, EventForm, NewMeetingTypeForm, MeetingTypeForm, |
|
39 |
from .forms import (AgendaAddForm, AgendaEditForm, NewEventForm, EventForm, NewMeetingTypeForm, MeetingTypeForm,
|
|
40 | 40 |
TimePeriodForm, ImportEventsForm, NewDeskForm, DeskForm, TimePeriodExceptionForm, |
41 | 41 |
ExceptionsImportForm, AgendasImportForm, TimePeriodAddForm) |
42 | 42 |
from .utils import import_site |
... | ... | |
567 | 567 |
class AgendaAddEventView(ManagedAgendaMixin, CreateView): |
568 | 568 |
template_name = 'chrono/manager_event_form.html' |
569 | 569 |
model = Event |
570 |
form_class = EventForm |
|
570 |
form_class = NewEventForm
|
|
571 | 571 | |
572 | 572 |
agenda_add_event = AgendaAddEventView.as_view() |
573 | 573 |
tests/test_agendas.py | ||
---|---|---|
123 | 123 |
agenda.save() |
124 | 124 |
assert agenda.slug == 'foo-bar' |
125 | 125 | |
126 |
event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar') |
|
127 |
assert event.slug == 'foo-bar' |
|
128 | ||
129 | ||
126 | 130 |
def test_existing_slug(): |
127 | 131 |
agenda = Agenda(label=u'Foo bar', slug='bar') |
128 | 132 |
agenda.save() |
129 | 133 |
assert agenda.slug == 'bar' |
130 | 134 | |
135 |
event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar', slug='bar') |
|
136 |
assert event.slug == 'bar' |
|
137 | ||
138 | ||
131 | 139 |
def test_duplicate_slugs(): |
132 | 140 |
agenda = Agenda(label=u'Foo baz') |
133 | 141 |
agenda.save() |
... | ... | |
139 | 147 |
agenda.save() |
140 | 148 |
assert agenda.slug == 'foo-baz-2' |
141 | 149 | |
150 |
event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar') |
|
151 |
assert event.slug == 'foo-bar' |
|
152 |
event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar') |
|
153 |
assert event.slug == 'foo-bar-1' |
|
154 |
event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar') |
|
155 |
assert event.slug == 'foo-bar-2' |
|
156 | ||
157 | ||
142 | 158 |
def test_event_manager(): |
143 | 159 |
agenda = Agenda(label=u'Foo baz') |
144 | 160 |
agenda.save() |
tests/test_manager.py | ||
---|---|---|
636 | 636 |
'"2016-09-19"\t"18:00"\t"10"'.encode('iso-8859-15'), 'text/csv') |
637 | 637 |
resp = resp.form.submit(status=302) |
638 | 638 |
assert Event.objects.count() == 2 |
639 |
Event.objects.all().delete() |
|
640 | ||
641 |
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200) |
|
642 |
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5,label,slug', 'text/csv') |
|
643 |
resp = resp.form.submit(status=302) |
|
644 |
assert Event.objects.count() == 1 |
|
645 |
event = Event.objects.latest('pk') |
|
646 |
assert event.start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0)) |
|
647 |
assert event.places == 10 |
|
648 |
assert event.waiting_list_places == 5 |
|
649 |
assert event.label == 'label' |
|
650 |
assert event.slug == 'slug' |
|
651 |
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200) |
|
652 |
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5,label,slug', 'text/csv') |
|
653 |
resp = resp.form.submit(status=200) |
|
654 |
assert 'Invalid file format. (slug: Event with this Identifier already exists.' in resp.text |
|
639 | 655 | |
640 | 656 | |
641 | 657 |
def test_add_meetings_agenda(app, admin_user): |
642 |
- |