misc: notify bookings when event is marked as checked (#75896)
gitea/chrono/pipeline/head This commit looks good
Details
gitea/chrono/pipeline/head This commit looks good
Details
This commit is contained in:
parent
5265839e69
commit
ad2b04f89f
|
@ -20,6 +20,7 @@ import dataclasses
|
|||
import datetime
|
||||
import functools
|
||||
import itertools
|
||||
import logging
|
||||
import math
|
||||
import sys
|
||||
import uuid
|
||||
|
@ -1595,6 +1596,45 @@ class Event(models.Model):
|
|||
return
|
||||
self.checked = True
|
||||
self.save(update_fields=['checked'])
|
||||
self.async_notify_checked()
|
||||
|
||||
def async_notify_checked(self):
|
||||
if 'uwsgi' in sys.modules:
|
||||
from chrono.utils.spooler import event_notify_checked
|
||||
|
||||
tenant = getattr(connection, 'tenant', None)
|
||||
transaction.on_commit(
|
||||
lambda: event_notify_checked.spool(
|
||||
event_id=str(self.pk), domain=getattr(tenant, 'domain_url', None)
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
self.notify_checked()
|
||||
|
||||
def notify_checked(self):
|
||||
for booking in self.booking_set.filter(user_was_present__isnull=False):
|
||||
if booking.user_was_present is True and booking.presence_callback_url:
|
||||
url = booking.presence_callback_url
|
||||
elif booking.user_was_present is False and booking.absence_callback_url:
|
||||
url = booking.absence_callback_url
|
||||
else:
|
||||
continue
|
||||
payload = {
|
||||
'user_was_present': booking.user_was_present,
|
||||
'user_check_type_slug': booking.user_check_type_slug,
|
||||
'user_check_type_label': booking.user_check_type_label,
|
||||
}
|
||||
try:
|
||||
response = requests_wrapper.post(url, json=payload, remote_service='auto', timeout=15)
|
||||
if response and not response.ok:
|
||||
logging.error(
|
||||
'error (HTTP %s) notifying checked booking (%s)', response.status_code, booking.id
|
||||
)
|
||||
except requests.Timeout:
|
||||
logging.error('error (timeout) notifying checked booking (%s)', booking.id)
|
||||
except Exception as e: # noqa pylint: disable=broad-except
|
||||
logging.error('error (%s) notifying checked booking (%s)', e, booking.id)
|
||||
|
||||
def in_bookable_period(self, bypass_delays=False):
|
||||
if self.publication_datetime and now() < self.publication_datetime:
|
||||
|
|
|
@ -3066,6 +3066,7 @@ class EventCheck(APIView):
|
|||
if not event.checked:
|
||||
event.checked = True
|
||||
event.save(update_fields=['checked'])
|
||||
event.async_notify_checked()
|
||||
response = {
|
||||
'err': 0,
|
||||
}
|
||||
|
|
|
@ -2749,6 +2749,7 @@ class EventCheckedView(EventCheckMixin, ViewableAgendaMixin, View):
|
|||
if not self.event.checked:
|
||||
self.event.checked = True
|
||||
self.event.save(update_fields=['checked'])
|
||||
self.event.async_notify_checked()
|
||||
return self.response(request)
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
from django.db import connection
|
||||
from uwsgidecorators import spool # pylint: disable=import-error
|
||||
|
||||
from chrono.agendas.models import ICSError, TimePeriodExceptionSource
|
||||
from chrono.agendas.models import Event, ICSError, TimePeriodExceptionSource
|
||||
|
||||
|
||||
def set_connection(domain):
|
||||
|
@ -55,3 +55,17 @@ def refresh_exceptions_from_settings(args):
|
|||
return
|
||||
|
||||
source.refresh_from_settings()
|
||||
|
||||
|
||||
@spool
|
||||
def event_notify_checked(args):
|
||||
if args.get('domain'):
|
||||
# multitenant installation
|
||||
set_connection(args['domain'])
|
||||
|
||||
try:
|
||||
event = Event.objects.get(pk=args['event_id'])
|
||||
except Event.DoesNotExist:
|
||||
return
|
||||
|
||||
event.notify_checked()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import datetime
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django.db import connection
|
||||
|
@ -189,6 +190,49 @@ def test_event_checked(app, user):
|
|||
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, event.slug), status=404)
|
||||
|
||||
|
||||
def test_event_notify_checked(app, user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
event = Event.objects.create(
|
||||
label='xyz',
|
||||
start_datetime=now() - datetime.timedelta(days=1),
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
)
|
||||
assert event.checked is False
|
||||
|
||||
for i in range(8):
|
||||
user_was_present = None
|
||||
if i < 3:
|
||||
user_was_present = True
|
||||
elif i < 7:
|
||||
user_was_present = False
|
||||
Booking.objects.create(
|
||||
event=event,
|
||||
user_was_present=user_was_present,
|
||||
presence_callback_url='https://example.invalid/presence/%s' % i,
|
||||
absence_callback_url='https://example.invalid/absence/%s' % i,
|
||||
)
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
|
||||
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
|
||||
mock_response = mock.Mock(status_code=200)
|
||||
mock_send.return_value = mock_response
|
||||
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, event.slug))
|
||||
event.refresh_from_db()
|
||||
assert event.checked is True
|
||||
|
||||
assert {x[0][0].url for x in mock_send.call_args_list} == {
|
||||
'https://example.invalid/presence/0',
|
||||
'https://example.invalid/presence/1',
|
||||
'https://example.invalid/presence/2',
|
||||
'https://example.invalid/absence/3',
|
||||
'https://example.invalid/absence/4',
|
||||
'https://example.invalid/absence/5',
|
||||
'https://example.invalid/absence/6',
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'days_in, days_out, err_msg',
|
||||
[
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import codecs
|
||||
import datetime
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
@ -1798,6 +1799,53 @@ def test_event_checked(app, admin_user):
|
|||
assert '<span class="invoiced tag">Invoiced</span>' in resp
|
||||
|
||||
|
||||
def test_event_notify_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)
|
||||
|
||||
for i in range(8):
|
||||
user_was_present = None
|
||||
if i < 3:
|
||||
user_was_present = True
|
||||
elif i < 7:
|
||||
user_was_present = False
|
||||
Booking.objects.create(
|
||||
event=event,
|
||||
user_was_present=user_was_present,
|
||||
presence_callback_url='https://example.invalid/presence/%s' % i,
|
||||
absence_callback_url='https://example.invalid/absence/%s' % i,
|
||||
)
|
||||
resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
|
||||
|
||||
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
|
||||
mock_response = mock.Mock(status_code=200)
|
||||
mock_send.return_value = mock_response
|
||||
resp = app.post(
|
||||
'/manage/agendas/%s/events/%s/checked' % (agenda.pk, event.pk),
|
||||
params={'csrfmiddlewaretoken': resp.context['csrf_token']},
|
||||
)
|
||||
assert {x[0][0].url for x in mock_send.call_args_list} == {
|
||||
'https://example.invalid/presence/0',
|
||||
'https://example.invalid/presence/1',
|
||||
'https://example.invalid/presence/2',
|
||||
'https://example.invalid/absence/3',
|
||||
'https://example.invalid/absence/4',
|
||||
'https://example.invalid/absence/5',
|
||||
'https://example.invalid/absence/6',
|
||||
}
|
||||
assert set(json.loads(mock_send.call_args_list[0][0][0].body).keys()) == {
|
||||
'user_check_type_label',
|
||||
'user_check_type_slug',
|
||||
'user_was_present',
|
||||
}
|
||||
|
||||
|
||||
@mock.patch('chrono.manager.forms.get_agenda_check_types')
|
||||
def test_event_check_filters(check_types, app, admin_user):
|
||||
check_types.return_value = [
|
||||
|
|
Loading…
Reference in New Issue