Projet

Général

Profil

0001-general-make-event-slugs-optional-37987.patch

Frédéric Péters, 27 novembre 2019 10:23

Télécharger (12,2 ko)

Voir les différences:

Subject: [PATCH] general: make event slugs optional (#37987)

 chrono/agendas/migrations/0028_event_slug.py   | 18 +-----------------
 .../migrations/0029_auto_20191106_1320.py      |  2 +-
 chrono/agendas/models.py                       |  7 +++----
 chrono/manager/forms.py                        |  7 ++++++-
 .../chrono/manager_agenda_settings.html        |  2 +-
 chrono/manager/views.py                        |  9 +++++++++
 tests/test_agendas.py                          | 13 -------------
 tests/test_api.py                              | 10 ++++++++--
 tests/test_manager.py                          |  3 ++-
 9 files changed, 31 insertions(+), 40 deletions(-)
chrono/agendas/migrations/0028_event_slug.py
5 5
from django.utils.text import slugify
6 6

  
7 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 8
class Migration(migrations.Migration):
24 9

  
25 10
    dependencies = [
......
30 15
        migrations.AddField(
31 16
            model_name='event',
32 17
            name='slug',
33
            field=models.SlugField(default='', max_length=160, verbose_name='Identifier'),
18
            field=models.SlugField(default=None, null=True, blank=True, max_length=160, verbose_name='Identifier'),
34 19
            preserve_default=False,
35 20
        ),
36
        migrations.RunPython(set_slug_on_events, lambda x, y: None),
37 21
    ]
chrono/agendas/migrations/0029_auto_20191106_1320.py
15 15
        migrations.AlterField(
16 16
            model_name='event',
17 17
            name='slug',
18
            field=models.SlugField(max_length=160, unique=True, verbose_name='Identifier'),
18
            field=models.SlugField(default=None, null=True, blank=True, max_length=160, verbose_name='Identifier'),
19 19
        ),
20 20
    ]
chrono/agendas/models.py
288 288
            _('Places in waiting list'), default=0)
289 289
    label = models.CharField(_('Label'), max_length=150, null=True, blank=True,
290 290
            help_text=_('Optional label to identify this date.'))
291
    slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
291
    slug = models.SlugField(_('Identifier'), max_length=160, null=True, blank=True, default=None)
292 292
    description = models.TextField(_('Description'), null=True, blank=True,
293 293
            help_text=_('Optional event description.'))
294 294
    full = models.BooleanField(default=False)
......
297 297

  
298 298
    class Meta:
299 299
        ordering = ['agenda', 'start_datetime', 'label']
300
        unique_together = ('agenda', 'slug')
300 301

  
301 302
    def __str__(self):
302 303
        if self.label:
......
305 306

  
306 307
    def save(self, *args, **kwargs):
307 308
        self.check_full()
308
        if not self.slug:
309
            self.slug = generate_slug(self)
310 309
        return super(Event, self).save(*args, **kwargs)
311 310

  
312 311
    def check_full(self):
......
347 346
    def import_json(cls, data):
348 347
        data['start_datetime'] = make_aware(datetime.datetime.strptime(
349 348
            data['start_datetime'], '%Y-%m-%d %H:%M:%S'))
350
        if 'slug' in data:
349
        if data.get('slug'):
351 350
            event, created = cls.objects.get_or_create(slug=data['slug'], defaults=data)
352 351
            if not created:
353 352
                for k, v in data.items():
chrono/manager/forms.py
174 174
                        'as columns.'))
175 175
    events = None
176 176

  
177
    def __init__(self, agenda_pk, **kwargs):
178
        self.agenda_pk = agenda_pk
179
        super(ImportEventsForm, self).__init__(**kwargs)
180

  
177 181
    def clean_events_csv_file(self):
178 182
        content = self.cleaned_data['events_csv_file'].read()
179 183
        if b'\0' in content:
......
209 213
            if i == 0 and csvline[0].strip('#') in ('date', 'Date', _('date'), _('Date')):
210 214
                continue
211 215
            event = Event()
216
            event.agenda_id = self.agenda_pk
212 217
            for datetime_fmt in ('%Y-%m-%d %H:%M', '%d/%m/%Y %H:%M',
213 218
                    '%d/%m/%Y %Hh%M', '%Y-%m-%d %H:%M:%S', '%d/%m/%Y %H:%M:%S'):
214 219
                try:
......
231 236
                    raise ValidationError(_('Invalid file format. (number of places in waiting list, line %d)') % (i+1))
232 237
            if len(csvline) >= 5:
233 238
                event.label = force_text(csvline[4])
234
            exclude = ['agenda', 'desk', 'meeting_type']
239
            exclude = ['desk', 'meeting_type']
235 240
            if len(csvline) >= 6:
236 241
                event.slug = ' '.join([force_text(x) for x in csvline[5:]])
237 242
            else:
chrono/manager/templates/chrono/manager_agenda_settings.html
50 50
          data-total="{{event.waiting_list_places}}" data-booked="{{event.waiting_list}}"
51 51
        {% endif %}
52 52
        ><a rel="popup" href="{% if user_can_manage %}{% url 'chrono-manager-event-edit' pk=event.id %}{% else %}#{% endif %}">
53
        {% if event.label %}{{event.label}} {% endif %}[{% trans "identifier:" %} {{ event.slug }}] /
53
        {% if event.label %}{{event.label}} / {% endif %}
54 54
        {{ event.start_datetime }}
55 55
        {% if event.full %}/ <span class="full">{% trans "full" %}</span>{% endif %}
56 56
        (
chrono/manager/views.py
591 591
    template_name = 'chrono/manager_import_events.html'
592 592
    agenda = None
593 593

  
594
    def get_form_kwargs(self):
595
        kwargs = super(AgendaImportEventsView, self).get_form_kwargs()
596
        kwargs['agenda_pk'] = self.kwargs['pk']
597
        return kwargs
598

  
594 599
    def form_valid(self, form):
595 600
        if form.events:
596 601
            for event in form.events:
597 602
                event.agenda_id = self.kwargs['pk']
603
                if event.slug and Event.objects.filter(
604
                        agenda_id=event.agenda_id,
605
                        slug=event.slug).exists():
606
                    raise ValidationError(_('Duplicated event identifier'))
598 607
                event.save()
599 608
            messages.info(self.request, _('%d events have been imported.') % len(form.events))
600 609
        return super(AgendaImportEventsView, self).form_valid(form)
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 127
def test_existing_slug():
131 128
    agenda = Agenda(label=u'Foo bar', slug='bar')
132 129
    agenda.save()
133 130
    assert agenda.slug == 'bar'
134 131

  
135
    event = Event.objects.create(start_datetime=now(), places=42, agenda=agenda, label='Foo bar', slug='bar')
136
    assert event.slug == 'bar'
137

  
138 132

  
139 133
def test_duplicate_slugs():
140 134
    agenda = Agenda(label=u'Foo baz')
......
147 141
    agenda.save()
148 142
    assert agenda.slug == 'foo-baz-2'
149 143

  
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 144

  
158 145
def test_event_manager():
159 146
    agenda = Agenda(label=u'Foo baz')
tests/test_api.py
351 351
    assert Booking.objects.count() == 1
352 352

  
353 353
    # access by slug
354
    event.slug = 'bar'
355
    event.save()
354 356
    resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.slug))
355 357
    assert Booking.objects.count() == 2
356 358
    assert Booking.objects.filter(event__agenda=agenda).count() == 2
......
474 476
def test_booking_api_fillslots(app, some_data, user):
475 477
    agenda = Agenda.objects.filter(label=u'Foo bar')[0]
476 478
    events_ids = [x.id for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()]
479
    for i, event in enumerate([x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()]):
480
        event.slug = 'event-%s' % i
481
        event.save()
477 482
    events_slugs = [x.slug for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()]
478 483
    assert len(events_ids) == 3
479 484
    event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]  # first event
......
992 997
    assert resp.json['places']['waiting_list_reserved'] == 1
993 998

  
994 999
    # access by slug
1000
    event.slug = 'bar'
1001
    event.save()
995 1002
    resp = app.get('/api/agenda/%s/status/%s/' % (agenda_id, event.slug))
996 1003
    # not found event
997 1004
    resp = app.get('/api/agenda/%s/status/%s/' % (agenda_id, 'unknown'), status=404)
......
1453 1460
        booking_url = event_data['api']['fillslot_url']
1454 1461
        with CaptureQueriesContext(connection) as ctx:
1455 1462
            app.post(booking_url)
1456
            # 2 + idx: because of slug unicity
1457
            assert len(ctx.captured_queries) == queries_count_fillslot1 + 2 + idx
1463
            assert len(ctx.captured_queries) == queries_count_fillslot1
1458 1464

  
1459 1465
    with CaptureQueriesContext(connection) as ctx:
1460 1466
        app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
tests/test_manager.py
328 328
    resp = resp.form.submit()
329 329
    resp = resp.follow()
330 330
    event = Event.objects.get(places=10)
331
    assert event.slug is None
331 332
    assert not "This agenda doesn't have any event yet." in resp.text
332 333
    assert '/manage/events/%s/' % event.id in resp.text
333 334
    assert ('Feb. 15, %s, 5 p.m.' % year) in resp.text
......
651 652
    resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
652 653
    resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5,label,slug', 'text/csv')
653 654
    resp = resp.form.submit(status=200)
654
    assert 'Invalid file format. (slug: Event with this Identifier already exists.' in resp.text
655
    assert 'Invalid file format. (__all__: Event with this Agenda and Identifier already exists.' in resp.text
655 656

  
656 657

  
657 658
def test_add_meetings_agenda(app, admin_user):
658
-