At work, we need to build a scheduling system. We want to present the
user with a list of possible dates - and then the possible slots on
that date. I don’t want to have all the possible empty slots in the
database so I thought I would have to build them procedurally using
Python.
The code above loops over the days in the range - and then on
available days, loops over the hours in that day and returns a list of
datetimes. There is a lot of ugly adding of Python timedelta objects
and resetting the time to start iterating on a new day. It works - but
the next step, eliminating slots that are already full, is going to be
even uglier - lots of tedious “does this interval overlap with
existing scheduled events”.
When I started looking into how to check the overlap, I started to
looking into checking overlaps in the database - and found that a)
Postgres has a date range data type (tstzrange), b) Django’s Postgres
extensions has a field that wraps the Postgres tstzrange field
(DateTimeRangeField), and c) the Postgres docs even have
an example
of how to create indexes that prevent you from scheduling more than
one person to occupy a specific room at one time. All that ugly
python, turns into:
The only slightly tricky part of that was restricting allowed days to
MWF. I want my constant to use the day names, not the integers
Postgres uses for days of the week. So I needed to import Python’s
calendar module to convert “Monday” to an integer. Python uses 0
for Monday, but Postgres thinks Monday is 1, so add 1. Then it took me
a little while to figure out how to pass a list into the query in a
way that everything is properly interpolated and quoted; the trick:
tuple(allowed_days).
Now I just need to join to my reservations table to exclude slots
where the schedule is already full.