EduTrack Online - SiteOwner API

Version: 1.1 Date: April 29, 2026 Target Audience: SiteOwner Frontend Backend: Django REST Framework


Table of Contents

  1. Teacher Management
  2. Student Management
  3. Course Management
  4. View Assistants (Read-Only)
  5. Recharge Codes
  6. Revenue Analytics
  7. SiteOwner Dashboard

1. Teacher Management

GET /accounts/teachers/

Description: List all teachers with full details.

Authentication: SiteOwner

Query Parameters:

Parameter Type Description
is_active Boolean Filter by active status
subject Integer Filter by subject ID
grades Integer Filter by grade ID
search String Search by name
all String Set to true to bypass pagination

Success Response — 200 OK:

{ "count": "Integer", "next": "String (URL) | null", "previous": "String (URL) | null", "results": [ { "id": "Integer", "user_id": "Integer", "username": "String", "name": "String", "phone": "String", "secondary_phone": "String | null", "gmail": "String", "gender": "String (male|female)", "profile_picture": "String (URL) | null", "biography": "String | null", "facebook": "String | null", "subject_detail": { "id": "Integer", "name": "String", "grades": "Array[Integer]", "grades_detail": [ { "id": "Integer", "name": "String" } ], "divisions": "Array[Integer]", "divisions_detail": [ { "id": "Integer", "name": "String", "grades": [ { "id": "Integer", "name": "String" } ], "school_types": [ { "id": "Integer", "name": "String" } ] } ], "school_types": "Array[Integer]", "school_types_detail": [ { "id": "Integer", "name": "String" } ], "grade_ids": "Array[Integer]", "division_ids": "Array[Integer]", "school_type_ids": "Array[Integer]" }, "grades_detail": [ { "id": "Integer", "name": "String" } ], "video_security_detail": { "id": "Integer", "name": "String", "price_per_student": "Decimal" }, "is_active": "Boolean", "created_at": "DateTime (ISO 8601)", "subject_id": "Integer | null", "grade_ids": "Array[Integer]", "video_security_id": "Integer | null" } ] }

GET /accounts/teachers//

Authentication: SiteOwner

Success Response — 200 OK: Same structure as list item.


POST /accounts/teachers/

Description: Create a new teacher user and profile atomically. Supports profile picture upload via multipart/form-data.

Authentication: SiteOwner

Content-Type: multipart/form-data

Request Body:

{ "username": "String (Required)", "password": "String (Required) — Minimum 8 characters", "name": "String (Required)", "phone": "String (Required) — Egyptian format: 010/011/012/015 + 8 digits", "secondary_phone": "String (Optional) — Egyptian format", "gmail": "String (Required) — Must be globally unique", "gender": "String (Required) — male|female", "profile_picture": "File (Optional) — image/jpeg|image/png|image/webp", "biography": "String (Optional)", "facebook": "String (Optional)", "is_active": "Boolean (Optional) — Default: true", "subject": "Integer (Required) — Subject ID", "grades": "Array[Integer] (Required) — Grade IDs", "video_security": "Integer (Optional) — VideoSecurity ID" }

Success Response — 201 Created: Same structure as GET list item.

Error Responses:

Status Condition Response Body
400 Username exists {"username": ["Username already exists."]}
400 Email exists {"gmail": ["This email is already associated with an account."]}
400 Invalid phone {"phone": ["Phone number must be exactly 11 digits..."]}
400 Weak password {"password": ["Password validation error"]}
403 Not authorized {"detail": "You do not have permission..."}

Business Rules:


PATCH /accounts/teachers//

Authentication: SiteOwner

Content-Type: multipart/form-data

Request Body: Same as POST (all fields optional for PATCH).

Success Response — 200 OK: Same structure as list item.

Error Responses:

Status Condition Response Body
400 Email exists (other user) {"gmail": ["This email is already associated with an account."]}
403 Not authorized {"detail": "You do not have permission..."}
404 Teacher not found {"detail": "Not found."}

2. Student Management

GET /accounts/students/

Description: List all students.

Authentication: SiteOwner

Query Parameters:

Parameter Type Description
status String Filter by status
grade Integer Filter by grade ID
school_type Integer Filter by school type ID
search String Search by name, student code, or national ID
all String Set to true to bypass pagination

Success Response — 200 OK: Array of student profiles.

{ "count": "Integer", "next": "String (URL) | null", "previous": "String (URL) | null", "results": [ { "id": "Integer", "user_id": "Integer", "username": "String", "student_code": "String", "name_ar": "String", "name_en": "String", "phone_number": "String", "father_number": "String | null", "mother_number": "String | null", "father_job": "String", "educational_state": "String (school)", "school_type": "Integer | null", "grade": "Integer | null", "division": "Integer | null", "school_name": "String | null", "national_id": "String", "birth_date": "Date (YYYY-MM-DD)", "gender": "String (male|female)", "gmail": "String", "facebook_link": "String | null", "governorate": "Integer", "area": "Integer", "status": "String", "is_active": "Boolean", "created_at": "DateTime (ISO 8601)", "updated_at": "DateTime (ISO 8601)" } ] }

GET /accounts/students//

Authentication: SiteOwner

Success Response — 200 OK: Single student profile.


PATCH /accounts/students//status/

Description: Update a student's status and active flag.

Authentication: SiteOwner

Content-Type: application/json

Request Body:

{ "status": "String (Optional) — verified|pending|declined|suspended_temporary|suspended_permanent", "is_active": "Boolean (Optional)" }

Success Response — 200 OK:

{ "status": "String", "is_active": "Boolean" }

Business Rules:


3. Course Management

POST /courses/

Description: Create a new course.

Authentication: SiteOwner only

Content-Type: multipart/form-data

Request Body:

{ "name": "String (Required)", "teacher": "Integer (Required) — Teacher ID", "grade": "Integer (Required) — Grade ID", "description": "String (Optional)", "cover_picture": "File (Optional) — image/jpeg|image/png|image/webp", "is_active": "Boolean (Optional) — Default: true" }

Success Response — 201 Created:

{ "id": "Integer", "name": "String", "teacher": "Integer", "teacher_id": "Integer", "teacher_name": "String", "grade": "Integer", "grade_name": "String", "subject": "Integer", "subject_name": "String", "description": "String | null", "cover_picture": "String (URL) | null", "is_active": "Boolean", "topics": "Array[Object]", "topic_count": "Integer", "total_lectures": "Integer", "total_videos": "Integer", "created_at": "DateTime (ISO 8601)", "updated_at": "DateTime (ISO 8601)" }

Error Responses:

Status Condition Response Body
400 Teacher doesn't teach grade {"grade": ["Teacher X does not teach grade Y."]}
400 Duplicate course {"non_field_errors": ["A course with this Teacher, Grade, and Name already exists."]}
403 Not site owner {"detail": "You do not have permission..."}

Business Rules:


PATCH /courses//

Authentication: SiteOwner only

Content-Type: multipart/form-data

Request Body: Same as POST (all fields optional for PATCH).

Error Responses:

Status Condition Response Body
400 Changing teacher {"teacher": ["Cannot change the teacher of an existing course."]}
400 Changing grade {"grade": ["Cannot change the grade of an existing course."]}

Business Rules:


DELETE /courses//

Authentication: SiteOwner only

Error Responses:

Status Condition Response Body
400 Students enrolled {"detail": "Cannot delete a course that has students enrolled in it."}

Business Rules:


4. View Assistants (Read-Only)

GET /accounts/teacher/assistants/

Description: SiteOwner can view all assistants across all teachers.

Authentication: SiteOwner

Query Parameters:

Parameter Type Description
teacher Integer Filter by teacher ID
search String Search by name, phone, or gmail

Success Response — 200 OK:

{ "count": "Integer", "next": "String (URL) | null", "previous": "String (URL) | null", "results": [ { "id": "Integer", "user_id": "Integer", "username": "String", "name": "String", "phone": "String", "gmail": "String", "gender": "String (male|female)", "is_active": "Boolean", "created_at": "DateTime (ISO 8601)", "teacher": "Integer — Teacher ID", "teacher_name": "String - Teacher's name" } ] }

5. Recharge Codes

POST /payments/codes/

Description: Generate new recharge codes. Codes are course-specific and can only be redeemed for the course they are generated for.

Authentication: SiteOwner

Content-Type: application/json

Request Body:

{ "course": "Integer (Required) — Course ID", "value": "Decimal (Required) — Amount in EGP (minimum 1.00)", "expires_at": "DateTime (Optional) — ISO 8601, code cannot be redeemed after this date" }

Success Response — 201 Created:

{ "id": "Integer", "course": "Integer", "course_name": "String", "teacher_name": "String", "code": "String — Auto-generated (e.g. X7K9-M2P4-QR1W-L5D8)", "value": "Decimal", "is_used": "boolean - Whether this code has been redeemed", "used_by": null, "used_by_name": null, "used_at": null, "created_at": "DateTime (ISO 8601)", "expires_at": "DateTime (ISO 8601) | null", "created_by": "Integer", "created_by_username": "String" }

Error Responses:

Status Condition Response Body
400 Invalid course {"course": ["Invalid pk \"X\" - object does not exist."]}
400 Invalid value {"value": ["Ensure this value is greater than or equal to 1."]}
403 Not site owner {"detail": "You do not have permission..."}

Business Rules:


GET /payments/codes/

Description: List all recharge codes. Supports filtering and ordering.

Authentication: SiteOwner

Query Parameters:

Parameter Type Description
course Integer Filter by course ID
is_used Boolean Filter by usage status

Success Response — 200 OK:

{ "count": "Integer", "next": "String (URL) | null", "previous": "String (URL) | null", "results": [ { "id": "Integer", "course": "Integer", "course_name": "String", "teacher_name": "String", "code": "String", "value": "Decimal", "is_used": "Boolean", "used_by": "Integer | null", "used_by_name": "String | null", "used_at": "DateTime (ISO 8601) | null", "created_at": "DateTime (ISO 8601)", "expires_at": "DateTime (ISO 8601) | null", "created_by": "Integer", "created_by_username": "String" } ] }

6. Revenue Analytics

GET /payments/revenue/summary/

Description: SiteOwner revenue overview across all courses.

Authentication: SiteOwner

Success Response — 200 OK:

{ "summary": { "total_revenue": "String — Decimal as string", "total_purchases": "Integer", "total_students": "Integer", "total_courses": "Integer" }, "courses": [ { "id": "Integer", "name": "String", "is_active": "Boolean", "enrolled_count": "Integer", "purchase_count": "Integer", "revenue": "String — Decimal as string" } ] }

GET /payments/revenue/course//

Description: Deep revenue dive for a specific course. Includes per-topic and per-lecture breakdowns, plus student spending distribution.

Authentication: SiteOwner

Success Response — 200 OK:

{ "course_id": "Integer", "course_name": "String", "total_revenue": "String — Decimal as string", "total_purchases": "Integer", "topics": [ { "topic_id": "Integer", "topic_name": "String", "lecture_count": "Integer", "purchase_count": "Integer", "revenue": "String — Decimal as string" } ], "lectures": [ { "lecture_id": "Integer", "lecture_name": "String", "topic_id": "Integer", "topic_name": "String", "price": "String", "final_price": "String", "purchase_count": "Integer", "revenue": "String — Decimal as string" } ], "student_spending": [ { "student_id": "Integer", "student_name": "String", "student_code": "String", "total_spent": "String — Decimal as string", "lectures_purchased": "Integer" } ] }

GET /payments/revenue/lecture//

Description: Revenue details for a specific lecture, including purchase timeline and average time-to-purchase.

Authentication: SiteOwner

Success Response — 200 OK:

{ "lecture_id": "Integer", "lecture_name": "String", "course_name": "String", "price": "String", "final_price": "String", "total_revenue": "String — Decimal as string", "buyer_count": "Integer", "purchase_count": "Integer", "average_days_to_purchase": "Float", "purchase_timeline": [ { "student_name": "String", "student_code": "String", "amount_paid": "String — Decimal as string", "purchased_at": "DateTime (ISO 8601)" } ] }

GET /payments/revenue/students/

Description: Per-student revenue breakdown across all courses.

Authentication: SiteOwner

Query Parameters:

Parameter Type Description
course Integer Filter by course ID

Success Response — 200 OK:

[ { "student_id": "Integer", "student_name": "String", "student_code": "String", "courses_enrolled": "Integer", "total_spent": "String — Decimal as string", "lectures_purchased": "Integer", "last_purchase_at": "DateTime (ISO 8601) | null" } ]

GET /payments/revenue/trends/

Description: Revenue trends over time. Defaults to the past 30 days. Supports custom date ranges.

Authentication: SiteOwner

Query Parameters:

Parameter Type Description
start_date String (YYYY-MM-DD) Start of date range
end_date String (YYYY-MM-DD) End of date range

Success Response — 200 OK:

{ "start_date": "String (YYYY-MM-DD) - Start of date range", "end_date": "String (YYYY-MM-DD) - End of date range", "total_revenue": "String — Decimal as string", "total_purchases": "Integer", "daily_trends": [ { "date": "String (YYYY-MM-DD)", "revenue": "String — Decimal as string", "purchase_count": "Integer" } ] }

Business Rules:


7. SiteOwner Dashboard

GET /dashboards/site-owner/

Description: Returns comprehensive dashboard statistics for site owners. Aggregates system-wide metrics including user counts, time-based metrics, and detailed breakdowns.

Authentication: SiteOwner

Success Response — 200 OK:

{ "total_students": "integer - Total number of student accounts", "total_teachers": "integer - Total number of teacher accounts", "total_assistants": "integer - Total number of assistant accounts", "new_users_this_month": "integer - New users registered this month", "new_students_this_month": "integer - New students registered this month", "new_teachers_this_month": "integer - New teachers created this month", "students_by_status": { "verified": "integer - Count of verified students", "pending": "integer - Count of pending students", "declined": "integer - Count of declined students", "suspended_temporary": "integer - Count of temporarily suspended students", "suspended_permanent": "integer - Count of permanently suspended students" }, "students_by_grade_school_division": [ { "grade": "string - Grade name", "school_type": "string - School type name", "division": "string - Division name", "count": "integer - Number of students in this combination" } ], "teachers_active_count": "integer - Number of active teachers", "teachers_inactive_count": "integer - Number of inactive teachers", "teachers_by_grade_subject": [ { "grade": "string - Grade name", "subject": "string - Subject name", "count": "integer - Number of teachers for this grade/subject" } ], "total_subjects": "integer - Total number of subjects", "total_grades": "integer - Total number of grades", "total_school_types": "integer - Total number of school types", "total_divisions": "integer - Total number of divisions", "total_governorates": "integer - Total number of governorates", "total_areas": "integer - Total number of areas", "total_video_security_options": "integer - Total number of video security options" }

Error Responses:

Status Condition Response Body
401 Not authenticated {"detail": "Authentication credentials were not provided."}
403 Not site owner {"detail": "You do not have permission to perform this action."}

Generated from main API documentation. Covers only SiteOwner-accessible endpoints.