general: add method to get opening hours of a desk on a given day (#21244)

This commit is contained in:
Frédéric Péters 2018-01-18 16:28:43 +01:00
parent 0f0b25f17d
commit b364bdb1d6
4 changed files with 116 additions and 1 deletions

View File

@ -35,6 +35,9 @@ from django.utils.translation import ugettext_lazy as _
from jsonfield import JSONField
from ..interval import Intervals
AGENDA_KINDS = (
('events', _('Events')),
('meetings', _('Meetings')),
@ -513,6 +516,22 @@ class Desk(models.Model):
return total_created
def get_opening_hours(self, date):
openslots = Intervals()
for timeperiod in self.timeperiod_set.filter(weekday=date.weekday()):
start_datetime = make_aware(datetime.datetime.combine(date, timeperiod.start_time))
end_datetime = make_aware(datetime.datetime.combine(date, timeperiod.end_time))
openslots.add(start_datetime, end_datetime)
aware_date = make_aware(datetime.datetime(date.year, date.month, date.day))
aware_next_date = aware_date + datetime.timedelta(days=1)
for exception in self.timeperiodexception_set.filter(
start_datetime__lt=aware_next_date,
end_datetime__gt=aware_date):
openslots.remove(exception.start_datetime, exception.end_datetime)
return openslots.search(aware_date, aware_next_date)
class TimePeriodException(models.Model):
desk = models.ForeignKey(Desk)

View File

@ -26,6 +26,9 @@ class Interval(object):
self.end = end
self.data = data
def __eq__(self, other):
return self.begin == other.begin and self.end == other.end and self.data == other.data
def overlap(self, begin, end):
if end <= self.begin:
return False
@ -69,6 +72,20 @@ class Intervals(object):
yield self.points[i], interval
i += 1
def remove(self, begin, end):
'Substract interval'
for interval in list(self.iter()):
# create interval with new borders
if interval.overlap(begin, end):
if begin > interval.begin and end < interval.end:
self.add(interval.begin, begin)
self.add(end, interval.end)
elif interval.begin < begin:
self.add(interval.begin, begin)
elif interval.end > end:
self.add(end, interval.end)
self.__remove_interval(interval)
def remove_overlap(self, begin, end):
'Remove all overlapping intervals'
for point, interval in self.__iter_interval(begin, end, modify=True):

View File

@ -55,3 +55,18 @@ def test_intervals():
assert not intervals.overlap(i, i + 1)
for i in range(15, 20):
assert intervals.overlap(i, i + 1)
def test_interval_remove():
intervals = Intervals()
intervals.add(9, 12)
intervals.add(14, 17)
intervals.remove(11, 24)
assert list(intervals.search(0, 24)) == [Interval(9, 11)]
intervals.remove(8, 10)
assert list(intervals.search(0, 24)) == [Interval(10, 11)]
intervals = Intervals()
intervals.add(9, 12)
intervals.add(14, 17)
intervals.remove(10, 11)
assert list(intervals.search(0, 24)) == [Interval(9, 10), Interval(11, 12), Interval(14, 17)]

View File

@ -4,7 +4,7 @@ import datetime
import pytest
from django.test import override_settings
from django.utils.timezone import make_aware
from django.utils.timezone import localtime, make_aware
from chrono.agendas.models import Agenda, TimePeriod, TimePeriodException, MeetingType, Desk
@ -126,3 +126,67 @@ def test_time_period_exception_as_string():
start_datetime=make_aware(datetime.datetime(2018, 1, 18, 10, 0)),
end_datetime=make_aware(datetime.datetime(2018, 1, 20, 12, 0)))
) == u'18 jan. 2018 10:00 → 20 jan. 2018 12:00'
def test_desk_opening_hours():
agenda = Agenda(label=u'Foo bar', slug='bar')
agenda.save()
desk = Desk.objects.create(label='Desk 1', agenda=agenda)
# nothing yet
hours = list(desk.get_opening_hours(datetime.date(2018, 1, 22)))
assert len(hours) == 0
# morning
TimePeriod(desk=desk, weekday=0,
start_time=datetime.time(9, 0),
end_time=datetime.time(12, 0)).save()
hours = list(desk.get_opening_hours(datetime.date(2018, 1, 22)))
assert len(hours) == 1
assert hours[0].begin.time() == datetime.time(9, 0)
assert hours[0].end.time() == datetime.time(12, 0)
# and afternoon
TimePeriod(desk=desk, weekday=0,
start_time=datetime.time(14, 0),
end_time=datetime.time(17, 0)).save()
hours = list(desk.get_opening_hours(datetime.date(2018, 1, 22)))
assert len(hours) == 2
assert hours[0].begin.time() == datetime.time(9, 0)
assert hours[0].end.time() == datetime.time(12, 0)
assert hours[1].begin.time() == datetime.time(14, 0)
assert hours[1].end.time() == datetime.time(17, 0)
# full day exception
exception = TimePeriodException(
desk=desk,
start_datetime=make_aware(datetime.datetime(2018, 1, 22)),
end_datetime=make_aware(datetime.datetime(2018, 1, 23)))
exception.save()
hours = list(desk.get_opening_hours(datetime.date(2018, 1, 22)))
assert len(hours) == 0
# closed from 11am
exception.start_datetime = make_aware(datetime.datetime(2018, 1, 22, 11, 0))
exception.save()
hours = list(desk.get_opening_hours(datetime.date(2018, 1, 22)))
assert len(hours) == 1
assert localtime(hours[0].begin).time() == datetime.time(9, 0)
assert localtime(hours[0].end).time() == datetime.time(11, 0)
# closed in the middle
exception.start_datetime = make_aware(datetime.datetime(2018, 1, 22, 10, 0))
exception.end_datetime = make_aware(datetime.datetime(2018, 1, 22, 11, 0))
exception.save()
hours = list(desk.get_opening_hours(datetime.date(2018, 1, 22)))
assert len(hours) == 3
assert localtime(hours[0].begin).time() == datetime.time(9, 0)
assert localtime(hours[0].end).time() == datetime.time(10, 0)
assert localtime(hours[1].begin).time() == datetime.time(11, 0)
assert localtime(hours[1].end).time() == datetime.time(12, 0)
assert localtime(hours[2].begin).time() == datetime.time(14, 0)
assert localtime(hours[2].end).time() == datetime.time(17, 0)