From dc752547400bab55dbfaaa1f3e4430c15985a8e6 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Sat, 16 Dec 2017 05:37:00 +0100 Subject: [PATCH 2/3] api: replace intervaltree by chrono.interval (#20732) --- chrono/api/views.py | 66 +++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/chrono/api/views.py b/chrono/api/views.py index e4506ac..7e1db08 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -19,8 +19,6 @@ from copy import deepcopy import datetime import operator -from intervaltree import IntervalTree - from django.core.urlresolvers import reverse from django.http import Http404 from django.shortcuts import get_object_or_404 @@ -34,6 +32,7 @@ from rest_framework.views import APIView from ..agendas.models import (Agenda, Event, Booking, MeetingType, TimePeriod, Desk) +from ..interval import Intervals def get_exceptions_by_desk(agenda): @@ -56,10 +55,11 @@ def get_all_slots(agenda, meeting_type): 'meeting_type': meeting_type } - open_slots_by_desk = defaultdict(lambda: IntervalTree()) + open_slots_by_desk = defaultdict(lambda: Intervals()) for time_period in TimePeriod.objects.filter(desk__agenda=agenda): for slot in time_period.get_time_slots(**time_period_filters): - open_slots_by_desk[time_period.desk_id].addi(slot.start_datetime, slot.end_datetime, slot) + slot.full = False + open_slots_by_desk[time_period.desk_id].add(slot.start_datetime, slot.end_datetime, slot) # remove excluded slot excluded_slot_by_desk = get_exceptions_by_desk(agenda) @@ -67,23 +67,20 @@ def get_all_slots(agenda, meeting_type): for interval in excluded_interval: begin, end = interval open_slots_by_desk[desk].remove_overlap(localtime(begin), localtime(end)) - # keep a copy of all time slot before removing busy time slots - all_time_slots = reduce(operator.__or__, deepcopy(open_slots_by_desk).values()) for event in agenda.event_set.filter( agenda=agenda, start_datetime__gte=min_datetime, start_datetime__lte=max_datetime + datetime.timedelta(meeting_type.duration)).select_related( - 'meeting_type').extra( - select={ - 'booking_count': """SELECT COUNT(*) FROM agendas_booking - WHERE agendas_booking.event_id = agendas_event.id - AND agendas_booking.cancellation_datetime IS NOT NULL"""}): - if event.booking_count: - continue - open_slots_by_desk[event.desk_id].remove_overlap(event.start_datetime, event.end_datetime) + 'meeting_type').exclude( + booking__cancellation_datetime__isnull=False): + for slot in open_slots_by_desk[event.desk_id].search_data(event.start_datetime, event.end_datetime): + slot.full = True - open_slots = reduce(operator.__or__, open_slots_by_desk.values()) - return open_slots, all_time_slots + slots = [] + for desk in open_slots_by_desk: + slots.extend(open_slots_by_desk[desk].iter_data()) + slots.sort(key=lambda slot: slot.start_datetime) + return slots def get_agenda_detail(request, agenda): @@ -207,24 +204,16 @@ class MeetingDatetimes(GenericAPIView): now_datetime = now() - open_slots, all_time_slots = get_all_slots(agenda, meeting_type) - open_entries = {} - closed_entries = {} - - for interval in all_time_slots.all_intervals: - time_slot = interval.data - if time_slot.start_datetime < now_datetime: + slots = get_all_slots(agenda, meeting_type) + entries = {} + for slot in slots: + if slot.start_datetime < now_datetime: continue - key = '%s-%s' % (time_slot.start_datetime, time_slot.end_datetime) - if open_slots.search(time_slot.start_datetime, time_slot.end_datetime, strict=True): - time_slot.full = False - open_entries[key] = time_slot - else: - time_slot.full = True - closed_entries[key] = time_slot - - closed_entries.update(open_entries) - entries = sorted(closed_entries.values(), key=lambda x: x.start_datetime) + key = (slot.start_datetime, slot.end_datetime) + if key in entries and slot.full: + continue + entries[key] = slot + slots = sorted(entries.values(), key=lambda x: x.start_datetime) fake_event_pk = '__event_id__' fillslot_url = request.build_absolute_uri( @@ -241,7 +230,7 @@ class MeetingDatetimes(GenericAPIView): 'api': { 'fillslot_url': fillslot_url.replace(fake_event_pk, str(x.id)), }, - } for x in entries]} + } for x in slots]} return Response(response) meeting_datetimes = MeetingDatetimes.as_view() @@ -334,12 +323,13 @@ class Fillslot(GenericAPIView): start_datetime=start_datetime, full=False, places=1) - open_slots, _ = get_all_slots(agenda, MeetingType.objects.get(id=meeting_type_id)) + slots = get_all_slots(agenda, MeetingType.objects.get(id=meeting_type_id)) # sort available matching slots by desk id - slot = sorted(open_slots[event.start_datetime:event.end_datetime], key=lambda x: x.data.desk.id) - if slot: + slots = [slot for slot in slots if not slot.full and slot.start_datetime >= event.start_datetime and slot.end_datetime <= event.end_datetime] + slots.sort(key=lambda x: x.desk.id) + if slots: # book first available desk - available_desk = slot[0].data.desk + available_desk = slots[0].desk if not available_desk: return Response({'err': 1, 'reason': 'no more desk available'}) -- 2.15.1