general: exhaustively list available meeting datetimes (#19150)
This commit is contained in:
parent
b93098d4e6
commit
add27d0933
|
@ -16,6 +16,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
import fractions
|
||||
import requests
|
||||
import vobject
|
||||
|
||||
|
@ -96,6 +97,15 @@ class Agenda(models.Model):
|
|||
group_ids = [x.id for x in user.groups.all()]
|
||||
return bool(self.view_role_id in group_ids)
|
||||
|
||||
def get_base_meeting_duration(self):
|
||||
durations = [x.duration for x in MeetingType.objects.filter(agenda=self)]
|
||||
if not durations:
|
||||
raise ValueError()
|
||||
gcd = durations[0]
|
||||
for duration in durations[1:]:
|
||||
gcd = fractions.gcd(duration, gcd)
|
||||
return gcd
|
||||
|
||||
def export_json(self):
|
||||
agenda = {
|
||||
'label': self.label,
|
||||
|
@ -188,7 +198,8 @@ class TimePeriod(models.Model):
|
|||
}
|
||||
|
||||
def get_time_slots(self, min_datetime, max_datetime, meeting_type):
|
||||
duration = datetime.timedelta(minutes=meeting_type.duration)
|
||||
meeting_duration = datetime.timedelta(minutes=meeting_type.duration)
|
||||
duration = datetime.timedelta(minutes=self.desk.agenda.get_base_meeting_duration())
|
||||
min_datetime = make_naive(min_datetime)
|
||||
max_datetime = make_naive(max_datetime)
|
||||
|
||||
|
@ -200,19 +211,20 @@ class TimePeriod(models.Model):
|
|||
event_datetime = real_min_datetime.replace(hour=self.start_time.hour,
|
||||
minute=self.start_time.minute, second=0, microsecond=0)
|
||||
while event_datetime < max_datetime:
|
||||
end_time = event_datetime + duration
|
||||
end_time = event_datetime + meeting_duration
|
||||
next_time = event_datetime + duration
|
||||
if end_time.time() > self.end_time:
|
||||
# back to morning
|
||||
event_datetime = event_datetime.replace(hour=self.start_time.hour, minute=self.start_time.minute)
|
||||
# but next week
|
||||
event_datetime += datetime.timedelta(days=7)
|
||||
end_time = event_datetime + duration
|
||||
next_time = event_datetime + duration
|
||||
|
||||
if event_datetime > max_datetime:
|
||||
break
|
||||
|
||||
yield TimeSlot(start_datetime=make_aware(event_datetime), meeting_type=meeting_type, desk=self.desk)
|
||||
event_datetime = end_time
|
||||
event_datetime = next_time
|
||||
|
||||
|
||||
class MeetingType(models.Model):
|
||||
|
|
|
@ -10,7 +10,7 @@ from django.core.management import call_command
|
|||
from django.core.management.base import CommandError
|
||||
|
||||
from chrono.agendas.models import (Agenda, Event, Booking, MeetingType,
|
||||
Desk, TimePeriodException, ICSError)
|
||||
Desk, TimePeriod, TimePeriodException, ICSError)
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
@ -330,3 +330,22 @@ END:VCALENDAR"""
|
|||
mocked_get.return_value = mocked_response
|
||||
call_command('sync_desks_timeperiod_exceptions')
|
||||
assert not TimePeriodException.objects.filter(desk=desk).exists()
|
||||
|
||||
def test_base_meeting_duration():
|
||||
agenda = Agenda(label='Meeting', kind='meetings')
|
||||
agenda.save()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
agenda.get_base_meeting_duration()
|
||||
|
||||
meeting_type = MeetingType(agenda=agenda, label='Foo', duration=30)
|
||||
meeting_type.save()
|
||||
assert agenda.get_base_meeting_duration() == 30
|
||||
|
||||
meeting_type = MeetingType(agenda=agenda, label='Bar', duration=60)
|
||||
meeting_type.save()
|
||||
assert agenda.get_base_meeting_duration() == 30
|
||||
|
||||
meeting_type = MeetingType(agenda=agenda, label='Bar', duration=45)
|
||||
meeting_type.save()
|
||||
assert agenda.get_base_meeting_duration() == 15
|
||||
|
|
|
@ -1035,3 +1035,60 @@ def test_agenda_meeting_api_desk_info(app, meetings_agenda, user):
|
|||
resp = app.post(booking_url2)
|
||||
assert resp.json['desk']['label'] == desk.label
|
||||
assert resp.json['desk']['slug'] == desk.slug
|
||||
|
||||
|
||||
def test_agenda_meeting_gcd_durations(app, meetings_agenda, user):
|
||||
meetings_agenda.maximal_booking_delay = 8
|
||||
meetings_agenda.save()
|
||||
|
||||
time_period = TimePeriod.objects.get(end_time=datetime.time(12, 0))
|
||||
time_period.end_time = datetime.time(13, 0)
|
||||
time_period.save()
|
||||
|
||||
meeting_type_30 = MeetingType.objects.get(duration=30)
|
||||
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type_30.id)
|
||||
assert len(resp.json['data']) == 20
|
||||
|
||||
meeting_type_20 = MeetingType(agenda=meetings_agenda, label='Lorem', duration=20)
|
||||
meeting_type_20.save()
|
||||
|
||||
assert meetings_agenda.get_base_meeting_duration() == 10
|
||||
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type_30.id)
|
||||
assert len(resp.json['data']) == 56
|
||||
# 16:30 is time period end time (17:00) minus meeting type duration
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-23 16:30:00'
|
||||
|
||||
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type_20.id)
|
||||
assert len(resp.json['data']) == 58
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-23 16:40:00'
|
||||
|
||||
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type_30.id)
|
||||
event_id = resp.json['data'][0]['id']
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
app.post('/api/agenda/%s/fillslot/%s/' % (meetings_agenda.id, event_id))
|
||||
assert Booking.objects.count() == 1
|
||||
|
||||
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type_20.id)
|
||||
assert len([x for x in resp.json['data'] if not x.get('disabled')]) == 55
|
||||
event_id = [x for x in resp.json['data'] if not x.get('disabled')][0]['id']
|
||||
resp = app.post('/api/agenda/%s/fillslot/%s/' % (meetings_agenda.id, event_id))
|
||||
assert resp.json['datetime'].startswith('2017-05-22T10:30:00')
|
||||
assert Booking.objects.count() == 2
|
||||
|
||||
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type_30.id)
|
||||
event_id = [x for x in resp.json['data'] if not x.get('disabled')][0]['id']
|
||||
resp = app.post('/api/agenda/%s/fillslot/%s/' % (meetings_agenda.id, event_id))
|
||||
assert resp.json['datetime'].startswith('2017-05-22T10:50:00')
|
||||
assert Booking.objects.count() == 3
|
||||
|
||||
# create a gap
|
||||
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type_30.id)
|
||||
event_id = [x for x in resp.json['data'] if not x.get('disabled')][1]['id']
|
||||
resp = app.post('/api/agenda/%s/fillslot/%s/' % (meetings_agenda.id, event_id))
|
||||
assert resp.json['datetime'].startswith('2017-05-22T11:30:00')
|
||||
assert Booking.objects.count() == 4
|
||||
|
||||
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type_20.id)
|
||||
assert [x for x in resp.json['data'] if not x.get('disabled')][0]['datetime'].startswith('2017-05-22 12:00:00')
|
||||
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type_30.id)
|
||||
assert [x for x in resp.json['data'] if not x.get('disabled')][0]['datetime'].startswith('2017-05-22 12:00:00')
|
||||
|
|
|
@ -12,13 +12,15 @@ def test_timeperiod_time_slots():
|
|||
agenda = Agenda(label=u'Foo bar', slug='bar')
|
||||
agenda.save()
|
||||
desk = Desk.objects.create(label='Desk 1', agenda=agenda)
|
||||
meeting_type = MeetingType(duration=60, agenda=agenda)
|
||||
meeting_type.save()
|
||||
timeperiod = TimePeriod(desk=desk, weekday=0,
|
||||
start_time=datetime.time(9, 0),
|
||||
end_time=datetime.time(12, 0))
|
||||
events = timeperiod.get_time_slots(
|
||||
min_datetime=make_aware(datetime.datetime(2016, 9, 1)),
|
||||
max_datetime=make_aware(datetime.datetime(2016, 10, 1)),
|
||||
meeting_type=MeetingType(duration=60))
|
||||
meeting_type=meeting_type)
|
||||
events = list(sorted(events, key=lambda x: x.start_datetime))
|
||||
assert events[0].start_datetime.timetuple()[:5] == (2016, 9, 5, 9, 0)
|
||||
assert events[1].start_datetime.timetuple()[:5] == (2016, 9, 5, 10, 0)
|
||||
|
@ -35,7 +37,7 @@ def test_timeperiod_time_slots():
|
|||
events = timeperiod.get_time_slots(
|
||||
min_datetime=make_aware(datetime.datetime(2016, 9, 1)),
|
||||
max_datetime=make_aware(datetime.datetime(2016, 10, 1)),
|
||||
meeting_type=MeetingType(duration=60))
|
||||
meeting_type=meeting_type)
|
||||
events = list(sorted(events, key=lambda x: x.start_datetime))
|
||||
assert events[0].start_datetime.timetuple()[:5] == (2016, 9, 6, 9, 0)
|
||||
assert events[-1].start_datetime.timetuple()[:5] == (2016, 9, 27, 11, 0)
|
||||
|
@ -48,7 +50,7 @@ def test_timeperiod_time_slots():
|
|||
events = timeperiod.get_time_slots(
|
||||
min_datetime=make_aware(datetime.datetime(2016, 9, 1)),
|
||||
max_datetime=make_aware(datetime.datetime(2016, 10, 1)),
|
||||
meeting_type=MeetingType(duration=60))
|
||||
meeting_type=meeting_type)
|
||||
events = list(sorted(events, key=lambda x: x.start_datetime))
|
||||
assert events[0].start_datetime.timetuple()[:5] == (2016, 9, 1, 9, 0)
|
||||
assert events[-1].start_datetime.timetuple()[:5] == (2016, 9, 29, 11, 0)
|
||||
|
@ -61,7 +63,7 @@ def test_timeperiod_time_slots():
|
|||
events = timeperiod.get_time_slots(
|
||||
min_datetime=make_aware(datetime.datetime(2016, 9, 1)),
|
||||
max_datetime=make_aware(datetime.datetime(2016, 10, 1)),
|
||||
meeting_type=MeetingType(duration=60))
|
||||
meeting_type=meeting_type)
|
||||
events = list(sorted(events, key=lambda x: x.start_datetime))
|
||||
assert events[0].start_datetime.timetuple()[:5] == (2016, 9, 2, 9, 0)
|
||||
assert events[-1].start_datetime.timetuple()[:5] == (2016, 9, 30, 11, 0)
|
||||
|
@ -74,20 +76,22 @@ def test_timeperiod_time_slots():
|
|||
events = timeperiod.get_time_slots(
|
||||
min_datetime=make_aware(datetime.datetime(2016, 9, 1)),
|
||||
max_datetime=make_aware(datetime.datetime(2016, 10, 1)),
|
||||
meeting_type=MeetingType(duration=60))
|
||||
meeting_type=meeting_type)
|
||||
events = list(sorted(events, key=lambda x: x.start_datetime))
|
||||
assert events[0].start_datetime.timetuple()[:5] == (2016, 9, 3, 9, 0)
|
||||
assert events[-1].start_datetime.timetuple()[:5] == (2016, 9, 24, 11, 0)
|
||||
assert len(events) == 12
|
||||
|
||||
# shorter duration -> double the events
|
||||
meeting_type.duration = 30
|
||||
meeting_type.save()
|
||||
timeperiod = TimePeriod(desk=desk, weekday=5,
|
||||
start_time=datetime.time(9, 0),
|
||||
end_time=datetime.time(12, 0))
|
||||
events = timeperiod.get_time_slots(
|
||||
min_datetime=make_aware(datetime.datetime(2016, 9, 1)),
|
||||
max_datetime=make_aware(datetime.datetime(2016, 10, 1)),
|
||||
meeting_type=MeetingType(duration=30))
|
||||
meeting_type=meeting_type)
|
||||
events = list(sorted(events, key=lambda x: x.start_datetime))
|
||||
assert events[0].start_datetime.timetuple()[:5] == (2016, 9, 3, 9, 0)
|
||||
assert events[-1].start_datetime.timetuple()[:5] == (2016, 9, 24, 11, 30)
|
||||
|
|
Loading…
Reference in New Issue