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)