Getting Started¶
Installation¶
Getting the code¶
The recommended way to install django-agenda is via pip (on Windows,
replace pip3 with pip)
$ pip3 install django-agenda
Configuring Django¶
Add "django_agenda" to your INSTALLED_APPS setting:
INSTALLED_APPS = [
# ...
"django_agenda",
]
Concepts¶
Before we actually talk about any concepts, I’m going to bring up a bunch of use-cases
- Hovercraft Academy is a school, and it wants a system that will allow it to allocate the usage of their classrooms.
- Eel Incorporated is a company. They want a system to manage their employees schedules so they know when they’re busy and don’t make overlapping meetings.
Second, let’s talk about the ideas behind how the scheduling system works from this perspective. There are two models that you must think through when implementing this:
- The “Schedule” model: This is the model that “owns” the schedule. It
can be any type of model, even a built-in model like the user model from
django.contrib.auth. This model doesn’t need to actually include any scheduling logic, it’s just the thing that the schedule is attached to. Here’s some examples:- For Hovercraft Academy, this would be the classrooms.
- For Eal Incorporated, this would be the employees.
- The “Booking” model: This is the model that reserves time from the
schedule. This model is responsible for the majority of the configurable
scheduling logic, like knowing what time, if any time at all, should be
reserved, and if reserved times should be marked busy, and if busy times
can overlap. Here’s some examples:
- For Hovercraft Academy, this would be a classroom reservation.
- For Eal Incorporated, this would be the meetings.
There’s also three other models that we’ll cover shortly.
Creating & Linking Models¶
Django Agenda doesn’t provide models for you. We did in earlier versions, and used contenttypes to link the models. That turned out to make for poor usability and fragile code, since we couldn’t trust the database to understand anything. Instead, we provide a bunch of abstract base classes that you can use to construct your models from.
First, you’ll need to decide what your schedule model is. Here’s an example using Hovercraft Academy:
from django.db import models
class Room(models.Model):
number = models.SlugField(primary_key=True)
Second, you need to set up the 3 supporting models: Availability, Availability Occurrence, and TimeSlot. The best way to do this, is to use the relevant abstract classes, and specify a few special fields, so that the models can find each other.
In order to help keep all our models straight, here’s some pointers.
- Availabilities are what the end user sets. They can be set in any time zone and they can recur.
- Availability Occurrences are mostly for internal use. They’re basically copies of Availabilities, but they don’t recur.
- TimeSlots are the times that have already been blocked off from the Availabilities. As a general rule, a time is free if it is covered by the Availability Occurrences, and not covered by a busy TimeSlot.
Continuing our example:
from django_agenda.models import (AbstractAvailability,
AbstractAvailabilityOccurrence,
AbstractTimeSlot)
class Availability(AbstractAvailability):
class AgendaMeta:
schedule_model = Room
schedule_field = "room" # optional
class AvailabilityOccurrence(AbstractAvailabilityOccurrence):
class AgendaMeta:
availability_model = Availability
schedule_model = Room
schedule_field = "room" # optional
class TimeSlot(AbstractTimeSlot):
class AgendaMeta:
availability_model = Availability
schedule_model = Room
booking_model = "RoomReservation" # booking class, more details shortly
schedule_field = "room" # optional
Finally, we need to make the booking class.
from django.db import models
from django_agenda.models import AbstractBooking
from django_agenda.time_span import TimeSpan
class RoomReservation(AbstractBooking):
class AgendaMeta:
schedule_model = Room
owner = models.ForeignKey(
to=settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
related_name="reservations",
)
start_time = models.DateTimeField(db_index=True)
end_time = models.DateTimeField(db_index=True)
approved = models.BooleanField(default=False)
def get_reserved_spans(self):
# we only reserve the time if the reservation has been approved
if self.approved:
yield TimeSpan(self.start_time, self.end_time)
Now, we can do something like this:
import pytz
from datetime import date, time, datetime
start_date = date(2004, 1, 1)
start_time = time(8)
end_time = time(17)
tz = pytz.timezone("America/Vancouver")
room = Room.objects.create(number="foo")
# available from 8 AM to 5 PM
availability = Availability.objects.create(
room=room,
start_date=start_date,
start_time=start_time,
end_time=end_time,
timezone=tz,
)
# note: you have to run this when your availabilities change, or when
# your availability occurrence data gets stale
availability.recreate_occurrences(
datetime(2004, 1, 1, 1, tzinfo=tz),
datetime(2004, 1, 2, 1, tzinfo=tz),
)
# reserve from 9-11
reservation = RoomReservation(
owner=<some user>,
start_time=datetime(2004, 1, 1, 9, tzinfo=tz),
end_time=datetime(2004, 1, 1, 11, tzinfo=tz),
)
# this will work
reservation.clean()
reservation.save()
# reserve from 10-12
reservation = RoomReservation(
owner=<some user>,
start_time=datetime(2004, 1, 1, 10, tzinfo=tz),
end_time=datetime(2004, 1, 1, 12, tzinfo=tz),
)
# this won’t work, time already reserved.
reservation.clean()
Generating Availability Occurrences¶
The main tricky thing is that availability occurrences aren’t automatically generated by default. This is because there could easily be an infinite number of them, so we can’t generate all of them, and we leave it up to you to decide how far in the future to generate these availability occurrences.
The AbstractAvailability.recreate_occurrences method can be used to
regenerate them. It takes a start & end datetime and regenerates the
occurrences between that range. It’s recommended that you call this when
availabilities are changed and on a regular schedule. For example, if you
plan to generate availabilities 1 year in advance, you want to call it every
week or so, otherwise, after a year, you’re going to run out of free time.