A full-featured institutional management system built with Django, designed for Kenyan higher education institutions. π°πͺ
Handles everything from student enrollment and academic session management to fee collection, timetabling, and role-based access control. A student's portal that doesn't make you want to drop out.
Built out of genuine frustration with Kenyan university portals β the kind that require a specific browser, a stable connection, and apparently a blood offering to load your fee balance.
- ποΈ Multi-level institutional hierarchy β Institution β School β Department β Programme β Class
- ποΈ Academic session and semester management with automated rollover
- π Curriculum management with course enrollment (core, elective, and common units)
- β‘ Automatic enrollment of students into core courses on registration
- π Timetable management with venue and lecturer clash detection
- π Exam scheduling with cross-programme clash detection
- π Comprehensive student profiles (personal, family, emergency contacts, academic)
- β Student reporting per session (online and physical)
- βΈοΈ Deferral and graduation tracking
- πͺ Proxy model views: Resident Students, Deferred Students, Graduated Students
- π§Ύ Per-class fee structures with itemised breakdowns (tuition, registration, hostel, etc.)
- π Per-student fee accounts with real-time balance tracking
- π³ Payment recording (M-Pesa, bank transfer, cash)
β οΈ Overdraft detection and carry-forward to next session or refund flagging
- π₯ Custom user model with role-based access (Student, Staff, Admin)
- ποΈ Role hierarchy: Institution Admin β School Admin β Dept Admin β Lecturer β Student
- π Scoped Django admin β each role sees only the data within their jurisdiction
- β‘ Automatic group and permission assignment on user creation
- π CAT and exam result recording per student per course
I know, I know.
But hear me out β my university portal was so bad it became a personality trait. Slow on a good day, broken on a bad one, and somehow requiring a specific browser, a stable connection, and the right phase of the moon just to check your fee balance.
I'm a CS student. I got bored. Here we are.
Built with curiosity, mild institutional frustration, and the audacity to think a student could just... build a better one.
β Bett π°πͺ, 2026
wanna read me rumble more? β why.md π€
| Layer | Technology |
|---|---|
| π Backend | Python, Django |
| π§βπ» Frontend | HTML,CSS,JS |
| ποΈ Database | PostgreSQL |
| π Auth | Django AbstractBaseUser + custom UserManager |
| β‘ Automation | Django signals for enrollment and session events |
StudentsPortal/
βββ base/
β βββ models.py/ # All models
β βββ admin.py/ # Custom ModelAdmin classes
β βββ signals.py # Signal receivers
β βββ managers.py # Custom UserManager
β βββ forms.py/ # UserCreationForm, UserChangeForm
β βββ apps.py # AppConfig with signals loader
β βββ fixtures/ # Seed data - depracted
β βββ templates/ # html templates for the site
β βββ static/ # static files
β βββ management/
β βββ tests/
β
βββ docs/ # Full documentation
βββ user-upload/ # media folder for user uploaded files
βββ manage.py
βββ requirements.txt
User Custom AbstractBaseUser (email login)
βββ Student OneToOne β User, academic + personal profile
βββ Lecturer OneToOne β User, department
βββ DeptAdmin OneToOne β User, department
βββ SchoolAdmin OneToOne β User, school
βββ InstitutionAdmin OneToOne β User
School
βββ Department
βββ Programme
βββ Tclass (Class)
βββ Curriculum (Tclass + Course + Session + Lecturer)
Session Academic year + semester, institution-wide
Reporting Student check-in per session
FeeStructure Per class per session fee breakdown
StudentFeeAccount Per student ledger
Payment Individual transactions (M-Pesa, bank, cash)
OverDraft Overpayment tracking with carry-forward/refund status
Results CAT and exam scores per student per course
Timetable Class schedule with venue and lecturer conflict prevention
- Python 3.10+
- PostgreSQL
git clone https://github.com/thelaughingbett/Naet.git
cd Naet
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtCopy the example env file and fill in your values:
cp .env.example .envSECRET_KEY=your-secret-key
DEBUG=True
DATABASE_URL=postgres://user:password@localhost:5432/studentsportalpython manage.py migrate
python manage.py createsuperuser
python manage.py loaddata base/fixtures/data.json # optional seed datapython manage.py runserverDemo data is non-persistent
Navigate to /admin and log in with your superuser credentials.
Role-based access is enforced automatically:
| Role | Access |
|---|---|
| ποΈ Institution Admin | Full access to everything |
| π« School Admin | Own school's departments, students, lecturers |
| π’ Dept Admin | Own department's students and lecturers |
| π¨βπ« Lecturer | View-only access to students |
-
π UUID primary keys on all models for security and portability
-
ποΈ Session is institution-wide β one active session at a time, referenced by all academic and financial records
-
πͺ Proxy models β
DeferredStudent,ResidentStudent, andGraduatedStudentprovide filtered admin views of the same student table without duplicating data -
β‘ Signal-driven enrollment β students are automatically enrolled in core and common unit courses when created
- π Attendance tracking (per timetable slot)
- π Exam scheduling and invigilation assignment
- π± SMS notifications via Africa's Talking API
- π REST API (Django REST Framework)
- πΎ IoT sensor integration for smart campus monitoring
Everything you need to understand how this system is built and why.
| Document | What's inside |
|---|---|
| πΊοΈ System Overview | High-level sketch of the whole system β read this first |
| ποΈ Architecture | Big picture β how the layers fit together |
| ποΈ Database Schema | Every model and their relationships |
| π Models Reference | Field-by-field model breakdown |
| π Admin & Permissions | Who can see and do what |
| Module | Description |
|---|---|
| π§ Email Generation | Automated email creation and delivery |
| π’ ERP | Enterprise resource planning integration |
| π Events | Event scheduling and management |
| π° News | News publishing and feed management |
| π Notifications | Email and SMS delivery via pluggable backends |
| π³ Payments | Payment processing and transaction logic |
| π Results | Academic results and grade management |
| π Timetabling | Schedule management and clash detection |
Built by Emmanuel Bett . π°πͺ
GitHub Β·
This system collects and processes sensitive personal data including national ID numbers, financial records, family information, and special category data (religion, ethnicity) under the Kenya Data Protection Act, 2019.
Any institution deploying this system must:
- Register as a data controller with the ODPC at odpc.go.ke
- Appoint a Data Protection Officer
- Display a privacy notice at registration
- Enable HTTPS β no exceptions
Full details in DATA_PROTECTION.md π
Beyond data protection, institutions deploying this system have obligations under:
| Law | Relevance |
|---|---|
| Computer Misuse and Cybercrimes Act, 2018 | Unauthorized access, credential security |
| Universities Act, 2012 (CUE) | Academic records retention, CUE audit readiness |
| Kenya Revenue Authority | 7-year financial record retention, fee receipting |
| HELB | Semester enrollment verification exports |
| Persons with Disabilities Act | Portal accessibility β WCAG 2.1 AA |
Full details in LEGAL.md βοΈ
Generic, event-driven synchronisation of any model to any external ERP system.
Something happens anywhere in the system
βββ dispatch_erp_event(instance, event)
βββ transaction.on_commit β erp_sync.delay()
Celery task
βββ resolves instance from DB
βββ looks up event in ERPRegistry
βββ runs each registered handler independently
βββ logs every attempt to ERPSyncLog
Retry schedule (exponential backoff, per handler)
βββ attempt 1 β 60s
βββ attempt 2 β 120s
βββ attempt 3 β 240s
βββ attempt 4 β 480s
βββ attempt 5 β exhausted β notify admin
model.action
payment.confirmed enrollment.approved
payment.failed deferment.created
reporting.submitted result.published
student.graduated
Implement AbstractERPTask, register it β nothing else changes:
class MyTask(AbstractERPTask):
event = 'payment.confirmed'
model = 'Payment'
max_retries = 5
retry_backoff = 120
def sync(self, instance) -> ERPSyncResult:
# instance is a Payment object
...from erp.dispatch import dispatch_erp_event
dispatch_erp_event(payment, 'payment.confirmed')
dispatch_erp_event(enrollment, 'enrollment.approved')
dispatch_erp_event(reporting, 'reporting.submitted')
dispatch_erp_event(deferment, 'deferment.created')Full details β docs/modules/erp/index.md
π°πͺ Built for the Kenyan institutional context β by someone who suffered through the alternative
Is it finished? No. Is it better than what you're currently using? Almost certainly yes.