manager: display extra user block on demand on check page (#75413)
gitea/chrono/pipeline/head This commit looks good
Details
gitea/chrono/pipeline/head This commit looks good
Details
This commit is contained in:
parent
1a5cfa7436
commit
22721d9cfa
|
@ -0,0 +1,22 @@
|
|||
from django.db import migrations, models
|
||||
|
||||
import chrono.agendas.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('agendas', '0148_agenda_minimal_booking_time'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='agenda',
|
||||
name='booking_extra_user_block_template',
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
help_text='Displayed on check page',
|
||||
validators=[chrono.agendas.models.django_template_validator],
|
||||
verbose_name='Extra user block template',
|
||||
),
|
||||
),
|
||||
]
|
|
@ -51,7 +51,14 @@ from django.db.models import (
|
|||
Value,
|
||||
)
|
||||
from django.db.models.functions import Cast, Coalesce, Concat, ExtractWeek, ExtractWeekDay, JSONObject
|
||||
from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist, engines
|
||||
from django.template import (
|
||||
Context,
|
||||
RequestContext,
|
||||
Template,
|
||||
TemplateSyntaxError,
|
||||
VariableDoesNotExist,
|
||||
engines,
|
||||
)
|
||||
from django.urls import reverse
|
||||
from django.utils import functional
|
||||
from django.utils.dates import WEEKDAYS
|
||||
|
@ -240,6 +247,12 @@ class Agenda(models.Model):
|
|||
blank=True,
|
||||
validators=[django_template_validator],
|
||||
)
|
||||
booking_extra_user_block_template = models.TextField(
|
||||
_('Extra user block template'),
|
||||
blank=True,
|
||||
validators=[django_template_validator],
|
||||
help_text=_('Displayed on check page'),
|
||||
)
|
||||
event_display_template = models.CharField(
|
||||
_('Event display template'),
|
||||
max_length=256,
|
||||
|
@ -2240,6 +2253,18 @@ class Booking(models.Model):
|
|||
except (VariableDoesNotExist, TemplateSyntaxError):
|
||||
return
|
||||
|
||||
def get_extra_user_block(self, request):
|
||||
context = RequestContext(request)
|
||||
context.update(
|
||||
{
|
||||
'booking': self,
|
||||
}
|
||||
)
|
||||
try:
|
||||
return Template(self.event.agenda.booking_extra_user_block_template).render(context)
|
||||
except (VariableDoesNotExist, TemplateSyntaxError):
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def anonymize_bookings(cls, bookings_queryset):
|
||||
bookings_queryset.update(
|
||||
|
@ -3303,6 +3328,18 @@ class Subscription(models.Model):
|
|||
except (VariableDoesNotExist, TemplateSyntaxError):
|
||||
return
|
||||
|
||||
def get_extra_user_block(self, request):
|
||||
context = RequestContext(request)
|
||||
context.update(
|
||||
{
|
||||
'booking': self,
|
||||
}
|
||||
)
|
||||
try:
|
||||
return Template(self.agenda.booking_extra_user_block_template).render(context)
|
||||
except (VariableDoesNotExist, TemplateSyntaxError):
|
||||
return
|
||||
|
||||
|
||||
class Person(models.Model):
|
||||
user_external_id = models.CharField(max_length=250, unique=True)
|
||||
|
|
|
@ -1417,7 +1417,9 @@ class AgendaBookingCheckSettingsForm(forms.ModelForm):
|
|||
'mark_event_checked_auto',
|
||||
'disable_check_update',
|
||||
'enable_check_for_future_events',
|
||||
'booking_extra_user_block_template',
|
||||
]
|
||||
widgets = {'booking_extra_user_block_template': forms.Textarea(attrs={'rows': 3})}
|
||||
|
||||
|
||||
class AgendaNotificationsForm(forms.ModelForm):
|
||||
|
|
|
@ -574,3 +574,24 @@ div.agenda-settings .pk-tabs--container {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
span.togglable {
|
||||
padding: 1em 2ex;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.untoggled span.togglable:after {
|
||||
font-family: FontAwesome;
|
||||
content: "\f107"; /* angle-down */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggled span.togglable:after {
|
||||
font-family: FontAwesome;
|
||||
content: "\f106"; /* angle-up */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.extra-user-block {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
{% for result in results %}
|
||||
<tr class="booking">
|
||||
<tr class="booking {% if agenda.booking_extra_user_block_template %}untoggled{% endif %}">
|
||||
{% include "chrono/manager_event_check_booking_fragment.html" with booking=result %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -91,8 +91,11 @@
|
|||
<table class="main check-bookings">
|
||||
<tbody>
|
||||
{% for booking in waiting %}
|
||||
<tr>
|
||||
<td class="booking-username waiting">{{ booking.get_user_block }}{% if booking.places_count > 1 %} ({{ booking.places_count }} {% trans "places" %}){% endif %}</td>
|
||||
<tr class="booking {% if agenda.booking_extra_user_block_template %}untoggled{% endif %}">
|
||||
<td class="booking-username waiting" {% if agenda.booking_extra_user_block_template %}data-extra-user-block-url="{% url 'chrono-manager-booking-extra-user-block' pk=agenda.pk booking_pk=booking.pk %}{% endif %}">
|
||||
{% if agenda.booking_extra_user_block_template %}<span class="togglable"></span>{% endif %}
|
||||
{{ booking.get_user_block }}{% if booking.places_count > 1 %} ({{ booking.places_count }} {% trans "places" %}){% endif %}
|
||||
</td>
|
||||
<td>{% if booking.cancellation_datetime %}({% trans "Cancelled" %}){% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -101,4 +104,31 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$(document).on('click', '.togglable', function(event) {
|
||||
event.preventDefault();
|
||||
var $toggle = $(this);
|
||||
var $tr = $toggle.parents('tr');
|
||||
var $td = $toggle.parents('td.booking-username');
|
||||
if ($('.extra-user-block', $td).length == 0) {
|
||||
$.ajax({
|
||||
url: $td.data('extra-user-block-url')
|
||||
}).done(function(html) {
|
||||
$tr.toggleClass('toggled').toggleClass('untoggled');
|
||||
$td.append('<div class="extra-user-block">' + html + '</div>');
|
||||
});
|
||||
} else {
|
||||
if ($tr.hasClass('toggled')) {
|
||||
$tr.toggleClass('toggled').toggleClass('untoggled');
|
||||
$('.extra-user-block', $td).hide();
|
||||
} else {
|
||||
$tr.toggleClass('toggled').toggleClass('untoggled');
|
||||
$('.extra-user-block', $td).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
{% load i18n %}
|
||||
|
||||
<td class="booking-username main-list">{{ booking.get_user_block }}{% if booking.places_count > 1 %} ({{ booking.places_count }} {% trans "places" %}){% endif %}</td>
|
||||
<td class="booking-username main-list" {% if agenda.booking_extra_user_block_template %}data-extra-user-block-url="{% if booking.kind == "subscription" %}{% url 'chrono-manager-subscription-extra-user-block' pk=agenda.pk subscription_pk=booking.pk %}{% else %}{% url 'chrono-manager-booking-extra-user-block' pk=agenda.pk booking_pk=booking.pk %}{% endif %}"{% endif %}>
|
||||
{% if agenda.booking_extra_user_block_template %}<span class="togglable"></span>{% endif %}
|
||||
{{ booking.get_user_block }}{% if booking.places_count > 1 %} ({{ booking.places_count }} {% trans "places" %}){% endif %}
|
||||
</td>
|
||||
<td class="booking-status {% if booking.kind != "subscription" and booking.cancellation_datetime is None and booking.user_was_present is None %}without-status{% endif %}" data-{{ booking.kind }}-id="{{ booking.id }}">
|
||||
{% if booking.kind == "subscription" %}
|
||||
({% trans "Not booked" %})
|
||||
|
|
|
@ -114,6 +114,10 @@
|
|||
<li>{% trans "Automatically mark event as checked when all bookings have been checked:" %} {{ agenda.mark_event_checked_auto|yesno }}</li>
|
||||
<li>{% trans "Prevent the check of bookings when event was marked as checked:" %} {{ agenda.disable_check_update|yesno }}</li>
|
||||
<li>{% trans "Enable the check of bookings when event has not passed:" %} {{ agenda.enable_check_for_future_events|yesno }}</li>
|
||||
<li>
|
||||
{% trans "Extra user block template:" %}
|
||||
<pre>{{ agenda.booking_extra_user_block_template }}</pre>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="panel--buttons">
|
||||
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-booking-check-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
|
||||
|
|
|
@ -424,6 +424,16 @@ urlpatterns = [
|
|||
views.subscription_absence,
|
||||
name='chrono-manager-subscription-absence',
|
||||
),
|
||||
path(
|
||||
'agendas/<int:pk>/bookings/<int:booking_pk>/extra-user-block',
|
||||
views.booking_extra_user_block,
|
||||
name='chrono-manager-booking-extra-user-block',
|
||||
),
|
||||
path(
|
||||
'agendas/<int:pk>/subscriptions/<int:subscription_pk>/extra-user-block',
|
||||
views.subscription_extra_user_block,
|
||||
name='chrono-manager-subscription-extra-user-block',
|
||||
),
|
||||
re_path(
|
||||
r'^agendas/events.csv$',
|
||||
views.agenda_import_events_sample_csv,
|
||||
|
|
|
@ -1141,9 +1141,6 @@ class AgendaBookingCheckSettingsView(AgendaEditView):
|
|||
def set_agenda(self, **kwargs):
|
||||
self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events')
|
||||
|
||||
def get_initial(self):
|
||||
return {'booking_user_block_template': self.agenda.get_booking_user_block_template()}
|
||||
|
||||
|
||||
agenda_booking_check_settings = AgendaBookingCheckSettingsView.as_view()
|
||||
|
||||
|
@ -3642,6 +3639,34 @@ class SubscriptionAbsenceView(ViewableAgendaMixin, SubscriptionCheckMixin, Absen
|
|||
subscription_absence = SubscriptionAbsenceView.as_view()
|
||||
|
||||
|
||||
class BookingExtraUserBlock(ViewableAgendaMixin, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
booking = get_object_or_404(
|
||||
Booking,
|
||||
pk=kwargs['booking_pk'],
|
||||
event__agenda=self.agenda,
|
||||
event__cancelled=False,
|
||||
primary_booking__isnull=True,
|
||||
)
|
||||
return HttpResponse(booking.get_extra_user_block(request))
|
||||
|
||||
|
||||
booking_extra_user_block = BookingExtraUserBlock.as_view()
|
||||
|
||||
|
||||
class SubscriptionExtraUserBlock(ViewableAgendaMixin, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
subscription = get_object_or_404(
|
||||
Subscription,
|
||||
agenda=self.agenda,
|
||||
pk=kwargs['subscription_pk'],
|
||||
)
|
||||
return HttpResponse(subscription.get_extra_user_block(request))
|
||||
|
||||
|
||||
subscription_extra_user_block = SubscriptionExtraUserBlock.as_view()
|
||||
|
||||
|
||||
class EventCancelView(ViewableAgendaMixin, UpdateView):
|
||||
template_name = 'chrono/manager_confirm_event_cancellation.html'
|
||||
model = Event
|
||||
|
|
|
@ -697,6 +697,24 @@ def test_options_agenda_booking_check_options(app, admin_user):
|
|||
agenda.refresh_from_db()
|
||||
assert agenda.enable_check_for_future_events is True
|
||||
|
||||
# check extra user block
|
||||
assert agenda.booking_extra_user_block_template == ''
|
||||
resp = app.get(url)
|
||||
resp.form['booking_extra_user_block_template'] = '{{ booking.user_name }} Foo Bar'
|
||||
resp = resp.form.submit()
|
||||
agenda.refresh_from_db()
|
||||
assert agenda.booking_extra_user_block_template == '{{ booking.user_name }} Foo Bar'
|
||||
|
||||
invalid_templates = [
|
||||
'{{ syntax error }}',
|
||||
'{{ booking.user_name|invalidfilter }}',
|
||||
]
|
||||
for template in invalid_templates:
|
||||
resp = app.get(url)
|
||||
resp.form['booking_extra_user_block_template'] = template
|
||||
resp = resp.form.submit()
|
||||
assert 'syntax error' in resp.text
|
||||
|
||||
# check kind
|
||||
agenda.kind = 'meetings'
|
||||
agenda.save()
|
||||
|
|
|
@ -2792,6 +2792,77 @@ def test_event_check_subscription(check_types, app, admin_user):
|
|||
)
|
||||
|
||||
|
||||
@mock.patch('chrono.manager.forms.get_agenda_check_types')
|
||||
def test_event_check_extra_user_block(check_types, app, admin_user):
|
||||
check_types.return_value = []
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
event = Event.objects.create(
|
||||
label='xyz',
|
||||
start_datetime=now() - datetime.timedelta(days=1),
|
||||
places=10,
|
||||
waiting_list_places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
subscription = Subscription.objects.create(
|
||||
agenda=agenda,
|
||||
user_external_id='user:1',
|
||||
user_first_name='Subscription',
|
||||
user_last_name='41',
|
||||
date_start=now() - datetime.timedelta(days=1),
|
||||
date_end=now(),
|
||||
)
|
||||
booking = Booking.objects.create(
|
||||
event=event, user_first_name='User', user_last_name='42', user_external_id='user:2'
|
||||
)
|
||||
cancelled_booking = Booking.objects.create(
|
||||
event=event, user_first_name='User', user_last_name='43', user_external_id='user:3'
|
||||
)
|
||||
cancelled_booking.cancel()
|
||||
waiting_booking = Booking.objects.create(
|
||||
event=event,
|
||||
user_first_name='User',
|
||||
user_last_name='44',
|
||||
user_external_id='user:4',
|
||||
in_waiting_list=True,
|
||||
)
|
||||
|
||||
login(app)
|
||||
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
|
||||
assert '/manage/agendas/%s/subscriptions/%s/extra-user-block' % (agenda.pk, subscription.pk) not in resp
|
||||
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, booking.pk) not in resp
|
||||
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, cancelled_booking.pk) not in resp
|
||||
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, waiting_booking.pk) not in resp
|
||||
|
||||
agenda.booking_extra_user_block_template = '{{ booking.user_name }} Foo Bar'
|
||||
agenda.save()
|
||||
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
|
||||
assert '/manage/agendas/%s/subscriptions/%s/extra-user-block' % (agenda.pk, subscription.pk) in resp
|
||||
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, booking.pk) in resp
|
||||
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, cancelled_booking.pk) in resp
|
||||
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, waiting_booking.pk) in resp
|
||||
|
||||
Subscription.objects.create(
|
||||
agenda=agenda,
|
||||
user_external_id='user:2',
|
||||
user_first_name='Subscription',
|
||||
user_last_name='42',
|
||||
date_start=now() - datetime.timedelta(days=1),
|
||||
date_end=now(),
|
||||
)
|
||||
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
|
||||
# booking url, not subscription url
|
||||
assert '/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, booking.pk) in resp
|
||||
|
||||
resp = app.get('/manage/agendas/%s/subscriptions/%s/extra-user-block' % (agenda.pk, subscription.pk))
|
||||
assert resp.text == 'Subscription 41 Foo Bar'
|
||||
resp = app.get('/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, booking.pk))
|
||||
assert resp.text == 'User 42 Foo Bar'
|
||||
resp = app.get('/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, cancelled_booking.pk))
|
||||
assert resp.text == 'User 43 Foo Bar'
|
||||
resp = app.get('/manage/agendas/%s/bookings/%s/extra-user-block' % (agenda.pk, waiting_booking.pk))
|
||||
assert resp.text == 'User 44 Foo Bar'
|
||||
|
||||
|
||||
@mock.patch('chrono.manager.forms.get_agenda_check_types')
|
||||
def test_event_check_subscription_ajax(check_types, app, admin_user):
|
||||
check_types.return_value = [
|
||||
|
@ -3049,13 +3120,13 @@ def test_event_check_primary_booking(app, admin_user):
|
|||
assert 'Bookings (4/10)' in resp.text
|
||||
user_bookings = resp.pyquery.find('td.booking-username.main-list')
|
||||
assert len(user_bookings) == 2
|
||||
assert user_bookings[0].text == 'User 42 (3 places)'
|
||||
assert user_bookings[1].text == 'John Doe'
|
||||
assert user_bookings[0].text.replace('\n', '').strip() == 'User 42 (3 places)'
|
||||
assert user_bookings[1].text.replace('\n', '').strip() == 'John Doe'
|
||||
|
||||
assert 'Waiting List (2/5)' in resp.text
|
||||
user_bookings = resp.pyquery.find('td.booking-username.waiting')
|
||||
assert len(user_bookings) == 1
|
||||
assert user_bookings[0].text == 'Jane Doe (2 places)'
|
||||
assert user_bookings[0].text.replace('\n', '').strip() == 'Jane Doe (2 places)'
|
||||
|
||||
|
||||
def test_duplicate_event(app, admin_user):
|
||||
|
|
Loading…
Reference in New Issue