agendas: add shared custody models (#62146)
This commit is contained in:
parent
2ce8babd54
commit
adad089c09
|
@ -0,0 +1,137 @@
|
|||
# Generated by Django 2.2.19 on 2022-03-03 16:03
|
||||
|
||||
import django.contrib.postgres.fields
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agendas', '0109_auto_20220203_1051'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Person',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('user_external_id', models.CharField(max_length=250, unique=True)),
|
||||
('name', models.CharField(max_length=250)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SharedCustodyAgenda',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('children', models.ManyToManyField(related_name='agendas', to='agendas.Person')),
|
||||
(
|
||||
'first_guardian',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='+',
|
||||
to='agendas.Person',
|
||||
verbose_name='First guardian',
|
||||
),
|
||||
),
|
||||
(
|
||||
'second_guardian',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='+',
|
||||
to='agendas.Person',
|
||||
verbose_name='Second guardian',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SharedCustodyRule',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
(
|
||||
'days',
|
||||
django.contrib.postgres.fields.ArrayField(
|
||||
base_field=models.IntegerField(
|
||||
choices=[
|
||||
(0, 'Mo'),
|
||||
(1, 'Tu'),
|
||||
(2, 'We'),
|
||||
(3, 'Th'),
|
||||
(4, 'Fr'),
|
||||
(5, 'Sa'),
|
||||
(6, 'Su'),
|
||||
]
|
||||
),
|
||||
size=None,
|
||||
verbose_name='Days',
|
||||
),
|
||||
),
|
||||
(
|
||||
'weeks',
|
||||
models.CharField(
|
||||
blank=True,
|
||||
choices=[('', 'All'), ('even', 'Even'), ('odd', 'Odd')],
|
||||
max_length=16,
|
||||
verbose_name='Weeks',
|
||||
),
|
||||
),
|
||||
(
|
||||
'agenda',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='rules',
|
||||
to='agendas.SharedCustodyAgenda',
|
||||
),
|
||||
),
|
||||
(
|
||||
'guardian',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to='agendas.Person',
|
||||
verbose_name='Guardian',
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['days__0', 'weeks'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SharedCustodyPeriod',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('date_start', models.DateField(verbose_name='Start')),
|
||||
('date_end', models.DateField(verbose_name='End')),
|
||||
(
|
||||
'agenda',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='periods',
|
||||
to='agendas.SharedCustodyAgenda',
|
||||
),
|
||||
),
|
||||
(
|
||||
'guardian',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, related_name='+', to='agendas.Person'
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ['date_start'],
|
||||
},
|
||||
),
|
||||
]
|
|
@ -23,6 +23,7 @@ import math
|
|||
import sys
|
||||
import uuid
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
import requests
|
||||
import vobject
|
||||
|
@ -34,6 +35,7 @@ from django.core.exceptions import FieldDoesNotExist, ValidationError
|
|||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import IntegrityError, connection, models, transaction
|
||||
from django.db.models import Count, F, Max, Prefetch, Q
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist, engines
|
||||
from django.urls import reverse
|
||||
from django.utils import functional
|
||||
|
@ -45,11 +47,12 @@ from django.utils.module_loading import import_string
|
|||
from django.utils.safestring import mark_safe
|
||||
from django.utils.text import slugify
|
||||
from django.utils.timezone import is_aware, localtime, make_aware, make_naive, now, utc
|
||||
from django.utils.translation import ugettext
|
||||
from django.utils.translation import pgettext, ugettext
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext
|
||||
|
||||
from chrono.interval import Interval, IntervalSet
|
||||
from chrono.utils.db import SumCardinality
|
||||
from chrono.utils.publik_urls import translate_from_publik_url
|
||||
from chrono.utils.requests_wrapper import requests as requests_wrapper
|
||||
|
||||
|
@ -75,6 +78,16 @@ WEEKDAYS_PLURAL = {
|
|||
6: _('Sundays'),
|
||||
}
|
||||
|
||||
WEEKDAY_CHOICES = [
|
||||
(0, _('Mo')),
|
||||
(1, _('Tu')),
|
||||
(2, _('We')),
|
||||
(3, _('Th')),
|
||||
(4, _('Fr')),
|
||||
(5, _('Sa')),
|
||||
(6, _('Su')),
|
||||
]
|
||||
|
||||
|
||||
def is_midnight(dtime):
|
||||
dtime = localtime(dtime)
|
||||
|
@ -1397,16 +1410,6 @@ class MeetingType(models.Model):
|
|||
|
||||
|
||||
class Event(models.Model):
|
||||
WEEKDAY_CHOICES = [
|
||||
(0, _('Mo')),
|
||||
(1, _('Tu')),
|
||||
(2, _('We')),
|
||||
(3, _('Th')),
|
||||
(4, _('Fr')),
|
||||
(5, _('Sa')),
|
||||
(6, _('Su')),
|
||||
]
|
||||
|
||||
INTERVAL_CHOICES = [
|
||||
(1, _('Every week')),
|
||||
(2, _('Every two weeks')),
|
||||
|
@ -3024,3 +3027,168 @@ class Subscription(models.Model):
|
|||
return Template(self.agenda.get_booking_user_block_template()).render(template_vars)
|
||||
except (VariableDoesNotExist, TemplateSyntaxError):
|
||||
return
|
||||
|
||||
|
||||
class Person(models.Model):
|
||||
user_external_id = models.CharField(max_length=250, unique=True)
|
||||
name = models.CharField(max_length=250)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SharedCustodySlot:
|
||||
guardian: Person = field(compare=False)
|
||||
date: datetime.date
|
||||
|
||||
def __str__(self):
|
||||
return self.guardian.name
|
||||
|
||||
|
||||
class SharedCustodyAgenda(models.Model):
|
||||
first_guardian = models.ForeignKey(
|
||||
Person, verbose_name=_('First guardian'), on_delete=models.CASCADE, related_name='+'
|
||||
)
|
||||
second_guardian = models.ForeignKey(
|
||||
Person, verbose_name=_('Second guardian'), on_delete=models.CASCADE, related_name='+'
|
||||
)
|
||||
children = models.ManyToManyField(Person, related_name='agendas')
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return _('Custody agenda of %(first_guardian)s and %(second_guardian)s') % {
|
||||
'first_guardian': self.first_guardian.name,
|
||||
'second_guardian': self.second_guardian.name,
|
||||
}
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('chrono-manager-shared-custody-agenda-view', kwargs={'pk': self.pk})
|
||||
|
||||
def get_settings_url(self):
|
||||
return reverse('chrono-manager-shared-custody-agenda-settings', kwargs={'pk': self.pk})
|
||||
|
||||
def get_custody_slots(self, min_date, max_date):
|
||||
slots = set()
|
||||
|
||||
periods = self.periods.filter(date_start__lt=max_date, date_end__gt=min_date)
|
||||
for period in periods:
|
||||
date = period.date_start
|
||||
while date < period.date_end and date < max_date:
|
||||
slots.add(SharedCustodySlot(guardian=period.guardian, date=date))
|
||||
date += datetime.timedelta(days=1)
|
||||
|
||||
for rule in self.rules.all():
|
||||
slots.update(rule.get_slots(min_date, max_date))
|
||||
|
||||
slots = sorted(slots, key=lambda x: x.date)
|
||||
return slots
|
||||
|
||||
def is_complete(self):
|
||||
day_counts = self.rules.aggregate(
|
||||
all_week=Coalesce(SumCardinality('days', filter=Q(weeks='')), 0),
|
||||
even_week=Coalesce(SumCardinality('days', filter=Q(weeks='even')), 0),
|
||||
odd_week=Coalesce(SumCardinality('days', filter=Q(weeks='odd')), 0),
|
||||
)
|
||||
even_week_day_count = day_counts['all_week'] + day_counts['even_week']
|
||||
odd_week_day_count = day_counts['all_week'] + day_counts['odd_week']
|
||||
return bool(even_week_day_count == 7 and odd_week_day_count == 7)
|
||||
|
||||
def rule_overlaps(self, days, weeks, instance=None):
|
||||
qs = self.rules
|
||||
if hasattr(instance, 'pk'):
|
||||
qs = qs.exclude(pk=instance.pk)
|
||||
|
||||
if weeks:
|
||||
qs = qs.filter(Q(weeks='') | Q(weeks=weeks))
|
||||
|
||||
qs = qs.filter(days__overlap=days)
|
||||
return qs.exists()
|
||||
|
||||
def period_overlaps(self, date_start, date_end, instance=None):
|
||||
qs = self.periods
|
||||
if hasattr(instance, 'pk'):
|
||||
qs = qs.exclude(pk=instance.pk)
|
||||
|
||||
qs = qs.extra(
|
||||
where=["(date_start, date_end) OVERLAPS (%s, %s)"],
|
||||
params=[date_start, date_end],
|
||||
)
|
||||
return qs.exists()
|
||||
|
||||
|
||||
class SharedCustodyRule(models.Model):
|
||||
WEEK_CHOICES = [
|
||||
('', pgettext('weeks', 'All')),
|
||||
('even', _('Even')),
|
||||
('odd', _('Odd')),
|
||||
]
|
||||
|
||||
agenda = models.ForeignKey(SharedCustodyAgenda, on_delete=models.CASCADE, related_name='rules')
|
||||
days = ArrayField(
|
||||
models.IntegerField(choices=WEEKDAY_CHOICES),
|
||||
verbose_name=_('Days'),
|
||||
)
|
||||
weeks = models.CharField(_('Weeks'), choices=WEEK_CHOICES, blank=True, max_length=16)
|
||||
guardian = models.ForeignKey(Person, verbose_name=_('Guardian'), on_delete=models.CASCADE)
|
||||
|
||||
def get_slots(self, min_date, max_date):
|
||||
recurrence_rule = {
|
||||
'freq': WEEKLY,
|
||||
'byweekday': self.days,
|
||||
}
|
||||
if self.weeks == 'odd':
|
||||
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)
|
||||
]
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
days_count = len(self.days)
|
||||
if days_count == 7:
|
||||
repeat = _('daily')
|
||||
elif days_count > 1 and (self.days[-1] - self.days[0]) == days_count - 1:
|
||||
# days are contiguous
|
||||
repeat = _('from %(weekday)s to %(last_weekday)s') % {
|
||||
'weekday': str(WEEKDAYS[self.days[0]]),
|
||||
'last_weekday': str(WEEKDAYS[self.days[-1]]),
|
||||
}
|
||||
else:
|
||||
repeat = _('on %(weekdays)s') % {
|
||||
'weekdays': ', '.join([str(WEEKDAYS_PLURAL[i]) for i in self.days])
|
||||
}
|
||||
|
||||
if self.weeks == 'odd':
|
||||
repeat = '%s, %s' % (repeat, _('on odd weeks'))
|
||||
elif self.weeks == 'even':
|
||||
repeat = '%s, %s' % (repeat, _('on even weeks'))
|
||||
|
||||
return repeat
|
||||
|
||||
class Meta:
|
||||
ordering = ['days__0', 'weeks']
|
||||
|
||||
|
||||
class SharedCustodyPeriod(models.Model):
|
||||
agenda = models.ForeignKey(SharedCustodyAgenda, on_delete=models.CASCADE, related_name='periods')
|
||||
guardian = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='+')
|
||||
date_start = models.DateField(_('Start'))
|
||||
date_end = models.DateField(_('End'))
|
||||
|
||||
class Meta:
|
||||
ordering = ['date_start']
|
||||
|
||||
def __str__(self):
|
||||
if self.date_end == self.date_start + datetime.timedelta(days=1):
|
||||
exc_repr = '%s' % date_format(self.date_start, 'SHORT_DATE_FORMAT')
|
||||
else:
|
||||
exc_repr = '%s → %s' % (
|
||||
date_format(self.date_start, 'SHORT_DATE_FORMAT'),
|
||||
date_format(self.date_end, 'SHORT_DATE_FORMAT'),
|
||||
)
|
||||
return '%s, %s' % (self.guardian.name, exc_repr)
|
||||
|
|
|
@ -35,6 +35,7 @@ from django.utils.timezone import localtime, make_aware, now
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from chrono.agendas.models import (
|
||||
WEEKDAY_CHOICES,
|
||||
WEEKDAYS_LIST,
|
||||
AbsenceReason,
|
||||
AbsenceReasonGroup,
|
||||
|
@ -177,7 +178,7 @@ class NewEventForm(forms.ModelForm):
|
|||
help_text=_('This field will not be editable once event has bookings.'),
|
||||
)
|
||||
recurrence_days = forms.TypedMultipleChoiceField(
|
||||
choices=Event.WEEKDAY_CHOICES,
|
||||
choices=WEEKDAY_CHOICES,
|
||||
coerce=int,
|
||||
required=False,
|
||||
widget=WeekdaysWidget,
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.db.migrations.operations.base import Operation
|
||||
from django.db.models import Aggregate
|
||||
|
||||
|
||||
class SumCardinality(Aggregate):
|
||||
template = 'SUM(CARDINALITY(%(expressions)s))'
|
||||
|
||||
|
||||
class EnsureJsonbType(Operation):
|
||||
|
|
|
@ -25,7 +25,11 @@ from chrono.agendas.models import (
|
|||
EventCancellationReport,
|
||||
ICSError,
|
||||
MeetingType,
|
||||
Person,
|
||||
Resource,
|
||||
SharedCustodyAgenda,
|
||||
SharedCustodyPeriod,
|
||||
SharedCustodyRule,
|
||||
TimePeriod,
|
||||
TimePeriodException,
|
||||
TimePeriodExceptionSource,
|
||||
|
@ -2630,3 +2634,220 @@ def test_recurring_events_create_past_recurrences(freezer):
|
|||
)
|
||||
daily_event.create_all_recurrences()
|
||||
assert daily_event.recurrences.count() == 6
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2022-02-22 14:00') # Tuesday of 8th week
|
||||
def test_shared_custody_agenda():
|
||||
father = Person.objects.create(user_external_id='father_id', name='John Doe')
|
||||
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe')
|
||||
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother)
|
||||
|
||||
SharedCustodyRule.objects.create(agenda=agenda, days=list(range(7)), weeks='even', guardian=father)
|
||||
SharedCustodyRule.objects.create(agenda=agenda, days=list(range(7)), weeks='odd', guardian=mother)
|
||||
|
||||
slots = agenda.get_custody_slots(now().date(), now().date() + datetime.timedelta(days=30))
|
||||
assert [x.date for x in slots] == [now().date() + datetime.timedelta(days=i) for i in range(30)]
|
||||
assert all(x.guardian == father for x in slots if x.date.isocalendar()[1] % 2 == 0)
|
||||
assert all(x.guardian == mother for x in slots if x.date.isocalendar()[1] % 2 == 1)
|
||||
|
||||
# add mother custody period on father's week, on 23/02 and 24/02
|
||||
SharedCustodyPeriod.objects.create(
|
||||
agenda=agenda,
|
||||
guardian=mother,
|
||||
date_start=datetime.date(year=2022, month=2, day=23),
|
||||
date_end=datetime.date(year=2022, month=2, day=25),
|
||||
)
|
||||
slots = agenda.get_custody_slots(now().date(), now().date() + datetime.timedelta(days=5))
|
||||
slots = [(x.date.strftime('%d/%m'), x.guardian.name) for x in slots]
|
||||
assert slots == [
|
||||
('22/02', 'John Doe'),
|
||||
('23/02', 'Jane Doe'),
|
||||
('24/02', 'Jane Doe'),
|
||||
('25/02', 'John Doe'),
|
||||
('26/02', 'John Doe'),
|
||||
]
|
||||
|
||||
# add father custody period on father's week, nothing should change
|
||||
SharedCustodyPeriod.objects.create(
|
||||
agenda=agenda,
|
||||
guardian=father,
|
||||
date_start=datetime.date(year=2022, month=2, day=25),
|
||||
date_end=datetime.date(year=2022, month=3, day=3),
|
||||
)
|
||||
slots = agenda.get_custody_slots(now().date(), now().date() + datetime.timedelta(days=5))
|
||||
slots = [(x.date.strftime('%d/%m'), x.guardian.name) for x in slots]
|
||||
assert slots == [
|
||||
('22/02', 'John Doe'),
|
||||
('23/02', 'Jane Doe'),
|
||||
('24/02', 'Jane Doe'),
|
||||
('25/02', 'John Doe'),
|
||||
('26/02', 'John Doe'),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2022-02-22 14:00') # Tuesday
|
||||
def test_shared_custody_agenda_different_periodicity():
|
||||
father = Person.objects.create(user_external_id='father_id', name='John Doe')
|
||||
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe')
|
||||
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother)
|
||||
|
||||
SharedCustodyRule.objects.create(agenda=agenda, days=[1, 2, 3], guardian=father)
|
||||
SharedCustodyRule.objects.create(agenda=agenda, days=[0, 4, 5, 6], guardian=mother)
|
||||
slots = agenda.get_custody_slots(now().date(), now().date() + datetime.timedelta(days=14))
|
||||
assert [(x.date.strftime('%A %d/%m'), x.guardian.name) for x in slots] == [
|
||||
('Tuesday 22/02', 'John Doe'),
|
||||
('Wednesday 23/02', 'John Doe'),
|
||||
('Thursday 24/02', 'John Doe'),
|
||||
('Friday 25/02', 'Jane Doe'),
|
||||
('Saturday 26/02', 'Jane Doe'),
|
||||
('Sunday 27/02', 'Jane Doe'),
|
||||
('Monday 28/02', 'Jane Doe'),
|
||||
('Tuesday 01/03', 'John Doe'),
|
||||
('Wednesday 02/03', 'John Doe'),
|
||||
('Thursday 03/03', 'John Doe'),
|
||||
('Friday 04/03', 'Jane Doe'),
|
||||
('Saturday 05/03', 'Jane Doe'),
|
||||
('Sunday 06/03', 'Jane Doe'),
|
||||
('Monday 07/03', 'Jane Doe'),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'rules,complete',
|
||||
(
|
||||
([], False),
|
||||
([{'days': [0]}], False),
|
||||
([{'days': [0], 'weeks': 'odd'}], False),
|
||||
([{'days': list(range(7))}], True),
|
||||
([{'days': list(range(7)), 'weeks': 'odd'}], False),
|
||||
([{'days': list(range(7)), 'weeks': 'odd'}, {'days': list(range(7)), 'weeks': 'even'}], True),
|
||||
([{'days': [0, 1, 2]}, {'days': [3, 4, 5, 6]}], True),
|
||||
([{'days': [0, 1, 2]}, {'days': [3, 4, 5]}], False),
|
||||
([{'days': [0, 1, 2, 3]}, {'days': [3, 4, 5, 6]}], False), # overlapping rules, should not exist
|
||||
(
|
||||
[
|
||||
{'days': [0, 1, 2]},
|
||||
{'days': [3, 4, 5]},
|
||||
{'days': [6], 'weeks': 'odd'},
|
||||
{'days': [6], 'weeks': 'even'},
|
||||
],
|
||||
True,
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_shared_custody_agenda_is_complete(rules, complete):
|
||||
father = Person.objects.create(user_external_id='father_id', name='John Doe')
|
||||
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe')
|
||||
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother)
|
||||
|
||||
for i, rule in enumerate(rules):
|
||||
guardian = father if i % 2 else mother
|
||||
SharedCustodyRule.objects.create(agenda=agenda, guardian=guardian, **rule)
|
||||
|
||||
assert agenda.is_complete() is complete
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'rules,days,weeks,overlaps',
|
||||
(
|
||||
([], [1], '', False),
|
||||
([{'days': [0]}], [1], '', False),
|
||||
([{'days': [1]}], [1], '', True),
|
||||
([{'days': [0]}, {'days': [1]}], [1], '', True),
|
||||
([{'days': [0], 'weeks': 'odd'}, {'days': [1]}], [1], '', True),
|
||||
([{'days': [0]}, {'days': [1], 'weeks': 'odd'}], [1], '', True),
|
||||
([{'days': [0]}, {'days': [1]}], [1], 'odd', True),
|
||||
([{'days': [0]}, {'days': [1], 'weeks': 'odd'}], [1], 'even', False),
|
||||
([{'days': [0, 1], 'weeks': 'odd'}], [1], 'odd', True),
|
||||
([{'days': [0, 1]}], [1], '', True),
|
||||
([{'days': [0, 1], 'weeks': 'even'}], [1], 'odd', False),
|
||||
([{'days': [0, 1]}], [0, 3, 4], '', True),
|
||||
([{'days': [0, 1]}], [2, 3], '', False),
|
||||
),
|
||||
)
|
||||
def test_shared_custody_agenda_rule_overlaps(rules, days, weeks, overlaps):
|
||||
father = Person.objects.create(user_external_id='father_id', name='John Doe')
|
||||
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe')
|
||||
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother)
|
||||
|
||||
for i, rule in enumerate(rules):
|
||||
guardian = father if i % 2 else mother
|
||||
SharedCustodyRule.objects.create(agenda=agenda, guardian=guardian, **rule)
|
||||
|
||||
assert agenda.rule_overlaps(days, weeks) is overlaps
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'periods,date_start,date_end,overlaps',
|
||||
(
|
||||
([], '2022-02-03', '2022-02-04', False),
|
||||
([('2022-02-03', '2022-02-04')], '2022-02-03', '2022-02-04', True),
|
||||
([('2022-02-03', '2022-02-04')], '2022-02-01', '2022-02-04', True),
|
||||
([('2022-02-03', '2022-02-04')], '2022-02-03', '2022-02-06', True),
|
||||
([('2022-02-03', '2022-02-04')], '2022-02-04', '2022-02-06', False),
|
||||
([('2022-02-03', '2022-02-04')], '2022-02-01', '2022-02-03', False),
|
||||
([('2022-02-03', '2022-02-04'), ('2022-01-31', '2022-02-01')], '2022-02-01', '2022-02-03', False),
|
||||
([('2022-02-03', '2022-02-04'), ('2022-01-31', '2022-02-01')], '2022-01-01', '2022-02-10', True),
|
||||
([('2022-02-03', '2022-02-10')], '2022-02-05', '2022-02-06', True),
|
||||
),
|
||||
)
|
||||
def test_shared_custody_agenda_period_overlaps(periods, date_start, date_end, overlaps):
|
||||
father = Person.objects.create(user_external_id='father_id', name='John Doe')
|
||||
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe')
|
||||
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother)
|
||||
|
||||
for i, dates in enumerate(periods):
|
||||
guardian = father if i % 2 else mother
|
||||
SharedCustodyPeriod.objects.create(
|
||||
agenda=agenda, guardian=guardian, date_start=dates[0], date_end=dates[1]
|
||||
)
|
||||
|
||||
assert agenda.period_overlaps(date_start, date_end) is overlaps
|
||||
|
||||
|
||||
def test_shared_custody_agenda_rule_label():
|
||||
father = Person.objects.create(user_external_id='father_id', name='John Doe')
|
||||
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe')
|
||||
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother)
|
||||
|
||||
rule = SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7)))
|
||||
assert rule.label == 'daily'
|
||||
|
||||
rule.days = [1, 2, 3, 4]
|
||||
rule.save()
|
||||
assert rule.label == 'from Tuesday to Friday'
|
||||
|
||||
rule.days = [4, 5, 6]
|
||||
rule.save()
|
||||
assert rule.label == 'from Friday to Sunday'
|
||||
|
||||
rule.days = [1, 4, 6]
|
||||
rule.save()
|
||||
assert rule.label == 'on Tuesdays, Fridays, Sundays'
|
||||
|
||||
rule.days = [0]
|
||||
rule.weeks = 'even'
|
||||
rule.save()
|
||||
assert rule.label == 'on Mondays, on even weeks'
|
||||
|
||||
rule.weeks = 'odd'
|
||||
rule.save()
|
||||
assert rule.label == 'on Mondays, on odd weeks'
|
||||
|
||||
|
||||
def test_shared_custody_agenda_period_label(freezer):
|
||||
father = Person.objects.create(user_external_id='father_id', name='John Doe')
|
||||
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe')
|
||||
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother)
|
||||
|
||||
period = SharedCustodyPeriod.objects.create(
|
||||
agenda=agenda,
|
||||
guardian=father,
|
||||
date_start=datetime.date(2021, 7, 10),
|
||||
date_end=datetime.date(2021, 7, 11),
|
||||
)
|
||||
assert str(period) == 'John Doe, 07/10/2021'
|
||||
|
||||
period.date_end = datetime.date(2021, 7, 13)
|
||||
period.save()
|
||||
assert str(period) == 'John Doe, 07/10/2021 → 07/13/2021'
|
||||
|
|
Loading…
Reference in New Issue