From 2425eabb7feb88930a9a222342d28a625f2b3d76 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 6 May 2021 15:20:09 +0200 Subject: [PATCH] agendas: allow custom event api text (#53661) --- .../0088_agenda_event_display_template.py | 23 ++++++++++++++++++ chrono/agendas/models.py | 8 +++++++ chrono/api/views.py | 5 +++- chrono/manager/forms.py | 2 ++ tests/manager/test_all.py | 3 +++ tests/test_api.py | 24 +++++++++++++++++-- 6 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 chrono/agendas/migrations/0088_agenda_event_display_template.py diff --git a/chrono/agendas/migrations/0088_agenda_event_display_template.py b/chrono/agendas/migrations/0088_agenda_event_display_template.py new file mode 100644 index 0000000..9643e0c --- /dev/null +++ b/chrono/agendas/migrations/0088_agenda_event_display_template.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.19 on 2021-05-19 12:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agendas', '0087_booking_user_name'), + ] + + operations = [ + migrations.AddField( + model_name='agenda', + name='event_display_template', + field=models.CharField( + blank=True, + help_text='By default event labels will be displayed to users. This allows for a custom template to include additional informations. For example, "{{ event.label }} - {{ event.start_datetime }}" will show event datetime after label.', + max_length=256, + verbose_name='Event display template', + ), + ), + ] diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py index eb2e166..c7bf65a 100644 --- a/chrono/agendas/models.py +++ b/chrono/agendas/models.py @@ -211,6 +211,14 @@ class Agenda(models.Model): blank=True, validators=[django_template_validator], ) + event_display_template = models.CharField( + _('Event display template'), + max_length=256, + blank=True, + help_text=_( + 'By default event labels will be displayed to users. This allows for a custom template to include additional informations. For example, "{{ event.label }} - {{ event.start_datetime }}" will show event datetime after label.' + ), + ) class Meta: ordering = ['label'] diff --git a/chrono/api/views.py b/chrono/api/views.py index 45be12a..4cdeaba 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -23,6 +23,7 @@ from django.db import transaction from django.db.models import Prefetch, Q from django.http import Http404, HttpResponse from django.shortcuts import get_object_or_404 +from django.template import Context, Template from django.urls import reverse from django.utils.dateparse import parse_date, parse_datetime from django.utils.encoding import force_text @@ -414,7 +415,9 @@ def is_event_disabled(event, min_places=1): def get_event_detail(request, event, agenda=None, min_places=1): agenda = agenda or event.agenda event_text = force_text(event) - if event.label and event.primary_event is not None: + if agenda.event_display_template: + event_text = Template(agenda.event_display_template).render(Context({'event': event})) + elif event.label and event.primary_event is not None: event_text = '%s (%s)' % ( event.label, date_format(localtime(event.start_datetime), 'DATETIME_FORMAT'), diff --git a/chrono/manager/forms.py b/chrono/manager/forms.py index 0af7e9b..4742e2e 100644 --- a/chrono/manager/forms.py +++ b/chrono/manager/forms.py @@ -101,12 +101,14 @@ class AgendaEditForm(forms.ModelForm): 'anonymize_delay', 'default_view', 'booking_form_url', + 'event_display_template', ] def __init__(self, *args, **kwargs): super(AgendaEditForm, self).__init__(*args, **kwargs) if kwargs['instance'].kind != 'events': del self.fields['booking_form_url'] + del self.fields['event_display_template'] self.fields['default_view'].choices = [ (k, v) for k, v in self.fields['default_view'].choices if k != 'open_events' ] diff --git a/tests/manager/test_all.py b/tests/manager/test_all.py index 61bbd9e..82523b1 100644 --- a/tests/manager/test_all.py +++ b/tests/manager/test_all.py @@ -469,6 +469,7 @@ def test_options_agenda(app, admin_user): assert resp.context['form'].initial['default_view'] == 'month' assert 'open_events' in [k for k, v in resp.context['form'].fields['default_view'].choices] assert 'booking_form_url' in resp.context['form'].fields + assert 'event_display_template' in resp.context['form'].fields resp = resp.form.submit() assert resp.location.endswith('/manage/agendas/%s/settings' % agenda_events.pk) resp = resp.follow() @@ -482,6 +483,7 @@ def test_options_agenda(app, admin_user): assert 'default_view' in resp.context['form'].fields assert 'open_events' not in [k for k, v in resp.context['form'].fields['default_view'].choices] assert 'booking_form_url' not in resp.context['form'].fields + assert 'event_display_template' not in resp.context['form'].fields resp.form['default_view'] = 'month' resp.form.submit() @@ -492,6 +494,7 @@ def test_options_agenda(app, admin_user): assert resp.form['default_view'].value == 'month' assert 'default_view' in resp.context['form'].fields assert 'booking_form_url' not in resp.context['form'].fields + assert 'event_display_template' not in resp.context['form'].fields assert 'open_events' not in [k for k, v in resp.context['form'].fields['default_view'].choices] diff --git a/tests/test_api.py b/tests/test_api.py index 1cb92ca..c4345ef 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -540,9 +540,10 @@ def test_datetime_api_fr(app): assert 'data' in resp.json +@pytest.mark.freeze_time('2021-05-06 14:00') def test_datetime_api_label(app): agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0) - Event.objects.create( + event = Event.objects.create( label='Hello world', slug='event-slug', start_datetime=(now() + datetime.timedelta(days=1)), @@ -550,7 +551,20 @@ def test_datetime_api_label(app): agenda=agenda, ) resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug) - assert 'Hello world' in [x['text'] for x in resp.json['data']] + assert 'Hello world' == resp.json['data'][0]['text'] + + agenda.event_display_template = '{{ event.label }} - {{ event.start_datetime }}' + agenda.save() + resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug) + assert resp.json['data'][0]['text'] == 'Hello world - May 7, 2021, 4 p.m.' + + Booking.objects.create(event=event) + agenda.event_display_template = ( + '{{ event.label }} ({{ event.remaining_places }}/{{ event.places }} places remaining)' + ) + agenda.save() + resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug) + assert resp.json['data'][0]['text'] == 'Hello world (4/5 places remaining)' def test_datetime_api_urls(app): @@ -6338,6 +6352,12 @@ def test_recurring_events_api(app, user, freezer): assert len(resp.json['data']) == 2 assert resp.json['data'][0]['id'] == 'abc:2021-02-02-1305' + # events follow agenda display template + agenda.event_display_template = '{{ event.label }} - {{ event.start_datetime }}' + agenda.save() + resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug) + assert resp.json['data'][0]['text'] == 'Test - Feb. 2, 2021, 1:05 p.m.' + def test_recurring_events_api_various_times(app, user, mock_now): agenda = Agenda.objects.create( -- 2.20.1