19 |
19 |
import datetime
|
20 |
20 |
import operator
|
21 |
21 |
|
22 |
|
from intervaltree import IntervalTree
|
23 |
|
|
24 |
22 |
from django.core.urlresolvers import reverse
|
25 |
23 |
from django.http import Http404
|
26 |
24 |
from django.shortcuts import get_object_or_404
|
... | ... | |
34 |
32 |
|
35 |
33 |
from ..agendas.models import (Agenda, Event, Booking, MeetingType,
|
36 |
34 |
TimePeriod, Desk)
|
|
35 |
from ..interval import Intervals
|
37 |
36 |
|
38 |
37 |
|
39 |
38 |
def get_exceptions_by_desk(agenda):
|
... | ... | |
56 |
55 |
'meeting_type': meeting_type
|
57 |
56 |
}
|
58 |
57 |
|
59 |
|
open_slots_by_desk = defaultdict(lambda: IntervalTree())
|
|
58 |
open_slots_by_desk = defaultdict(lambda: Intervals())
|
60 |
59 |
for time_period in TimePeriod.objects.filter(desk__agenda=agenda):
|
61 |
60 |
for slot in time_period.get_time_slots(**time_period_filters):
|
62 |
|
open_slots_by_desk[time_period.desk_id].addi(slot.start_datetime, slot.end_datetime, slot)
|
|
61 |
slot.full = False
|
|
62 |
open_slots_by_desk[time_period.desk_id].add(slot.start_datetime, slot.end_datetime, slot)
|
63 |
63 |
|
64 |
64 |
# remove excluded slot
|
65 |
65 |
excluded_slot_by_desk = get_exceptions_by_desk(agenda)
|
... | ... | |
67 |
67 |
for interval in excluded_interval:
|
68 |
68 |
begin, end = interval
|
69 |
69 |
open_slots_by_desk[desk].remove_overlap(localtime(begin), localtime(end))
|
70 |
|
# keep a copy of all time slot before removing busy time slots
|
71 |
|
all_time_slots = reduce(operator.__or__, deepcopy(open_slots_by_desk).values())
|
72 |
70 |
|
73 |
71 |
for event in agenda.event_set.filter(
|
74 |
72 |
agenda=agenda, start_datetime__gte=min_datetime,
|
75 |
73 |
start_datetime__lte=max_datetime + datetime.timedelta(meeting_type.duration)).select_related(
|
76 |
|
'meeting_type').extra(
|
77 |
|
select={
|
78 |
|
'booking_count': """SELECT COUNT(*) FROM agendas_booking
|
79 |
|
WHERE agendas_booking.event_id = agendas_event.id
|
80 |
|
AND agendas_booking.cancellation_datetime IS NOT NULL"""}):
|
81 |
|
if event.booking_count:
|
82 |
|
continue
|
83 |
|
open_slots_by_desk[event.desk_id].remove_overlap(event.start_datetime, event.end_datetime)
|
|
74 |
'meeting_type').exclude(
|
|
75 |
booking__cancellation_datetime__isnull=False):
|
|
76 |
for slot in open_slots_by_desk[event.desk_id].search_data(event.start_datetime, event.end_datetime):
|
|
77 |
slot.full = True
|
84 |
78 |
|
85 |
|
open_slots = reduce(operator.__or__, open_slots_by_desk.values())
|
86 |
|
return open_slots, all_time_slots
|
|
79 |
slots = []
|
|
80 |
for desk in open_slots_by_desk:
|
|
81 |
slots.extend(open_slots_by_desk[desk].iter_data())
|
|
82 |
slots.sort(key=lambda slot: slot.start_datetime)
|
|
83 |
return slots
|
87 |
84 |
|
88 |
85 |
|
89 |
86 |
def get_agenda_detail(request, agenda):
|
... | ... | |
207 |
204 |
|
208 |
205 |
now_datetime = now()
|
209 |
206 |
|
210 |
|
open_slots, all_time_slots = get_all_slots(agenda, meeting_type)
|
211 |
|
open_entries = {}
|
212 |
|
closed_entries = {}
|
213 |
|
|
214 |
|
for interval in all_time_slots.all_intervals:
|
215 |
|
time_slot = interval.data
|
216 |
|
if time_slot.start_datetime < now_datetime:
|
|
207 |
slots = get_all_slots(agenda, meeting_type)
|
|
208 |
entries = {}
|
|
209 |
for slot in slots:
|
|
210 |
if slot.start_datetime < now_datetime:
|
217 |
211 |
continue
|
218 |
|
key = '%s-%s' % (time_slot.start_datetime, time_slot.end_datetime)
|
219 |
|
if open_slots.search(time_slot.start_datetime, time_slot.end_datetime, strict=True):
|
220 |
|
time_slot.full = False
|
221 |
|
open_entries[key] = time_slot
|
222 |
|
else:
|
223 |
|
time_slot.full = True
|
224 |
|
closed_entries[key] = time_slot
|
225 |
|
|
226 |
|
closed_entries.update(open_entries)
|
227 |
|
entries = sorted(closed_entries.values(), key=lambda x: x.start_datetime)
|
|
212 |
key = (slot.start_datetime, slot.end_datetime)
|
|
213 |
if key in entries and slot.full:
|
|
214 |
continue
|
|
215 |
entries[key] = slot
|
|
216 |
slots = sorted(entries.values(), key=lambda x: x.start_datetime)
|
228 |
217 |
|
229 |
218 |
fake_event_pk = '__event_id__'
|
230 |
219 |
fillslot_url = request.build_absolute_uri(
|
... | ... | |
241 |
230 |
'api': {
|
242 |
231 |
'fillslot_url': fillslot_url.replace(fake_event_pk, str(x.id)),
|
243 |
232 |
},
|
244 |
|
} for x in entries]}
|
|
233 |
} for x in slots]}
|
245 |
234 |
return Response(response)
|
246 |
235 |
|
247 |
236 |
meeting_datetimes = MeetingDatetimes.as_view()
|
... | ... | |
334 |
323 |
start_datetime=start_datetime,
|
335 |
324 |
full=False, places=1)
|
336 |
325 |
|
337 |
|
open_slots, _ = get_all_slots(agenda, MeetingType.objects.get(id=meeting_type_id))
|
|
326 |
slots = get_all_slots(agenda, MeetingType.objects.get(id=meeting_type_id))
|
338 |
327 |
# sort available matching slots by desk id
|
339 |
|
slot = sorted(open_slots[event.start_datetime:event.end_datetime], key=lambda x: x.data.desk.id)
|
340 |
|
if slot:
|
|
328 |
slots = [slot for slot in slots if not slot.full and slot.start_datetime >= event.start_datetime and slot.end_datetime <= event.end_datetime]
|
|
329 |
slots.sort(key=lambda x: x.desk.id)
|
|
330 |
if slots:
|
341 |
331 |
# book first available desk
|
342 |
|
available_desk = slot[0].data.desk
|
|
332 |
available_desk = slots[0].desk
|
343 |
333 |
|
344 |
334 |
if not available_desk:
|
345 |
335 |
return Response({'err': 1, 'reason': 'no more desk available'})
|
346 |
|
-
|