views:

182

answers:

2

I am trying to implement a calendar system with the ability to schedule other people for appointments. The system has to be able to prevent scheduling a person during another appointment or during their unavailable time.

I have looked at all the existing django calendar projects I have found on the internet and none of them seem to have this built-into them (if I missed it somehow, please let me know).

Perhaps I am just getting too tired, but the only way I can think of doing this seems a little messy. Here goes in pseudo code:

  • when a user tries to create a new appointment, grab the new appointment's start_time and end_time
  • for each appointment on that same day, check if
    • existing_start_time < new_start_time AND existing_end_time > new_start_time (is the new appointments start time in between any existing appointment's start and end times)
    • existing_start_time < new_end_time AND existing_end_time > new_end_time (is the new appointments end time in between any existing appointment's start and end times)
  • if no objects were found, then go ahead and add the new appointment

Considering Django has no filtering based on time, this must all be done using .extra() on the queryset.

So, I am asking if there is a better way. A pythonic trick or module or anything that might simplify this. Or an existing project that has what I need or can lead me in the right direction.

Thanks.

+6  A: 

What about using Django's range test.

For example:

appoinment = Appointment()
appointment.start_time = datetime.datetime.now()
# 1 hour appointment
appointment.end_time = appointment.start_time + datetime.timedelta(hours=1)
# more stuff here
appointment.save()

# Checking for collision
# where the start time for an appointment is between the the start and end times
# You would want to filter this on user, etc 
# There is also a problem if you book an appointment within another appointment
start_conflict = Appointment.objects.filter(
                     start_time__range=(appointment.start_time,
                                        appointment.end_time))
end_conflict = Appointment.objects.filter(
                   end_time__range=(appointment.start_time,
                                    appointment.end_time))

during_conflict = Appointment.objects.filter(
                      start_date__lte=appointment.start_time, 
                      end_date__gte=appointment.end_time)

if (start_conflict or end_conflict or during_conflict):
    # reject, for there is a conflict

Something like that? I haven't tried this myself so you may have to tweak it a bit.

EDIT: Added the during_conflict bit.

Nick Presta
+1 Awesome! Hadn't seen range testing was built into Django's QuerySet API.
Dominic Rodger
Thank you for the great tip. This was missing events that started before and ended after the new appointment. For example: If a client had an appointment from 1 to 5, this would not prevent someone from booking 2 to 3. I added the following to include such situations:during_conflict = Appointment.objects.filter(start_date__lte=appointment.start_time, end_date__gte=appointment.end_time)if (start_conflict or end_conflict or during_conflict):
mhost
Excellent. I'm glad this was helpful. I'm going to add your case so the answer is more complete.
Nick Presta
A: 

One caveat here is the different timezones of different users, and bring Daylight saving time into the mix things become very complicated.

You might want to take a look at pytz module for taking care of the timezone issue.

Sidmitra