A Django REST Framework API for tracking job applications through their full status lifecycle, where a custom user model with token authentication, strict per-user data isolation, and an auditable status-history trail are the central engineering problems, not an afterthought.
Nobody has started this yet — be first.
Logging in with the correct password fails
POST /api/auth/login/ is the only way an existing user gets back into the app once their original registration token is gone (expired client storage, a new device, a cleared browser). Right now, every single login attempt with completely correct credentials fails with "Unable to log in with the provided credentials." That is a full authentication outage for the returning-user flow -- new signups still work (registration hands back a token directly), but anyone who already has an account and needs to log back in cannot get past this endpoint. This is the kind of bug that generates a support ticket flood and an on-call page within minutes of shipping, because it doesn't fail for one edge case -- it fails for 100% of real login attempts.
About this project
A Django REST Framework API for a job-application tracker: users register, authenticate via DRF tokens, and manage Application rows that move through a fixed lifecycle -- applied -> interview -> offer -> rejected -- with rejected terminal. Every state change is validated against a single ALLOWED_TRANSITIONS table and, in the same operation, appends an immutable StatusChange record, so the API can never end up with a status that isn't backed by a corresponding entry in its own history.
Three problems sit at the center of the tickets, not the edges: a custom model wired in from project init (the only point in a Django project's life where that's safe) paired with DRF token authentication instead of sessions; as the single choke point that has to filter to the requesting user before any object is ever looked up by id, so a request for someone else's row finds nothing to 403 on and correctly 404s instead; and 's exposure to a classic check-then-act race, where two independently-fetched in-memory copies of the same row can validate against a stale status and silently corrupt the very audit trail the app exists to guarantee.
The application list shows the oldest applications first
The whole point of a job application tracker's dashboard is "what's happening with my job search right now" -- the applications a user is actively waiting to hear back on need to be easy to find. Right now GET /api/applications/ returns the user's oldest application first and their newest one last, so for anyone with more than a page's worth of applications, the thing they care about most is buried at the bottom (or on a later page) while stale applications from months ago sit at the top forever. Users will reasonably conclude the app "isn't tracking their new application" when it's actually right there -- just at the end of the list.
accounts.UserApplicationViewSet.get_queryset()Application.apply_transition()Seven tickets are deliberately injected into an otherwise fully working, fully tested codebase -- four are one-to-a-few-line bugs (a swapped keyword argument, a missing - in a Meta.ordering tuple, an empty transition set, a queryset that quietly stopped filtering by owner), and three are enhancements ranging from a straightforward serializer-level uniqueness check to hardening a real time-of-check-to-time-of-use race with select_for_update(). Every ticket has its own dedicated Django test -- run in isolation from the rest, by explicit dotted label, specifically so one ticket's brokenness can never mask or leak into another's -- that goes green only once the fix is actually correct.