# EnCoach Platform — Odoo 19 Backend SRS **Version:** 2.0 **Date:** March 2026 **Status:** Definitive **Purpose:** Complete backend specification for an Odoo 19 developer to build all required modules, models, and REST API endpoints for the EnCoach Adaptive Learning Platform. Includes courseware, communication, notification, FAQ, enhanced approval workflows, plagiarism detection, and official exam access modes. --- ## Table of Contents 1. [Introduction](#1-introduction) 2. [Architecture Overview](#2-architecture-overview) 3. [OpenEduCat Integration Strategy](#3-openeducat-integration-strategy) 4. [Module Inventory](#4-module-inventory) 5. [Authentication and User Management](#5-authentication-and-user-management) 6. [Entity and Permission System](#6-entity-and-permission-system) 7. [LMS Core (OpenEduCat Bridge)](#7-lms-core-openeducat-bridge) 8. [Academic Year, Term, and Department](#8-academic-year-term-and-department) 9. [Admission and Enrollment](#9-admission-and-enrollment) 10. [Subject Registration](#10-subject-registration) 11. [Exam Engine (EnCoach AI)](#11-exam-engine-encoach-ai) 12. [Institutional Exams (OpenEduCat)](#12-institutional-exams-openeducat) 13. [Assignment System](#13-assignment-system) 14. [Course Assignment Submissions (OpenEduCat)](#14-course-assignment-submissions-openeducat) 15. [Classroom and Group Management](#15-classroom-and-group-management) 16. [Subject Taxonomy Engine](#16-subject-taxonomy-engine) 17. [Adaptive Learning Engine](#17-adaptive-learning-engine) 18. [Learning Resources](#18-learning-resources) 19. [AI Services](#19-ai-services) 20. [Training Content](#20-training-content) 21. [Analytics and Statistics](#21-analytics-and-statistics) 22. [Subscription and Payments](#22-subscription-and-payments) 23. [Support Tickets](#23-support-tickets) 24. [File Storage](#24-file-storage) 25. [Approval Workflows (Enhanced)](#25-approval-workflows-enhanced) 26. [Courseware and Content Delivery](#26-courseware-and-content-delivery) 27. [Communication System](#27-communication-system) 28. [Notification Engine](#28-notification-engine) 29. [FAQ System](#29-faq-system) 30. [Plagiarism Detection](#30-plagiarism-detection) 31. [Official Exam Access](#31-official-exam-access) 32. [Non-Functional Requirements](#32-non-functional-requirements) 33. [Implementation Priority](#33-implementation-priority) --- ## 1. Introduction ### 1.1 Scope This document specifies the complete Odoo 19 backend for the EnCoach Adaptive Learning Platform. The backend serves a React SPA frontend via REST API. All endpoints use JSON request/response with JWT authentication (except explicitly public endpoints). ### 1.2 Frontend Contract The React frontend has 29 API service modules that define the exact endpoints the backend must implement. This SRS documents every endpoint, its expected request/response shape, and the underlying Odoo models. ### 1.3 Key Principles - **OpenEduCat First:** All traditional LMS features (courses, batches, students, faculty, timetable, attendance, exams, assignments, admissions) use OpenEduCat community modules ported to Odoo 19. - **EnCoach Extensions:** Custom modules extend OpenEduCat models with fields the platform needs (e.g., `description`, `image`, `max_capacity` on `op.course`). - **REST API Layer:** All data is exposed via REST controllers in `encoach_api` and `encoach_lms_api` modules. The frontend never uses Odoo's web client or XML-RPC. - **AI Services:** AI functionality (GPT, Whisper, Polly, ELAI, GPTZero, FAISS) is encapsulated in dedicated `encoach_ai_*` modules with API endpoints. --- ## 2. Architecture Overview ``` React SPA (Vite + TypeScript) │ ▼ REST API (JSON + JWT) ┌─────────────────────────────────┐ │ Odoo 19 Server │ │ │ │ ┌──────────┐ ┌──────────────┐ │ │ │ encoach_ │ │ OpenEduCat │ │ │ │ modules │ │ (ported v19)│ │ │ └──────────┘ └──────────────┘ │ │ │ │ │ │ ▼ ▼ │ │ PostgreSQL 16 │ └─────────────────────────────────┘ │ ▼ External Services OpenAI API │ AWS Polly │ ELAI │ GPTZero ``` ### 2.1 Technology Stack | Layer | Technology | |-------|-----------| | Framework | Odoo 19 Community Edition | | Language | Python 3.12+ | | Database | PostgreSQL 16 | | LMS Foundation | OpenEduCat (ported from v17 to v19) | | AI/ML | OpenAI GPT-4o, Whisper (local), AWS Polly, Sentence Transformers, FAISS | | Authentication | JWT (PyJWT) | | File Storage | Odoo ir.attachment + optional S3 | | Deployment | Docker, Docker Compose | --- ## 3. OpenEduCat Integration Strategy ### 3.1 Modules to Port (v17 → v19) | Module | Models | Porting Notes | |--------|--------|---------------| | `openeducat_core` | `op.course`, `op.batch`, `op.student`, `op.faculty`, `op.subject`, `op.department`, `op.category`, `op.academic.year`, `op.academic.term`, `op.student.course`, `op.subject.registration` | Core dependency for all other modules | | `openeducat_timetable` | `op.session`, `op.timing` | Depends on `openeducat_core` | | `openeducat_attendance` | `op.attendance.register`, `op.attendance.sheet`, `op.attendance.line`, `op.attendance.type` | Depends on `openeducat_core`, `openeducat_timetable` | | `openeducat_classroom` | `op.classroom` | Depends on `openeducat_facility` | | `openeducat_facility` | `op.facility`, `op.facility.line` | Dependency of classroom | | `openeducat_exam` | `op.exam.session`, `op.exam`, `op.exam.type`, `op.exam.attendees`, `op.exam.room`, `op.marksheet.register`, `op.marksheet.line`, `op.result.line`, `op.result.template`, `op.grade.configuration` | Depends on `openeducat_core` | | `openeducat_assignment` | `op.assignment`, `op.assignment.sub.line`, `grading.assignment`, `grading.assignment.type` | Depends on `openeducat_core` | | `openeducat_admission` | `op.admission`, `op.admission.register` | Depends on `openeducat_core` | ### 3.2 Model Extensions (via `encoach_lms_api`) The `encoach_lms_api` module uses `_inherit` to add fields missing from OpenEduCat but required by the frontend: | Model | Added Fields | |-------|-------------| | `op.course` | `description` (Text), `image` (Binary), `max_capacity` (Integer), `status` (Selection: draft/active/archived), `department_id` (Many2one to `op.department`) | | `op.batch` | `max_students` (Integer), `status` (Selection: draft/active/completed) | | `op.student` | `enrollment_date` (Date), `status` (Selection: active/inactive/graduated/suspended) | | `op.faculty` | `department_id` (Many2one to `op.department`), `specialization` (Char) | | `op.subject` | `credits` (Float), `department_id` (Many2one to `op.department`) | ### 3.3 Serialization All OpenEduCat models need `to_api_dict()` methods on the `encoach_lms_api` inheriting models that convert records to JSON-serializable dictionaries with snake_case keys. Many2one fields are serialized as `{field}_id` (integer) and `{field}_name` (string). --- ## 4. Module Inventory | # | Module | Type | Description | |---|--------|------|-------------| | 1 | `openeducat_core` | OpenEduCat (port v19) | Course, batch, student, faculty, subject, department, academic year/term | | 2 | `openeducat_timetable` | OpenEduCat (port v19) | Timetable sessions and timings | | 3 | `openeducat_attendance` | OpenEduCat (port v19) | Attendance sheets, lines, registers | | 4 | `openeducat_classroom` | OpenEduCat (port v19) | Physical classrooms | | 5 | `openeducat_facility` | OpenEduCat (port v19) | Facility management | | 6 | `openeducat_exam` | OpenEduCat (port v19) | Institutional exam sessions, marksheets, grading | | 7 | `openeducat_assignment` | OpenEduCat (port v19) | Course assignments and submissions | | 8 | `openeducat_admission` | OpenEduCat (port v19) | Admission registers and applications | | 9 | `encoach_core` | Custom | `encoach.user` extensions on `res.users`, entities, roles, permissions, codes, invites | | 10 | `encoach_api` | Custom | REST API controllers for auth, users, entities, permissions, exams, assignments, classrooms, tickets, subscriptions, stats, storage, approvals | | 11 | `encoach_lms_api` | Custom | REST API bridge for OpenEduCat models (courses, batches, timetable, attendance, grades, academic years, departments, admissions, subject registration, institutional exams, marksheets, course assignments) | | 12 | `encoach_ai` | Custom | OpenAI service wrapper, token counting, blacklist, system parameter management | | 13 | `encoach_ai_media` | Custom | TTS (AWS Polly), STT (Whisper), ELAI avatar video generation | | 14 | `encoach_ai_generation` | Custom | AI exam content generation for all subjects | | 15 | `encoach_ai_grading` | Custom | AI grading for writing, speaking, math, IT | | 16 | `encoach_exam` | Custom | `encoach.exam` model for AI-powered exams | | 17 | `encoach_assignment` | Custom | `encoach.assignment` model (exam-wrapper assignments) | | 18 | `encoach_evaluation` | Custom | Evaluation job tracking | | 19 | `encoach_stats` | Custom | Session tracking and statistics | | 20 | `encoach_training` | Custom | Training tips, walkthroughs, FAISS embeddings | | 21 | `encoach_classroom` | Custom | `encoach.classroom.group` for virtual classrooms | | 22 | `encoach_subscription` | Custom | Packages, payments, discounts, Stripe/PayPal/Paymob | | 23 | `encoach_registration` | Custom | User registration and batch import | | 24 | `encoach_ticket` | Custom | Support tickets | | 25 | `encoach_taxonomy` | Custom | Subject, domain, topic, learning objective models | | 26 | `encoach_resources` | Custom | Learning resource library | | 27 | `encoach_adaptive` | Custom | Proficiency tracking, learning plans, progress, content cache | | 28 | `encoach_adaptive_api` | Custom | REST controllers for adaptive learning | | 29 | `encoach_adaptive_ai` | Custom | AI services for diagnostics, plan generation, coaching, content generation | | 30 | `encoach_sis` | Custom | UTAS SIS integration (sync, mapping) | | 31 | `encoach_branding` | Custom | Whitelabeling configuration | | 32 | `encoach_courseware` | Custom | Course chapters, chapter materials, chapter progress tracking, AI workbench for content generation | | 33 | `encoach_communication` | Custom | Discussion boards, threaded posts, announcements, direct messaging | | 34 | `encoach_notification` | Custom | Notification engine, configurable rules, email/in-app delivery, user preferences | | 35 | `encoach_faq` | Custom | FAQ categories and items, role-filtered, searchable | **Total: 8 OpenEduCat (ported) + 27 custom = 35 modules** --- ## 5. Authentication and User Management ### 5.1 Authentication Model JWT-based authentication. Tokens are issued on login and validated on every API request. ### 5.2 User Model Extends `res.users` with `encoach.user` mixin: | Field | Type | Description | |-------|------|-------------| | `user_type` | Selection | `student`, `teacher`, `admin`, `corporate`, `mastercorporate`, `agent`, `developer` | | `is_verified` | Boolean | Email verification status | | `entity_ids` | Many2many | Associated entities | | `phone` | Char | Phone number | | `birth_date` | Date | Date of birth | | `gender` | Selection | `m`, `f`, `o` | | `op_student_id` | Many2one | Link to `op.student` if user_type is student | | `op_faculty_id` | Many2one | Link to `op.faculty` if user_type is teacher | ### 5.3 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/login` | Public | Authenticate user, return JWT + user object | | `POST` | `/api/logout` | JWT | Invalidate token | | `GET` | `/api/user` | JWT | Get current authenticated user | | `POST` | `/api/reset/sendVerification` | Public | Send password reset / verification email | | `GET` | `/api/users/list` | JWT | Paginated user list with filters (`type`, `entity_id`, `page`, `size`) | | `GET` | `/api/users/{id}` | JWT | Get user by ID | | `PATCH` | `/api/users/update` | JWT | Update user (id in body) | | `POST` | `/api/users/create` | JWT | Create user | | `POST` | `/api/batch_users` | JWT | Batch create users from CSV/JSON | | `GET` | `/api/users/export` | JWT | Export users as CSV blob | ### 5.4 Login Response Shape ```json { "token": "eyJ...", "user": { "id": 1, "name": "John Doe", "email": "john@example.com", "user_type": "admin", "is_verified": true, "entities": [ { "id": 1, "name": "UTAS", "logo": "..." } ] } } ``` --- ## 6. Entity and Permission System ### 6.1 Entity Model (`encoach.entity`) | Field | Type | Description | |-------|------|-------------| | `name` | Char | Entity name | | `code` | Char | Unique code | | `type` | Selection | `corporate`, `university`, `freelance` | | `logo` | Binary | Entity logo | | `active` | Boolean | Active status | | `user_ids` | Many2many | Members | | `role_ids` | One2many | Custom roles | ### 6.2 Role and Permission Models **`encoach.role`** | Field | Type | Description | |-------|------|-------------| | `name` | Char | Role name | | `entity_id` | Many2one | Parent entity | | `permission_ids` | Many2many | Assigned permissions | **`encoach.permission`** 77+ entity-scoped permissions (e.g., `view_students`, `edit_exam`, `view_payment_record`, `manage_entities`). ### 6.3 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/entities` | JWT | Paginated entities | | `GET` | `/api/entities/{id}` | JWT | Entity detail | | `POST` | `/api/entities` | JWT | Create entity | | `PATCH` | `/api/entities/{id}` | JWT | Update entity | | `DELETE` | `/api/entities/{id}` | JWT | Delete entity | | `GET` | `/api/entities/{entityId}/roles` | JWT | Roles for entity | | `POST` | `/api/entities/{entityId}/roles` | JWT | Create role | | `PATCH` | `/api/roles/{roleId}/permissions` | JWT | Update role permissions | | `GET` | `/api/permissions` | JWT | List permissions (filter by `entity_id`) | --- ## 7. LMS Core (OpenEduCat Bridge) ### 7.1 Course API Backed by `op.course` (extended via `_inherit` in `encoach_lms_api`). | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/courses` | JWT | Paginated courses (filter: `status`, `department_id`) | | `GET` | `/api/courses/{id}` | JWT | Course detail with subjects, batches | | `POST` | `/api/courses` | JWT | Create course | | `PATCH` | `/api/courses/{id}` | JWT | Update course | | `DELETE` | `/api/courses/{id}` | JWT | Delete course | | `POST` | `/api/courses/ai-generate` | JWT | AI-generate course outline (calls OpenAI) | **Course Response Shape:** ```json { "id": 1, "name": "IELTS Preparation", "code": "IELTS-101", "description": "Comprehensive IELTS preparation course", "department_id": 1, "department_name": "English", "max_capacity": 30, "status": "active", "subject_ids": [1, 2, 3], "subject_names": ["Reading", "Writing", "Listening"], "batch_count": 2, "student_count": 45, "image": "base64..." } ``` ### 7.2 Batch API Backed by `op.batch` (extended). | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/batches` | JWT | Paginated batches (filter: `course_id`, `status`) | | `GET` | `/api/batches/{id}` | JWT | Batch detail with student list | | `POST` | `/api/batches` | JWT | Create batch | | `PATCH` | `/api/batches/{id}` | JWT | Update batch | | `DELETE` | `/api/batches/{id}` | JWT | Delete batch | ### 7.3 Timetable API Backed by `op.session` and `op.timing`. | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/timetable` | JWT | Sessions (filter: `course_id`, `batch_id`, `faculty_id`, `start_date`, `end_date`) | | `POST` | `/api/timetable` | JWT | Create timetable session | **Session Response Shape:** ```json { "id": 1, "name": "IELTS Reading - Week 1", "course_id": 1, "course_name": "IELTS Preparation", "batch_id": 1, "batch_name": "Batch A", "faculty_id": 5, "faculty_name": "Dr. Smith", "subject_id": 1, "subject_name": "Reading", "classroom_id": 3, "classroom_name": "Room 101", "start_datetime": "2026-03-15T09:00:00", "end_datetime": "2026-03-15T10:30:00", "day": "Monday" } ``` ### 7.4 Attendance API Backed by `op.attendance.sheet` and `op.attendance.line`. | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/attendance` | JWT | Attendance records (filter: `course_id`, `batch_id`, `student_id`, `date_from`, `date_to`) | | `POST` | `/api/attendance` | JWT | Record attendance (batch of student attendance lines) | **Attendance Record Request:** ```json { "session_id": 1, "attendance_date": "2026-03-15", "lines": [ { "student_id": 1, "present": true }, { "student_id": 2, "present": false, "remark": "Sick leave" } ] } ``` ### 7.5 Grades API | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/grades` | JWT | Grade records (filter: `student_id`, `course_id`, `batch_id`) | --- ## 8. Academic Year, Term, and Department ### 8.1 Academic Year and Term Backed by `op.academic.year` and `op.academic.term` (OpenEduCat core). | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/academic-years` | JWT | List academic years with terms | | `GET` | `/api/academic-years/{id}` | JWT | Year detail with terms | | `POST` | `/api/academic-years` | JWT | Create academic year | | `PATCH` | `/api/academic-years/{id}` | JWT | Update year | | `DELETE` | `/api/academic-years/{id}` | JWT | Delete year | | `POST` | `/api/academic-years/{id}/generate-terms` | JWT | Auto-generate terms from `term_structure` | | `GET` | `/api/academic-terms` | JWT | List terms (filter: `year_id`) | | `POST` | `/api/academic-terms` | JWT | Create term | | `PATCH` | `/api/academic-terms/{id}` | JWT | Update term | | `DELETE` | `/api/academic-terms/{id}` | JWT | Delete term | **Generate Terms Logic:** Given `term_structure` on the academic year, compute term dates: - `two_sem`: 2 terms splitting the year in half - `two_sem_qua`: 2 semesters, each with 2 quarters (4 terms total) - `three_sem`: 3 trimesters - `four_Quarter`: 4 quarters - `final_year`: single term spanning the full year ### 8.2 Department Backed by `op.department` (OpenEduCat core). | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/departments` | JWT | List departments | | `POST` | `/api/departments` | JWT | Create department | | `PATCH` | `/api/departments/{id}` | JWT | Update department | | `DELETE` | `/api/departments/{id}` | JWT | Delete department | **Department Response:** ```json { "id": 1, "name": "Department of Mathematics", "code": "MATH", "parent_id": null, "parent_name": null, "course_count": 5, "faculty_count": 12 } ``` --- ## 9. Admission and Enrollment ### 9.1 Admission Register Backed by `op.admission.register` (OpenEduCat). | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/admission-registers` | JWT | List registers | | `POST` | `/api/admission-registers` | JWT | Create register | | `PATCH` | `/api/admission-registers/{id}` | JWT | Update register | | `POST` | `/api/admission-registers/{id}/confirm` | JWT | Confirm register (opens enrollment) | | `POST` | `/api/admission-registers/{id}/close` | JWT | Close register | ### 9.2 Admission Backed by `op.admission` (OpenEduCat). | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/admissions` | JWT | List admissions (filter: `state`, `register_id`, `course_id`, `page`, `size`) | | `GET` | `/api/admissions/{id}` | JWT | Admission detail | | `POST` | `/api/admissions` | Public* | Submit admission application | | `POST` | `/api/admissions/{id}/submit` | JWT | Submit for review | | `POST` | `/api/admissions/{id}/confirm` | JWT | Confirm admission | | `POST` | `/api/admissions/{id}/admit` | JWT | Admit applicant | | `POST` | `/api/admissions/{id}/reject` | JWT | Reject admission | *Note: `POST /api/admissions` is public when submitted via the `/apply` page. Backend should allow unauthenticated submission when the register allows public applications. ### 9.3 Admit Action Logic When an admission is admitted (`/admit`): 1. Create `op.student` record from admission data 2. Create `res.users` record with `user_type=student` 3. Create `op.student.course` linking student to course/batch 4. Set `admission.student_id` to the new student 5. Set `admission.state` to `admission` 6. Send welcome email with login credentials --- ## 10. Subject Registration ### 10.1 Model Backed by `op.subject.registration` (OpenEduCat core). ### 10.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/subject-registrations` | JWT | List registrations (filter: `student_id`, `course_id`, `state`) | | `POST` | `/api/subject-registrations` | JWT | Create registration | | `PATCH` | `/api/subject-registrations/{id}` | JWT | Update selected subjects | | `POST` | `/api/subject-registrations/{id}/confirm` | JWT | Confirm registration | | `POST` | `/api/subject-registrations/{id}/reject` | JWT | Reject registration | | `GET` | `/api/subject-registrations/available` | JWT | Get available subjects for current student's enrolled courses | ### 10.3 Available Subjects Logic For the current user (student): 1. Find all `op.student.course` records for this student 2. Get subjects linked to those courses 3. Exclude subjects already registered in confirmed registrations 4. Check unit load limits from `op.course` configuration 5. Return list with `min_unit_load`, `max_unit_load` --- ## 11. Exam Engine (EnCoach AI) ### 11.1 Model (`encoach.exam`) | Field | Type | Description | |-------|------|-------------| | `name` | Char | Exam title | | `module` | Selection | `reading`, `writing`, `listening`, `speaking`, `math`, `it`, ... | | `subject_id` | Many2one | Link to `encoach.subject` (taxonomy) | | `topic_ids` | Many2many | Link to `encoach.topic` | | `entity_id` | Many2one | Owning entity | | `structure_id` | Many2one | Exam structure template | | `rubric_id` | Many2one | Grading rubric | | `exercises` | Text (JSON) | Generated exercises content | | `is_public` | Boolean | Public access flag | | `status` | Selection | `draft`, `published`, `archived` | ### 11.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/exam/{module}` | JWT | List exams for module (paginated) | | `GET` | `/api/exam/{module}/{id}` | JWT | Exam detail | | `POST` | `/api/exam` | JWT | Create exam | | `PATCH` | `/api/exam/{id}` | JWT | Update exam | | `DELETE` | `/api/exam/{id}` | JWT | Delete exam | | `PATCH` | `/api/exam/{id}/access` | JWT | Set public/private | | `GET` | `/api/rubrics` | JWT | List rubrics (paginated) | | `POST` | `/api/rubrics` | JWT | Create rubric | | `GET` | `/api/rubric-groups` | JWT | List rubric groups | | `GET` | `/api/exam-structures` | JWT | List exam structures | | `POST` | `/api/exam-structures` | JWT | Create exam structure | | `DELETE` | `/api/exam-structures/{id}` | JWT | Delete structure | | `GET` | `/api/exam/avatars` | JWT | List ELAI avatars | | `POST` | `/api/exam/{module}/generate` | JWT | Generate exercises from existing exam | | `POST` | `/api/exam/{module}/generate/scratch` | JWT | Generate full exam from scratch | ### 11.3 Generation Logic Uses OpenAI GPT-4o to generate exam content. Prompt templates vary by module: - **Reading/Listening:** Passage + comprehension questions - **Writing:** Task description + sample topics - **Speaking:** Dialogue prompts + follow-up questions - **Math:** Problem sets with numerical answers, configurable difficulty - **IT:** Code completion, debugging, conceptual questions --- ## 12. Institutional Exams (OpenEduCat) ### 12.1 Overview Traditional institutional exams (midterms, finals) managed through OpenEduCat. Separate from EnCoach AI exams. ### 12.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/inst-exam-sessions` | JWT | List exam sessions | | `POST` | `/api/inst-exam-sessions` | JWT | Create session | | `GET` | `/api/inst-exam-sessions/{id}` | JWT | Session detail with exams | | `PATCH` | `/api/inst-exam-sessions/{id}` | JWT | Update session | | `DELETE` | `/api/inst-exam-sessions/{id}` | JWT | Delete session | | `POST` | `/api/inst-exam-sessions/{id}/schedule` | JWT | Schedule session | | `POST` | `/api/inst-exam-sessions/{id}/done` | JWT | Mark session done | | `POST` | `/api/inst-exams` | JWT | Add exam to session | | `PATCH` | `/api/inst-exams/{id}` | JWT | Update exam | | `POST` | `/api/inst-exams/{id}/attendees` | JWT | Add attendees to exam | | `PATCH` | `/api/inst-exams/{id}/marks` | JWT | Enter marks for attendees | | `GET` | `/api/exam-types` | JWT | List exam types | | `POST` | `/api/exam-types` | JWT | Create exam type | | `GET` | `/api/grade-config` | JWT | List grade configurations | | `POST` | `/api/grade-config` | JWT | Create grade config | | `GET` | `/api/result-templates` | JWT | List result templates | | `POST` | `/api/result-templates` | JWT | Create result template | | `POST` | `/api/result-templates/{id}/generate` | JWT | Generate marksheets | | `GET` | `/api/marksheets` | JWT | List marksheets | | `GET` | `/api/marksheets/{id}` | JWT | Marksheet detail with student results | | `POST` | `/api/marksheets/{id}/validate` | JWT | Validate marksheet | ### 12.3 Enter Marks Request ```json { "attendees": [ { "student_id": 1, "marks": 85 }, { "student_id": 2, "marks": 72 } ] } ``` ### 12.4 Generate Marksheets Logic 1. Create `op.marksheet.register` from `op.result.template` 2. For each student in the batch: - Create `op.marksheet.line` - For each exam in the session: create `op.result.line` with marks from `op.exam.attendees` - Compute total marks, percentage - If evaluation_type is `grade`: lookup `op.grade.configuration` to assign grade - Compute pass/fail from `min_marks` on each exam 3. Compute `total_pass` and `total_failed` on register --- ## 13. Assignment System (EnCoach AI) ### 13.1 Model (`encoach.assignment`) Exam-wrapper assignments where an EnCoach exam is assigned to students/classrooms. ### 13.2 Enhanced Fields (Late Submission + Plagiarism) Add to `encoach.assignment`: | Field | Type | Description | |-------|------|-------------| | `late_deadline` | Datetime | Final late submission cutoff | | `penalty_type` | Selection | `none`, `percentage`, `fixed_deduction` | | `penalty_value` | Float | Penalty amount | | `max_submissions` | Integer | Maximum allowed submissions (0 = unlimited) | | `allow_extensions` | Boolean | Student extension requests | | `plagiarism_enabled` | Boolean | Enable GPTZero plagiarism check | | `plagiarism_max_checks` | Integer | Max plagiarism checks per submission | | `reminder_count` | Integer | Number of reminders | | `reminder_frequency` | Selection | `once`, `daily`, `custom` | ### 13.3 Extension Request Model (`encoach.extension.request`) | Field | Type | Description | |-------|------|-------------| | `student_id` | Many2one (`res.users`) | Requesting student | | `assignment_id` | Many2one | Target assignment | | `reason` | Text | Justification | | `requested_date` | Datetime | New deadline | | `status` | Selection | `pending`, `approved`, `rejected` | | `approved_by` | Many2one | Approving teacher | | `response_note` | Text | Teacher response | ### 13.4 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/assignments` | JWT | Paginated assignments | | `GET` | `/api/assignments/{id}` | JWT | Assignment detail | | `POST` | `/api/assignments` | JWT | Create assignment (includes late submission fields) | | `PATCH` | `/api/assignments/{id}` | JWT | Update assignment | | `DELETE` | `/api/assignments/{id}` | JWT | Delete assignment | | `POST` | `/api/assignments/{id}/archive` | JWT | Archive assignment | | `POST` | `/api/assignments/{id}/start` | JWT | Start assignment (student) | | `POST` | `/api/assignments/{id}/request-extension` | JWT (student) | Request deadline extension | | `GET` | `/api/assignments/{id}/extension-requests` | JWT (teacher) | List extension requests | | `POST` | `/api/extension-requests/{id}/respond` | JWT (teacher) | Approve/reject extension | --- ## 14. Course Assignment Submissions (OpenEduCat) ### 14.1 Overview Traditional file-based assignments where students upload work and teachers grade it. Uses OpenEduCat models. ### 14.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/course-assignments` | JWT | List course assignments (filter: `course_id`, `batch_id`, `state`) | | `POST` | `/api/course-assignments` | JWT | Create course assignment | | `GET` | `/api/course-assignments/{id}` | JWT | Assignment detail with submissions | | `PATCH` | `/api/course-assignments/{id}` | JWT | Update assignment | | `POST` | `/api/course-assignments/{id}/publish` | JWT | Publish assignment | | `POST` | `/api/course-assignments/{id}/finish` | JWT | Close assignment | | `GET` | `/api/course-assignments/{id}/submissions` | JWT | List student submissions | | `POST` | `/api/course-assignments/{id}/submit` | JWT | Student file upload (multipart/form-data) | | `PATCH` | `/api/submissions/{id}` | JWT | Grade submission (marks, feedback, status) | | `GET` | `/api/assignment-types` | JWT | List assignment types | | `POST` | `/api/assignment-types` | JWT | Create assignment type | ### 14.3 File Upload Student submission accepts `multipart/form-data`: - `file`: Binary file attachment - `description`: Optional text description Files are stored as `ir.attachment` records linked to `op.assignment.sub.line`. ### 14.4 Grading Request ```json { "marks": 85, "state": "accept", "note": "Well researched paper. Minor formatting issues." } ``` --- ## 15. Classroom and Group Management ### 15.1 Model (`encoach.classroom.group`) Virtual classrooms for grouping students within entities. ### 15.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/groups` | JWT | Paginated groups | | `GET` | `/api/groups/{id}` | JWT | Group detail with members | | `POST` | `/api/groups` | JWT | Create group | | `DELETE` | `/api/groups/{id}` | JWT | Delete group | | `POST` | `/api/groups/transfer` | JWT | Transfer students between groups | | `POST` | `/api/groups/{id}/members` | JWT | Add members | | `POST` | `/api/groups/{id}/members/remove` | JWT | Remove members | --- ## 16. Subject Taxonomy Engine ### 16.1 Models **`encoach.subject`** (distinct from `op.subject`) | Field | Type | Description | |-------|------|-------------| | `name` | Char | Subject name (e.g., "IELTS", "Mathematics", "Information Technology") | | `code` | Char | Unique code | | `description` | Text | Description | | `icon` | Char | Icon identifier | | `active` | Boolean | Active flag | | `domain_ids` | One2many | Child domains | **`encoach.domain`** | Field | Type | Description | |-------|------|-------------| | `name` | Char | Domain name (e.g., "Algebra", "Networking") | | `subject_id` | Many2one | Parent subject | | `topic_ids` | One2many | Child topics | **`encoach.topic`** | Field | Type | Description | |-------|------|-------------| | `name` | Char | Topic name | | `domain_id` | Many2one | Parent domain | | `subject_id` | Many2one (related) | Subject via domain | | `difficulty` | Selection | `beginner`, `intermediate`, `advanced` | | `prerequisite_ids` | Many2many | Prerequisite topics | | `learning_objective_ids` | One2many | Learning objectives | **`encoach.learning.objective`** | Field | Type | Description | |-------|------|-------------| | `name` | Char | Objective description | | `topic_id` | Many2one | Parent topic | | `bloom_level` | Selection | `remember`, `understand`, `apply`, `analyze`, `evaluate`, `create` | ### 16.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/subjects` | JWT | List subjects | | `GET` | `/api/subjects/{id}` | JWT | Subject detail | | `POST` | `/api/subjects` | JWT | Create subject | | `PATCH` | `/api/subjects/{id}` | JWT | Update subject | | `DELETE` | `/api/subjects/{id}` | JWT | Delete subject | | `GET` | `/api/subjects/{subjectId}/taxonomy` | JWT | Full taxonomy tree (domains → topics → objectives) | | `POST` | `/api/subjects/{subjectId}/taxonomy/import` | JWT | Import taxonomy from file (multipart) | | `GET` | `/api/domains` | JWT | List domains (filter: `subject_id`) | | `POST` | `/api/domains` | JWT | Create domain | | `PATCH` | `/api/domains/{id}` | JWT | Update domain | | `DELETE` | `/api/domains/{id}` | JWT | Delete domain | | `POST` | `/api/domains/{domainId}/ai-suggest` | JWT | AI-suggest topics for domain | | `GET` | `/api/topics` | JWT | List topics (filter: `domain_id`, `subject_id`) | | `POST` | `/api/topics` | JWT | Create topic | | `PATCH` | `/api/topics/{id}` | JWT | Update topic | | `DELETE` | `/api/topics/{id}` | JWT | Delete topic | --- ## 17. Adaptive Learning Engine ### 17.1 Models **`encoach.proficiency`** | Field | Type | Description | |-------|------|-------------| | `student_id` | Many2one | Student user | | `subject_id` | Many2one | Subject | | `topic_id` | Many2one | Topic | | `mastery_score` | Float | 0.0 to 1.0 | | `confidence` | Float | Statistical confidence | | `last_assessed` | Datetime | Last assessment time | | `assessment_count` | Integer | Number of assessments | **`encoach.learning.plan`** | Field | Type | Description | |-------|------|-------------| | `student_id` | Many2one | Student | | `subject_id` | Many2one | Subject | | `status` | Selection | `active`, `paused`, `completed` | | `items` | One2many | Plan items (topics in order) | | `generated_by` | Selection | `ai`, `manual` | | `progress` | Float | Computed completion percentage | **`encoach.learning.plan.item`** | Field | Type | Description | |-------|------|-------------| | `plan_id` | Many2one | Parent plan | | `topic_id` | Many2one | Topic | | `sequence` | Integer | Order in plan | | `status` | Selection | `locked`, `available`, `in_progress`, `completed` | | `estimated_hours` | Float | Estimated time | | `mastery_threshold` | Float | Required mastery to complete | **`encoach.diagnostic.session`** | Field | Type | Description | |-------|------|-------------| | `student_id` | Many2one | Student | | `subject_id` | Many2one | Subject | | `status` | Selection | `in_progress`, `completed` | | `questions_answered` | Integer | Count | | `result_data` | Text (JSON) | Topic-level results | **`encoach.content.cache`** | Field | Type | Description | |-------|------|-------------| | `topic_id` | Many2one | Topic | | `content_type` | Selection | `explanation`, `practice`, `quiz` | | `content` | Text (JSON) | Cached AI content | | `generated_at` | Datetime | Generation time | | `ttl_hours` | Integer | Cache TTL | ### 17.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/diagnostic/start` | JWT | Start diagnostic for subject | | `POST` | `/api/diagnostic/answer` | JWT | Submit diagnostic answer (adaptive next question) | | `GET` | `/api/diagnostic/{sessionId}/result` | JWT | Get diagnostic result | | `GET` | `/api/proficiency` | JWT | Get proficiency records (filter: `subject_id`, `student_id`) | | `GET` | `/api/proficiency/summary` | JWT | Aggregate proficiency summary | | `GET` | `/api/proficiency/class` | JWT | Class-wide proficiency | | `GET` | `/api/learning-plan` | JWT | Get learning plan (filter: `subject_id`) | | `POST` | `/api/learning-plan/generate` | JWT | AI-generate learning plan | | `PATCH` | `/api/learning-plan/{id}` | JWT | Update plan | | `POST` | `/api/learning-plan/{id}/pause` | JWT | Pause plan | | `POST` | `/api/learning-plan/{id}/resume` | JWT | Resume plan | | `GET` | `/api/topics/{topicId}/content` | JWT | Get topic learning content | | `POST` | `/api/topics/{topicId}/generate-content` | JWT | AI-generate topic content | | `POST` | `/api/topics/{topicId}/practice` | JWT | Generate practice questions | | `POST` | `/api/topics/{topicId}/practice/grade` | JWT | Grade practice answers | | `POST` | `/api/topics/{topicId}/mastery-quiz` | JWT | Start mastery quiz | | `POST` | `/api/topics/{topicId}/mastery-quiz/submit` | JWT | Submit mastery quiz and update proficiency | ### 17.3 Diagnostic Algorithm Computer Adaptive Testing (CAT): 1. Start with medium-difficulty questions 2. If correct → harder question; if incorrect → easier question 3. Uses Item Response Theory (IRT) to estimate ability 4. Covers multiple topics within the subject 5. Terminates after sufficient confidence or max questions (20-30) 6. Produces per-topic mastery scores ### 17.4 Learning Plan Generation AI-generated study plan: 1. Read student's proficiency profile 2. Identify weak topics (mastery < threshold) 3. Respect prerequisite graph (topological sort) 4. Estimate time per topic based on gap 5. Order topics: prerequisites first, then weakest areas 6. Return ordered list of `plan_items` --- ## 18. Learning Resources ### 18.1 Model (`encoach.resource`) | Field | Type | Description | |-------|------|-------------| | `name` | Char | Resource title | | `type` | Selection | `video`, `document`, `link`, `interactive` | | `subject_id` | Many2one | Subject | | `topic_ids` | Many2many | Topics | | `file` | Binary | Uploaded file | | `url` | Char | External URL | | `difficulty` | Selection | `beginner`, `intermediate`, `advanced` | | `duration_minutes` | Integer | Estimated duration | | `active` | Boolean | Soft-delete flag | ### 18.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/resources` | JWT | Paginated resources (filter: `subject_id`, `topic_id`, `type`, `difficulty`) | | `POST` | `/api/resources` | JWT | Upload resource (multipart) | | `PATCH` | `/api/resources/{id}` | JWT | Update resource metadata | | `DELETE` | `/api/resources/{id}` | JWT | Soft-delete resource | | `POST` | `/api/resources/{id}/complete` | JWT | Mark resource as completed by student | | `POST` | `/api/resources/{id}/rate` | JWT | Rate resource (1-5) | | `GET` | `/api/resources/{id}/download` | JWT | Download resource file | --- ## 19. AI Services ### 19.1 AI Coaching | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/coach/chat` | JWT | General AI chat (context-aware) | | `POST` | `/api/coach/hint` | JWT | Hint for topic/question | | `POST` | `/api/coach/explain` | JWT | Explain scores or concepts | | `POST` | `/api/coach/suggest` | JWT | Study suggestions | | `POST` | `/api/coach/writing-help` | JWT | Writing feedback with improved text | | `GET` | `/api/coach/tip` | JWT | Context-aware tip (FAISS similarity) | ### 19.2 AI Evaluation | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/evaluate/writing` | JWT | Evaluate writing (IELTS band scoring / rubric-based) | | `POST` | `/api/evaluate/speaking` | JWT | Evaluate speaking (audio → transcribe → grade) | | `POST` | `/api/grading/multiple` | JWT | Grade multiple-choice / numerical answers | | `POST` | `/api/transcribe` | JWT | Audio transcription (Whisper, multipart upload) | ### 19.3 AI Generation | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/exam/{module}/generate` | JWT | Generate exam exercises from template | | `POST` | `/api/exam/{module}/generate/scratch` | JWT | Generate full exam from scratch | ### 19.4 Media Generation | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/exam/listening/media` | JWT | Generate listening audio (AWS Polly TTS) | | `POST` | `/api/exam/speaking/media` | JWT | Generate speaking video (ELAI) | | `GET` | `/api/exam/avatars` | JWT | List ELAI avatars with voice metadata | ### 19.5 AI Analytics | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/ai/search` | JWT | AI-powered semantic search | | `POST` | `/api/ai/insights` | JWT | AI-generated insights from data | | `GET` | `/api/ai/alerts` | JWT | AI-detected alerts (at-risk students, anomalies) | | `POST` | `/api/ai/report-narrative` | JWT | AI narrative for reports | | `POST` | `/api/ai/batch-optimize` | JWT | Batch optimization suggestions | | `POST` | `/api/ai/grade-suggest` | JWT | AI grading suggestion | ### 19.6 AI Service Configuration All AI services read configuration from `ir.config_parameter`: | Parameter Key | Description | Default | |--------------|-------------|---------| | `encoach.openai_api_key` | OpenAI API key | Required | | `encoach.openai_model` | Default model | `gpt-4o` | | `encoach.openai_model_light` | Light model for simple tasks | `gpt-3.5-turbo` | | `encoach.aws_access_key_id` | AWS access key | Required | | `encoach.aws_secret_access_key` | AWS secret key | Required | | `encoach.aws_region` | AWS region | `eu-west-1` | | `encoach.polly_voice_id` | Default Polly voice | `Joanna` | | `encoach.polly_engine` | Polly engine | `neural` | | `encoach.whisper_model` | Whisper model size | `base` | | `encoach.whisper_workers` | Whisper worker threads | `4` | | `encoach.elai_api_key` | ELAI API key | Required | | `encoach.gptzero_api_key` | GPTZero API key | Required | | `encoach.faiss_embedding_model` | Sentence transformer model | `all-MiniLM-L6-v2` | --- ## 20. Training Content ### 20.1 Models **`encoach.training.tip`** | Field | Type | Description | |-------|------|-------------| | `title` | Char | Tip title | | `content` | Text | Tip content | | `subject_id` | Many2one | Subject | | `module` | Char | Module tag | | `embedding` | Binary(attachment=False) | FAISS embedding vector | **`encoach.training.walkthrough`** | Field | Type | Description | |-------|------|-------------| | `title` | Char | Walkthrough title | | `steps` | Text (JSON) | Step-by-step content | | `module` | Char | Module tag | ### 20.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/training` | JWT | Training records (filter: `entity_id`, `user_id`, `module`) | | `GET` | `/api/training/tips` | JWT | Training tips | | `GET` | `/api/training/walkthrough` | JWT | Walkthroughs (filter: `module`) | --- ## 21. Analytics and Statistics ### 21.1 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/sessions` | JWT | Exam sessions | | `GET` | `/api/stats` | JWT | Exam statistics | | `GET` | `/api/statistical` | JWT | Aggregated statistical data | | `GET` | `/api/stats/performance` | JWT | Performance metrics | | `GET` | `/api/analytics/student` | JWT | Student analytics (filter: `student_id`, `subject_id`, `date_range`) | | `GET` | `/api/analytics/class` | JWT | Class analytics (filter: `batch_id`, `course_id`) | | `GET` | `/api/analytics/subject` | JWT | Subject analytics (filter: `subject_id`) | | `GET` | `/api/analytics/content-gaps` | JWT | Content gap analysis | --- ## 22. Subscription and Payments ### 22.1 Models **`encoach.package`** | Field | Type | Description | |-------|------|-------------| | `name` | Char | Package name | | `description` | Text | Description | | `price` | Float | Price | | `currency` | Char | Currency code | | `duration_days` | Integer | Access duration | | `subject_ids` | Many2many | Included subjects | | `features` | Text (JSON) | Feature list | | `active` | Boolean | Available for purchase | **`encoach.payment`** | Field | Type | Description | |-------|------|-------------| | `user_id` | Many2one | Paying user | | `entity_id` | Many2one | Entity (if institutional) | | `package_id` | Many2one | Package | | `amount` | Float | Payment amount | | `currency` | Char | Currency | | `provider` | Selection | `stripe`, `paypal`, `paymob`, `invoice` | | `status` | Selection | `pending`, `completed`, `failed`, `refunded` | | `provider_ref` | Char | External reference | | `created_at` | Datetime | Payment date | **`encoach.discount`** | Field | Type | Description | |-------|------|-------------| | `code` | Char | Discount code | | `percentage` | Float | Discount percentage | | `valid_from` | Datetime | Start date | | `valid_to` | Datetime | End date | | `max_uses` | Integer | Maximum uses | | `current_uses` | Integer | Current use count | ### 22.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/packages` | JWT | List packages | | `GET` | `/api/payments` | JWT | Paginated payments | | `POST` | `/api/stripe/checkout` | JWT | Create Stripe checkout session, return URL | | `POST` | `/api/paypal/checkout` | JWT | Create PayPal order, return approval URL | | `POST` | `/api/paymob/checkout` | JWT | Create Paymob payment, return URL | | `GET` | `/api/discounts` | JWT | List discounts | | `POST` | `/api/discounts` | JWT | Create discount | | `DELETE` | `/api/discounts/{id}` | JWT | Delete discount | ### 22.3 Webhook Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/stripe/webhook` | Webhook signature | Stripe payment events | | `POST` | `/api/paypal/webhook` | Webhook signature | PayPal payment events | | `POST` | `/api/paymob/webhook` | Webhook HMAC | Paymob transaction callback | --- ## 23. Support Tickets ### 23.1 Model (`encoach.ticket`) | Field | Type | Description | |-------|------|-------------| | `subject` | Char | Ticket subject | | `description` | Text | Description | | `type` | Selection | `bug`, `feature`, `support`, `feedback` | | `status` | Selection | `open`, `in_progress`, `resolved`, `closed` | | `reporter_id` | Many2one | Reporter user | | `assignee_id` | Many2one | Assigned staff | | `entity_id` | Many2one | Entity context | | `source` | Selection | `platform`, `webpage` | ### 23.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/tickets` | JWT | Paginated tickets (filter: `status`, `type`, `assignee_id`, `source`) | | `POST` | `/api/tickets` | JWT | Create ticket | | `PATCH` | `/api/tickets/{id}` | JWT | Update ticket | | `GET` | `/api/tickets/assignedToUser` | JWT | Tickets assigned to current user | --- ## 24. File Storage ### 24.1 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/storage/upload` | JWT | Upload file (multipart/form-data), return attachment ID and URL | | `GET` | `/api/storage/url` | JWT | Resolve download URL for filename | Files are stored as `ir.attachment` records. For production, configure Odoo to use S3-compatible storage. --- ## 25. Approval Workflows (Enhanced) ### 25.1 Model (`encoach.approval.workflow`) | Field | Type | Description | |-------|------|-------------| | `name` | Char | Workflow name | | `type` | Selection | `exam_publish`, `assignment_publish`, `content_publish`, `material_publish` | | `stage_ids` | One2many | Approval stages (ordered) | | `entity_id` | Many2one | Entity | | `allow_bypass` | Boolean | Whether bypass is allowed | | `active` | Boolean | Active flag | ### 25.2 Model (`encoach.approval.stage`) | Field | Type | Description | |-------|------|-------------| | `workflow_id` | Many2one | Parent workflow | | `sequence` | Integer | Stage order | | `approver_id` | Many2one | Assigned approver (`res.users`) | | `max_days` | Integer | Maximum days before auto-escalation | | `auto_escalate` | Boolean | Whether to auto-escalate on timeout | | `notification_email` | Char | Email for stage notifications | | `status` | Selection | `pending`, `approved`, `rejected`, `escalated`, `bypassed` | | `comment` | Text | Reviewer comment | | `acted_at` | Datetime | When action was taken | ### 25.3 Model (`encoach.approval.request`) | Field | Type | Description | |-------|------|-------------| | `workflow_id` | Many2one | Workflow template | | `res_model` | Char | Target model (e.g., `encoach.exam`) | | `res_id` | Integer | Target record ID | | `current_stage_id` | Many2one | Current active stage | | `requester_id` | Many2one | User who submitted for approval | | `state` | Selection | `in_progress`, `approved`, `rejected`, `bypassed` | | `bypass_reason` | Text | Justification if bypassed | | `created_at` | Datetime | Request creation time | ### 25.4 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/approval-workflows` | JWT | Paginated workflows | | `POST` | `/api/approval-workflows` | JWT | Create workflow with stages | | `GET` | `/api/approval-workflows/{id}` | JWT | Get workflow with stages | | `PATCH` | `/api/approval-workflows/{id}` | JWT | Update workflow and stages | | `DELETE` | `/api/approval-workflows/{id}` | JWT | Delete workflow | | `POST` | `/api/approval-requests` | JWT | Submit item for approval | | `GET` | `/api/approval-requests` | JWT | List approval requests (filter: state, type) | | `POST` | `/api/approval-requests/{id}/approve` | JWT | Approve current stage | | `POST` | `/api/approval-requests/{id}/reject` | JWT | Reject at current stage | | `POST` | `/api/approval-requests/{id}/bypass` | JWT | Bypass approval (requires justification) | | `POST` | `/api/approval-requests/{id}/return` | JWT | Return to specific stage | ### 25.5 Cron: Auto-Escalation A server cron runs daily to check approval stages that have exceeded `max_days`. For stages with `auto_escalate = True`, the system automatically advances to the next stage and sends a notification email to the new approver with escalation reason. --- ## 26. Courseware and Content Delivery **Module:** `encoach_courseware` **Dependencies:** `encoach_core`, `openeducat_core`, `encoach_ai` ### 26.1 Model (`encoach.course.chapter`) | Field | Type | Description | |-------|------|-------------| | `name` | Char (required) | Chapter title | | `course_id` | Many2one (`op.course`) | Parent course | | `sequence` | Integer | Display order | | `description` | Text | Chapter description | | `start_date` | Datetime | Scheduled start date | | `end_date` | Datetime | Optional end date | | `unlock_mode` | Selection | `auto_date`, `manual`, `prerequisite` | | `is_unlocked` | Boolean | Current unlock status | | `topic_id` | Many2one (`encoach.topic`) | Bridge to adaptive engine | | `material_ids` | One2many | Chapter materials | | `assignment_ids` | Many2many (`encoach.assignment`) | Linked assignments | | `exam_ids` | Many2many (`encoach.exam`) | Linked exams/quizzes | | `active` | Boolean | Soft-delete flag | ### 26.2 Model (`encoach.chapter.material`) | Field | Type | Description | |-------|------|-------------| | `name` | Char (required) | Material title | | `chapter_id` | Many2one | Parent chapter | | `type` | Selection | `pdf`, `document`, `video`, `audio`, `image`, `link`, `article` | | `file` | Binary (attachment=True) | Uploaded file | | `url` | Char | External URL | | `description` | Text | Description | | `sequence` | Integer | Display order | | `allow_download` | Boolean | Student download permission | | `is_book` | Boolean | Course book flag | | `book_chapters` | Text | JSON book chapter structure | | `active` | Boolean | Soft-delete flag | ### 26.3 Model (`encoach.chapter.progress`) | Field | Type | Description | |-------|------|-------------| | `student_id` | Many2one (`res.users`) | Student | | `chapter_id` | Many2one | Chapter | | `status` | Selection | `not_started`, `in_progress`, `completed` | | `started_at` | Datetime | First access time | | `completed_at` | Datetime | Completion time | | `materials_completed` | Integer | Count of materials viewed | | `materials_total` | Integer | Computed total materials | ### 26.4 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/courses/{courseId}/chapters` | JWT | List chapters (ordered by sequence) | | `POST` | `/api/courses/{courseId}/chapters` | JWT (teacher) | Create chapter | | `GET` | `/api/chapters/{id}` | JWT | Chapter detail with material count | | `PATCH` | `/api/chapters/{id}` | JWT (teacher) | Update chapter | | `DELETE` | `/api/chapters/{id}` | JWT (teacher) | Soft-delete chapter | | `POST` | `/api/chapters/{id}/unlock` | JWT (teacher) | Manually unlock | | `POST` | `/api/chapters/{id}/lock` | JWT (teacher) | Manually lock | | `PATCH` | `/api/courses/{courseId}/chapters/reorder` | JWT (teacher) | Reorder (body: `{ids: []}`) | | `GET` | `/api/chapters/{id}/progress` | JWT | Student progress | | `POST` | `/api/chapters/{id}/progress/complete` | JWT (student) | Mark chapter completed | | `GET` | `/api/chapters/{chapterId}/materials` | JWT | List materials | | `POST` | `/api/chapters/{chapterId}/materials` | JWT (teacher) | Upload material (multipart) | | `PATCH` | `/api/materials/{id}` | JWT (teacher) | Update material metadata | | `DELETE` | `/api/materials/{id}` | JWT (teacher) | Delete material | | `GET` | `/api/materials/{id}/download` | JWT | Download file (streaming response) | | `POST` | `/api/materials/{id}/viewed` | JWT (student) | Record material view | | `PATCH` | `/api/chapters/{chapterId}/materials/reorder` | JWT (teacher) | Reorder materials | ### 26.5 AI Workbench Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/workbench/generate-outline` | JWT (teacher) | AI generates course outline from requirements (topic, objectives, complexity) | | `POST` | `/api/workbench/generate-chapter` | JWT (teacher) | AI generates content for a specific chapter | | `POST` | `/api/workbench/generate-rubric` | JWT (teacher) | AI generates grading rubric | | `POST` | `/api/workbench/regenerate` | JWT (teacher) | Regenerate specific section | | `POST` | `/api/workbench/suggest-materials` | JWT (teacher) | AI suggests supplementary materials based on student performance profiles | | `POST` | `/api/workbench/publish` | JWT (teacher) | Publish generated content to course chapters (may trigger approval workflow) | AI Workbench uses GPT-4o (temp 0.7 for generation) and calls `encoach_ai` service layer. ### 26.6 Cron: Auto-Unlock Chapters Server cron runs every hour. For chapters with `unlock_mode = 'auto_date'` and `start_date <= now()` and `is_unlocked = False`, set `is_unlocked = True` and trigger notification. --- ## 27. Communication System **Module:** `encoach_communication` **Dependencies:** `encoach_core`, `openeducat_core` ### 27.1 Model (`encoach.discussion.board`) | Field | Type | Description | |-------|------|-------------| | `name` | Char (required) | Board title | | `course_id` | Many2one (`op.course`) | Associated course | | `batch_id` | Many2one (`op.batch`) | Associated batch (optional) | | `chapter_id` | Many2one (`encoach.course.chapter`) | Associated chapter (optional) | | `assignment_id` | Many2one | Associated assignment (optional) | | `is_enabled` | Boolean | Teacher toggle | | `allow_student_posts` | Boolean | Student thread creation permission | | `post_count` | Integer | Computed count of posts | ### 27.2 Model (`encoach.discussion.post`) | Field | Type | Description | |-------|------|-------------| | `board_id` | Many2one | Parent board | | `parent_id` | Many2one (self) | Parent post for threading | | `author_id` | Many2one (`res.users`) | Author | | `title` | Char | Thread title (root posts only) | | `content` | Text | Post body (markdown) | | `attachment_ids` | Many2many (`ir.attachment`) | File attachments | | `is_pinned` | Boolean | Teacher pin | | `is_resolved` | Boolean | Resolved flag (Q&A mode) | | `reply_count` | Integer | Computed child count | | `created_at` | Datetime | Post timestamp | ### 27.3 Model (`encoach.announcement`) | Field | Type | Description | |-------|------|-------------| | `title` | Char (required) | Announcement title | | `content` | Text | Body (markdown) | | `author_id` | Many2one (`res.users`) | Author | | `course_id` | Many2one (`op.course`) | Target course (null = system-wide) | | `batch_id` | Many2one (`op.batch`) | Target batch (null = all batches) | | `priority` | Selection | `normal`, `important`, `urgent` | | `is_published` | Boolean | Published status | | `published_at` | Datetime | Publish timestamp | | `expires_at` | Datetime | Expiry time | | `send_email` | Boolean | Also send via email | | `attachment_ids` | Many2many (`ir.attachment`) | Attachments | ### 27.4 Model (`encoach.message`) | Field | Type | Description | |-------|------|-------------| | `sender_id` | Many2one (`res.users`) | Sender | | `recipient_id` | Many2one (`res.users`) | Recipient | | `subject` | Char | Subject line | | `content` | Text | Message body | | `is_read` | Boolean | Read flag | | `read_at` | Datetime | Read timestamp | | `attachment_ids` | Many2many (`ir.attachment`) | Attachments | | `send_email_copy` | Boolean | Send email copy | | `created_at` | Datetime | Sent time | ### 27.5 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/discussion-boards` | JWT | List boards (filter: `course_id`, `batch_id`) | | `POST` | `/api/discussion-boards` | JWT (teacher) | Create board | | `PATCH` | `/api/discussion-boards/{id}` | JWT (teacher) | Update settings | | `GET` | `/api/discussion-boards/{id}/posts` | JWT | Paginated posts (threaded) | | `POST` | `/api/discussion-boards/{id}/posts` | JWT | Create post/reply | | `PATCH` | `/api/posts/{id}` | JWT | Update own post | | `DELETE` | `/api/posts/{id}` | JWT (teacher/admin) | Delete post | | `POST` | `/api/posts/{id}/pin` | JWT (teacher) | Pin/unpin toggle | | `POST` | `/api/posts/{id}/resolve` | JWT (teacher) | Mark as resolved | | `GET` | `/api/announcements` | JWT | List announcements (filter: `course_id`, `priority`) | | `POST` | `/api/announcements` | JWT (teacher/admin) | Create announcement | | `PATCH` | `/api/announcements/{id}` | JWT | Update | | `DELETE` | `/api/announcements/{id}` | JWT | Delete | | `POST` | `/api/announcements/{id}/publish` | JWT | Publish | | `GET` | `/api/messages` | JWT | Inbox (filter: `is_read`) | | `GET` | `/api/messages/sent` | JWT | Sent messages | | `POST` | `/api/messages` | JWT | Send message | | `GET` | `/api/messages/{id}` | JWT | Message detail (auto-marks read) | | `DELETE` | `/api/messages/{id}` | JWT | Delete message | | `GET` | `/api/messages/unread-count` | JWT | Unread count `{count: N}` | ### 27.6 Email Integration When `send_email = True` on announcements or `send_email_copy = True` on messages, the module uses Odoo's `mail.thread` mixin to send emails through the configured outgoing mail server. --- ## 28. Notification Engine **Module:** `encoach_notification` **Dependencies:** `encoach_core` ### 28.1 Model (`encoach.notification`) | Field | Type | Description | |-------|------|-------------| | `user_id` | Many2one (`res.users`) | Recipient | | `title` | Char | Notification title | | `message` | Text | Body | | `type` | Selection | `deadline`, `chapter_unlock`, `result_release`, `announcement`, `assignment`, `exam`, `message`, `system` | | `action_url` | Char | Frontend URL to navigate to | | `is_read` | Boolean | Read status | | `read_at` | Datetime | Read timestamp | | `channel` | Selection | `in_app`, `email`, `both` | | `created_at` | Datetime | Created time | ### 28.2 Model (`encoach.notification.rule`) | Field | Type | Description | |-------|------|-------------| | `name` | Char | Rule name | | `event_type` | Selection | `assignment_due`, `exam_due`, `chapter_unlock`, `result_release`, `submission_graded`, `extension_response` | | `days_before` | Integer | Days before event to trigger | | `frequency` | Selection | `once`, `daily`, `custom` | | `custom_intervals` | Text (JSON) | Custom intervals array (e.g., `[7, 3, 1]`) | | `channel` | Selection | `in_app`, `email`, `both` | | `entity_id` | Many2one | Entity scope | | `active` | Boolean | Active flag | ### 28.3 Model (`encoach.notification.preferences`) | Field | Type | Description | |-------|------|-------------| | `user_id` | Many2one (`res.users`) | One per user | | `email_enabled` | Boolean | Master email toggle | | `assignment_alerts` | Boolean | Assignment notifications | | `exam_alerts` | Boolean | Exam notifications | | `chapter_alerts` | Boolean | Chapter unlock notifications | | `announcement_alerts` | Boolean | Announcement notifications | | `message_alerts` | Boolean | Message notifications | ### 28.4 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/notifications` | JWT | Paginated list (filter: `type`, `is_read`) | | `POST` | `/api/notifications/{id}/read` | JWT | Mark as read | | `POST` | `/api/notifications/read-all` | JWT | Mark all as read | | `GET` | `/api/notifications/unread-count` | JWT | `{count: N}` | | `GET` | `/api/notification-rules` | JWT (admin) | List rules | | `POST` | `/api/notification-rules` | JWT (admin) | Create rule | | `PATCH` | `/api/notification-rules/{id}` | JWT (admin) | Update rule | | `DELETE` | `/api/notification-rules/{id}` | JWT (admin) | Delete rule | | `GET` | `/api/notification-preferences` | JWT | Get user preferences | | `PATCH` | `/api/notification-preferences` | JWT | Update preferences | ### 28.5 Crons 1. **Deadline Reminders:** Runs daily. Checks all active rules, matches upcoming events (assignments, exams) within `days_before`, creates notifications and sends emails per user preferences. 2. **Chapter Unlock Notifications:** Triggered by the courseware cron when chapters are auto-unlocked. --- ## 29. FAQ System **Module:** `encoach_faq` **Dependencies:** `encoach_core` ### 29.1 Model (`encoach.faq.category`) | Field | Type | Description | |-------|------|-------------| | `name` | Char | Category name | | `sequence` | Integer | Display order | | `icon` | Char | Icon identifier | | `audience` | Selection | `student`, `entity`, `both` | | `active` | Boolean | Active flag | ### 29.2 Model (`encoach.faq.item`) | Field | Type | Description | |-------|------|-------------| | `question` | Char | Question text | | `answer` | Text | Answer (markdown, embedded images/video) | | `category_id` | Many2one | Parent category | | `audience` | Selection | `student`, `entity`, `both` | | `image` | Binary | Optional image | | `video_url` | Char | Optional video URL | | `sequence` | Integer | Display order | | `active` | Boolean | Active flag | ### 29.3 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `GET` | `/api/faq/categories` | Public/JWT | List categories (filter: `audience`) | | `POST` | `/api/faq/categories` | JWT (admin) | Create category | | `PATCH` | `/api/faq/categories/{id}` | JWT (admin) | Update category | | `DELETE` | `/api/faq/categories/{id}` | JWT (admin) | Delete category | | `GET` | `/api/faq/items` | Public/JWT | List items (filter: `category_id`, `audience`, `search`) | | `POST` | `/api/faq/items` | JWT (admin) | Create item | | `PATCH` | `/api/faq/items/{id}` | JWT (admin) | Update item | | `DELETE` | `/api/faq/items/{id}` | JWT (admin) | Delete item | --- ## 30. Plagiarism Detection **Module enhancement:** `encoach_ai_grading` (add plagiarism sub-service) ### 30.1 Plagiarism Report Storage Add to `encoach.submission` or create `encoach.plagiarism.report`: | Field | Type | Description | |-------|------|-------------| | `submission_id` | Many2one | Target submission | | `overall_score` | Float | AI probability score (0-1) | | `per_sentence` | Text (JSON) | Per-sentence analysis from GPTZero | | `report_pdf` | Binary | Generated PDF report | | `checked_at` | Datetime | When check was run | ### 30.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/plagiarism/check` | JWT (teacher) | Submit text for GPTZero analysis | | `GET` | `/api/plagiarism/report/{submissionId}` | JWT | Get plagiarism report | | `GET` | `/api/plagiarism/report/{submissionId}/download` | JWT | Download PDF report | ### 30.3 Integration - Uses GPTZero `v2/predict/text` endpoint (already configured in `encoach_ai_grading`) - Can be enabled/disabled per assignment via `plagiarism_enabled` boolean on `encoach.assignment` - Configurable max submissions for plagiarism check via `plagiarism_max_checks` integer --- ## 31. Official Exam Access **Module enhancement:** `encoach_exam` (add official access modes) ### 31.1 Model Extensions Add to `encoach.exam`: | Field | Type | Description | |-------|------|-------------| | `is_exercise` | Boolean | Exercise (practice, no journey tracking) vs Exam | | `is_official` | Boolean | Official exam flag | | `access_mode` | Selection | `normal`, `link`, `landing_page`, `separate_login` | | `access_token` | Char | Unique access token (auto-generated) | | `access_code` | Char | Exam access code | | `activation_start` | Datetime | Exam start time | | `activation_end` | Datetime | Exam end time | | `generate_unique_per_student` | Boolean | AI generates unique variant per student | | `reminder_count` | Integer | Number of reminders to send | | `reminder_frequency` | Selection | `once`, `daily`, `custom` | ### 31.2 API Endpoints | Method | Path | Auth | Description | |--------|------|------|-------------| | `POST` | `/api/exam/{id}/generate-link` | JWT (teacher) | Generate unique access token/URL | | `GET` | `/api/exam/access/{token}` | Public | Access exam via token (validates token, returns exam info) | | `POST` | `/api/exam/official-login` | Public | Login with exam code + credentials | | `POST` | `/api/exam/{id}/suspend` | JWT (teacher) | Suspend exam | | `POST` | `/api/exam/{id}/revoke` | JWT (teacher) | Revoke exam access | --- ## 32. Non-Functional Requirements ### 32.1 Performance | ID | Requirement | |----|-------------| | NFR-PERF-01 | API response time < 500ms for CRUD endpoints | | NFR-PERF-02 | AI endpoints may take up to 30s (use async job for long operations) | | NFR-PERF-03 | Pagination: default `size=20`, max `size=100` | ### 32.2 Security | ID | Requirement | |----|-------------| | NFR-SEC-01 | JWT tokens expire after 24 hours | | NFR-SEC-02 | All endpoints validate entity-scoped permissions | | NFR-SEC-03 | Rate limiting on AI endpoints (configurable) | | NFR-SEC-04 | CORS configured for frontend origin | | NFR-SEC-05 | File upload size limit: 50MB | | NFR-SEC-06 | API keys stored in `ir.config_parameter`, never in code | ### 32.3 Scalability | ID | Requirement | |----|-------------| | NFR-SCALE-01 | Support 10,000+ concurrent students | | NFR-SCALE-02 | Support 50+ subjects, 500+ topics per subject, 1000+ resources per subject | | NFR-SCALE-03 | Database indices on all foreign keys and frequently filtered fields | ### 32.4 Reliability | ID | Requirement | |----|-------------| | NFR-REL-01 | AI service failures return graceful error (never 500) | | NFR-REL-02 | Webhook processing is idempotent | | NFR-REL-03 | Soft-delete for resources (preserve completion records) | ### 32.5 API Conventions | Convention | Detail | |-----------|--------| | Date format | ISO 8601 (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`) | | Pagination | `?page=1&size=20` returns `{ "items": [...], "total": N, "page": 1, "size": 20 }` | | Error shape | `{ "error": "message", "code": "ERROR_CODE" }` with HTTP status | | ID fields | Many2one serialized as `{field}_id` (int) + `{field}_name` (str) | | List fields | Many2many serialized as `{field}_ids` (int[]) + `{field}_names` (str[]) | | Binary fields | Base64-encoded strings or separate download endpoint | | Auth header | `Authorization: Bearer ` | --- ## 33. Implementation Priority | Priority | Modules | Rationale | |----------|---------|-----------| | **P0** | `encoach_core`, `encoach_api` (auth, users, entities, permissions) | Foundation — blocks everything | | **P0** | `openeducat_core` (port to v19), `encoach_lms_api` (courses, batches, basic serialization) | LMS foundation | | **P1** | `encoach_taxonomy`, `encoach_adaptive`, `encoach_adaptive_api`, `encoach_adaptive_ai` | Core adaptive learning loop | | **P1** | `encoach_exam`, `encoach_ai_generation`, `encoach_ai_grading` | Exam engine | | **P1** | `encoach_resources` | Learning content | | **P2** | `encoach_courseware` (chapters, materials, progress, AI workbench) | Structured course delivery | | **P2** | `encoach_communication` (discussions, announcements, messaging) | Collaboration | | **P2** | `openeducat_timetable`, `openeducat_attendance`, `openeducat_classroom` (port to v19) | LMS timetable/attendance | | **P2** | `openeducat_admission` (port to v19) + admission endpoints | Enrollment workflow | | **P2** | Academic years/terms, departments endpoints | Institutional calendar | | **P2** | `encoach_ai`, `encoach_ai_media` | AI services + media | | **P2** | `encoach_assignment`, `encoach_classroom` | Assignments + groups | | **P3** | `encoach_notification` (rules, email delivery, preferences) | User engagement | | **P3** | Enhanced approval workflows (stages, escalation, bypass) | Governance | | **P3** | Plagiarism detection (GPTZero integration) | Academic integrity | | **P3** | Official exam access modes (link, landing, separate login) | Exam security | | **P3** | Late submission handling, extension requests | Assignment workflow | | **P3** | `openeducat_exam` (port to v19) + institutional exam endpoints | Institutional assessment | | **P3** | `openeducat_assignment` (port to v19) + submission endpoints | Homework workflow | | **P3** | Subject registration endpoints | Student subject selection | | **P3** | `encoach_training`, analytics endpoints | Training + analytics | | **P3** | `encoach_sis`, `encoach_branding` | University integration | | **P4** | `encoach_faq` (categories, items, role-filtered) | User support | | **P4** | `encoach_subscription` (Stripe, PayPal, Paymob) | Payments | | **P4** | `encoach_ticket` | Support | | **P4** | Storage | Operational features | --- ## Appendix A: Complete Endpoint Summary Total API endpoints: **280+** | Service Area | Endpoint Count | |-------------|---------------| | Auth & Users | 10 | | Entities & Permissions | 9 | | LMS (Courses, Batches, Timetable, Attendance, Grades) | 16 | | Academic Years, Terms, Departments | 14 | | Admissions | 12 | | Subject Registration | 6 | | EnCoach Exams (AI) | 15 | | Institutional Exams | 21 | | EnCoach Assignments | 7 | | Course Assignments & Submissions | 11 | | Classrooms & Groups | 7 | | Taxonomy | 16 | | Adaptive Learning | 17 | | Resources | 7 | | AI Coaching | 6 | | AI Evaluation | 4 | | AI Generation | 2 | | AI Media | 3 | | AI Analytics | 6 | | Training | 3 | | Statistics | 4 | | Subscriptions & Payments | 8 + 3 webhooks | | Tickets | 4 | | Storage | 2 | | Approval Workflows (Enhanced) | 11 | | **Courseware (Chapters + Materials + Workbench)** | **23** | | **Discussion Boards + Posts** | **9** | | **Announcements** | **5** | | **Messaging** | **6** | | **Notifications + Rules + Preferences** | **10** | | **FAQ (Categories + Items)** | **8** | | **Plagiarism Detection** | **3** | | **Official Exam Access** | **5** | --- ## Appendix B: Model Relationship Diagram ``` res.users ──extends──> encoach.user │ │ │ ├── entity_ids ──> encoach.entity │ │ └── role_ids ──> encoach.role │ │ └── permission_ids ──> encoach.permission │ │ │ ├── op_student_id ──> op.student ──> op.student.course ──> op.course │ │ ├── op.batch │ │ ├── op.subject │ │ └── op.department │ │ │ └── op_faculty_id ──> op.faculty │ ├── encoach.exam ──> encoach.subject (taxonomy) │ ├── encoach.domain │ │ └── encoach.topic │ │ ├── encoach.learning.objective │ │ ├── encoach.proficiency │ │ └── encoach.resource │ │ │ └── encoach.learning.plan │ └── encoach.learning.plan.item ──> encoach.topic │ ├── op.academic.year ──> op.academic.term │ ├── op.admission.register ──> op.admission ──> op.student │ ├── op.exam.session ──> op.exam ──> op.exam.attendees │ └── op.marksheet.register ──> op.marksheet.line │ ├── op.assignment ──> op.assignment.sub.line (submissions) │ ├── encoach.course.chapter ──> op.course │ ├── encoach.chapter.material │ ├── encoach.chapter.progress ──> res.users │ └── encoach.topic (bridge to adaptive) │ ├── encoach.discussion.board ──> op.course / op.batch / encoach.course.chapter │ └── encoach.discussion.post (self-referencing for threads) │ ├── encoach.announcement ──> op.course / op.batch │ ├── encoach.message ──> res.users (sender/recipient) │ ├── encoach.notification ──> res.users │ └── encoach.notification.rule (event triggers) │ ├── encoach.faq.category ──> encoach.faq.item │ ├── encoach.approval.workflow ──> encoach.approval.stage │ └── encoach.approval.request │ └── encoach.plagiarism.report ──> submission ``` --- *This SRS is the definitive backend specification for the EnCoach platform on Odoo 19. It covers all 280+ REST API endpoints expected by the React frontend, 35 Odoo modules (8 OpenEduCat + 27 custom), and all AI service integrations.*