api: make shared custody work with recurring events in datetimes (#62598)
This commit is contained in:
parent
6311c5146a
commit
f158dc02ef
|
@ -850,7 +850,13 @@ class Agenda(models.Model):
|
|||
for event in recurring_events:
|
||||
events.extend(
|
||||
event.get_recurrences(
|
||||
min_start, max_start, excluded_datetimes.get(event.pk), exceptions, slug_separator=':'
|
||||
min_start,
|
||||
max_start,
|
||||
excluded_datetimes.get(event.pk),
|
||||
exceptions,
|
||||
slug_separator=':',
|
||||
shared_custody_rules=getattr(self, 'prefetched_custody_rules', None),
|
||||
shared_custody_periods=getattr(self, 'prefetched_custody_periods', None),
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -1084,6 +1090,28 @@ class Agenda(models.Model):
|
|||
for e in agendas_exceptions
|
||||
if e.desk_id == desk.pk or e.unavailability_calendar_id in uc_ids
|
||||
]
|
||||
|
||||
if guardian_external_id:
|
||||
custody_rules = SharedCustodyRule.objects.filter(
|
||||
guardian__user_external_id=guardian_external_id,
|
||||
agenda__children__user_external_id=user_external_id,
|
||||
)
|
||||
|
||||
all_periods = SharedCustodyPeriod.objects.filter(
|
||||
agenda__children__user_external_id=user_external_id
|
||||
)
|
||||
if max_start:
|
||||
all_periods = all_periods.filter(date_start__lte=max_start)
|
||||
if min_start:
|
||||
all_periods = all_periods.filter(date_end__gt=min_start)
|
||||
custody_periods = all_periods.filter(guardian__user_external_id=guardian_external_id)
|
||||
custody_exceptions = all_periods.exclude(guardian__user_external_id=guardian_external_id)
|
||||
|
||||
for agenda in agendas:
|
||||
agenda.prefetched_custody_rules = custody_rules
|
||||
agenda.prefetched_custody_periods = custody_periods
|
||||
agenda.prefetched_exceptions.extend(custody_exceptions)
|
||||
|
||||
return agendas
|
||||
|
||||
def is_available_for_simple_management(self):
|
||||
|
@ -1772,7 +1800,14 @@ class Event(models.Model):
|
|||
raise e
|
||||
|
||||
def get_recurrences(
|
||||
self, min_datetime, max_datetime, excluded_datetimes=None, exceptions=None, slug_separator='--'
|
||||
self,
|
||||
min_datetime,
|
||||
max_datetime,
|
||||
excluded_datetimes=None,
|
||||
exceptions=None,
|
||||
slug_separator='--',
|
||||
shared_custody_rules=None,
|
||||
shared_custody_periods=None,
|
||||
):
|
||||
recurrences = []
|
||||
rrule_set = rruleset()
|
||||
|
@ -1781,9 +1816,10 @@ class Event(models.Model):
|
|||
|
||||
if exceptions is None:
|
||||
exceptions = self.agenda.get_recurrence_exceptions(min_datetime, max_datetime)
|
||||
|
||||
event_start = localtime(self.start_datetime)
|
||||
for exception in exceptions:
|
||||
exception_start = localtime(exception.start_datetime)
|
||||
event_start = localtime(self.start_datetime)
|
||||
if event_start.time() < exception_start.time():
|
||||
exception_start += datetime.timedelta(days=1)
|
||||
exception_start = exception_start.replace(
|
||||
|
@ -1811,11 +1847,32 @@ class Event(models.Model):
|
|||
url=self.url,
|
||||
)
|
||||
|
||||
if shared_custody_rules:
|
||||
for rule in shared_custody_rules:
|
||||
recurrence_rule = self.recurrence_rule
|
||||
recurrence_rule['byweekday'] = set(recurrence_rule['byweekday']).intersection(
|
||||
rule.recurrence_rule['byweekday']
|
||||
)
|
||||
if not recurrence_rule['byweekday']:
|
||||
continue
|
||||
recurrence_rule['byweekno'] = rule.recurrence_rule.get('byweekno')
|
||||
rrule_set.rrule(rrule(dtstart=make_naive(self.start_datetime), **recurrence_rule))
|
||||
|
||||
for period in shared_custody_periods:
|
||||
period_start = event_start.replace(
|
||||
year=period.date_start.year, month=period.date_start.month, day=period.date_start.day
|
||||
)
|
||||
rule = rrule(dtstart=make_naive(self.start_datetime), **self.recurrence_rule)
|
||||
for start_datetime in rule.between(
|
||||
make_naive(period_start), make_naive(period.end_datetime), inc=True
|
||||
):
|
||||
rrule_set.rdate(start_datetime)
|
||||
else:
|
||||
rrule_set.rrule(rrule(dtstart=make_naive(self.start_datetime), **self.recurrence_rule))
|
||||
|
||||
# remove pytz info because dateutil doesn't support DST changes
|
||||
min_datetime = make_naive(min_datetime)
|
||||
max_datetime = make_naive(max_datetime)
|
||||
rrule_set.rrule(rrule(dtstart=make_naive(self.start_datetime), **self.recurrence_rule))
|
||||
|
||||
for start_datetime in rrule_set.between(min_datetime, max_datetime, inc=True):
|
||||
event = copy.copy(event_base)
|
||||
# add timezone back
|
||||
|
@ -3217,6 +3274,15 @@ class SharedCustodyRule(models.Model):
|
|||
guardian = models.ForeignKey(Person, verbose_name=_('Guardian'), on_delete=models.CASCADE)
|
||||
|
||||
def get_slots(self, min_date, max_date):
|
||||
return [
|
||||
SharedCustodySlot(self.guardian, dt.date())
|
||||
for dt in rrule(
|
||||
dtstart=min_date, until=max_date - datetime.timedelta(days=1), **self.recurrence_rule
|
||||
)
|
||||
]
|
||||
|
||||
@property
|
||||
def recurrence_rule(self):
|
||||
recurrence_rule = {
|
||||
'freq': WEEKLY,
|
||||
'byweekday': self.days,
|
||||
|
@ -3225,11 +3291,7 @@ class SharedCustodyRule(models.Model):
|
|||
recurrence_rule['byweekno'] = list(range(1, 55, 2))
|
||||
elif self.weeks == 'even':
|
||||
recurrence_rule['byweekno'] = list(range(0, 54, 2))
|
||||
|
||||
return [
|
||||
SharedCustodySlot(self.guardian, dt.date())
|
||||
for dt in rrule(dtstart=min_date, until=max_date - datetime.timedelta(days=1), **recurrence_rule)
|
||||
]
|
||||
return recurrence_rule
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
|
@ -3276,3 +3338,11 @@ class SharedCustodyPeriod(models.Model):
|
|||
date_format(self.date_end, 'SHORT_DATE_FORMAT'),
|
||||
)
|
||||
return '%s, %s' % (self.guardian.name, exc_repr)
|
||||
|
||||
@property
|
||||
def start_datetime(self):
|
||||
return make_aware(datetime.datetime.combine(self.date_start, datetime.time(0, 0)))
|
||||
|
||||
@property
|
||||
def end_datetime(self):
|
||||
return make_aware(datetime.datetime.combine(self.date_end, datetime.time(0, 0)))
|
||||
|
|
|
@ -2044,7 +2044,7 @@ def test_datetimes_multiple_agendas_queries(app):
|
|||
},
|
||||
)
|
||||
assert len(resp.json['data']) == 30
|
||||
assert len(ctx.captured_queries) == 8
|
||||
assert len(ctx.captured_queries) == 9
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2021-05-06 14:00')
|
||||
|
@ -2498,3 +2498,213 @@ def test_datetimes_multiple_agendas_shared_custody_other_rules(app):
|
|||
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
|
||||
)
|
||||
assert len(resp.json['data']) == 2
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2022-03-07 14:00') # Monday of 10th week
|
||||
def test_datetimes_multiple_agendas_shared_custody_recurring_event(app):
|
||||
agenda = Agenda.objects.create(label='First agenda', kind='events', maximal_booking_delay=30)
|
||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||
wednesday_event = Event.objects.create(
|
||||
slug='event-wednesday',
|
||||
start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=9, hour=14, minute=0)),
|
||||
recurrence_days=[2],
|
||||
places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
thursday_event = Event.objects.create(
|
||||
slug='event-thursday',
|
||||
start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=10, hour=14, minute=0)),
|
||||
recurrence_days=[3],
|
||||
places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
Subscription.objects.create(
|
||||
agenda=agenda,
|
||||
user_external_id='child_id',
|
||||
date_start=now(),
|
||||
date_end=now() + datetime.timedelta(days=30),
|
||||
)
|
||||
|
||||
father = Person.objects.create(user_external_id='father_id', name='John Doe')
|
||||
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe')
|
||||
child = Person.objects.create(user_external_id='child_id', name='James Doe')
|
||||
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother)
|
||||
agenda.children.add(child)
|
||||
|
||||
father_rule = SharedCustodyRule.objects.create(
|
||||
agenda=agenda, guardian=father, days=list(range(7)), weeks='even'
|
||||
)
|
||||
mother_rule = SharedCustodyRule.objects.create(
|
||||
agenda=agenda, guardian=mother, days=list(range(7)), weeks='odd'
|
||||
)
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={'subscribed': 'all', 'user_external_id': 'child_id'},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-09-1400',
|
||||
'first-agenda@event-thursday:2022-03-10-1400',
|
||||
'first-agenda@event-wednesday:2022-03-16-1400',
|
||||
'first-agenda@event-thursday:2022-03-17-1400',
|
||||
'first-agenda@event-wednesday:2022-03-23-1400',
|
||||
'first-agenda@event-thursday:2022-03-24-1400',
|
||||
'first-agenda@event-wednesday:2022-03-30-1400',
|
||||
'first-agenda@event-thursday:2022-03-31-1400',
|
||||
]
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-09-1400',
|
||||
'first-agenda@event-thursday:2022-03-10-1400',
|
||||
'first-agenda@event-wednesday:2022-03-23-1400',
|
||||
'first-agenda@event-thursday:2022-03-24-1400',
|
||||
]
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-16-1400',
|
||||
'first-agenda@event-thursday:2022-03-17-1400',
|
||||
'first-agenda@event-wednesday:2022-03-30-1400',
|
||||
'first-agenda@event-thursday:2022-03-31-1400',
|
||||
]
|
||||
|
||||
# add father custody period spanning odd week event
|
||||
SharedCustodyPeriod.objects.create(
|
||||
agenda=agenda,
|
||||
guardian=father,
|
||||
date_start=datetime.date(2022, 3, 16),
|
||||
date_end=datetime.date(2022, 3, 18),
|
||||
)
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-09-1400',
|
||||
'first-agenda@event-thursday:2022-03-10-1400',
|
||||
'first-agenda@event-wednesday:2022-03-16-1400',
|
||||
'first-agenda@event-thursday:2022-03-17-1400',
|
||||
'first-agenda@event-wednesday:2022-03-23-1400',
|
||||
'first-agenda@event-thursday:2022-03-24-1400',
|
||||
]
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-30-1400',
|
||||
'first-agenda@event-thursday:2022-03-31-1400',
|
||||
]
|
||||
|
||||
# check date_start/date_end
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={
|
||||
'subscribed': 'all',
|
||||
'user_external_id': 'child_id',
|
||||
'guardian_external_id': 'father_id',
|
||||
'date_start': '2022-03-16',
|
||||
'date_end': '2022-03-17',
|
||||
},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-16-1400',
|
||||
]
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={
|
||||
'subscribed': 'all',
|
||||
'user_external_id': 'child_id',
|
||||
'guardian_external_id': 'father_id',
|
||||
'date_start': '2022-03-17',
|
||||
'date_end': '2022-03-18',
|
||||
},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-thursday:2022-03-17-1400',
|
||||
]
|
||||
|
||||
# weirder rules
|
||||
father_rule.days = [0, 1]
|
||||
father_rule.weeks = ''
|
||||
father_rule.save()
|
||||
SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=[2])
|
||||
|
||||
mother_rule.weeks = ''
|
||||
mother_rule.days = [3, 4, 5, 6]
|
||||
mother_rule.save()
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-09-1400',
|
||||
'first-agenda@event-wednesday:2022-03-16-1400',
|
||||
'first-agenda@event-thursday:2022-03-17-1400',
|
||||
'first-agenda@event-wednesday:2022-03-23-1400',
|
||||
'first-agenda@event-wednesday:2022-03-30-1400',
|
||||
]
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-thursday:2022-03-10-1400',
|
||||
'first-agenda@event-thursday:2022-03-24-1400',
|
||||
'first-agenda@event-thursday:2022-03-31-1400',
|
||||
]
|
||||
|
||||
thursday_event.delete()
|
||||
wednesday_event.recurrence_week_interval = 2
|
||||
wednesday_event.save()
|
||||
|
||||
resp = app.get('/api/agendas/datetimes/', params={'subscribed': 'all', 'user_external_id': 'child_id'})
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-09-1400',
|
||||
'first-agenda@event-wednesday:2022-03-23-1400',
|
||||
]
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-09-1400',
|
||||
'first-agenda@event-wednesday:2022-03-23-1400',
|
||||
]
|
||||
|
||||
# add mother custody period spanning even week event
|
||||
SharedCustodyPeriod.objects.create(
|
||||
agenda=agenda,
|
||||
guardian=mother,
|
||||
date_start=datetime.date(2022, 3, 9),
|
||||
date_end=datetime.date(2022, 3, 10),
|
||||
)
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-23-1400',
|
||||
]
|
||||
|
||||
resp = app.get(
|
||||
'/api/agendas/datetimes/',
|
||||
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'},
|
||||
)
|
||||
assert [d['id'] for d in resp.json['data']] == [
|
||||
'first-agenda@event-wednesday:2022-03-09-1400',
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue