A dual-stack school timetable generator combining a Qt5 C++ GUI with a Flask + Vanilla JS web interface, sharing an SQLite database. Includes two independent solver pipelines β a high-performance C++ backtracking/MRV solver for uniform period grids, and a Python greedy solver with per-class dynamic timelines for schools that need different lesson durations across streams.
- Two solver engines: C++ BacktrackingSolver (MRV + LCV + domain propagation) and GreedySolver; Python GreedySolver with dynamic timelines
- Per-class lesson durations: Form 4 can use 60-minute periods while Form 1 uses 40-minute periods, all under the same break schedule
- Configurable time blocks: Define assemblies, morning tea, lunch, games β any break type, any duration, per day of the week
- Full constraint set:
maxPerDay,blockSize(double periods),secondTeacherId, week types (A/B/Every), teacher max consecutive, class period windows, room capacity/type - Fixed events: Lock specific (day, period) slots for recurring events (lunch, assembly)
- Teacher preferences: Mark slots as BLOCKED/PREFERRED/UNDESIRABLE per teacher
- Substitutions: Request, suggest, approve, and track substitute teacher assignments
- Divisions: Group classes for parallel scheduling
- Versioning: Save, compare, and restore timetable solutions (aSc-style)
- Drag & drop: Manual slot adjustment with undo/redo and conflict checking
- Export: Professional aSc-style PDF (ReportLab), HTML, CSV
- Analytics: Teacher load distribution, room utilization, gap statistics
- Flask web API: 60+ REST endpoints β full CRUD, generate, export, analytics
- Custom Timetable modal: Load sample Kenyan school data, adjust breaks & durations, generate PDF in one click
- Docker support: Containerized deployment
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β C++ Binary (timetableGen) β
β β
β main.cpp βββ QApplication βββ MainWindow βββ Qt5 GUI β
β β (16 widget classes) β
β ββββ startFlaskWebServer() βββ python3 web/app.py β
β β (port 5000-5009) β
β ββββ --solve-from-db βββ DataManager βββ TimetableEngine β
β βββ BacktrackingSolverβ
β βββ GreedySolver β
β β
β DataManager βββ SQLiteService βββ timetable.db β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Flask Server (web/app.py) β
β β
β 60+ API endpoints: CRUD, generate, export, analytics, versions β
β β
β /api/generate (POST) βββ C++ binary --solve-from-db (subprocess) β
β /api/generate-timetable (POST) βββ Python greedy solver + PDF β
β /api/export/pdf (GET) βββ HTMLβpdfkit or ReportLab β
β β
β timeline_gen.py βββ per-class dynamic timelines + greedy solver β
β pdf_generator.py βββ ReportLab aSc-style PDF (direct canvas) β
β β
β templates/index.html βββ SPA shell β
β static/js/app.js βββ 1665 lines vanilla JS β
β static/css/asc.css βββ 290 lines β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
offlineTimeTableCpp/
βββ main.cpp # Entry point β GUI mode or --solve-from-db
βββ Makefile # GNU Make build
βββ CMakeLists.txt # CMake build (alternative)
βββ pyproject.toml # Python dependencies
βββ AGENTS.md # Developer guide
β
βββ models/ # Domain structs (17 headers)
β βββ Teacher.h, Subject.h, SchoolClass.h, Room.h, Lesson.h
β βββ FixedEvent.h, TimeBlock.h, LessonUnit.h
β βββ Day.h, Period.h, RoomType.h, SubjectRequirement.h
β βββ Division.h, Substitution.h
β βββ TeacherConstraint.h, TeacherPreference.h, ConstraintViolation.h
β
βββ services/ # Business logic (19 .cpp/.h pairs)
β βββ DataManager.h/.cpp # Central data hub + CRUD
β βββ SQLiteService.h/.cpp # All database operations
β βββ TimetableEngine.h/.cpp # Solver orchestrator
β βββ BacktrackingSolver.h/.cpp # MRV + LCV + domain propagation
β βββ GreedySolver.h/.cpp # Heuristic fallback
β βββ FeasibilityChecker.h/.cpp # Pre-solve validation
β βββ ConflictChecker.h/.cpp # Post-placement checks
β βββ TimetableEvaluator.h/.cpp # Score computation
β βββ AnalyticsService.h/.cpp # Statistics
β βββ ExportService.h/.cpp # CSV / HTML export
β βββ PdfReportService.h/.cpp # Qt5 PDF export
β βββ TimelineGenerator.h/.cpp # C++ dynamic timeline
β βββ ... # Benchmark, UndoRedo, ConstraintExplain
β
βββ timetable/ # Grid model
β βββ Timetable.h/.cpp # TimetableCell, 2D grid storage
β
βββ gui/ # Qt5 GUI widgets (16 files)
β βββ MainWindow.cpp
β βββ ribbon/RibbonToolbar.cpp
β βββ sidebar/DataSidebar.cpp
β βββ timetableview/{TimetableViewWidget,TimetableScene,LessonCardItem}.cpp
β βββ dashboard/DashboardWidget.cpp
β βββ teachers/TeacherDialog.cpp, subjects/SubjectDialog.cpp
β βββ classes/ClassDialog.cpp, rooms/RoomDialog.cpp
β βββ substitutions/SubstitutionWidget.cpp
β βββ divisions/DivisionWidget.cpp
β βββ constraints/ConstraintRelaxationDialog.cpp
β
βββ web/ # Python web layer
β βββ app.py # Flask server β 60 API endpoints
β βββ timetable_gen.py # Dynamic timeline + greedy solver
β βββ pdf_generator.py # ReportLab aSc-style PDF
β βββ static/js/app.js # SPA frontend (1665 lines)
β βββ static/css/asc.css # Styles (290 lines)
β βββ templates/index.html # SPA shell
β
βββ tests/ # Test suites
β βββ test_runner.cpp # 12 C++ tests (315 lines)
β βββ test_api.py # 17 Flask integration tests
β βββ test_timetable_gen.py# 24 Python unit tests
β
βββ utils/PathUtil.h # Path resolution
βββ data/ # SQLite database (gitignored)
βββ docs/ # Documentation
β βββ SYSTEM_REVIEW.md # Full system review
βββ Dockerfile # Container build
βββ run.sh # Convenience launcher
- Compiler: GCC (g++) with C++17 support
- Qt5: Widgets, Sql, PrintSupport modules
- pkg-config (for Qt discovery)
Install on Ubuntu/Debian:
sudo apt install g++ make pkg-config qtbase5-dev libqt5sql5-sqlite libqt5printsupport5pip install flask flask-cors # Required
pip install reportlab # Optional β for aSc-style PDF export
pip install pdfkit # Optional β for HTMLβPDF export (requires wkhtmltopdf)make # Release build (./timetableGen)
make BUILD=debug # Debug build with sanitizers
make test # Build and run C++ tests
make clean # Remove build artifacts
make format # Run clang-format on all source filescmake -B build_cmake -S .
cmake --build build_cmake -j
./build_cmake/timetableGen# C++ tests
make test
# or
./build_cmake/test_runner
# Python unit tests
python3 -m pytest tests/test_timetable_gen.py -v
# Flask integration tests
python3 tests/test_api.py./timetableGenLaunches the Qt5 desktop application and automatically starts the Flask web server on http://127.0.0.1:5000.
./timetableGen --solve-from-dbLoads timetable data from the SQLite database, runs the C++ solver, and writes a JSON result to stdout. Used by the Flask /api/generate endpoint.
python3 web/app.pyStarts the Flask server without the C++ GUI. The web interface provides full CRUD, generation, export, and analytics.
- Click Custom TT in the Timetable tab
- Click Load Sample School to load realistic Kenyan school data (Alliance High School)
- Adjust day start/end, break times, per-class lesson durations
- Click Generate Timetable β downloads a professional aSc-style PDF
| Endpoint | Method | Purpose |
|---|---|---|
/api/teachers, /api/subjects, /api/classes, /api/rooms |
GET/POST/PUT/DELETE | Entity CRUD |
/api/lessons |
GET/POST/PUT/DELETE | Lesson CRUD with combined classes |
/api/generate |
POST | Run C++ solver, return JSON result |
/api/generate-timetable |
POST | Run Python dynamic solver, return PDF |
/api/sample-school |
GET | Sample Kenyan school configuration |
/api/analytics |
GET | Teacher load, room utilization, gap stats |
/api/export/html |
GET | Full HTML timetable document |
/api/export/pdf |
GET | PDF timetable (via pdfkit) |
/api/conflicts |
GET | Current conflict list |
/api/substitutions |
GET/POST | Substitution lifecycle |
/api/divisions |
GET/POST/PUT/DELETE | Division CRUD |
/api/versions |
GET/POST | Save/restore/compare timetable versions |
/api/data/export |
GET | Full JSON backup |
/api/data/import |
POST | JSON restore |
See docs/SYSTEM_REVIEW.md or web/app.py for the complete endpoint listing.
| Solver | Language | Algorithm | Grid Model | Use Case |
|---|---|---|---|---|
| BacktrackingSolver | C++ | MRV + LCV + domain propagation | Uniform (5Γ8) | High-quality solutions, all classes same period duration |
| GreedySolver (C++) | C++ | Weighted slot scoring | Uniform (5Γ8) | Fast fallback for simple schedules |
| GreedySolver (Python) | Python | First-fit with difficulty ordering | Per-class dynamic | Schools with different lesson durations per stream |
{
"school": { "name": "School Name" },
"configuration": {
"day_start": "07:30",
"day_end": "16:30",
"default_lesson_duration_minutes": 40
},
"time_blocks": [
{ "name": "Assembly", "type": "fixed_break",
"start": "07:30", "end": "08:00", "days": ["Monday", "Friday"] },
{ "name": "Lunch", "type": "fixed_break",
"start": "12:30", "end": "13:30", "days": ["All"] }
],
"teachers": [
{ "id": "T1", "name": "Paul Inyangala", "abbreviation": "PI",
"qualified_subjects": ["MATH"] }
],
"classes": [
{ "id": "F1A", "name": "Form 1 A",
"lesson_duration_minutes": 40,
"subject_requirements": [
{ "subject": "MATH", "abbreviation": "MATH",
"lessons_per_week": 5, "max_per_day": 2, "block_size": 1 }
]
}
]
}MIT β see LICENSE.MD
Built with Qt5, Flask, ReportLab, SQLite, and love for Kenyan schools.