0002-agendas-add-shared-custody-holiday-rules-62801.patch
chrono/agendas/migrations/0114_auto_20220324_1702.py | ||
---|---|---|
1 |
# Generated by Django 2.2.19 on 2022-03-24 16:02 |
|
2 | ||
3 |
import django.db.models.deletion |
|
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('agendas', '0113_auto_20220323_1708'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.CreateModel( |
|
15 |
name='SharedCustodyHolidayRule', |
|
16 |
fields=[ |
|
17 |
( |
|
18 |
'id', |
|
19 |
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), |
|
20 |
), |
|
21 |
( |
|
22 |
'years', |
|
23 |
models.CharField( |
|
24 |
blank=True, |
|
25 |
choices=[('', 'All'), ('even', 'Even'), ('odd', 'Odd')], |
|
26 |
max_length=16, |
|
27 |
verbose_name='Years', |
|
28 |
), |
|
29 |
), |
|
30 |
( |
|
31 |
'periodicity', |
|
32 |
models.CharField( |
|
33 |
blank=True, |
|
34 |
choices=[ |
|
35 |
('first-half', 'First half'), |
|
36 |
('second-half', 'Second half'), |
|
37 |
('first-and-third-quarters', 'First and third quarters'), |
|
38 |
('second-and-fourth-quarters', 'Second and fourth quarters'), |
|
39 |
], |
|
40 |
max_length=32, |
|
41 |
verbose_name='Periodicity', |
|
42 |
), |
|
43 |
), |
|
44 |
( |
|
45 |
'agenda', |
|
46 |
models.ForeignKey( |
|
47 |
on_delete=django.db.models.deletion.CASCADE, |
|
48 |
related_name='holiday_rules', |
|
49 |
to='agendas.SharedCustodyAgenda', |
|
50 |
), |
|
51 |
), |
|
52 |
( |
|
53 |
'guardian', |
|
54 |
models.ForeignKey( |
|
55 |
on_delete=django.db.models.deletion.CASCADE, |
|
56 |
to='agendas.Person', |
|
57 |
verbose_name='Guardian', |
|
58 |
), |
|
59 |
), |
|
60 |
( |
|
61 |
'holiday', |
|
62 |
models.ForeignKey( |
|
63 |
on_delete=django.db.models.deletion.CASCADE, |
|
64 |
to='agendas.TimePeriodExceptionGroup', |
|
65 |
verbose_name='Holiday', |
|
66 |
), |
|
67 |
), |
|
68 |
], |
|
69 |
options={ |
|
70 |
'ordering': ['holiday__label', 'guardian', 'years', 'periodicity'], |
|
71 |
}, |
|
72 |
), |
|
73 |
migrations.AddField( |
|
74 |
model_name='sharedcustodyperiod', |
|
75 |
name='holiday_rule', |
|
76 |
field=models.ForeignKey( |
|
77 |
null=True, on_delete=django.db.models.deletion.CASCADE, to='agendas.SharedCustodyHolidayRule' |
|
78 |
), |
|
79 |
), |
|
80 |
] |
chrono/agendas/models.py | ||
---|---|---|
27 | 27 | |
28 | 28 |
import requests |
29 | 29 |
import vobject |
30 |
from dateutil.relativedelta import SU, relativedelta |
|
30 | 31 |
from dateutil.rrule import DAILY, WEEKLY, rrule, rruleset |
31 | 32 |
from django.conf import settings |
32 | 33 |
from django.contrib.auth.models import Group |
... | ... | |
3150 | 3151 |
qs = qs.filter(days__overlap=days) |
3151 | 3152 |
return qs.exists() |
3152 | 3153 | |
3154 |
def holiday_rule_overlaps(self, holiday, years, periodicity, instance=None): |
|
3155 |
qs = self.holiday_rules.filter(holiday=holiday) |
|
3156 |
if hasattr(instance, 'pk'): |
|
3157 |
qs = qs.exclude(pk=instance.pk) |
|
3158 | ||
3159 |
if years: |
|
3160 |
qs = qs.filter(Q(years='') | Q(years=years)) |
|
3161 | ||
3162 |
if periodicity in ('first-half', 'second-half'): |
|
3163 |
qs = qs.filter( |
|
3164 |
periodicity__in=(periodicity, '', 'first-and-third-quarters', 'second-and-fourth-quarters') |
|
3165 |
) |
|
3166 |
elif periodicity in ('first-and-third-quarters', 'second-and-fourth-quarters'): |
|
3167 |
qs = qs.filter(periodicity__in=(periodicity, '', 'first-half', 'second-half')) |
|
3168 | ||
3169 |
return qs.exists() |
|
3170 | ||
3153 | 3171 |
def period_overlaps(self, date_start, date_end, instance=None): |
3154 |
qs = self.periods |
|
3172 |
qs = self.periods.filter(holiday_rule__isnull=True)
|
|
3155 | 3173 |
if hasattr(instance, 'pk'): |
3156 | 3174 |
qs = qs.exclude(pk=instance.pk) |
3157 | 3175 | |
... | ... | |
3219 | 3237 |
ordering = ['days__0', 'weeks'] |
3220 | 3238 | |
3221 | 3239 | |
3240 |
class SharedCustodyHolidayRule(models.Model): |
|
3241 |
YEAR_CHOICES = [ |
|
3242 |
('', pgettext_lazy('years', 'All')), |
|
3243 |
('even', pgettext_lazy('years', 'Even')), |
|
3244 |
('odd', pgettext_lazy('years', 'Odd')), |
|
3245 |
] |
|
3246 | ||
3247 |
PERIODICITY_CHOICES = [ |
|
3248 |
('first-half', _('First half')), |
|
3249 |
('second-half', _('Second half')), |
|
3250 |
('first-and-third-quarters', _('First and third quarters')), |
|
3251 |
('second-and-fourth-quarters', _('Second and fourth quarters')), |
|
3252 |
] |
|
3253 | ||
3254 |
agenda = models.ForeignKey(SharedCustodyAgenda, on_delete=models.CASCADE, related_name='holiday_rules') |
|
3255 |
holiday = models.ForeignKey(TimePeriodExceptionGroup, verbose_name=_('Holiday'), on_delete=models.CASCADE) |
|
3256 |
years = models.CharField(_('Years'), choices=YEAR_CHOICES, blank=True, max_length=16) |
|
3257 |
periodicity = models.CharField(_('Periodicity'), choices=PERIODICITY_CHOICES, blank=True, max_length=32) |
|
3258 |
guardian = models.ForeignKey(Person, verbose_name=_('Guardian'), on_delete=models.CASCADE) |
|
3259 | ||
3260 |
def update_or_create_periods(self): |
|
3261 |
shared_custody_periods = [] |
|
3262 |
for exception in self.holiday.exceptions.all(): |
|
3263 |
date_start = localtime(exception.start_datetime).date() |
|
3264 | ||
3265 |
if self.years == 'even' and date_start.year % 2: |
|
3266 |
continue |
|
3267 |
if self.years == 'odd' and not date_start.year % 2: |
|
3268 |
continue |
|
3269 | ||
3270 |
date_start_sunday = date_start + relativedelta(weekday=SU) |
|
3271 |
date_end = localtime(exception.end_datetime).date() |
|
3272 | ||
3273 |
number_of_weeks = (date_end - date_start_sunday).days // 7 |
|
3274 | ||
3275 |
periods = [] |
|
3276 |
if self.periodicity == 'first-half': |
|
3277 |
date_end = date_start_sunday + datetime.timedelta(days=7 * (number_of_weeks // 2)) |
|
3278 |
periods = [(date_start, date_end)] |
|
3279 |
elif self.periodicity == 'second-half': |
|
3280 |
date_start = date_start_sunday + datetime.timedelta(days=7 * (number_of_weeks // 2)) |
|
3281 |
periods = [(date_start, date_end)] |
|
3282 |
elif self.periodicity == 'first-and-third-quarters' and number_of_weeks >= 4: |
|
3283 |
weeks_in_quarters = round(number_of_weeks / 4) |
|
3284 |
first_quarters_date_end = date_start_sunday + datetime.timedelta(days=7 * weeks_in_quarters) |
|
3285 |
third_quarters_date_start = date_start_sunday + datetime.timedelta( |
|
3286 |
days=7 * weeks_in_quarters * 2 |
|
3287 |
) |
|
3288 |
third_quarters_date_end = date_start_sunday + datetime.timedelta( |
|
3289 |
days=7 * weeks_in_quarters * 3 |
|
3290 |
) |
|
3291 |
periods = [ |
|
3292 |
(date_start, first_quarters_date_end), |
|
3293 |
(third_quarters_date_start, third_quarters_date_end), |
|
3294 |
] |
|
3295 |
elif self.periodicity == 'second-and-fourth-quarters' and number_of_weeks >= 4: |
|
3296 |
weeks_in_quarters = round(number_of_weeks / 4) |
|
3297 |
second_quarters_date_start = date_start_sunday + datetime.timedelta( |
|
3298 |
days=7 * weeks_in_quarters |
|
3299 |
) |
|
3300 |
second_quarters_date_end = date_start_sunday + datetime.timedelta( |
|
3301 |
days=7 * weeks_in_quarters * 2 |
|
3302 |
) |
|
3303 |
fourth_quarters_date_start = date_start_sunday + datetime.timedelta( |
|
3304 |
days=7 * weeks_in_quarters * 3 |
|
3305 |
) |
|
3306 |
periods = [ |
|
3307 |
(second_quarters_date_start, second_quarters_date_end), |
|
3308 |
(fourth_quarters_date_start, date_end), |
|
3309 |
] |
|
3310 |
elif not self.periodicity: |
|
3311 |
periods = [(date_start, date_end)] |
|
3312 | ||
3313 |
for date_start, date_end in periods: |
|
3314 |
shared_custody_periods.append( |
|
3315 |
SharedCustodyPeriod( |
|
3316 |
guardian=self.guardian, |
|
3317 |
agenda=self.agenda, |
|
3318 |
holiday_rule=self, |
|
3319 |
date_start=date_start, |
|
3320 |
date_end=date_end, |
|
3321 |
) |
|
3322 |
) |
|
3323 | ||
3324 |
with transaction.atomic(): |
|
3325 |
SharedCustodyPeriod.objects.filter( |
|
3326 |
guardian=self.guardian, agenda=self.agenda, holiday_rule=self |
|
3327 |
).delete() |
|
3328 |
SharedCustodyPeriod.objects.bulk_create(shared_custody_periods) |
|
3329 | ||
3330 |
@property |
|
3331 |
def label(self): |
|
3332 |
label = self.holiday.label |
|
3333 | ||
3334 |
if self.periodicity == 'first-half': |
|
3335 |
label = '%s, %s' % (label, _('the first half')) |
|
3336 |
elif self.periodicity == 'second-half': |
|
3337 |
label = '%s, %s' % (label, _('the second half')) |
|
3338 |
elif self.periodicity == 'first-and-third-quarters': |
|
3339 |
label = '%s, %s' % (label, _('the first and third quarters')) |
|
3340 |
elif self.periodicity == 'second-and-fourth-quarters': |
|
3341 |
label = '%s, %s' % (label, _('the second and fourth quarters')) |
|
3342 | ||
3343 |
if self.years == 'odd': |
|
3344 |
label = '%s, %s' % (label, _('on odd years')) |
|
3345 |
elif self.years == 'even': |
|
3346 |
label = '%s, %s' % (label, _('on even years')) |
|
3347 | ||
3348 |
return label |
|
3349 | ||
3350 |
class Meta: |
|
3351 |
ordering = ['holiday__label', 'guardian', 'years', 'periodicity'] |
|
3352 | ||
3353 | ||
3222 | 3354 |
class SharedCustodyPeriod(models.Model): |
3223 | 3355 |
agenda = models.ForeignKey(SharedCustodyAgenda, on_delete=models.CASCADE, related_name='periods') |
3224 | 3356 |
guardian = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='+') |
3357 |
holiday_rule = models.ForeignKey(SharedCustodyHolidayRule, null=True, on_delete=models.CASCADE) |
|
3225 | 3358 |
date_start = models.DateField(_('Start')) |
3226 | 3359 |
date_end = models.DateField(_('End')) |
3227 | 3360 |
chrono/manager/forms.py | ||
---|---|---|
28 | 28 |
from django.contrib.auth.models import Group |
29 | 29 |
from django.core.exceptions import FieldDoesNotExist |
30 | 30 |
from django.db import transaction |
31 |
from django.db.models import DurationField, ExpressionWrapper, F |
|
31 | 32 |
from django.forms import ValidationError |
32 | 33 |
from django.utils.encoding import force_text |
33 | 34 |
from django.utils.formats import date_format |
... | ... | |
50 | 51 |
MeetingType, |
51 | 52 |
Person, |
52 | 53 |
Resource, |
54 |
SharedCustodyHolidayRule, |
|
53 | 55 |
SharedCustodyPeriod, |
54 | 56 |
SharedCustodyRule, |
55 | 57 |
Subscription, |
56 | 58 |
TimePeriod, |
57 | 59 |
TimePeriodException, |
60 |
TimePeriodExceptionGroup, |
|
58 | 61 |
TimePeriodExceptionSource, |
59 | 62 |
UnavailabilityCalendar, |
60 | 63 |
VirtualMember, |
... | ... | |
1339 | 1342 |
return cleaned_data |
1340 | 1343 | |
1341 | 1344 | |
1345 |
class SharedCustodyHolidayRuleForm(forms.ModelForm): |
|
1346 |
guardian = forms.ModelChoiceField(label=_('Guardian'), queryset=Person.objects.none()) |
|
1347 | ||
1348 |
class Meta: |
|
1349 |
model = SharedCustodyHolidayRule |
|
1350 |
fields = ['guardian', 'holiday', 'years', 'periodicity'] |
|
1351 | ||
1352 |
def __init__(self, *args, **kwargs): |
|
1353 |
super().__init__(*args, **kwargs) |
|
1354 |
self.fields['guardian'].empty_label = None |
|
1355 |
self.fields['guardian'].queryset = Person.objects.filter( |
|
1356 |
pk__in=[self.instance.agenda.first_guardian_id, self.instance.agenda.second_guardian_id] |
|
1357 |
) |
|
1358 |
self.fields['holiday'].queryset = TimePeriodExceptionGroup.objects.filter( |
|
1359 |
unavailability_calendar__slug='chrono-holidays', |
|
1360 |
exceptions__isnull=False, |
|
1361 |
).distinct() |
|
1362 | ||
1363 |
def clean(self): |
|
1364 |
cleaned_data = super().clean() |
|
1365 | ||
1366 |
holidays = cleaned_data['holiday'].exceptions.annotate( |
|
1367 |
delta=ExpressionWrapper(F('end_datetime') - F('start_datetime'), output_field=DurationField()) |
|
1368 |
) |
|
1369 |
is_short_holiday = holidays.filter(delta__lt=datetime.timedelta(days=28)).exists() |
|
1370 |
if 'quarters' in cleaned_data['periodicity'] and is_short_holiday: |
|
1371 |
raise ValidationError(_('Short holidays cannot be cut into quarters.')) |
|
1372 | ||
1373 |
if self.instance.agenda.holiday_rule_overlaps( |
|
1374 |
cleaned_data['holiday'], cleaned_data['years'], cleaned_data['periodicity'], self.instance |
|
1375 |
): |
|
1376 |
raise ValidationError(_('Rule overlaps existing rules.')) |
|
1377 | ||
1378 |
return cleaned_data |
|
1379 | ||
1380 |
def save(self, *args, **kwargs): |
|
1381 |
with transaction.atomic(): |
|
1382 |
super().save() |
|
1383 |
self.instance.update_or_create_periods() |
|
1384 | ||
1385 |
return self.instance |
|
1386 | ||
1387 | ||
1342 | 1388 |
class SharedCustodyPeriodForm(forms.ModelForm): |
1343 | 1389 |
guardian = forms.ModelChoiceField(label=_('Guardian'), queryset=Person.objects.none()) |
1344 | 1390 |
chrono/manager/templates/chrono/manager_shared_custody_agenda_settings.html | ||
---|---|---|
10 | 10 |
<h2>{% trans "Settings" %}</h2> |
11 | 11 |
<span class="actions"> |
12 | 12 |
<a rel="popup" href="{% url 'chrono-manager-shared-custody-agenda-add-period' pk=object.id %}">{% trans 'Add custody period' %}</a> |
13 |
{% if has_holidays %} |
|
14 |
<a rel="popup" href="{% url 'chrono-manager-shared-custody-agenda-add-holiday-rule' pk=object.id %}">{% trans 'Add custody rule during holidays' %}</a> |
|
15 |
{% endif %} |
|
13 | 16 |
<a rel="popup" href="{% url 'chrono-manager-shared-custody-agenda-add-rule' pk=object.id %}">{% trans 'Add custody rule' %}</a> |
14 | 17 |
</span> |
15 | 18 |
{% endblock %} |
... | ... | |
45 | 48 |
</div> |
46 | 49 |
</div> |
47 | 50 | |
51 |
{% if has_holidays %} |
|
52 |
<div class="section"> |
|
53 |
<h3>{% trans "Custody rules during holidays" %}</h3> |
|
54 |
<div> |
|
55 |
{% if agenda.holiday_rules.all %} |
|
56 |
<ul class="objects-list single-links"> |
|
57 |
{% for rule in agenda.holiday_rules.all %} |
|
58 |
<li><a rel="popup" href="{% url 'chrono-manager-shared-custody-agenda-edit-holiday-rule' pk=agenda.pk rule_pk=rule.pk %}"> |
|
59 |
<span class="rule-info"> |
|
60 |
{{ rule.guardian.name }}, {{ rule.label }} |
|
61 |
</span> |
|
62 |
</a> |
|
63 |
<a rel="popup" class="delete" href="{% url 'chrono-manager-shared-custody-agenda-delete-holiday-rule' pk=agenda.pk rule_pk=rule.pk %}?next=settings">{% trans "remove" %}</a> |
|
64 |
</li> |
|
65 |
{% endfor %} |
|
66 |
</ul> |
|
67 |
{% else %} |
|
68 |
<div class="big-msg-info"> |
|
69 |
{% blocktrans trimmed %} |
|
70 |
This agenda doesn't specify any custody rules during holidays. It means normal rules will be applied. |
|
71 |
{% endblocktrans %} |
|
72 |
</div> |
|
73 |
{% endif %} |
|
74 |
</div> |
|
75 |
</div> |
|
76 |
{% endif %} |
|
77 | ||
48 | 78 |
<div class="section"> |
49 | 79 |
<h3>{% trans "Exceptional custody periods" %}</h3> |
50 | 80 |
<div> |
51 |
{% if agenda.periods.all %}
|
|
81 |
{% if exceptional_periods %}
|
|
52 | 82 |
<ul class="objects-list single-links"> |
53 |
{% for period in agenda.periods.all %}
|
|
83 |
{% for period in exceptional_periods %}
|
|
54 | 84 |
<li> |
55 | 85 |
<a rel="popup" href="{% url 'chrono-manager-shared-custody-agenda-edit-period' pk=agenda.pk period_pk=period.pk %}"> |
56 | 86 |
{{ period }} |
chrono/manager/urls.py | ||
---|---|---|
411 | 411 |
views.shared_custody_agenda_delete_rule, |
412 | 412 |
name='chrono-manager-shared-custody-agenda-delete-rule', |
413 | 413 |
), |
414 |
url( |
|
415 |
r'^shared-custody/(?P<pk>\d+)/add-holiday-rule$', |
|
416 |
views.shared_custody_agenda_add_holiday_rule, |
|
417 |
name='chrono-manager-shared-custody-agenda-add-holiday-rule', |
|
418 |
), |
|
419 |
url( |
|
420 |
r'^shared-custody/(?P<pk>\d+)/holiday-rules/(?P<rule_pk>\d+)/edit$', |
|
421 |
views.shared_custody_agenda_edit_holiday_rule, |
|
422 |
name='chrono-manager-shared-custody-agenda-edit-holiday-rule', |
|
423 |
), |
|
424 |
url( |
|
425 |
r'^shared-custody/(?P<pk>\d+)/holiday-rules/(?P<rule_pk>\d+)/delete$', |
|
426 |
views.shared_custody_agenda_delete_holiday_rule, |
|
427 |
name='chrono-manager-shared-custody-agenda-delete-holiday-rule', |
|
428 |
), |
|
414 | 429 |
url( |
415 | 430 |
r'^shared-custody/(?P<pk>\d+)/add-period$', |
416 | 431 |
views.shared_custody_agenda_add_period, |
chrono/manager/views.py | ||
---|---|---|
76 | 76 |
MeetingType, |
77 | 77 |
Resource, |
78 | 78 |
SharedCustodyAgenda, |
79 |
SharedCustodyHolidayRule, |
|
79 | 80 |
SharedCustodyPeriod, |
80 | 81 |
SharedCustodyRule, |
81 | 82 |
TimePeriod, |
... | ... | |
114 | 115 |
NewEventForm, |
115 | 116 |
NewMeetingTypeForm, |
116 | 117 |
NewTimePeriodExceptionForm, |
118 |
SharedCustodyHolidayRuleForm, |
|
117 | 119 |
SharedCustodyPeriodForm, |
118 | 120 |
SharedCustodyRuleForm, |
119 | 121 |
SubscriptionCheckFilterSet, |
... | ... | |
3458 | 3460 |
template_name = 'chrono/manager_shared_custody_agenda_settings.html' |
3459 | 3461 |
model = SharedCustodyAgenda |
3460 | 3462 | |
3463 |
def get_context_data(self, **kwargs): |
|
3464 |
context = super().get_context_data(**kwargs) |
|
3465 |
context['has_holidays'] = UnavailabilityCalendar.objects.filter(slug='chrono-holidays').exists() |
|
3466 |
context['exceptional_periods'] = SharedCustodyPeriod.objects.filter(holiday_rule__isnull=True) |
|
3467 |
return context |
|
3468 | ||
3461 | 3469 | |
3462 | 3470 |
shared_custody_agenda_settings = SharedCustodyAgendaSettings.as_view() |
3463 | 3471 | |
... | ... | |
3492 | 3500 |
shared_custody_agenda_delete_rule = SharedCustodyAgendaDeleteRuleView.as_view() |
3493 | 3501 | |
3494 | 3502 | |
3503 |
class SharedCustodyAgendaAddHolidayRuleView(SharedCustodyAgendaMixin, CreateView): |
|
3504 |
title = _('Add custody rule during holidays') |
|
3505 |
template_name = 'chrono/manager_agenda_form.html' |
|
3506 |
form_class = SharedCustodyHolidayRuleForm |
|
3507 |
model = SharedCustodyHolidayRule |
|
3508 | ||
3509 | ||
3510 |
shared_custody_agenda_add_holiday_rule = SharedCustodyAgendaAddHolidayRuleView.as_view() |
|
3511 | ||
3512 | ||
3513 |
class SharedCustodyAgendaEditHolidayRuleView(SharedCustodyAgendaMixin, UpdateView): |
|
3514 |
title = _('Edit custody rule during holidays') |
|
3515 |
template_name = 'chrono/manager_agenda_form.html' |
|
3516 |
form_class = SharedCustodyHolidayRuleForm |
|
3517 |
model = SharedCustodyHolidayRule |
|
3518 |
pk_url_kwarg = 'rule_pk' |
|
3519 | ||
3520 | ||
3521 |
shared_custody_agenda_edit_holiday_rule = SharedCustodyAgendaEditHolidayRuleView.as_view() |
|
3522 | ||
3523 | ||
3524 |
class SharedCustodyAgendaDeleteHolidayRuleView(SharedCustodyAgendaMixin, DeleteView): |
|
3525 |
template_name = 'chrono/manager_confirm_delete.html' |
|
3526 |
model = SharedCustodyHolidayRule |
|
3527 |
pk_url_kwarg = 'rule_pk' |
|
3528 | ||
3529 | ||
3530 |
shared_custody_agenda_delete_holiday_rule = SharedCustodyAgendaDeleteHolidayRuleView.as_view() |
|
3531 | ||
3532 | ||
3495 | 3533 |
class SharedCustodyAgendaAddPeriodView(SharedCustodyAgendaMixin, CreateView): |
3496 | 3534 |
title = _('Add custody period') |
3497 | 3535 |
template_name = 'chrono/manager_agenda_form.html' |
tests/api/test_datetimes.py | ||
---|---|---|
15 | 15 |
Event, |
16 | 16 |
Person, |
17 | 17 |
SharedCustodyAgenda, |
18 |
SharedCustodyHolidayRule, |
|
18 | 19 |
SharedCustodyPeriod, |
19 | 20 |
SharedCustodyRule, |
20 | 21 |
Subscription, |
21 | 22 |
TimePeriodException, |
23 |
TimePeriodExceptionGroup, |
|
24 |
UnavailabilityCalendar, |
|
22 | 25 |
) |
23 | 26 |
from tests.utils import login |
24 | 27 | |
... | ... | |
2662 | 2665 |
assert [d['id'] for d in resp.json['data']] == [ |
2663 | 2666 |
'first-agenda@event-wednesday--2022-03-09-1400', |
2664 | 2667 |
] |
2668 | ||
2669 | ||
2670 |
@pytest.mark.freeze_time('2021-12-13 14:00') # Monday of 50th week |
|
2671 |
def test_datetimes_multiple_agendas_shared_custody_holiday_rules(app): |
|
2672 |
agenda = Agenda.objects.create(label='First agenda', kind='events') |
|
2673 |
Desk.objects.create(agenda=agenda, slug='_exceptions_holder') |
|
2674 |
Event.objects.create( |
|
2675 |
slug='event-wednesday', |
|
2676 |
start_datetime=make_aware(datetime.datetime(year=2021, month=12, day=25, hour=14, minute=0)), |
|
2677 |
places=5, |
|
2678 |
agenda=agenda, |
|
2679 |
) |
|
2680 |
Subscription.objects.create( |
|
2681 |
agenda=agenda, |
|
2682 |
user_external_id='child_id', |
|
2683 |
date_start=now(), |
|
2684 |
date_end=now() + datetime.timedelta(days=14), |
|
2685 |
) |
|
2686 | ||
2687 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
|
2688 |
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe') |
|
2689 |
child = Person.objects.create(user_external_id='child_id', name='James Doe') |
|
2690 |
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother) |
|
2691 |
agenda.children.add(child) |
|
2692 | ||
2693 |
SharedCustodyRule.objects.create(agenda=agenda, days=list(range(7)), weeks='even', guardian=father) |
|
2694 |
SharedCustodyRule.objects.create(agenda=agenda, days=list(range(7)), weeks='odd', guardian=mother) |
|
2695 | ||
2696 |
resp = app.get( |
|
2697 |
'/api/agendas/datetimes/', |
|
2698 |
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, |
|
2699 |
) |
|
2700 |
assert len(resp.json['data']) == 0 |
|
2701 | ||
2702 |
resp = app.get( |
|
2703 |
'/api/agendas/datetimes/', |
|
2704 |
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, |
|
2705 |
) |
|
2706 |
assert len(resp.json['data']) == 1 |
|
2707 | ||
2708 |
# add father custody during holidays |
|
2709 |
calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
2710 |
christmas_holiday = TimePeriodExceptionGroup.objects.create( |
|
2711 |
unavailability_calendar=calendar, label='Christmas', slug='christmas' |
|
2712 |
) |
|
2713 |
TimePeriodException.objects.create( |
|
2714 |
unavailability_calendar=calendar, |
|
2715 |
start_datetime=make_aware(datetime.datetime(year=2021, month=12, day=18, hour=0, minute=0)), |
|
2716 |
end_datetime=make_aware(datetime.datetime(year=2022, month=1, day=3, hour=0, minute=0)), |
|
2717 |
group=christmas_holiday, |
|
2718 |
) |
|
2719 | ||
2720 |
rule = SharedCustodyHolidayRule.objects.create(agenda=agenda, guardian=father, holiday=christmas_holiday) |
|
2721 |
rule.update_or_create_periods() |
|
2722 | ||
2723 |
resp = app.get( |
|
2724 |
'/api/agendas/datetimes/', |
|
2725 |
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'father_id'}, |
|
2726 |
) |
|
2727 |
assert len(resp.json['data']) == 1 |
|
2728 | ||
2729 |
resp = app.get( |
|
2730 |
'/api/agendas/datetimes/', |
|
2731 |
params={'subscribed': 'all', 'user_external_id': 'child_id', 'guardian_external_id': 'mother_id'}, |
|
2732 |
) |
|
2733 |
assert len(resp.json['data']) == 0 |
tests/manager/test_shared_custody_agenda.py | ||
---|---|---|
1 | 1 |
import datetime |
2 | 2 | |
3 | 3 |
import pytest |
4 | ||
5 |
from chrono.agendas.models import Person, SharedCustodyAgenda, SharedCustodyPeriod, SharedCustodyRule |
|
4 |
from django.core.files.base import ContentFile |
|
5 | ||
6 |
from chrono.agendas.models import ( |
|
7 |
Person, |
|
8 |
SharedCustodyAgenda, |
|
9 |
SharedCustodyHolidayRule, |
|
10 |
SharedCustodyPeriod, |
|
11 |
SharedCustodyRule, |
|
12 |
TimePeriodExceptionGroup, |
|
13 |
UnavailabilityCalendar, |
|
14 |
) |
|
6 | 15 |
from tests.utils import login |
7 | 16 | |
8 | 17 |
pytestmark = pytest.mark.django_db |
9 | 18 | |
10 | 19 | |
20 |
with open('tests/data/holidays.ics') as f: |
|
21 |
ICS_HOLIDAYS = f.read() |
|
22 | ||
23 | ||
11 | 24 |
@pytest.mark.freeze_time('2022-02-22 14:00') # Tuesday |
12 | 25 |
def test_shared_custody_agenda_settings_rules(app, admin_user): |
13 | 26 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
... | ... | |
153 | 166 |
assert resp.text == old_resp.text |
154 | 167 | |
155 | 168 |
app.get('/manage/shared-custody/%s/42/42/' % agenda.pk, status=404) |
169 | ||
170 | ||
171 |
def test_shared_custody_agenda_holiday_rules(app, admin_user): |
|
172 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
|
173 |
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe') |
|
174 |
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother) |
|
175 | ||
176 |
app = login(app) |
|
177 |
resp = app.get('/manage/shared-custody/%s/settings/' % agenda.pk) |
|
178 |
assert 'Add custody rule during holidays' not in resp.text |
|
179 |
assert 'Custody rules during holidays' not in resp.text |
|
180 | ||
181 |
# configure holidays |
|
182 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar', slug='chrono-holidays') |
|
183 |
source = unavailability_calendar.timeperiodexceptionsource_set.create( |
|
184 |
ics_filename='holidays.ics', ics_file=ContentFile(ICS_HOLIDAYS, name='holidays.ics') |
|
185 |
) |
|
186 |
source.refresh_timeperiod_exceptions_from_ics() |
|
187 | ||
188 |
resp = app.get('/manage/shared-custody/%s/settings/' % agenda.pk) |
|
189 |
resp = resp.click('Add custody rule during holidays') |
|
190 |
resp.form['guardian'] = father.pk |
|
191 |
resp.form['holiday'].select(text='Vacances de Noël') |
|
192 |
resp.form['years'] = 'odd' |
|
193 |
resp.form['periodicity'] = 'first-half' |
|
194 |
resp = resp.form.submit().follow() |
|
195 |
assert SharedCustodyHolidayRule.objects.count() == 1 |
|
196 |
assert SharedCustodyPeriod.objects.count() == 3 |
|
197 |
assert 'This agenda doesn\'t have any custody period.' in resp.text |
|
198 | ||
199 |
resp = resp.click('John Doe, Vacances de Noël, the first half, on odd years') |
|
200 |
resp.form['years'] = '' |
|
201 |
resp = resp.form.submit().follow() |
|
202 |
assert 'John Doe, Vacances de Noël, the first half' in resp.text |
|
203 |
assert SharedCustodyHolidayRule.objects.count() == 1 |
|
204 |
assert SharedCustodyPeriod.objects.count() == 6 |
|
205 | ||
206 |
resp = resp.click('Add custody rule during holidays') |
|
207 |
resp.form['guardian'] = mother.pk |
|
208 |
resp.form['holiday'].select(text='Vacances de Noël') |
|
209 |
resp.form['periodicity'] = 'first-half' |
|
210 |
resp = resp.form.submit() |
|
211 |
assert 'Rule overlaps existing rules.' in resp.text |
|
212 | ||
213 |
resp.form['periodicity'] = 'second-half' |
|
214 |
resp = resp.form.submit().follow() |
|
215 |
assert 'Jane Doe, Vacances de Noël, the second half' in resp.text |
|
216 | ||
217 |
assert SharedCustodyHolidayRule.objects.count() == 2 |
|
218 |
assert SharedCustodyPeriod.objects.count() == 12 |
|
219 | ||
220 |
resp = resp.click('remove', index=1) |
|
221 |
resp = resp.form.submit().follow() |
|
222 |
assert SharedCustodyHolidayRule.objects.count() == 1 |
|
223 |
assert SharedCustodyPeriod.objects.count() == 6 |
|
224 | ||
225 |
resp = resp.click('Add custody rule during holidays') |
|
226 |
resp.form['guardian'] = father.pk |
|
227 |
resp.form['holiday'].select(text='Vacances de Noël') |
|
228 |
resp.form['periodicity'] = 'first-and-third-quarters' |
|
229 |
resp = resp.form.submit() |
|
230 |
assert 'Short holidays cannot be cut into quarters.' in resp.text |
|
231 | ||
232 |
resp.form['holiday'].select(text='Vacances d’Été') |
|
233 |
resp = resp.form.submit().follow() |
|
234 |
assert 'John Doe, Vacances d’Été, the first and third quarters' in resp.text |
|
235 | ||
236 |
# if dates get deleted, rules still exist but holiday is not shown anymore |
|
237 |
summer_holidays = TimePeriodExceptionGroup.objects.get(slug='summer_holidays') |
|
238 |
summer_holidays.exceptions.all().delete() |
|
239 | ||
240 |
assert SharedCustodyHolidayRule.objects.filter(holiday=summer_holidays).exists() |
|
241 | ||
242 |
resp = resp.click('Add custody rule during holidays') |
|
243 |
assert [x[2] for x in resp.form['holiday'].options] == ['---------', 'Vacances de Noël'] |
tests/test_agendas.py | ||
---|---|---|
27 | 27 |
Person, |
28 | 28 |
Resource, |
29 | 29 |
SharedCustodyAgenda, |
30 |
SharedCustodyHolidayRule, |
|
30 | 31 |
SharedCustodyPeriod, |
31 | 32 |
SharedCustodyRule, |
32 | 33 |
TimePeriod, |
... | ... | |
2906 | 2907 |
assert agenda.rule_overlaps(days, weeks) is overlaps |
2907 | 2908 | |
2908 | 2909 | |
2910 |
def test_shared_custody_agenda_holiday_rule_overlaps(): |
|
2911 |
calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
2912 | ||
2913 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
|
2914 |
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe') |
|
2915 |
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother) |
|
2916 | ||
2917 |
summer_holiday = TimePeriodExceptionGroup.objects.create( |
|
2918 |
unavailability_calendar=calendar, label='Summer', slug='summer' |
|
2919 |
) |
|
2920 |
winter_holiday = TimePeriodExceptionGroup.objects.create( |
|
2921 |
unavailability_calendar=calendar, label='Winter', slug='winter' |
|
2922 |
) |
|
2923 | ||
2924 |
rule = SharedCustodyHolidayRule.objects.create(agenda=agenda, guardian=father, holiday=summer_holiday) |
|
2925 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='', periodicity='') is True |
|
2926 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='', periodicity='', instance=rule) is False |
|
2927 |
assert agenda.holiday_rule_overlaps(winter_holiday, years='', periodicity='') is False |
|
2928 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='odd', periodicity='') is True |
|
2929 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='', periodicity='first-half') is True |
|
2930 | ||
2931 |
rule.years = 'odd' |
|
2932 |
rule.save() |
|
2933 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='', periodicity='') is True |
|
2934 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='odd', periodicity='') is True |
|
2935 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='even', periodicity='') is False |
|
2936 | ||
2937 |
rule.periodicity = 'first-half' |
|
2938 |
rule.save() |
|
2939 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='', periodicity='') is True |
|
2940 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='odd', periodicity='first-half') is True |
|
2941 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='odd', periodicity='second-half') is False |
|
2942 |
assert ( |
|
2943 |
agenda.holiday_rule_overlaps(summer_holiday, years='odd', periodicity='first-and-third-quarters') |
|
2944 |
is True |
|
2945 |
) |
|
2946 |
assert ( |
|
2947 |
agenda.holiday_rule_overlaps(summer_holiday, years='odd', periodicity='second-and-fourth-quarters') |
|
2948 |
is True |
|
2949 |
) |
|
2950 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='even', periodicity='first-half') is False |
|
2951 | ||
2952 |
rule.periodicity = 'second-and-fourth-quarters' |
|
2953 |
rule.save() |
|
2954 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='', periodicity='') is True |
|
2955 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='odd', periodicity='first-half') is True |
|
2956 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='odd', periodicity='second-half') is True |
|
2957 |
assert ( |
|
2958 |
agenda.holiday_rule_overlaps(summer_holiday, years='odd', periodicity='first-and-third-quarters') |
|
2959 |
is False |
|
2960 |
) |
|
2961 |
assert ( |
|
2962 |
agenda.holiday_rule_overlaps(summer_holiday, years='odd', periodicity='second-and-fourth-quarters') |
|
2963 |
is True |
|
2964 |
) |
|
2965 |
assert agenda.holiday_rule_overlaps(summer_holiday, years='even', periodicity='first-half') is False |
|
2966 | ||
2967 | ||
2909 | 2968 |
@pytest.mark.parametrize( |
2910 | 2969 |
'periods,date_start,date_end,overlaps', |
2911 | 2970 |
( |
... | ... | |
2934 | 2993 |
assert agenda.period_overlaps(date_start, date_end) is overlaps |
2935 | 2994 | |
2936 | 2995 | |
2996 |
def test_shared_custody_agenda_period_holiday_rule_no_overlaps(): |
|
2997 |
calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
2998 | ||
2999 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
|
3000 |
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe') |
|
3001 |
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother) |
|
3002 | ||
3003 |
summer_holiday = TimePeriodExceptionGroup.objects.create( |
|
3004 |
unavailability_calendar=calendar, label='Summer', slug='summer' |
|
3005 |
) |
|
3006 | ||
3007 |
rule = SharedCustodyHolidayRule.objects.create(agenda=agenda, guardian=father, holiday=summer_holiday) |
|
3008 |
SharedCustodyPeriod.objects.create( |
|
3009 |
holiday_rule=rule, agenda=agenda, guardian=father, date_start='2022-02-03', date_end='2022-02-05' |
|
3010 |
) |
|
3011 | ||
3012 |
assert agenda.period_overlaps('2022-02-03', '2022-02-05') is False |
|
3013 | ||
3014 | ||
2937 | 3015 |
def test_shared_custody_agenda_rule_label(): |
2938 | 3016 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
2939 | 3017 |
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe') |
... | ... | |
2964 | 3042 |
assert rule.label == 'on Mondays, on odd weeks' |
2965 | 3043 | |
2966 | 3044 | |
3045 |
def test_shared_custody_agenda_holiday_rule_label(): |
|
3046 |
calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
3047 | ||
3048 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
|
3049 |
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe') |
|
3050 |
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother) |
|
3051 | ||
3052 |
summer_holiday = TimePeriodExceptionGroup.objects.create( |
|
3053 |
unavailability_calendar=calendar, label='Summer Holidays', slug='summer' |
|
3054 |
) |
|
3055 | ||
3056 |
rule = SharedCustodyHolidayRule.objects.create(agenda=agenda, guardian=father, holiday=summer_holiday) |
|
3057 |
assert rule.label == 'Summer Holidays' |
|
3058 | ||
3059 |
rule.years = 'even' |
|
3060 |
rule.save() |
|
3061 |
assert rule.label == 'Summer Holidays, on even years' |
|
3062 | ||
3063 |
rule.years = 'odd' |
|
3064 |
rule.save() |
|
3065 |
assert rule.label == 'Summer Holidays, on odd years' |
|
3066 | ||
3067 |
rule.periodicity = 'first-half' |
|
3068 |
rule.save() |
|
3069 |
assert rule.label == 'Summer Holidays, the first half, on odd years' |
|
3070 | ||
3071 |
rule.years = '' |
|
3072 |
rule.periodicity = 'second-half' |
|
3073 |
rule.save() |
|
3074 |
assert rule.label == 'Summer Holidays, the second half' |
|
3075 | ||
3076 |
rule.periodicity = 'first-and-third-quarters' |
|
3077 |
rule.save() |
|
3078 |
assert rule.label == 'Summer Holidays, the first and third quarters' |
|
3079 | ||
3080 |
rule.periodicity = 'second-and-fourth-quarters' |
|
3081 |
rule.save() |
|
3082 |
assert rule.label == 'Summer Holidays, the second and fourth quarters' |
|
3083 | ||
3084 | ||
2967 | 3085 |
def test_shared_custody_agenda_period_label(freezer): |
2968 | 3086 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
2969 | 3087 |
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe') |
... | ... | |
2980 | 3098 |
period.date_end = datetime.date(2021, 7, 13) |
2981 | 3099 |
period.save() |
2982 | 3100 |
assert str(period) == 'John Doe, 07/10/2021 → 07/13/2021' |
3101 | ||
3102 | ||
3103 |
def test_shared_custody_agenda_holiday_rule_create_periods(): |
|
3104 |
calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
3105 | ||
3106 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
|
3107 |
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe') |
|
3108 |
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother) |
|
3109 | ||
3110 |
summer_holiday = TimePeriodExceptionGroup.objects.create( |
|
3111 |
unavailability_calendar=calendar, label='Summer', slug='summer' |
|
3112 |
) |
|
3113 |
TimePeriodException.objects.create( |
|
3114 |
unavailability_calendar=calendar, |
|
3115 |
start_datetime=make_aware(datetime.datetime(year=2021, month=7, day=6, hour=0, minute=0)), |
|
3116 |
end_datetime=make_aware(datetime.datetime(year=2021, month=9, day=2, hour=0, minute=0)), |
|
3117 |
group=summer_holiday, |
|
3118 |
) |
|
3119 |
TimePeriodException.objects.create( |
|
3120 |
unavailability_calendar=calendar, |
|
3121 |
start_datetime=make_aware(datetime.datetime(year=2022, month=7, day=7, hour=0, minute=0)), |
|
3122 |
end_datetime=make_aware(datetime.datetime(year=2022, month=9, day=1, hour=0, minute=0)), |
|
3123 |
group=summer_holiday, |
|
3124 |
) |
|
3125 | ||
3126 |
rule = SharedCustodyHolidayRule.objects.create(agenda=agenda, guardian=father, holiday=summer_holiday) |
|
3127 |
rule.update_or_create_periods() |
|
3128 |
period1, period2 = SharedCustodyPeriod.objects.all() |
|
3129 |
assert period1.holiday_rule == rule |
|
3130 |
assert period1.guardian == father |
|
3131 |
assert period1.agenda == agenda |
|
3132 |
assert period1.date_start == datetime.date(year=2021, month=7, day=6) |
|
3133 |
assert period1.date_end == datetime.date(year=2021, month=9, day=2) |
|
3134 |
assert period2.holiday_rule == rule |
|
3135 |
assert period2.guardian == father |
|
3136 |
assert period2.agenda == agenda |
|
3137 |
assert period2.date_start == datetime.date(year=2022, month=7, day=7) |
|
3138 |
assert period2.date_end == datetime.date(year=2022, month=9, day=1) |
|
3139 | ||
3140 |
rule.years = 'odd' |
|
3141 |
rule.update_or_create_periods() |
|
3142 |
period = SharedCustodyPeriod.objects.get() |
|
3143 |
assert period.date_start == datetime.date(year=2021, month=7, day=6) |
|
3144 |
assert period.date_end == datetime.date(year=2021, month=9, day=2) |
|
3145 | ||
3146 |
rule.years = 'even' |
|
3147 |
rule.update_or_create_periods() |
|
3148 |
period = SharedCustodyPeriod.objects.get() |
|
3149 |
assert period.date_start == datetime.date(year=2022, month=7, day=7) |
|
3150 |
assert period.date_end == datetime.date(year=2022, month=9, day=1) |
|
3151 | ||
3152 |
rule.years = '' |
|
3153 |
rule.periodicity = 'first-half' |
|
3154 |
rule.update_or_create_periods() |
|
3155 |
period1, period2 = SharedCustodyPeriod.objects.all() |
|
3156 |
assert period1.date_start == datetime.date(year=2021, month=7, day=6) |
|
3157 |
assert period1.date_end == datetime.date(year=2021, month=8, day=1) |
|
3158 |
assert period1.date_end.weekday() == 6 |
|
3159 |
assert period2.date_start == datetime.date(year=2022, month=7, day=7) |
|
3160 |
assert period2.date_end == datetime.date(year=2022, month=7, day=31) |
|
3161 |
assert period2.date_end.weekday() == 6 |
|
3162 | ||
3163 |
rule.periodicity = 'second-half' |
|
3164 |
rule.update_or_create_periods() |
|
3165 |
period1, period2 = SharedCustodyPeriod.objects.all() |
|
3166 |
assert period1.date_start == datetime.date(year=2021, month=8, day=1) |
|
3167 |
assert period1.date_start.weekday() == 6 |
|
3168 |
assert period1.date_end == datetime.date(year=2021, month=9, day=2) |
|
3169 |
assert period2.date_start == datetime.date(year=2022, month=7, day=31) |
|
3170 |
assert period2.date_start.weekday() == 6 |
|
3171 |
assert period2.date_end == datetime.date(year=2022, month=9, day=1) |
|
3172 | ||
3173 |
rule.periodicity = 'first-and-third-quarters' |
|
3174 |
rule.update_or_create_periods() |
|
3175 |
period1, period2, period3, period4 = SharedCustodyPeriod.objects.all() |
|
3176 |
assert period1.date_start == datetime.date(year=2021, month=7, day=6) |
|
3177 |
assert period1.date_end == datetime.date(year=2021, month=7, day=25) |
|
3178 |
assert period1.date_end.weekday() == 6 |
|
3179 |
assert period2.date_start == datetime.date(year=2021, month=8, day=8) |
|
3180 |
assert period2.date_end == datetime.date(year=2021, month=8, day=22) |
|
3181 |
assert period2.date_end.weekday() == 6 |
|
3182 |
assert period3.date_start == datetime.date(year=2022, month=7, day=7) |
|
3183 |
assert period3.date_end == datetime.date(year=2022, month=7, day=24) |
|
3184 |
assert period3.date_end.weekday() == 6 |
|
3185 |
assert period4.date_start == datetime.date(year=2022, month=8, day=7) |
|
3186 |
assert period4.date_end == datetime.date(year=2022, month=8, day=21) |
|
3187 |
assert period4.date_end.weekday() == 6 |
|
3188 | ||
3189 |
rule.periodicity = 'second-and-fourth-quarters' |
|
3190 |
rule.update_or_create_periods() |
|
3191 |
period1, period2, period3, period4 = SharedCustodyPeriod.objects.all() |
|
3192 |
assert period1.date_start == datetime.date(year=2021, month=7, day=25) |
|
3193 |
assert period1.date_start.weekday() == 6 |
|
3194 |
assert period1.date_end == datetime.date(year=2021, month=8, day=8) |
|
3195 |
assert period2.date_start == datetime.date(year=2021, month=8, day=22) |
|
3196 |
assert period2.date_start.weekday() == 6 |
|
3197 |
assert period2.date_end == datetime.date(year=2021, month=9, day=2) |
|
3198 |
assert period3.date_start == datetime.date(year=2022, month=7, day=24) |
|
3199 |
assert period3.date_start.weekday() == 6 |
|
3200 |
assert period3.date_end == datetime.date(year=2022, month=8, day=7) |
|
3201 |
assert period4.date_start == datetime.date(year=2022, month=8, day=21) |
|
3202 |
assert period4.date_start.weekday() == 6 |
|
3203 |
assert period4.date_end == datetime.date(year=2022, month=9, day=1) |
|
3204 | ||
3205 | ||
3206 |
def test_shared_custody_agenda_holiday_rule_create_periods_christmas_holidays(): |
|
3207 |
calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
3208 | ||
3209 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
|
3210 |
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe') |
|
3211 |
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother) |
|
3212 | ||
3213 |
christmas_holiday = TimePeriodExceptionGroup.objects.create( |
|
3214 |
unavailability_calendar=calendar, label='Christmas', slug='christmas' |
|
3215 |
) |
|
3216 |
TimePeriodException.objects.create( |
|
3217 |
unavailability_calendar=calendar, |
|
3218 |
start_datetime=make_aware(datetime.datetime(year=2021, month=12, day=18, hour=0, minute=0)), |
|
3219 |
end_datetime=make_aware(datetime.datetime(year=2022, month=1, day=3, hour=0, minute=0)), |
|
3220 |
group=christmas_holiday, |
|
3221 |
) |
|
3222 |
TimePeriodException.objects.create( |
|
3223 |
unavailability_calendar=calendar, |
|
3224 |
start_datetime=make_aware(datetime.datetime(year=2022, month=12, day=17, hour=0, minute=0)), |
|
3225 |
end_datetime=make_aware(datetime.datetime(year=2023, month=1, day=3, hour=0, minute=0)), |
|
3226 |
group=christmas_holiday, |
|
3227 |
) |
|
3228 | ||
3229 |
rule = SharedCustodyHolidayRule.objects.create(agenda=agenda, guardian=father, holiday=christmas_holiday) |
|
3230 |
rule.update_or_create_periods() |
|
3231 |
period1, period2 = SharedCustodyPeriod.objects.all() |
|
3232 |
assert period1.date_start == datetime.date(year=2021, month=12, day=18) |
|
3233 |
assert period1.date_end == datetime.date(year=2022, month=1, day=3) |
|
3234 |
assert period2.date_start == datetime.date(year=2022, month=12, day=17) |
|
3235 |
assert period2.date_end == datetime.date(year=2023, month=1, day=3) |
|
3236 | ||
3237 |
rule.periodicity = 'first-half' |
|
3238 |
rule.update_or_create_periods() |
|
3239 |
period1, period2 = SharedCustodyPeriod.objects.all() |
|
3240 |
assert period1.date_start == datetime.date(year=2021, month=12, day=18) |
|
3241 |
assert period1.date_end == datetime.date(year=2021, month=12, day=26) |
|
3242 |
assert period1.date_end.weekday() == 6 |
|
3243 |
assert period2.date_start == datetime.date(year=2022, month=12, day=17) |
|
3244 |
assert period2.date_end == datetime.date(year=2022, month=12, day=25) |
|
3245 |
assert period2.date_end.weekday() == 6 |
|
3246 | ||
3247 |
rule.periodicity = 'second-half' |
|
3248 |
rule.update_or_create_periods() |
|
3249 |
period1, period2 = SharedCustodyPeriod.objects.all() |
|
3250 |
assert period1.date_start == datetime.date(year=2021, month=12, day=26) |
|
3251 |
assert period1.date_start.weekday() == 6 |
|
3252 |
assert period1.date_end == datetime.date(year=2022, month=1, day=3) |
|
3253 |
assert period2.date_start == datetime.date(year=2022, month=12, day=25) |
|
3254 |
assert period2.date_start.weekday() == 6 |
|
3255 |
assert period2.date_end == datetime.date(year=2023, month=1, day=3) |
|
3256 | ||
3257 |
rule.periodicity = 'first-and-third-quarters' |
|
3258 |
rule.update_or_create_periods() |
|
3259 |
assert not SharedCustodyPeriod.objects.exists() |
|
3260 | ||
3261 |
rule.periodicity = 'second-and-fourth-quarters' |
|
3262 |
rule.update_or_create_periods() |
|
3263 |
assert not SharedCustodyPeriod.objects.exists() |
|
3264 | ||
3265 | ||
3266 |
def test_shared_custody_agenda_holiday_rules_application(): |
|
3267 |
calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
3268 | ||
3269 |
father = Person.objects.create(user_external_id='father_id', name='John Doe') |
|
3270 |
mother = Person.objects.create(user_external_id='mother_id', name='Jane Doe') |
|
3271 |
agenda = SharedCustodyAgenda.objects.create(first_guardian=father, second_guardian=mother) |
|
3272 | ||
3273 |
christmas_holiday = TimePeriodExceptionGroup.objects.create( |
|
3274 |
unavailability_calendar=calendar, label='Christmas', slug='christmas' |
|
3275 |
) |
|
3276 |
TimePeriodException.objects.create( |
|
3277 |
unavailability_calendar=calendar, |
|
3278 |
start_datetime=make_aware(datetime.datetime(year=2021, month=12, day=18, hour=0, minute=0)), |
|
3279 |
end_datetime=make_aware(datetime.datetime(year=2022, month=1, day=3, hour=0, minute=0)), |
|
3280 |
group=christmas_holiday, |
|
3281 |
) |
|
3282 | ||
3283 |
SharedCustodyRule.objects.create(agenda=agenda, days=list(range(7)), weeks='even', guardian=father) |
|
3284 |
SharedCustodyRule.objects.create(agenda=agenda, days=list(range(7)), weeks='odd', guardian=mother) |
|
3285 | ||
3286 |
rule = SharedCustodyHolidayRule.objects.create(agenda=agenda, guardian=father, holiday=christmas_holiday) |
|
3287 |
rule.update_or_create_periods() |
|
3288 | ||
3289 |
date_start = datetime.date(year=2021, month=12, day=13) # Monday, even week |
|
3290 |
slots = agenda.get_custody_slots(date_start, date_start + datetime.timedelta(days=30)) |
|
3291 |
guardians = [x.guardian.name for x in slots] |
|
3292 |
assert all(name == 'John Doe' for name in guardians[:21]) |
|
3293 |
assert all(name == 'Jane Doe' for name in guardians[21:28]) |
|
3294 |
assert all(name == 'John Doe' for name in guardians[28:]) |
|
2983 |
- |