A modern MERN stack application for generating conflict-free college timetables using advanced constraint-solving algorithms.
- Global Scheduler: Generates zero-conflict timetables for all classes simultaneously using MRV heuristic and forward-checking
- Constraint Enforcement:
- No teacher/room/class conflicts
- Lab scheduling (continuous 2-period blocks at allowed starts: 1, 3, 5, 7)
- Teacher availability & load limits (per-day and per-week)
- Subject session-per-week compliance
- Modern Tech Stack:
- Backend: Node.js (ESM), Express, MongoDB (Mongoose v7+)
- Frontend: React (Create React App), Tailwind CSS
- Real-time diagnostics on scheduling failures
- Intuitive Admin UI:
- Create faculties, subjects, classes, rooms
- Edit faculty availability (click-to-toggle grid)
- Run generation and view timetables in tabular format
- Diagnostics modal showing constraint violations
- Node.js >= 18 (tested with Node 20/22)
- MongoDB (local or Atlas cloud)
-
Clone or navigate to project:
cd Schedulo -
Start MongoDB (Windows):
net start MongoDB # Or check Services > MongoDB Server -
Setup Backend:
cd server npm install npm run devShould print:
Database connected successfullyandServer running at http://localhost:3001 -
Setup Frontend (in a new terminal):
cd client npm install npm startOpens http://localhost:3000 in your browser
-
Seed Sample Data:
cd server node sample-seed.jsCreates 1 class (CSE-2A), 2 subjects (1 lecture + 1 lab), 2 faculties, 3 rooms, default config
-
Generate Timetable:
- In the browser, click Generate All Timetables
- Or via curl:
curl -X POST http://localhost:3001/api/timetable/generate-all \ -H "Content-Type: application/json" \ -d '{"periodsPerDay":8}'
-
View Results:
- In the browser, click a class card to view its timetable
- Check the table for scheduled subjects, faculty, and lab markings (blue background)
Schedulo/
├── server/ # Node.js + Express backend
│ ├── app.js # Express server entry
│ ├── package.json
│ ├── .env.example
│ ├── models/
│ │ ├── connection.js # MongoDB connection
│ │ ├── Faculty.js
│ │ ├── Subject.js
│ │ ├── ClassRoom.js
│ │ ├── Room.js
│ │ ├── Timetable.js
│ │ └── Config.js
│ ├── controllers/
│ │ ├── faculty.controller.js
│ │ ├── subject.controller.js
│ │ ├── class.controller.js
│ │ ├── room.controller.js
│ │ ├── config.controller.js
│ │ └── timetable.controller.js
│ ├── routes/
│ │ ├── faculty.routes.js
│ │ ├── subject.routes.js
│ │ ├── class.routes.js
│ │ ├── room.routes.js
│ │ ├── config.routes.js
│ │ └── timetable.routes.js
│ ├── services/
│ │ └── global-scheduler.service.js # Core scheduling engine
│ ├── sample-seed.js # Sample data seeder
│ └── README.md # Server-specific docs
│
├── client/ # React frontend
│ ├── public/
│ │ ├── index.html
│ │ ├── manifest.json
│ │ └── robots.txt
│ ├── src/
│ │ ├── api.js # Centralized API client
│ │ ├── index.js
│ │ ├── index.css # Tailwind directives
│ │ ├── App.js
│ │ ├── App.css
│ │ └── components/
│ │ ├── Dashboard.jsx # Main container
│ │ ├── FacultyForm.jsx # Create faculty + availability editor
│ │ ├── SubjectForm.jsx # Create subjects
│ │ ├── ClassForm.jsx # Create classes & assign subjects
│ │ ├── RoomForm.jsx # Create rooms
│ │ ├── ClassCard.jsx # Class selector
│ │ ├── TimetableGrid.jsx # Display timetable table
│ │ ├── DiagnosticsModal.jsx # Error modal with suggestions
│ │ └── AvailabilitySelector.jsx
│ ├── package.json
│ ├── tailwind.config.js # Tailwind CSS config
│ ├── postcss.config.js # PostCSS config
│ └── README.md
│
└── README.md # This file
GET /api/faculty- List all facultyPOST /api/faculty- Create facultyPUT /api/faculty/:id- UpdateDELETE /api/faculty/:id- Delete
GET /api/subjects- List subjectsPOST /api/subjects- Create subjectPUT /api/subjects/:id- UpdateDELETE /api/subjects/:id- Delete
GET /api/classes- List classes with subjects populatedPOST /api/classes- Create classPUT /api/classes/:id- UpdateDELETE /api/classes/:id- Delete
GET /api/rooms- List roomsPOST /api/rooms- Create roomPUT /api/rooms/:id- UpdateDELETE /api/rooms/:id- Delete
GET /api/config- Get global timetable configPUT /api/config- Update config
- POST /api/timetable/generate-all - Run global scheduler
- Body:
{ periodsPerDay?: 8, days?: ["Mon", ...] }(optional) - Response:
{ success: true, timetables: [...] }or{ success: false, error, diagnostics }
- Body:
- GET /api/timetable/all - List all timetables
- GET /api/timetable/class/:classId - Get timetable for one class
The scheduler uses backtracking with MRV (Minimum Remaining Values) heuristic and forward-checking to find a conflict-free timetable.
- Task Generation: For each class and subject, create tasks (one per session per week)
- Lectures: length 1
- Labs: length =
labSizePeriods(default 2)
- Domain Building: For each task, compute all valid placements
- Placement = (class, day, start period, room)
- Validity checks: fits in day, respects breaks, lab start valid, faculty available, teacher/room/class not busy
- MRV Heuristic: Always pick task with smallest domain to assign next
- Lab-First: Sort labs before lectures (more constrained)
- Forward-Checking: After placing a task, remove conflicting placements from remaining domains
- Backtracking: If a task has zero valid placements, backtrack and try another assignment
- Attempt Cap: Stop after 5M attempts to prevent infinite loops
- Diagnostics: On failure, identify problematic tasks and overloaded faculty
- ✓ No teacher double-booking across any class
- ✓ No room conflicts
- ✓ No class period overlap
- ✓ Lab sessions are exactly 2 periods (configurable) and start only at 1, 3, 5, 7 (1-based)
- ✓ Teacher availability: respect per-day availability array
- ✓ Teacher load: maxPerDay and maxPerWeek limits
- ✓ Subject sessions: exactly sessionsPerWeek scheduled
If scheduling fails, the response includes:
{
"success": false,
"error": "Scheduling failed after X attempts...",
"diagnostics": {
"totalTasks": 8,
"labTasks": 2,
"attempts": 1234567,
"maxAttempts": 5000000,
"problematicTasks": [
{
"task": "Database Lab",
"class": "CSE-2A",
"faculty": "Dr. Bob",
"reason": "No valid placement found (check availability, load limits, or increase periods)"
}
],
"overloadedFaculty": [
{
"name": "Dr. Alice",
"requiredSlots": 16,
"availableSlots": 40,
"suggestion": "Increase periodsPerDay or reduce subject sessions for this faculty"
}
]
}
}The frontend displays this in a modal with actionable suggestions.
{
name: String,
shortName: String,
availability: { Mon: [1..8], Tue: [...], ... },
maxLoadPerDay: Number,
maxLoadPerWeek: Number
}{
name: String,
code: String,
type: "lecture" | "lab",
sessionsPerWeek: Number,
labSizePeriods: Number,
faculty: ObjectId (Faculty)
}{
name: String,
department: String,
year: Number,
section: String,
subjects: [ObjectId], // Subject IDs
periodsPerDay: Number,
days: [String]
}{
name: String,
type: "lab" | "classroom",
capacity: Number
}{
classRoom: ObjectId,
periods: [
{
day: String,
periodIndex: Number, // 0-based
subject: ObjectId,
faculty: ObjectId,
room: ObjectId,
isLab: Boolean
}
],
generatedAt: Date
}{
workingDays: [String],
periodsPerDay: Number,
periodDurationMinutes: Number,
breaks: [{ afterPeriod: Number, durationMinutes: Number }],
labAllowedStarts: [Number] // 1-based: [1, 3, 5, 7]
}- Create some faculties, subjects, and a class in the browser
- Click Generate All Timetables
- Click a class card to see its timetable
- Verify:
- No teacher appears twice in the same period (across any class)
- Lab blocks are 2 periods and only at positions 1-2, 3-4, 5-6, 7-8
- All subject sessions are scheduled
# After running sample-seed.js:
# 1. Generate
curl -X POST http://localhost:3001/api/timetable/generate-all \
-H "Content-Type: application/json" \
-d '{"periodsPerDay":8}'
# 2. Get all timetables
curl http://localhost:3001/api/timetable/all
# 3. Check diagnostics (if generation failed)
# The response will show problematicTasks and overloadedFacultyCreate server/.env (copy from .env.example):
MONGO_URI=mongodb://127.0.0.1:27017/schedulo
# Or MongoDB Atlas:
# MONGO_URI=mongodb+srv://user:pass@cluster.mongodb.net/schedulo
NODE_ENV=development
PORT=3001
- Ensure MongoDB is running:
net start MongoDB - Check
MONGO_URIin.env
- Run
node sample-seed.jsto create sample data - Or manually create a class with at least one subject
- Problematic tasks: Check faculty availability, increase
sessionsPerWeek, or add periods - Overloaded faculty: Reduce subject sessions, add more faculties, or increase
maxLoadPerWeek - Insufficient rooms: Add more lab or classroom rooms in the UI
- Verify backend is running on
http://localhost:3001 - Check for CORS errors in browser console
- Ensure both are on the same machine or update API_BASE in
client/src/api.js
- Scheduler optimized for ~30 classes / ~400 tasks
- Uses backtracking with forward-checking (NP-hard problem)
- Max 5M attempts prevents timeout
- Labs scheduled first (longer, more constrained)
- Deterministic ordering for reproducibility
For larger institutions, consider:
- Multi-stage scheduling (labs → lectures)
- Incremental constraint relaxation
- Genetic/simulated-annealing approaches (future enhancement)
| Layer | Technology | Version |
|---|---|---|
| Frontend | React | 19.x |
| Styling | Tailwind CSS | 3.x |
| Backend | Node.js + Express | 20+ / 5.x |
| Database | MongoDB | 6.x+ |
| ODM | Mongoose | 7.x+ |
| Build | Create React App | 5.x |
ISC
To extend or modify:
- Backend changes: Update models/controllers/routes in
server/ - Frontend changes: Update components in
client/src/components/ - Scheduler tweaks: Modify
server/services/global-scheduler.service.js - API integration: Update
client/src/api.js
For issues or questions:
- Check
server/README.mdfor backend-specific docs - Review scheduler algorithm section above
- Check browser console for client errors
- Check server logs for backend errors
Happy scheduling! 🎓📅