TEAM PROJECT · 2025 · AI / RAG · ROLE: BACKEND
CurriculumGenerator
Upload course PDFs, get a structured curriculum back.
- Role
- Backend developer (36 of 54 commits)
- Timeline
- Nov 2025
- Team
- Team of four
- Stack
- Python, Flask, Redis + RQ, MongoDB, ChromaDB, SentenceTransformers, React
The problem
Turning a pile of reference material into a structured course outline takes days. CurriculumGenerator takes course PDFs and returns a structured curriculum, saved with its title, hours and modules so you can come back to it later.
This was a team project. I wrote most of the backend, and I say below exactly what was mine and what was my teammates'.
Constraints
- A generation takes long enough that a normal web request would time out before it finished.
- The answer has to come from the uploaded material, so retrieval has to happen before generation.
- The app needs a predictable shape back, so the model must return curriculum JSON in a fixed schema.
- The background worker has to run on Windows, macOS and Linux.
What I built
From PDF to curriculum
Every generation runs the same pipeline:
- PyMuPDF extracts the text from the uploaded PDFs.
- The text is split into chunks, either overlapping or paragraph-aware.
- SentenceTransformers turns each chunk into an embedding.
- The embeddings are stored in ChromaDB.
- A strict prompt, with the retrieved material, makes the model return the curriculum as JSON in a fixed schema.
The API and the job queue
The backend is a Flask API with endpoints to list all curricula, fetch one by id, and export one as a PDF. Generation does not run inside the request. It runs as a job on a Redis queue using RQ, and a worker picks it up. MongoDB keeps the curriculum history and its metadata.
My part and the team's
The repository I link is my fork of a teammate's original. It has 54 commits, and 36 of them are mine.
- Backend API and the job flow
- Prompt and model updates
- Fix for a bug that overwrote data in the database
- Timeout tuning on both frontend and backend
- Fix for a PDF flag bug
- Viewing past curricula from history
- The export-PDF endpoint
- Parsing curricula that arrive as a string or as an object
- Suhani Jagtap: the frontend, the PDFs and the export styling. It is her original repository.
- Aarnav and arpitchhabra003 also contributed to the project.
Hard problems
Generations that outlast a request
A long generation and a web request do not fit together: the request times out while the model is still working. The job queue solves this by separating the two. The API accepts the request and puts a job on the queue, and the worker does the slow part. I also tuned the timeouts on both the frontend and the backend so neither side gave up early.
A worker that runs on every OS
RQ's standard Worker forks a process for each job, and Windows cannot fork. SimpleWorker runs jobs inside the worker process instead. The worker code in core/worker.py picks SimpleWorker on Windows and macOS and Worker on Linux, so the same code starts anywhere.
Bugs in the data path
The rest of my work was in how data moved around: a bug where the database was being overwritten, a bug with the PDF flag, and curriculum data that could come back as either a string or an object and needed parsing for both.
Results
Generation works end to end: upload PDFs, get a structured curriculum, see it in the history later, and export it as a PDF. Of the 54 commits in the repository, 36 are mine.
What I'd do next
Add tests around curriculum parsing. It has to handle two formats, which is exactly where regressions hide.
Then compare the two chunking modes, overlapping and paragraph-aware, on the same PDFs, and keep the one that gives better curricula.
Next project →
01Multi-Org CRM PlatformOne API over six separate CRM organisations, built so writes stay correct even when the CRM gives no guarantees.- Java 21
- Spring Boot
- Apache Camel
- PostgreSQL
- Firestore
- Cloud Run
- Cloud Tasks
- GitHub Actions