0008-api-restrict-slots-with-exluded-timeperiods-40058.patch
chrono/agendas/models.py | ||
---|---|---|
333 | 333 |
'end_time': self.end_time.strftime('%H:%M'), |
334 | 334 |
} |
335 | 335 | |
336 |
def get_effective_timeperiods(self, excluded_timeperiods): |
|
337 |
effective_timeperiods = [self] |
|
338 |
for excluded_timeperiod in excluded_timeperiods: |
|
339 |
res = [] |
|
340 |
for effective_timeperiod in effective_timeperiods: |
|
341 |
if ( |
|
342 |
excluded_timeperiod.weekday != effective_timeperiod.weekday |
|
343 |
or excluded_timeperiod.start_time >= effective_timeperiod.end_time |
|
344 |
or excluded_timeperiod.end_time <= effective_timeperiod.start_time |
|
345 |
): |
|
346 |
res.append(effective_timeperiod) |
|
347 |
continue |
|
348 |
if ( |
|
349 |
excluded_timeperiod.start_time <= effective_timeperiod.start_time |
|
350 |
and excluded_timeperiod.end_time >= effective_timeperiod.end_time |
|
351 |
): |
|
352 |
# completely exclude |
|
353 |
continue |
|
354 |
if excluded_timeperiod.start_time > effective_timeperiod.start_time: |
|
355 |
res.append( |
|
356 |
TimePeriod( |
|
357 |
weekday=effective_timeperiod.weekday, |
|
358 |
start_time=effective_timeperiod.start_time, |
|
359 |
end_time=excluded_timeperiod.start_time, |
|
360 |
desk=effective_timeperiod.desk, |
|
361 |
) |
|
362 |
) |
|
363 |
if excluded_timeperiod.end_time < effective_timeperiod.end_time: |
|
364 |
res.append( |
|
365 |
TimePeriod( |
|
366 |
weekday=effective_timeperiod.weekday, |
|
367 |
start_time=excluded_timeperiod.end_time, |
|
368 |
end_time=effective_timeperiod.end_time, |
|
369 |
desk=effective_timeperiod.desk, |
|
370 |
) |
|
371 |
) |
|
372 | ||
373 |
effective_timeperiods = res |
|
374 | ||
375 |
return effective_timeperiods |
|
376 | ||
336 | 377 |
def get_time_slots(self, min_datetime, max_datetime, meeting_type): |
337 | 378 |
meeting_duration = datetime.timedelta(minutes=meeting_type.duration) |
338 | 379 |
duration = datetime.timedelta(minutes=self.desk.agenda.get_base_meeting_duration()) |
chrono/api/views.py | ||
---|---|---|
76 | 76 |
base_date = now().date() |
77 | 77 | |
78 | 78 |
agendas = agenda.get_real_agendas() |
79 |
base_agenda = agenda |
|
79 | 80 | |
80 | 81 |
open_slots = {} |
81 | 82 |
for agenda in agendas: |
... | ... | |
88 | 89 |
if used_time_period_filters['max_datetime'] is None: |
89 | 90 |
used_time_period_filters['max_datetime'] = get_max_datetime(agenda) |
90 | 91 | |
91 |
for time_period in TimePeriod.objects.filter(desk__agenda=agenda): |
|
92 |
duration = ( |
|
93 |
datetime.datetime.combine(base_date, time_period.end_time) |
|
94 |
- datetime.datetime.combine(base_date, time_period.start_time) |
|
95 |
).seconds / 60 |
|
96 |
if duration < meeting_type.duration: |
|
97 |
# skip time period that can't even hold a single meeting |
|
98 |
continue |
|
99 |
for slot in time_period.get_time_slots(**used_time_period_filters): |
|
100 |
slot.full = False |
|
101 |
open_slots[agenda][time_period.desk_id].add(slot.start_datetime, slot.end_datetime, slot) |
|
92 |
for raw_time_period in TimePeriod.objects.filter(desk__agenda=agenda): |
|
93 |
for time_period in raw_time_period.get_effective_timeperiods( |
|
94 |
base_agenda.excluded_timeperiods.all() |
|
95 |
): |
|
96 |
duration = ( |
|
97 |
datetime.datetime.combine(base_date, time_period.end_time) |
|
98 |
- datetime.datetime.combine(base_date, time_period.start_time) |
|
99 |
).seconds / 60 |
|
100 |
if duration < meeting_type.duration: |
|
101 |
# skip time period that can't even hold a single meeting |
|
102 |
continue |
|
103 |
for slot in time_period.get_time_slots(**used_time_period_filters): |
|
104 |
slot.full = False |
|
105 |
open_slots[agenda][time_period.desk_id].add(slot.start_datetime, slot.end_datetime, slot) |
|
102 | 106 | |
103 | 107 |
# remove excluded slot |
104 | 108 |
for agenda in agendas: |
tests/test_agendas.py | ||
---|---|---|
16 | 16 |
Event, |
17 | 17 |
ICSError, |
18 | 18 |
MeetingType, |
19 |
TimePeriod, |
|
19 | 20 |
TimePeriodException, |
20 | 21 |
TimePeriodExceptionSource, |
21 | 22 |
VirtualMember, |
... | ... | |
594 | 595 |
meeting_type = MeetingType(agenda=agenda2, label='Bar', duration=60) |
595 | 596 |
meeting_type.save() |
596 | 597 |
assert virt_agenda.get_base_meeting_duration() == 60 |
598 | ||
599 | ||
600 |
def test_get_effective_timeperiods(): |
|
601 |
time_period = TimePeriod(weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)) |
|
602 |
# empty exclusion set |
|
603 |
effective_timeperiods = time_period.get_effective_timeperiods(TimePeriod.objects.none()) |
|
604 |
assert len(effective_timeperiods) == 1 |
|
605 |
effective_timeperiod = effective_timeperiods[0] |
|
606 |
assert effective_timeperiod.weekday == time_period.weekday |
|
607 |
assert effective_timeperiod.start_time == time_period.start_time |
|
608 |
assert effective_timeperiod.end_time == time_period.end_time |
|
609 | ||
610 |
# exclusions are on a different day |
|
611 |
excluded_timeperiods = [ |
|
612 |
TimePeriod(weekday=1, start_time=datetime.time(17, 0), end_time=datetime.time(18, 0)), |
|
613 |
TimePeriod(weekday=2, start_time=datetime.time(17, 0), end_time=datetime.time(18, 0)), |
|
614 |
] |
|
615 |
effective_timeperiods = time_period.get_effective_timeperiods(excluded_timeperiods) |
|
616 |
assert len(effective_timeperiods) == 1 |
|
617 |
effective_timeperiod = effective_timeperiods[0] |
|
618 |
assert effective_timeperiod.weekday == time_period.weekday |
|
619 |
assert effective_timeperiod.start_time == time_period.start_time |
|
620 |
assert effective_timeperiod.end_time == time_period.end_time |
|
621 | ||
622 |
# one exclusion, end_time should be earlier |
|
623 |
excluded_timeperiods = [ |
|
624 |
TimePeriod(weekday=0, start_time=datetime.time(17, 0), end_time=datetime.time(18, 0)) |
|
625 |
] |
|
626 |
effective_timeperiods = time_period.get_effective_timeperiods(excluded_timeperiods) |
|
627 |
assert len(effective_timeperiods) == 1 |
|
628 |
effective_timeperiod = effective_timeperiods[0] |
|
629 |
assert effective_timeperiod.weekday == time_period.weekday |
|
630 |
assert effective_timeperiod.start_time == datetime.time(10, 0) |
|
631 |
assert effective_timeperiod.end_time == datetime.time(17, 0) |
|
632 | ||
633 |
# one exclusion, start_time should be later |
|
634 |
excluded_timeperiods = [ |
|
635 |
TimePeriod(weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(16, 0)) |
|
636 |
] |
|
637 |
effective_timeperiods = time_period.get_effective_timeperiods(excluded_timeperiods) |
|
638 |
assert len(effective_timeperiods) == 1 |
|
639 |
effective_timeperiod = effective_timeperiods[0] |
|
640 |
assert effective_timeperiod.weekday == time_period.weekday |
|
641 |
assert effective_timeperiod.start_time == datetime.time(16, 0) |
|
642 |
assert effective_timeperiod.end_time == datetime.time(18, 0) |
|
643 | ||
644 |
# one exclusion, splits effective timeperiod in two |
|
645 |
excluded_timeperiods = [ |
|
646 |
TimePeriod(weekday=0, start_time=datetime.time(12, 0), end_time=datetime.time(16, 0)) |
|
647 |
] |
|
648 |
effective_timeperiods = time_period.get_effective_timeperiods(excluded_timeperiods) |
|
649 |
assert len(effective_timeperiods) == 2 |
|
650 |
effective_timeperiod = effective_timeperiods[0] |
|
651 |
assert effective_timeperiod.weekday == time_period.weekday |
|
652 |
assert effective_timeperiod.start_time == datetime.time(10, 0) |
|
653 |
assert effective_timeperiod.end_time == datetime.time(12, 0) |
|
654 |
effective_timeperiod = effective_timeperiods[1] |
|
655 |
assert effective_timeperiod.weekday == time_period.weekday |
|
656 |
assert effective_timeperiod.start_time == datetime.time(16, 0) |
|
657 |
assert effective_timeperiod.end_time == datetime.time(18, 0) |
|
658 | ||
659 |
# several exclusion, splits effective timeperiod into pieces |
|
660 |
excluded_timeperiods = [ |
|
661 |
TimePeriod(weekday=0, start_time=datetime.time(12, 0), end_time=datetime.time(13, 0)), |
|
662 |
TimePeriod(weekday=0, start_time=datetime.time(10, 30), end_time=datetime.time(11, 30)), |
|
663 |
TimePeriod(weekday=0, start_time=datetime.time(16, 30), end_time=datetime.time(17, 00)), |
|
664 |
] |
|
665 |
effective_timeperiods = time_period.get_effective_timeperiods(excluded_timeperiods) |
|
666 |
assert len(effective_timeperiods) == 4 |
|
667 | ||
668 |
effective_timeperiod = effective_timeperiods[0] |
|
669 |
assert effective_timeperiod.weekday == time_period.weekday |
|
670 |
assert effective_timeperiod.start_time == datetime.time(10, 0) |
|
671 |
assert effective_timeperiod.end_time == datetime.time(10, 30) |
|
672 | ||
673 |
effective_timeperiod = effective_timeperiods[1] |
|
674 |
assert effective_timeperiod.weekday == time_period.weekday |
|
675 |
assert effective_timeperiod.start_time == datetime.time(11, 30) |
|
676 |
assert effective_timeperiod.end_time == datetime.time(12, 0) |
|
677 | ||
678 |
effective_timeperiod = effective_timeperiods[2] |
|
679 |
assert effective_timeperiod.weekday == time_period.weekday |
|
680 |
assert effective_timeperiod.start_time == datetime.time(13, 0) |
|
681 |
assert effective_timeperiod.end_time == datetime.time(16, 30) |
|
682 | ||
683 |
effective_timeperiod = effective_timeperiods[3] |
|
684 |
assert effective_timeperiod.weekday == time_period.weekday |
|
685 |
assert effective_timeperiod.start_time == datetime.time(17, 0) |
|
686 |
assert effective_timeperiod.end_time == datetime.time(18, 0) |
tests/test_api.py | ||
---|---|---|
2505 | 2505 |
assert len(resp.json['data']) == 12 |
2506 | 2506 | |
2507 | 2507 | |
2508 |
def test_virtual_agendas_meetings_datetimes_exluded_periods(app, mock_now): |
|
2509 |
foo_agenda = Agenda.objects.create(label='Foo Meeting', kind='meetings', maximal_booking_delay=7) |
|
2510 |
MeetingType.objects.create(agenda=foo_agenda, label='Meeting Type', duration=30) |
|
2511 |
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1') |
|
2512 |
TimePeriod.objects.create( |
|
2513 |
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1, |
|
2514 |
) |
|
2515 |
TimePeriod.objects.create( |
|
2516 |
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1, |
|
2517 |
) |
|
2518 |
virt_agenda = Agenda.objects.create(label='Virtual Agenda', kind='virtual') |
|
2519 |
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=foo_agenda) |
|
2520 | ||
2521 |
api_url = '/api/agenda/%s/meetings/meeting-type/datetimes/' % (virt_agenda.slug) |
|
2522 |
resp = app.get(api_url) |
|
2523 |
# 8 slots |
|
2524 |
data = resp.json['data'] |
|
2525 |
assert len(data) == 8 |
|
2526 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
2527 |
assert data[1]['datetime'] == '2017-05-22 10:30:00' |
|
2528 |
assert data[2]['datetime'] == '2017-05-22 11:00:00' |
|
2529 | ||
2530 |
# exclude one hour the first day |
|
2531 |
tp1 = TimePeriod.objects.create( |
|
2532 |
weekday=0, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0), agenda=virt_agenda |
|
2533 |
) |
|
2534 |
resp = app.get(api_url) |
|
2535 |
data = resp.json['data'] |
|
2536 |
assert len(data) == 6 |
|
2537 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
2538 |
assert data[1]['datetime'] == '2017-05-22 10:30:00' |
|
2539 |
# no more slots the 22 thanks to the exclusion period |
|
2540 |
assert data[2]['datetime'] == '2017-05-23 10:00:00' |
|
2541 | ||
2542 |
# exclude the second day |
|
2543 |
tp2 = TimePeriod.objects.create( |
|
2544 |
weekday=1, start_time=datetime.time(9, 0), end_time=datetime.time(18, 0), agenda=virt_agenda |
|
2545 |
) |
|
2546 |
resp = app.get(api_url) |
|
2547 |
data = resp.json['data'] |
|
2548 |
assert len(data) == 2 |
|
2549 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
2550 |
assert data[1]['datetime'] == '2017-05-22 10:30:00' |
|
2551 | ||
2552 |
# go back to no restriction |
|
2553 |
tp1.delete() |
|
2554 |
tp2.delete() |
|
2555 |
resp = app.get(api_url) |
|
2556 |
data = resp.json['data'] |
|
2557 |
assert len(data) == 8 |
|
2558 | ||
2559 |
# excluded period applies to every desk |
|
2560 |
foo_desk_2 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 2') |
|
2561 |
TimePeriod.objects.create( |
|
2562 |
weekday=3, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_2, |
|
2563 |
) |
|
2564 |
TimePeriod.objects.create( |
|
2565 |
weekday=4, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_2, |
|
2566 |
) |
|
2567 |
resp = app.get(api_url) |
|
2568 |
data = resp.json['data'] |
|
2569 |
assert len(data) == 16 |
|
2570 | ||
2571 |
# exclude one hour the first day |
|
2572 |
tp1 = TimePeriod.objects.create( |
|
2573 |
weekday=0, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0), agenda=virt_agenda |
|
2574 |
) |
|
2575 |
resp = app.get(api_url) |
|
2576 |
data = resp.json['data'] |
|
2577 |
assert len(data) == 14 |
|
2578 | ||
2579 |
# exclude one hour the last day |
|
2580 |
tp2 = TimePeriod.objects.create( |
|
2581 |
weekday=4, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0), agenda=virt_agenda |
|
2582 |
) |
|
2583 |
resp = app.get(api_url) |
|
2584 |
data = resp.json['data'] |
|
2585 |
assert len(data) == 12 |
|
2586 | ||
2587 |
# go back to no restriction |
|
2588 |
tp1.delete() |
|
2589 |
tp2.delete() |
|
2590 |
resp = app.get(api_url) |
|
2591 |
data = resp.json['data'] |
|
2592 |
assert len(data) == 16 |
|
2593 | ||
2594 |
# add a second real agenda |
|
2595 |
bar_agenda = Agenda.objects.create(label='Bar Meeting', kind='meetings', maximal_booking_delay=7) |
|
2596 |
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=bar_agenda) |
|
2597 |
MeetingType.objects.create(agenda=bar_agenda, label='Meeting Type', duration=30) |
|
2598 |
bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1') |
|
2599 |
bar_desk_2 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 2') |
|
2600 |
TimePeriod.objects.create( |
|
2601 |
weekday=0, start_time=datetime.time(14, 0), end_time=datetime.time(16, 0), desk=bar_desk_1, |
|
2602 |
) |
|
2603 |
TimePeriod.objects.create( |
|
2604 |
weekday=1, start_time=datetime.time(14, 0), end_time=datetime.time(16, 0), desk=bar_desk_1, |
|
2605 |
) |
|
2606 |
TimePeriod.objects.create( |
|
2607 |
weekday=2, start_time=datetime.time(14, 0), end_time=datetime.time(16, 0), desk=bar_desk_2, |
|
2608 |
) |
|
2609 |
TimePeriod.objects.create( |
|
2610 |
weekday=3, start_time=datetime.time(14, 0), end_time=datetime.time(16, 0), desk=bar_desk_2, |
|
2611 |
) |
|
2612 |
resp = app.get(api_url) |
|
2613 |
data = resp.json['data'] |
|
2614 |
assert len(data) == 32 |
|
2615 | ||
2616 |
# exclude the first day, 11 to 15 : 4 slots |
|
2617 |
tp1 = TimePeriod.objects.create( |
|
2618 |
weekday=0, start_time=datetime.time(11, 0), end_time=datetime.time(15, 0), agenda=virt_agenda |
|
2619 |
) |
|
2620 |
resp = app.get(api_url) |
|
2621 |
data = resp.json['data'] |
|
2622 |
assert len(data) == 28 |
|
2623 | ||
2624 | ||
2508 | 2625 |
def test_virtual_agendas_meetings_exception(app, user, virtual_meetings_agenda): |
2509 | 2626 |
app.authorization = ('Basic', ('john.doe', 'password')) |
2510 | 2627 |
real_agenda = virtual_meetings_agenda.real_agendas.first() |
2511 |
- |