2017-12-16 04:17:54 +01:00
|
|
|
|
# chrono - agendas system
|
|
|
|
|
# Copyright (C) 2017 Entr'ouvert
|
|
|
|
|
#
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
|
|
|
# under the terms of the GNU Affero General Public License as published
|
|
|
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
# (at your option) any later version.
|
|
|
|
|
#
|
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
|
#
|
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
import bisect
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
import collections
|
2017-12-16 04:17:54 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
Interval = collections.namedtuple('Interval', ['begin', 'end'])
|
2017-12-16 04:17:54 +01:00
|
|
|
|
|
|
|
|
|
|
2021-07-09 16:04:34 +02:00
|
|
|
|
class IntervalSet:
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
"""Store a set made of an union of disjoint open/closed intervals (it
|
|
|
|
|
currently does not really care about their openness), i.e.
|
2017-12-16 04:17:54 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
S = [a[0], b[0]] ∪ ∪ [a[n-1], b[n-1]]
|
2018-01-18 16:28:43 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
where
|
2017-12-16 04:17:54 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
forall i < n. a_i < b_i
|
|
|
|
|
forall i < (n-1). b_i < a[i+1]
|
2020-12-29 10:42:33 +01:00
|
|
|
|
"""
|
2018-01-22 01:55:26 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
__slots__ = ['begin', 'end']
|
2017-12-16 04:17:54 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
def __init__(self, iterable=(), already_sorted=False):
|
2020-12-29 10:42:33 +01:00
|
|
|
|
"""
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
Initialize a new IntervalSet from a list of Interval or 2-tuple.
|
2017-12-16 04:17:54 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
Iterable will be sorted, if it's already sorted use the
|
|
|
|
|
from_ordered() classmethod.
|
2018-01-22 01:55:26 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
It's faster than using add() because intervals are merged as we
|
|
|
|
|
traverse the list, and self.begin and self.end are built in O(n)
|
|
|
|
|
time where len(iterable) = n.
|
2020-12-29 10:42:33 +01:00
|
|
|
|
"""
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
if not already_sorted:
|
|
|
|
|
iterable = sorted(iterable)
|
|
|
|
|
self.begin = []
|
|
|
|
|
self.end = []
|
|
|
|
|
last_begin, last_end = None, None
|
|
|
|
|
for begin, end in iterable:
|
|
|
|
|
# check good order property along the way
|
|
|
|
|
if last_begin and not (last_begin < begin or last_begin == begin and last_end <= end):
|
|
|
|
|
raise ValueError('not well ordered: ! %s <= %s' % ((last_begin, last_end), (begin, end)))
|
|
|
|
|
if self.begin and begin <= self.end[-1]:
|
|
|
|
|
self.end[-1] = max(self.end[-1], end)
|
|
|
|
|
else:
|
|
|
|
|
self.begin.append(begin)
|
|
|
|
|
self.end.append(end)
|
|
|
|
|
last_begin, last_end = begin, end
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def simple(cls, begin, end=Ellipsis):
|
|
|
|
|
begin, end = cls._begin_or_interval(begin, end)
|
|
|
|
|
return cls.from_ordered([(begin, end)])
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_ordered(cls, iterable):
|
|
|
|
|
return cls(iterable, already_sorted=True)
|
2018-01-22 01:55:26 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
def __repr__(self):
|
|
|
|
|
return repr(list(map(tuple, self)))
|
2018-01-22 01:55:26 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
@classmethod
|
|
|
|
|
def _begin_or_interval(cls, begin, end=Ellipsis):
|
|
|
|
|
if end is Ellipsis:
|
|
|
|
|
return begin
|
|
|
|
|
else:
|
|
|
|
|
return begin, end
|
2019-12-16 16:21:24 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
def add(self, begin, end=Ellipsis):
|
|
|
|
|
"""Add a new interval to the set, eventually merging it with actual
|
|
|
|
|
ones.
|
2017-12-16 04:17:54 +01:00
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
It uses bisect_left() to maintaint the ordering of intervals.
|
2020-12-29 10:42:33 +01:00
|
|
|
|
"""
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
begin, end = self._begin_or_interval(begin, end)
|
|
|
|
|
# insert an interval by merging with previous and following intervals
|
|
|
|
|
# if they overlap
|
|
|
|
|
i = bisect.bisect_left(self.begin, begin)
|
|
|
|
|
# merge with previous intervals
|
2021-07-09 16:11:35 +02:00
|
|
|
|
while i > 0 and begin <= self.end[i - 1]:
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
# [begin, end] overlaps previous interval
|
|
|
|
|
# so remove it to merge them.
|
|
|
|
|
previous_begin = self.begin.pop(i - 1)
|
|
|
|
|
previous_end = self.end.pop(i - 1)
|
|
|
|
|
i = i - 1
|
|
|
|
|
if previous_begin < begin:
|
|
|
|
|
# but it does not include it, so replace if begin by previous.begin
|
|
|
|
|
begin = previous_begin
|
2021-07-09 16:41:33 +02:00
|
|
|
|
# [begin, end] is completely included in previous interval,
|
|
|
|
|
# replace end by previous.end
|
|
|
|
|
end = max(end, previous_end)
|
2018-01-22 01:55:26 +01:00
|
|
|
|
break
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
# merge with following
|
|
|
|
|
while i < len(self.begin) and self.begin[i] <= end:
|
|
|
|
|
# [begin, end] overlaps next interval, so remove it to merge them.
|
|
|
|
|
next_end = self.end.pop(i)
|
|
|
|
|
self.begin.pop(i)
|
|
|
|
|
if end < next_end:
|
|
|
|
|
# but it does not include it, so replace end by next.end
|
|
|
|
|
end = next_end
|
|
|
|
|
# no symetry with the previous "while" loop as .bisect_left()
|
|
|
|
|
# garanty that begin is left or equal to next.begin.
|
|
|
|
|
break
|
|
|
|
|
self.begin.insert(i, begin)
|
|
|
|
|
self.end.insert(i, end)
|
|
|
|
|
|
|
|
|
|
def overlaps(self, begin, end=Ellipsis):
|
2020-12-29 10:42:33 +01:00
|
|
|
|
"""
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
Find if the [begin, end] has a non-empty (or closed) overlaps with
|
|
|
|
|
one of the contained intervals.
|
2020-12-29 10:42:33 +01:00
|
|
|
|
"""
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
begin, end = self._begin_or_interval(begin, end)
|
|
|
|
|
# look for non-zero size overlap
|
|
|
|
|
i = bisect.bisect_left(self.begin, begin)
|
2021-07-09 16:11:35 +02:00
|
|
|
|
if i > 0 and begin < self.end[i - 1]:
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
return True
|
|
|
|
|
if i < len(self.begin) and self.begin[i] < end:
|
|
|
|
|
return True
|
2017-12-16 04:17:54 +01:00
|
|
|
|
return False
|
|
|
|
|
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
def __iter__(self):
|
2020-12-29 10:42:33 +01:00
|
|
|
|
"""
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
Generate the ordered list of included intervals as 2-tuples.
|
2020-12-29 10:42:33 +01:00
|
|
|
|
"""
|
api: optimize get_all_slots() and around it (#42169)
Workflow in get_all_slots() is simplified :
* first we accumulate, for each desk, the set of time slots when a booking cannot
occur or is already booked,
* then we generate the list of possible time slots and match them to the
exclusion and already booked set.
Intervals is replaced by a simpler data-structure, IntervalSet, it does
not need to be a map, a simple set is enough.
Also :
* moved TimePeriod.get_effective_timeperiods() to the agenda level , it
deduplictes TimePeriod between desks and remove excluded TimePeriod for
virtual agendas.
* added a named-tuple WeekTime to represent a TimePeriod base unit, so
we can use them in IntervalSet easily (as they can be compared) to
compute the effective time periods,
* the fact that base_duration is unique for a given virtual agenda is
now accounted and stated everywhere,
* the fact that generated time slots must have time in the local
timezone for the API to work is now stated everywhere,
* In get_all_slots(), also :
* integrated the code of get_exceptions_by_desk() into get_all_slots()
to further reduce the number of SQL queries.
* used_min/max_datetime is reduced by the exclusion periods, and
effective time periods are grouped based on the used_min/max_datetime of
each agenda.
* pre-filter slots for uniqueness when generating available datetimes
(but for filling slot we still need exact availability information
for each desk)
2020-04-30 12:16:38 +02:00
|
|
|
|
return map(Interval._make, zip(self.begin, self.end))
|
|
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
|
if isinstance(other, self.__class__):
|
|
|
|
|
return list(self) == list(other)
|
|
|
|
|
if hasattr(other, '__iter__'):
|
|
|
|
|
return self == self.cast(other)
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def __bool__(self):
|
|
|
|
|
return bool(self.begin)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def cast(cls, value):
|
|
|
|
|
if isinstance(value, cls):
|
|
|
|
|
return value
|
|
|
|
|
return cls(value)
|
|
|
|
|
|
|
|
|
|
def __sub__(self, other):
|
|
|
|
|
l1 = iter(self)
|
|
|
|
|
l2 = iter(self.cast(other))
|
|
|
|
|
|
|
|
|
|
def gen():
|
|
|
|
|
state = 3
|
|
|
|
|
while True:
|
|
|
|
|
if state & 1:
|
|
|
|
|
c1 = next(l1, None)
|
|
|
|
|
if state & 2:
|
|
|
|
|
c2 = next(l2, None)
|
|
|
|
|
if not c1:
|
|
|
|
|
break
|
|
|
|
|
if not c2:
|
|
|
|
|
yield c1
|
|
|
|
|
for c1 in l1:
|
|
|
|
|
yield c1
|
|
|
|
|
break
|
|
|
|
|
if c1[1] <= c2[0]:
|
|
|
|
|
yield c1
|
|
|
|
|
state = 1
|
|
|
|
|
elif c2[1] <= c1[0]:
|
|
|
|
|
state = 2
|
|
|
|
|
elif c1[1] < c2[1]:
|
|
|
|
|
if c1[0] < c2[0]:
|
|
|
|
|
yield (c1[0], c2[0])
|
|
|
|
|
state = 1
|
|
|
|
|
elif c2[1] < c1[1]:
|
|
|
|
|
if c1[0] < c2[0]:
|
|
|
|
|
yield (c1[0], c2[0])
|
|
|
|
|
c1 = (c2[1], c1[1])
|
|
|
|
|
state = 2
|
|
|
|
|
elif c1[1] == c2[1]:
|
|
|
|
|
if c1[0] < c2[0]:
|
|
|
|
|
yield (c1[0], c2[0])
|
|
|
|
|
state = 3
|
|
|
|
|
else:
|
|
|
|
|
raise Exception('not reachable')
|
|
|
|
|
|
|
|
|
|
return self.__class__.from_ordered(gen())
|
|
|
|
|
|
|
|
|
|
def min(self):
|
|
|
|
|
if self:
|
|
|
|
|
return self.begin[0]
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def max(self):
|
|
|
|
|
if self:
|
|
|
|
|
return self.end[-1]
|
|
|
|
|
return None
|