Projet

Général

Profil

0001-manager-display-link-to-booking-form-on-events-45417.patch

Lauréline Guérin, 25 septembre 2020 11:01

Télécharger (15,3 ko)

Voir les différences:

Subject: [PATCH] manager: display link to booking form on events (#45417)

 .../migrations/0062_auto_20200915_1401.py     |  4 +-
 .../migrations/0063_booking_form_url.py       | 25 ++++++
 chrono/agendas/models.py                      | 22 ++++++
 chrono/manager/forms.py                       |  2 +
 .../chrono/manager_agenda_event_fragment.html |  4 +-
 .../chrono/manager_event_detail.html          |  3 +
 tests/test_import_export.py                   | 23 ++++++
 tests/test_manager.py                         | 77 ++++++++++++++-----
 8 files changed, 138 insertions(+), 22 deletions(-)
 create mode 100644 chrono/agendas/migrations/0063_booking_form_url.py
chrono/agendas/migrations/0062_auto_20200915_1401.py
39 39
                    'email_extra_info',
40 40
                    models.TextField(
41 41
                        blank=True,
42
                        help_text='Basic information such as event name, time and date are already included',
42
                        help_text='Basic information such as event name, time and date are already included.',
43 43
                        verbose_name='Additional text to incude in emails',
44 44
                    ),
45 45
                ),
......
48 48
                    'sms_extra_info',
49 49
                    models.TextField(
50 50
                        blank=True,
51
                        help_text='Basic information such as event name, time and date are already included',
51
                        help_text='Basic information such as event name, time and date are already included.',
52 52
                        verbose_name='Additional text to incude in SMS',
53 53
                    ),
54 54
                ),
chrono/agendas/migrations/0063_booking_form_url.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
import chrono.agendas.models
5
from django.db import migrations, models
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('agendas', '0062_auto_20200915_1401'),
12
    ]
13

  
14
    operations = [
15
        migrations.AddField(
16
            model_name='agenda',
17
            name='booking_form_url',
18
            field=models.CharField(
19
                blank=True,
20
                max_length=200,
21
                validators=[chrono.agendas.models.django_template_validator],
22
                verbose_name='Booking form URL',
23
            ),
24
        ),
25
    ]
chrono/agendas/models.py
35 35
from django.core.validators import MaxValueValidator
36 36
from django.db import models, transaction
37 37
from django.db.models import Count, Q, Case, When
38
from django.template import engines, Context, Template, TemplateSyntaxError, VariableDoesNotExist
38 39
from django.urls import reverse
39 40
from django.utils import functional
40 41
from django.utils.dates import WEEKDAYS
......
115 116
        raise ValidationError(_('This value cannot be a number.'))
116 117

  
117 118

  
119
def django_template_validator(value):
120
    try:
121
        engines['django'].from_string(value)
122
    except TemplateSyntaxError as e:
123
        raise ValidationError(_('syntax error: %s') % e)
124

  
125

  
118 126
class ICSError(Exception):
119 127
    pass
120 128

  
......
171 179
        'Category', verbose_name=_('Category'), blank=True, null=True, on_delete=models.SET_NULL
172 180
    )
173 181
    default_view = models.CharField(_('Default view'), max_length=20, choices=AGENDA_VIEWS, default='month')
182
    booking_form_url = models.CharField(
183
        _('Booking form URL'), max_length=200, blank=True, validators=[django_template_validator]
184
    )
174 185

  
175 186
    class Meta:
176 187
        ordering = ['label']
......
289 300
        if hasattr(self, 'reminder_settings'):
290 301
            agenda['reminder_settings'] = self.reminder_settings.export_json()
291 302
        if self.kind == 'events':
303
            agenda['default_view'] = self.default_view
304
            agenda['booking_form_url'] = self.booking_form_url
292 305
            agenda['events'] = [x.export_json() for x in self.event_set.all()]
293 306
            if hasattr(self, 'notifications_settings'):
294 307
                agenda['notifications_settings'] = self.notifications_settings.export_json()
......
487 500
            )
488 501
        return entries
489 502

  
503
    def get_booking_form_url(self):
504
        if not self.booking_form_url:
505
            return
506
        template_vars = Context(settings.TEMPLATE_VARS)
507
        try:
508
            return Template(self.booking_form_url).render(template_vars)
509
        except (VariableDoesNotExist, TemplateSyntaxError):
510
            return
511

  
490 512

  
491 513
class VirtualMember(models.Model):
492 514
    '''Trough model to link virtual agendas to their real agendas.
chrono/manager/forms.py
75 75
            'minimal_booking_delay',
76 76
            'maximal_booking_delay',
77 77
            'default_view',
78
            'booking_form_url',
78 79
        ]
79 80

  
80 81
    def __init__(self, *args, **kwargs):
......
84 85
            self.fields['maximal_booking_delay'].required = True
85 86
        if kwargs['instance'].kind != 'events':
86 87
            del self.fields['default_view']
88
            del self.fields['booking_form_url']
87 89

  
88 90

  
89 91
class ResourceAddForm(forms.ModelForm):
chrono/manager/templates/chrono/manager_agenda_event_fragment.html
44 44
        </a>
45 45
    {% if settings_view %}
46 46
    <a rel="popup" class="delete" href="{% url 'chrono-manager-event-delete' pk=agenda.pk event_pk=event.pk %}?next=settings">{% trans "remove" %}</a>
47
    {% elif not event.cancellation_status %}
48
    <a rel="popup" class="link-action-text cancel" href="{% url 'chrono-manager-event-cancel' pk=agenda.pk event_pk=event.pk %}?next={{ request.path }}">{% trans "Cancel" %}</a>
47
    {% elif agenda.booking_form_url %}
48
    <a class="link-action-text" href="{{ agenda.get_booking_form_url }}?agenda={{ agenda.slug }}&event={{ event.slug }}">{% trans "Booking form" %}</a>
49 49
    {% endif %}
50 50
    <span class="occupation-bar"></span>
51 51
</li>
chrono/manager/templates/chrono/manager_event_detail.html
33 33
<a rel="popup" class="link-action-text cancel" href="{% url 'chrono-manager-event-cancel' pk=agenda.pk event_pk=event.pk %}?next={{ request.path }}">{% trans "Cancel" %}</a>
34 34
<a href="{% url 'chrono-manager-event-edit' pk=agenda.id event_pk=object.id %}">{% trans "Options" %}</a>
35 35
{% endif %}
36
{% if object.agenda.booking_form_url %}
37
<a href="{{ object.agenda.get_booking_form_url }}?agenda={{ object.agenda.slug }}&event={{ event.slug }}">{% trans "Booking form" %}</a>
38
{% endif %}
36 39
</span>
37 40
{% endblock %}
38 41

  
tests/test_import_export.py
134 134
    shutil.rmtree(tempdir)
135 135

  
136 136

  
137
def test_import_export_events_agenda_options(app):
138
    agenda = Agenda.objects.create(
139
        label='Foo Bar',
140
        kind='events',
141
        default_view='open_events',
142
        booking_form_url='{{ eservices_url }}backoffice/submission/inscription-aux-activites/',
143
    )
144

  
145
    output = get_output_of_command('export_site')
146
    assert len(json.loads(output)['agendas']) == 1
147
    import_site(data={}, clean=True)
148

  
149
    with tempfile.NamedTemporaryFile() as f:
150
        f.write(force_bytes(output))
151
        f.flush()
152
        call_command('import_site', f.name)
153

  
154
    assert Agenda.objects.count() == 1
155
    agenda = Agenda.objects.first()
156
    assert agenda.default_view == 'open_events'
157
    assert agenda.booking_form_url == '{{ eservices_url }}backoffice/submission/inscription-aux-activites/'
158

  
159

  
137 160
def test_import_export_event_details(app):
138 161
    agenda = Agenda.objects.create(label='Foo Bar', kind='events')
139 162
    Event.objects.create(
tests/test_manager.py
17 17
from django.utils.encoding import force_text
18 18
from django.utils.timezone import make_aware, now, localtime
19 19

  
20
import datetime
21 20
import freezegun
22 21
import pytest
23 22
import requests
......
877 876
    assert resp.form['label'].value == 'Foo bar'
878 877
    resp.form['label'] = 'Foo baz'
879 878
    assert 'default_view' in resp.context['form'].fields
879
    assert 'booking_form_url' in resp.context['form'].fields
880 880
    resp = resp.form.submit()
881 881
    assert resp.location.endswith('/manage/agendas/%s/settings' % agenda_events.pk)
882 882
    resp = resp.follow()
......
886 886

  
887 887
    resp = app.get('/manage/agendas/%s/edit' % agenda_meetings.pk)
888 888
    assert 'default_view' not in resp.context['form'].fields
889
    assert 'booking_form_url' not in resp.context['form'].fields
889 890
    resp = app.get('/manage/agendas/%s/edit' % agenda_virtual.pk)
890 891
    assert 'default_view' not in resp.context['form'].fields
892
    assert 'booking_form_url' not in resp.context['form'].fields
891 893

  
892 894

  
893 895
def test_options_agenda_cant_unset_delays(app, admin_user):
......
3805 3807
    event = Event.objects.create(
3806 3808
        label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
3807 3809
    )
3808
    day = event.start_datetime
3809 3810

  
3810 3811
    login(app)
3811
    resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
3812
    assert '0/10 bookings' in resp.text
3812
    resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
3813
    assert 'Bookings (0/10)' in resp.text
3813 3814

  
3814 3815
    resp = resp.click('Cancel', href='/cancel')
3815
    assert not 'related bookings' in resp.text
3816
    assert 'related bookings' not in resp.text
3816 3817

  
3817
    booking = Booking.objects.create(event=event)
3818
    booking2 = Booking.objects.create(event=event)
3818
    Booking.objects.create(event=event)
3819
    Booking.objects.create(event=event)
3819 3820

  
3820
    resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
3821
    assert '2/10 bookings' in resp.text
3821
    resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
3822
    assert 'Bookings (2/10)' in resp.text
3822 3823

  
3823
    resp = resp.click('Cancel', href='/cancel')
3824
    resp = resp.click('Cancel', href='manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
3824 3825
    assert '2 related bookings will also be cancelled.' in resp.text
3825 3826

  
3826 3827
    resp = resp.form.submit().follow()
3827 3828
    assert 'Cancelled' in resp.text
3828
    assert '0/10 bookings' in resp.text
3829
    assert 'Bookings (0/10)' in resp.text
3829 3830
    assert Booking.objects.filter(event=event, cancellation_datetime__isnull=False).count() == 2
3830 3831

  
3831 3832

  
......
3847 3848
    resp = resp.click('Cancellation error reports')
3848 3849
    assert 'No error report' in resp.text
3849 3850

  
3850
    resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
3851
    resp = resp.click('Cancel', href='/cancel')
3851
    resp = app.get('/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
3852 3852
    resp = resp.form.submit().follow()
3853 3853
    assert 'Cancellation in progress' in resp.text
3854 3854

  
......
3891 3891
    event = Event.objects.create(
3892 3892
        label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
3893 3893
    )
3894
    booking = Booking.objects.create(event=event)
3895
    booking2 = Booking.objects.create(event=event, backoffice_url='http://example.org/backoffice/xx/')
3896
    day = event.start_datetime
3894
    Booking.objects.create(event=event)
3895
    Booking.objects.create(event=event, backoffice_url='http://example.org/backoffice/xx/')
3897 3896

  
3898 3897
    login(app)
3899
    resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
3900
    resp = resp.click('Cancel', href='/cancel')
3898
    resp = app.get('/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
3901 3899
    assert 'event has bookings with no callback url configured' in resp.text
3902 3900
    assert 'Proceed with cancellation' not in resp.text
3903 3901

  
3904 3902

  
3903
def test_event_booking_form_url(settings, app, admin_user):
3904
    settings.TEMPLATE_VARS = {'eservices_url': 'http://demarches/'}
3905
    agenda = Agenda.objects.create(label='Events', kind='events')
3906
    event = Event.objects.create(
3907
        label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
3908
    )
3909
    day = event.start_datetime
3910

  
3911
    login(app)
3912

  
3913
    assert agenda.get_booking_form_url() is None
3914
    resp = app.get('/manage/agendas/%d/%d/%d/' % (agenda.pk, day.year, day.month))
3915
    assert 'Booking form' not in resp.text
3916
    resp = app.get('/manage/agendas/%d/events/open/' % agenda.pk)
3917
    assert 'Booking form' not in resp.text
3918
    resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
3919
    assert 'Booking form' not in resp.text
3920

  
3921
    agenda.booking_form_url = '{{ eservices_url }}backoffice/submission/inscription-aux-activites/'
3922
    agenda.save()
3923
    assert (
3924
        agenda.get_booking_form_url() == 'http://demarches/backoffice/submission/inscription-aux-activites/'
3925
    )
3926
    resp = app.get('/manage/agendas/%d/%d/%d/' % (agenda.pk, day.year, day.month))
3927
    assert (
3928
        '<a class="link-action-text" href="http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s">Booking form</a>'
3929
        % (agenda.slug, event.slug)
3930
        in resp.text
3931
    )
3932
    resp = app.get('/manage/agendas/%d/events/open/' % agenda.pk)
3933
    assert (
3934
        '<a class="link-action-text" href="http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s">Booking form</a>'
3935
        % (agenda.slug, event.slug)
3936
        in resp.text
3937
    )
3938
    resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
3939
    assert (
3940
        '<a href="http://demarches/backoffice/submission/inscription-aux-activites/?agenda=%s&event=%s">Booking form</a>'
3941
        % (agenda.slug, event.slug)
3942
        in resp.text
3943
    )
3944

  
3945

  
3905 3946
def test_agenda_notifications(app, admin_user, managers_group):
3906 3947
    agenda = Agenda.objects.create(label='Events', kind='events')
3907 3948

  
3908
-