← All projects

Drum Lesson Studio Site

In development

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

FreelanceWeb AppPythonDjangoPostgreSQLSchedulingDomain ModelingClient Work

At a glance

  • Outcome: Accepts guardian lesson requests through a public form. Instructors review fit, propose introductory times, control recurring placements, and use a mobile Today page listing lessons, authorized contacts, and verification, pause, or payment warnings. Guardians use expiring single-purpose links, not a portal.
  • Status: In development; no public site. On September 11, 2026: 15 commits since August 25, last changed August 29. Dokku staging has run since August 28 with TLS, HTTP basic auth, synthetic data only, and refused indexing.
  • Role: Built solo under contract through my LLC.
  • Stack & libraries: Runs Django 5.2 on PostgreSQL 18 via psycopg 3 with server-rendered templates, Gunicorn, WhiteNoise, and fail-closed Stripe SDK adapter. Uses uv, pytest-django, ruff, Dokku health check, release-phase migrate, and two cron jobs. Has no CI; a checklist gates local releases.
  • Source: Keeps client work private except this case study. Withholds name, prices, address, and student information.
  • Limitations: No real payment moved; the Stripe adapter uses a mocked SDK, and the missing client-owned account makes calls raise StripeNotConfigured and stops enrollment at “Payment setup pending”. Backups and restore drill lack evidence, blocking operations; 13 concurrency and constraint tests skip on SQLite, so only PostgreSQL counts. Seven tests fail today: fixtures pin “now” to September 1 while views read the clock.

Domain model

Defines a 36-term glossary in the first commit and 43 terms now. Every entry bans words; five ban “Booking.” An Inquiry creates no reservation or enrollment; confirmation email says so. Keeps the calendar unpublished and permanent slots from self-service.

Date-bounded RecurrenceVersions generate LessonOccurrences for an ongoing Enrollment. Moving one supersedes it with an original link; moving a series closes its version and opens a successor without rewriting history. Enrollments use eleven states; each command adds an immutable transition.

Scheduling and value

Blocks introductory, recurring, fifth, replacement, and makeup lessons from sharing an instructor, room, or half hour. Two PostgreSQL exclusion constraints key active tstzrange by instructor and room using migration-installed btree_gist.

Tests ten cases across five kinds and both resources. Two barrier-synchronized threads collide in the same second; one lesson is created and one rejected. Also races a hold against a lesson, five inquiry posts against the rate limit, and two workers against one outbox row.

The first PostgreSQL run on August 28 found a nullable-join row lock SQLite ignored; locking only the enrollment row fixed it.

Records cancellations in an immutable append-only ledger with named entitlements. Five categories set kind and whether unredeemed value becomes money or expires. Tests the ordered four-kind trail from closure, instructor cancellation, expiry, and redemption.

Staging and launch

Includes five public pages, one review-only and noindex, plus 22 authenticated staff routes. Provides six guardian actions through digest-stored, expiring, revocable, audited links. An outbox records each notification in its causal transaction, keys it for idempotency, retries five times, and exposes failures for staff retry.

The first architecture decision record replaces a TypeScript Cloudflare shell and unifies scheduling, authorization, policies, and billing transactionally. The owner’s payment account, public name and domain, and reviewed policy text gate launch. Requests and payments remain unproven until a stranger completes both without help.