C:\GRIFFIN\PROJECTS\SOFTWARE\DRUM-LESSONS>

[@] MAP
###C############
#...........$..#
#.[]..[]..[]...P
#......@.......#
#.[]..[]..[]...#
#..............#
######H#########
SOFTWARE/DRUM-LE

Doors are hotkeys. Or click the screen and walk with the arrow keys.

Drum Lesson Studio Site

A Django drum-studio scheduler with database-enforced double-booking prevention and a ledger tracing every lesson owed.

Status
In development
Stack
Freelance · Web App · Python · Django · PostgreSQL · Scheduling · Domain Modeling · Client Work

At a glance

  • Outcome: A scheduler and client site for one drum studio. Guardians send lesson requests, instructors place introductory and recurring lessons, and each household gets a private space for lesson notes and practice files.
  • Status: Runs on a staging app behind HTTP basic auth with synthetic data. Payments wait on the client’s Stripe account; the adapter refuses to charge without it, so no real payment has moved.
  • Role: Built solo under contract; reviewed AI-assisted implementation.
  • Contribution: I ran working sessions with the studio owner and turned them into the domain glossary, booking rules and decision records; AI implemented against them, and I reviewed and tested the build.
  • Validation: 146 tests, including race tests against a PostgreSQL exclusion constraint that refuses overlapping lessons for one instructor.
  • Stack & libraries: Django 5.2, Wagtail 7.4, PostgreSQL 18, Gunicorn, Dokku, Stripe SDK.
  • Source: Private client work. The client’s name, prices and student information are withheld.

This is the public website and scheduling system for a drum studio, built under contract. A parent or guardian sends a lesson request from the website. The owner or an instructor books a first lesson, then a regular weekly slot. The family then logs in to a private page with lesson notes and practice files. Before any code, I wrote a glossary that defines every term in the system and lists the vague words to avoid; five entries rule out “booking.” A lesson request doesn’t reserve a slot or enroll anyone. The confirmation email says so.

Schedule changes never erase history. Each calendar lesson is generated from a student’s regular schedule, which has start and end dates. Moving one lesson creates a new one linked to the old one. Changing the whole schedule closes the old pattern and starts a new one. The studio can always look up what was promised to a family and when.

The database itself prevents double-booking an instructor or a room for the same half hour, so a second lesson is rejected no matter which screen tried to add it. Tests check this by having two processes grab the same slot at the same moment. A test passes only if one lesson is created and the other is rejected. Canceled lessons go into a record that’s only ever added to. The type of cancellation decides whether an unused lesson becomes money back or expires. Every lesson owed traces back to the day it was canceled.

Those tests caught one bug. They passed on SQLite, a lightweight database used for quick testing. The first run on PostgreSQL, the database the app runs on, failed on August 28 during test-server setup. The failing code checks whether an enrollment is ready to go live. It locks the enrollment record and, in the same query, loads three related records that might not exist yet: the policy version, the policy agreement and the payment status. PostgreSQL won’t lock records loaded that way when they might be missing. SQLite skips locking entirely, which is why earlier runs never caught it. The fix tells it to lock only the enrollment record. The whole test suite now runs on PostgreSQL. The setup guide says a SQLite pass doesn’t count. Thirteen PostgreSQL-only tests must pass without being skipped.

Families have logins. Students don’t. Private files check the family’s permission on every download and have no public link. Six one-time guardian actions use links that expire, can be canceled and are logged. Every notification is saved in the same step as whatever triggered it, so a failed email can be retried instead of lost. The app currently runs on a password-protected test server with made-up data. Payments stay off until the studio connects its own Stripe account.