# PTA Backend — Academic Year / Term / Class Level / Student / Promotion Subsystem
FastAPI backend for a Ghana PTA SaaS (Parents–Teachers Association dues, meetings,
announcements, SMS, reports). This README focuses on the **academic calendar +
class ladder + student enrollment + end-of-term promotion** part of the system.
- Repo layout
- Quick start
- Environment
- Data model
- Entities overview
- AcademicYear — `academic_years`
- AcademicTerm — `academic_terms`
- ClassLevel — `class_levels`
- Student — `students`
- ParentStudentLink — `parent_student_links`
- Class-level ladder & naming
- Canonical name normalization
- Academic calendar CRUD & lifecycle
- Academic years
- Terms
- Selecting the "current" term / year
- Closing a term + promotion trigger
- Promotion engine
- How the promotion ladder is built
- Per-student processing
- Promotion return schema
- JHS → SHS transition
- Form 1–3 ⇄ JHS 1–3 one-time migration script
- Student validation rules
- Migrations
- Testing locally
- Celery worker (SMS + scheduled locks)
- Design notes / known quirks for reviewers
---
## Repo layout
```text
app/
├── main.py # FastAPI app, startup DB probe
├── core/
│ ├── config.py # Settings; DATABASE_URL ⇄ _SYNC auto-derive
│ ├── database.py # Sync SQLAlchemy engine (Neon pooler aware)
│ ├── security.py # JWT, bcrypt, require_permission(...)
│ ├── middleware.py
│ └── redis_url.py
├── models/
│ ├── academic.py # AcademicYear, AcademicTerm, TermStatus enum
│ ├── class_level.py # ClassLevel
│ ├── student.py # Student
│ ├── parent_student_link.py # ParentStudentLink
│ └── ...
├── services/
│ ├── promotion.py # promote_students_for_year(...)
│ ├── class_level_names.py # normalize_class_level_name, find_class_level
│ ├── student_validation.py # validate_student_fields
│ ├── dues_balance.py …