manager: show when an event is checked (#53707)
This commit is contained in:
parent
dd424bc388
commit
3ba5dac136
|
@ -0,0 +1,24 @@
|
|||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agendas', '0094_change_recurrence_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='agenda',
|
||||
name='mark_event_checked_auto',
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
verbose_name='Automatically mark event as checked when all bookings have been checked',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='checked',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
|
@ -205,6 +205,9 @@ class Agenda(models.Model):
|
|||
_('Booking form URL'), max_length=200, blank=True, validators=[django_template_validator]
|
||||
)
|
||||
desk_simple_management = models.BooleanField(default=False)
|
||||
mark_event_checked_auto = models.BooleanField(
|
||||
_('Automatically mark event as checked when all bookings have been checked'), default=False
|
||||
)
|
||||
booking_check_filters = models.CharField(
|
||||
_('Filters'),
|
||||
max_length=250,
|
||||
|
@ -222,7 +225,11 @@ class Agenda(models.Model):
|
|||
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. Available variables: event.label (label), event.start_datetime (start date/time), event.places (places), event.remaining_places (remaining places), event.duration (duration), event.pricing (pricing).'
|
||||
'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. '
|
||||
'Available variables: event.label (label), event.start_datetime (start date/time), event.places (places), '
|
||||
'event.remaining_places (remaining places), event.duration (duration), event.pricing (pricing).'
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1263,6 +1270,7 @@ class Event(models.Model):
|
|||
full = models.BooleanField(default=False)
|
||||
cancelled = models.BooleanField(default=False)
|
||||
cancellation_scheduled = models.BooleanField(default=False)
|
||||
checked = models.BooleanField(default=False)
|
||||
meeting_type = models.ForeignKey(MeetingType, null=True, on_delete=models.CASCADE)
|
||||
desk = models.ForeignKey('Desk', null=True, on_delete=models.CASCADE)
|
||||
resources = models.ManyToManyField('Resource')
|
||||
|
@ -1312,6 +1320,21 @@ class Event(models.Model):
|
|||
)
|
||||
self.almost_full = bool(self.booked_places >= 0.9 * self.places)
|
||||
|
||||
def set_is_checked(self):
|
||||
if not self.agenda.mark_event_checked_auto:
|
||||
return
|
||||
if self.checked:
|
||||
return
|
||||
booking_qs = self.booking_set.filter(
|
||||
cancellation_datetime__isnull=True,
|
||||
in_waiting_list=False,
|
||||
user_was_present__isnull=True,
|
||||
)
|
||||
if booking_qs.exists():
|
||||
return
|
||||
self.checked = True
|
||||
self.save(update_fields=['checked'])
|
||||
|
||||
def in_bookable_period(self):
|
||||
if self.publication_date and localtime(now()).date() < self.publication_date:
|
||||
return False
|
||||
|
@ -1772,6 +1795,18 @@ class Booking(models.Model):
|
|||
self.secondary_booking_set.update(in_waiting_list=True)
|
||||
self.save()
|
||||
|
||||
def mark_user_absence(self, reason=None):
|
||||
self.user_absence_reason = reason
|
||||
self.user_was_present = False
|
||||
self.save()
|
||||
self.event.set_is_checked()
|
||||
|
||||
def mark_user_presence(self):
|
||||
self.user_absence_reason = ''
|
||||
self.user_was_present = True
|
||||
self.save()
|
||||
self.event.set_is_checked()
|
||||
|
||||
def get_user_block(self):
|
||||
template_vars = Context(settings.TEMPLATE_VARS)
|
||||
template_vars.update(
|
||||
|
@ -2640,7 +2675,8 @@ class AgendaReminderSettings(models.Model):
|
|||
choices=CHOICES,
|
||||
verbose_name=_('Send reminder'),
|
||||
help_text=_(
|
||||
'In order to prevent users from getting a reminder shortly after booking, a reminder is sent less only if at least 12 hours have elapsed since booking time.'
|
||||
'In order to prevent users from getting a reminder shortly after booking, '
|
||||
'a reminder is sent less only if at least 12 hours have elapsed since booking time.'
|
||||
),
|
||||
)
|
||||
send_email = models.BooleanField(default=False, verbose_name=_('Notify by email'))
|
||||
|
|
|
@ -1609,6 +1609,7 @@ class BookingAPI(APIView):
|
|||
serializer.save()
|
||||
if 'user_was_present' in request.data:
|
||||
self.booking.secondary_booking_set.update(user_was_present=self.booking.user_was_present)
|
||||
self.booking.event.set_is_checked()
|
||||
if 'user_absence_reason' in request.data:
|
||||
self.booking.secondary_booking_set.update(user_absence_reason=self.booking.user_absence_reason)
|
||||
if 'extra_data' in request.data:
|
||||
|
|
|
@ -832,7 +832,12 @@ class EventCancelForm(forms.ModelForm):
|
|||
class AgendaBookingCheckSettingsForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Agenda
|
||||
fields = ['absence_reasons_group', 'booking_check_filters', 'booking_user_block_template']
|
||||
fields = [
|
||||
'absence_reasons_group',
|
||||
'booking_check_filters',
|
||||
'booking_user_block_template',
|
||||
'mark_event_checked_auto',
|
||||
]
|
||||
widgets = {'booking_user_block_template': forms.Textarea(attrs={'rows': 3})}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
@ -11,8 +11,13 @@
|
|||
><a href="{% if view_mode == 'settings_view' %}{% url 'chrono-manager-event-edit' pk=agenda.pk event_pk=event.pk %}?next=settings{% elif event.pk %}{% url 'chrono-manager-event-view' pk=agenda.pk event_pk=event.pk %}{% else %}{% url 'chrono-manager-event-create-recurrence' pk=agenda.pk event_identifier=event.slug %}{% endif %}">
|
||||
{% if event.cancellation_status %}
|
||||
<span class="tag">{{ event.cancellation_status }}</span>
|
||||
{% elif event.main_list_full %}
|
||||
<span class="full tag">{% trans "Full" %}</span>
|
||||
{% else %}
|
||||
{% if event.main_list_full %}
|
||||
<span class="full tag">{% trans "Full" %}</span>
|
||||
{% endif %}
|
||||
{% if event.checked %}
|
||||
<span class="checked tag">{% trans "Checked" %}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<span class="event-info">
|
||||
{% if view_mode == 'settings_view' %}
|
||||
|
|
|
@ -27,6 +27,16 @@
|
|||
</form>
|
||||
<table class="main check-bookings">
|
||||
<tbody>
|
||||
{% if booked and not event.checked %}
|
||||
<tr class="booking all-bookings">
|
||||
<td class="booking-actions">
|
||||
<form method="post" action="{% url 'chrono-manager-event-checked' pk=agenda.pk event_pk=object.pk %}">
|
||||
{% csrf_token %}
|
||||
<button class="submit-button">{% trans "Mark the event as checked" %}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if booked_without_status %}
|
||||
<tr class="booking all-bookings">
|
||||
<td colspan="2"><b>{% trans "Mark all bookings without status:" %}</b></td>
|
||||
|
|
|
@ -61,8 +61,10 @@
|
|||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<p>{% trans "User block template" %}:</p>
|
||||
<p>{% trans "User block template:" %}</p>
|
||||
<pre>{{ agenda.get_booking_user_block_template }}</pre>
|
||||
|
||||
<p>{% trans "Automatically mark events when they are checked:" %} {{ agenda.mark_event_checked_auto|yesno }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -221,6 +221,11 @@ urlpatterns = [
|
|||
views.event_absence,
|
||||
name='chrono-manager-event-absence',
|
||||
),
|
||||
url(
|
||||
r'^agendas/(?P<pk>\d+)/events/(?P<event_pk>\d+)/checked$',
|
||||
views.event_checked,
|
||||
name='chrono-manager-event-checked',
|
||||
),
|
||||
url(
|
||||
r'^agendas/(?P<pk>\d+)/event_cancellation_report/(?P<report_pk>\d+)/$',
|
||||
views.event_cancellation_report,
|
||||
|
|
|
@ -2024,6 +2024,7 @@ class EventPresenceView(EventCheckMixin, ViewableAgendaMixin, View):
|
|||
def post(self, request, *args, **kwargs):
|
||||
bookings = self.get_bookings()
|
||||
bookings.update(user_absence_reason='', user_was_present=True)
|
||||
self.event.set_is_checked()
|
||||
return self.response(request)
|
||||
|
||||
|
||||
|
@ -2045,12 +2046,24 @@ class EventAbsenceView(EventCheckMixin, ViewableAgendaMixin, FormView):
|
|||
qs_kwargs['user_absence_reason'] = form.cleaned_data['reason']
|
||||
bookings = self.get_bookings()
|
||||
bookings.update(user_was_present=False, **qs_kwargs)
|
||||
self.event.set_is_checked()
|
||||
return self.response(request)
|
||||
|
||||
|
||||
event_absence = EventAbsenceView.as_view()
|
||||
|
||||
|
||||
class EventCheckedView(EventCheckMixin, ViewableAgendaMixin, View):
|
||||
def post(self, request, *args, **kwargs):
|
||||
if not self.event.checked:
|
||||
self.event.checked = True
|
||||
self.event.save(update_fields=['checked'])
|
||||
return self.response(request)
|
||||
|
||||
|
||||
event_checked = EventCheckedView.as_view()
|
||||
|
||||
|
||||
class AgendaAddResourceView(ManagedAgendaMixin, FormView):
|
||||
template_name = 'chrono/manager_agenda_resource_form.html'
|
||||
form_class = AgendaResourceForm
|
||||
|
@ -2750,9 +2763,7 @@ class BookingCheckMixin(object):
|
|||
class BookingPresenceView(ViewableAgendaMixin, BookingCheckMixin, View):
|
||||
def post(self, request, *args, **kwargs):
|
||||
booking = self.get_booking(**kwargs)
|
||||
booking.user_absence_reason = ''
|
||||
booking.user_was_present = True
|
||||
booking.save()
|
||||
booking.mark_user_presence()
|
||||
return self.response(request, booking)
|
||||
|
||||
|
||||
|
@ -2770,10 +2781,10 @@ class BookingAbsenceView(ViewableAgendaMixin, BookingCheckMixin, FormView):
|
|||
def post(self, request, *args, **kwargs):
|
||||
booking = self.get_booking(**kwargs)
|
||||
form = self.get_form()
|
||||
reason = None
|
||||
if form.is_valid():
|
||||
booking.user_absence_reason = form.cleaned_data['reason']
|
||||
booking.user_was_present = False
|
||||
booking.save()
|
||||
reason = form.cleaned_data['reason']
|
||||
booking.mark_user_absence(reason=reason)
|
||||
return self.response(request, booking)
|
||||
|
||||
|
||||
|
|
|
@ -410,6 +410,7 @@ def test_booking_patch_api_present(app, user, flag):
|
|||
agenda = Agenda.objects.create(kind='events')
|
||||
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
|
||||
booking = Booking.objects.create(event=event)
|
||||
assert agenda.mark_event_checked_auto is False
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
|
||||
|
@ -417,6 +418,16 @@ def test_booking_patch_api_present(app, user, flag):
|
|||
app.patch_json('/api/booking/%s/' % booking.pk, params={'user_was_present': flag})
|
||||
booking.refresh_from_db()
|
||||
assert booking.user_was_present == flag
|
||||
event.refresh_from_db()
|
||||
assert event.checked is False
|
||||
|
||||
agenda.mark_event_checked_auto = True
|
||||
agenda.save()
|
||||
app.patch_json('/api/booking/%s/' % booking.pk, params={'user_was_present': flag})
|
||||
booking.refresh_from_db()
|
||||
assert booking.user_was_present == flag
|
||||
event.refresh_from_db()
|
||||
assert event.checked == (flag is not None)
|
||||
|
||||
# reset
|
||||
app.patch_json('/api/booking/%s/' % booking.pk, params={'user_was_present': None})
|
||||
|
|
|
@ -562,6 +562,14 @@ def test_options_agenda_booking_check_options(app, admin_user):
|
|||
assert agenda.booking_check_filters == 'foo,bar,baz'
|
||||
assert agenda.get_booking_check_filters() == ['foo', 'bar', 'baz']
|
||||
|
||||
# check auto checked
|
||||
assert agenda.mark_event_checked_auto is False
|
||||
resp = app.get(url)
|
||||
resp.form['mark_event_checked_auto'] = True
|
||||
resp = resp.form.submit()
|
||||
agenda.refresh_from_db()
|
||||
assert agenda.mark_event_checked_auto is True
|
||||
|
||||
# check kind
|
||||
agenda.kind = 'meetings'
|
||||
agenda.save()
|
||||
|
|
|
@ -1186,6 +1186,40 @@ def test_event_check(app, admin_user):
|
|||
event.cancellation_datetime = None
|
||||
|
||||
|
||||
def test_event_checked(app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events', booking_check_filters='foo,bar')
|
||||
event = Event.objects.create(
|
||||
label='xyz',
|
||||
start_datetime=now() - datetime.timedelta(days=1),
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
)
|
||||
login(app)
|
||||
|
||||
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
|
||||
assert 'Mark the event as checked' not in resp
|
||||
|
||||
Booking.objects.create(event=event, user_first_name='User')
|
||||
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
|
||||
assert 'Mark the event as checked' in resp
|
||||
assert event.checked is False
|
||||
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
|
||||
assert 'checked tag' not in resp
|
||||
|
||||
token = resp.context['csrf_token']
|
||||
resp = app.post(
|
||||
'/manage/agendas/%s/events/%s/checked' % (agenda.pk, event.pk),
|
||||
params={'csrfmiddlewaretoken': token},
|
||||
)
|
||||
assert resp.location.endswith('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
|
||||
event.refresh_from_db()
|
||||
assert event.checked is True
|
||||
resp = resp.follow()
|
||||
assert 'Mark the event as checked' not in resp
|
||||
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
|
||||
assert 'checked tag' in resp
|
||||
|
||||
|
||||
def test_event_check_filters(app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events', booking_check_filters='foo,bar')
|
||||
event = Event.objects.create(
|
||||
|
@ -1282,6 +1316,7 @@ def test_event_check_booking(app, admin_user):
|
|||
agenda=agenda,
|
||||
)
|
||||
booking = Booking.objects.create(event=event, user_first_name='User', user_last_name='42')
|
||||
assert agenda.mark_event_checked_auto is False
|
||||
|
||||
login(app)
|
||||
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
|
||||
|
@ -1302,6 +1337,11 @@ def test_event_check_booking(app, admin_user):
|
|||
booking.refresh_from_db()
|
||||
assert booking.user_was_present is True
|
||||
assert booking.user_absence_reason == ''
|
||||
event.refresh_from_db()
|
||||
assert event.checked is False
|
||||
|
||||
agenda.mark_event_checked_auto = True
|
||||
agenda.save()
|
||||
|
||||
# set as absent without reason
|
||||
resp = app.post(
|
||||
|
@ -1314,6 +1354,8 @@ def test_event_check_booking(app, admin_user):
|
|||
booking.refresh_from_db()
|
||||
assert booking.user_was_present is False
|
||||
assert booking.user_absence_reason == ''
|
||||
event.refresh_from_db()
|
||||
assert event.checked is True
|
||||
|
||||
agenda.absence_reasons_group = group
|
||||
agenda.save()
|
||||
|
@ -1396,6 +1438,7 @@ def test_event_check_all_bookings(app, admin_user):
|
|||
agenda=agenda,
|
||||
)
|
||||
booking1 = Booking.objects.create(event=event, user_first_name='User', user_last_name='42')
|
||||
assert agenda.mark_event_checked_auto is False
|
||||
|
||||
login(app)
|
||||
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
|
||||
|
@ -1410,6 +1453,11 @@ def test_event_check_all_bookings(app, admin_user):
|
|||
booking1.refresh_from_db()
|
||||
assert booking1.user_was_present is False
|
||||
assert booking1.user_absence_reason == ''
|
||||
event.refresh_from_db()
|
||||
assert event.checked is False
|
||||
|
||||
agenda.mark_event_checked_auto = True
|
||||
agenda.save()
|
||||
|
||||
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
|
||||
assert 'Mark all bookings without status' not in resp
|
||||
|
@ -1431,6 +1479,8 @@ def test_event_check_all_bookings(app, admin_user):
|
|||
booking2.refresh_from_db()
|
||||
assert booking2.user_was_present is True
|
||||
assert booking2.user_absence_reason == ''
|
||||
event.refresh_from_db()
|
||||
assert event.checked is True
|
||||
|
||||
booking3 = Booking.objects.create(event=event, user_first_name='User', user_last_name='51')
|
||||
app.post(
|
||||
|
|
Loading…
Reference in New Issue